aboutsummaryrefslogtreecommitdiffstats
path: root/1.2-netsec/apps
diff options
context:
space:
mode:
Diffstat (limited to '1.2-netsec/apps')
-rw-r--r--1.2-netsec/apps/Makefile133
-rw-r--r--1.2-netsec/apps/app_adsiprog.c1606
-rw-r--r--1.2-netsec/apps/app_alarmreceiver.c867
-rw-r--r--1.2-netsec/apps/app_authenticate.c232
-rw-r--r--1.2-netsec/apps/app_cdr.c97
-rw-r--r--1.2-netsec/apps/app_chanisavail.c185
-rw-r--r--1.2-netsec/apps/app_chanspy.c587
-rw-r--r--1.2-netsec/apps/app_controlplayback.c190
-rw-r--r--1.2-netsec/apps/app_curl.c256
-rw-r--r--1.2-netsec/apps/app_cut.c473
-rw-r--r--1.2-netsec/apps/app_datetime.c101
-rw-r--r--1.2-netsec/apps/app_db.c323
-rw-r--r--1.2-netsec/apps/app_dial.c1748
-rw-r--r--1.2-netsec/apps/app_dictate.c358
-rw-r--r--1.2-netsec/apps/app_directed_pickup.c169
-rw-r--r--1.2-netsec/apps/app_directory.c516
-rw-r--r--1.2-netsec/apps/app_disa.c412
-rw-r--r--1.2-netsec/apps/app_dumpchan.c192
-rw-r--r--1.2-netsec/apps/app_echo.c123
-rw-r--r--1.2-netsec/apps/app_enumlookup.c270
-rw-r--r--1.2-netsec/apps/app_eval.c127
-rw-r--r--1.2-netsec/apps/app_exec.c135
-rw-r--r--1.2-netsec/apps/app_externalivr.c583
-rw-r--r--1.2-netsec/apps/app_festival.c532
-rw-r--r--1.2-netsec/apps/app_flash.c141
-rw-r--r--1.2-netsec/apps/app_forkcdr.c131
-rw-r--r--1.2-netsec/apps/app_getcpeid.c167
-rw-r--r--1.2-netsec/apps/app_groupcount.c340
-rw-r--r--1.2-netsec/apps/app_hasnewvoicemail.c264
-rw-r--r--1.2-netsec/apps/app_ices.c225
-rw-r--r--1.2-netsec/apps/app_image.c147
-rw-r--r--1.2-netsec/apps/app_intercom.c234
-rw-r--r--1.2-netsec/apps/app_ivrdemo.c144
-rw-r--r--1.2-netsec/apps/app_lookupblacklist.c141
-rw-r--r--1.2-netsec/apps/app_lookupcidname.c120
-rw-r--r--1.2-netsec/apps/app_macro.c396
-rw-r--r--1.2-netsec/apps/app_math.c297
-rw-r--r--1.2-netsec/apps/app_md5.c205
-rw-r--r--1.2-netsec/apps/app_meetme.c2251
-rw-r--r--1.2-netsec/apps/app_milliwatt.c170
-rw-r--r--1.2-netsec/apps/app_mixmonitor.c466
-rw-r--r--1.2-netsec/apps/app_mp3.c260
-rw-r--r--1.2-netsec/apps/app_nbscat.c241
-rw-r--r--1.2-netsec/apps/app_osplookup.c375
-rw-r--r--1.2-netsec/apps/app_page.c234
-rw-r--r--1.2-netsec/apps/app_parkandannounce.c281
-rw-r--r--1.2-netsec/apps/app_playback.c180
-rw-r--r--1.2-netsec/apps/app_privacy.c254
-rw-r--r--1.2-netsec/apps/app_queue.c3872
-rw-r--r--1.2-netsec/apps/app_random.c127
-rw-r--r--1.2-netsec/apps/app_read.c235
-rw-r--r--1.2-netsec/apps/app_readfile.c144
-rw-r--r--1.2-netsec/apps/app_realtime.c262
-rw-r--r--1.2-netsec/apps/app_record.c377
-rw-r--r--1.2-netsec/apps/app_rpt.c6560
-rw-r--r--1.2-netsec/apps/app_sayunixtime.c163
-rw-r--r--1.2-netsec/apps/app_senddtmf.c129
-rw-r--r--1.2-netsec/apps/app_sendtext.c156
-rw-r--r--1.2-netsec/apps/app_setcallerid.c178
-rw-r--r--1.2-netsec/apps/app_setcdruserfield.c179
-rw-r--r--1.2-netsec/apps/app_setcidname.c132
-rw-r--r--1.2-netsec/apps/app_setcidnum.c131
-rw-r--r--1.2-netsec/apps/app_setrdnis.c132
-rw-r--r--1.2-netsec/apps/app_settransfercapability.c145
-rw-r--r--1.2-netsec/apps/app_skel.c149
-rw-r--r--1.2-netsec/apps/app_sms.c1555
-rw-r--r--1.2-netsec/apps/app_softhangup.c139
-rw-r--r--1.2-netsec/apps/app_sql_postgres.c577
-rw-r--r--1.2-netsec/apps/app_stack.c193
-rw-r--r--1.2-netsec/apps/app_system.c176
-rw-r--r--1.2-netsec/apps/app_talkdetect.c250
-rw-r--r--1.2-netsec/apps/app_test.c529
-rw-r--r--1.2-netsec/apps/app_transfer.c182
-rw-r--r--1.2-netsec/apps/app_txtcidname.c164
-rw-r--r--1.2-netsec/apps/app_url.c196
-rw-r--r--1.2-netsec/apps/app_userevent.c137
-rw-r--r--1.2-netsec/apps/app_verbose.c135
-rw-r--r--1.2-netsec/apps/app_voicemail.c6757
-rw-r--r--1.2-netsec/apps/app_waitforring.c154
-rw-r--r--1.2-netsec/apps/app_waitforsilence.c210
-rw-r--r--1.2-netsec/apps/app_while.c369
-rw-r--r--1.2-netsec/apps/app_zapateller.c138
-rw-r--r--1.2-netsec/apps/app_zapbarge.c335
-rw-r--r--1.2-netsec/apps/app_zapras.c276
-rw-r--r--1.2-netsec/apps/app_zapscan.c399
-rw-r--r--1.2-netsec/apps/enter.h287
-rw-r--r--1.2-netsec/apps/leave.h207
-rw-r--r--1.2-netsec/apps/rpt_flow.pdfbin0 -> 51935 bytes
88 files changed, 44315 insertions, 0 deletions
diff --git a/1.2-netsec/apps/Makefile b/1.2-netsec/apps/Makefile
new file mode 100644
index 000000000..ca7f86e7b
--- /dev/null
+++ b/1.2-netsec/apps/Makefile
@@ -0,0 +1,133 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for PBX applications
+#
+# Copyright (C) 1999-2005, Digium
+#
+# Mark Spencer <markster@digium.com>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_mp3.so\
+ app_system.so app_echo.so app_record.so app_image.so app_url.so app_disa.so \
+ app_adsiprog.so app_getcpeid.so app_milliwatt.so \
+ app_zapateller.so app_setcallerid.so app_festival.so \
+ app_queue.so app_senddtmf.so app_parkandannounce.so \
+ app_setcidname.so app_lookupcidname.so app_macro.so \
+ app_authenticate.so app_softhangup.so app_lookupblacklist.so \
+ app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \
+ app_enumlookup.so app_transfer.so app_setcidnum.so app_cdr.so \
+ app_hasnewvoicemail.so app_sayunixtime.so app_cut.so app_read.so \
+ app_setcdruserfield.so app_random.so app_ices.so app_eval.so \
+ app_nbscat.so app_sendtext.so app_exec.so \
+ app_groupcount.so app_txtcidname.so app_controlplayback.so \
+ app_talkdetect.so app_alarmreceiver.so app_userevent.so app_verbose.so \
+ app_test.so app_forkcdr.so app_math.so app_realtime.so \
+ app_dumpchan.so app_waitforsilence.so app_while.so app_setrdnis.so \
+ app_md5.so app_readfile.so app_chanspy.so app_settransfercapability.so \
+ app_dictate.so app_externalivr.so app_directed_pickup.so \
+ app_mixmonitor.so app_stack.so
+
+#
+# Obsolete things...
+#
+#APPS+=app_sql_postgres.so
+#APPS+=app_sql_odbc.so
+
+#
+# Experimental things
+#
+#APPS+=app_ivrdemo.so
+#APPS+=app_skel.so
+#APPS+=app_rpt.so
+
+ifndef WITHOUT_ZAPTEL
+ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h),)
+ APPS+=app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so app_page.so
+endif
+endif # WITHOUT_ZAPTEL
+
+ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/osp/osp.h $(CROSS_COMPILE_TARGET)/usr/include/osp/osp.h),)
+APPS+=app_osplookup.so
+endif
+
+ifeq ($(findstring BSD,${OSARCH}),BSD)
+CFLAGS+=-I$(CROSS_COMPILE_TARGET)/usr/local/include -L$(CROSS_COMPILE_TARGET)/usr/local/lib
+endif
+
+CURLLIBS=$(shell $(CROSS_COMPILE_BIN)curl-config --libs)
+ifneq ($(shell if [[ 0x`$(CROSS_COMPILE_BIN)curl-config --vernum` -ge 0x70907 ]]; then echo "OK" ; fi),)
+ ifneq (${CURLLIBS},)
+ APPS+=app_curl.so
+ endif
+endif
+
+ifeq (${OSARCH},CYGWIN)
+CYGSOLINK=-Wl,--out-implib=lib$@.a -Wl,--export-all-symbols
+CYGSOLIB=-L.. -L. -L../res -lasterisk.dll -lres_features.so -lres_adsi.so -lres_monitor.so
+else
+CFLAGS+=-fPIC
+APPS+=app_sms.so
+endif
+
+#
+# If you have UnixODBC you can use ODBC voicemail
+# storage
+#
+# Uncomment to use ODBC storage
+#CFLAGS+=-DUSE_ODBC_STORAGE
+# Uncomment for extended ODBC voicemail storage
+#CFLAGS+=-DEXTENDED_ODBC_STORAGE
+# See doc/README.odbcstorage for more information
+
+all: $(APPS)
+
+clean:
+ rm -f *.so *.o look .depend
+
+%.so : %.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB}
+
+app_rpt.so : app_rpt.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -ltonezone
+
+install: all
+ for x in $(APPS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done
+ rm -f $(DESTDIR)$(MODULES_DIR)/app_datetime.so
+ rm -f $(DESTDIR)$(MODULES_DIR)/app_qcall.so
+
+app_curl.so: app_curl.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(CURLLIBS)
+
+app_sql_postgres.o: app_sql_postgres.c
+ $(CC) -pipe -I$(CROSS_COMPILE_TARGET)/usr/local/pgsql/include -I$(CROSS_COMPILE_TARGET)/usr/include/postgresql $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c
+
+app_sql_postgres.so: app_sql_postgres.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -L/usr/local/pgsql/lib -lpq
+
+app_sql_odbc.so: app_sql_odbc.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc
+
+look: look.c
+ $(CC) -pipe -O6 -g look.c -o look -lncurses
+
+ifeq (SunOS,$(shell uname))
+app_chanspy.so: app_chanspy.o
+ $(CC) $(SOLINK) -o $@ $< -lrt
+endif
+
+
+ifneq ($(wildcard .depend),)
+ include .depend
+endif
+
+depend: .depend
+
+.depend:
+ ../build_tools/mkdep $(CFLAGS) `ls *.c`
+
+env:
+ env
diff --git a/1.2-netsec/apps/app_adsiprog.c b/1.2-netsec/apps/app_adsiprog.c
new file mode 100644
index 000000000..79fe30122
--- /dev/null
+++ b/1.2-netsec/apps/app_adsiprog.c
@@ -0,0 +1,1606 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Program Asterisk ADSI Scripts into phone
+ *
+ * \ingroup applications
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+static char *tdesc = "Asterisk ADSI Programming Application";
+
+static char *app = "ADSIProg";
+
+static char *synopsis = "Load Asterisk ADSI Scripts into phone";
+
+/* #define DUMP_MESSAGES */
+
+static char *descrip =
+" ADSIProg(script): This application programs an ADSI Phone with the given\n"
+"script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+struct adsi_event {
+ int id;
+ char *name;
+};
+
+static struct adsi_event events[] = {
+ { 1, "CALLERID" },
+ { 2, "VMWI" },
+ { 3, "NEARANSWER" },
+ { 4, "FARANSWER" },
+ { 5, "ENDOFRING" },
+ { 6, "IDLE" },
+ { 7, "OFFHOOK" },
+ { 8, "CIDCW" },
+ { 9, "BUSY" },
+ { 10, "FARRING" },
+ { 11, "DIALTONE" },
+ { 12, "RECALL" },
+ { 13, "MESSAGE" },
+ { 14, "REORDER" },
+ { 15, "DISTINCTIVERING" },
+ { 16, "RING" },
+ { 17, "REMINDERRING" },
+ { 18, "SPECIALRING" },
+ { 19, "CODEDRING" },
+ { 20, "TIMER" },
+ { 21, "INUSE" },
+ { 22, "EVENT22" },
+ { 23, "EVENT23" },
+ { 24, "CPEID" },
+};
+
+static struct adsi_event justify[] = {
+ { 0, "CENTER" },
+ { 1, "RIGHT" },
+ { 2, "LEFT" },
+ { 3, "INDENT" },
+};
+
+#define STATE_NORMAL 0
+#define STATE_INKEY 1
+#define STATE_INSUB 2
+#define STATE_INIF 3
+
+#define MAX_RET_CODE 20
+#define MAX_SUB_LEN 255
+#define MAX_MAIN_LEN 1600
+
+#define ARG_STRING (1 << 0)
+#define ARG_NUMBER (1 << 1)
+
+struct adsi_soft_key {
+ char vname[40]; /* Which "variable" is associated with it */
+ int retstrlen; /* Length of return string */
+ int initlen; /* initial length */
+ int id;
+ int defined;
+ char retstr[80]; /* Return string data */
+};
+
+struct adsi_subscript {
+ char vname[40];
+ int id;
+ int defined;
+ int datalen;
+ int inscount;
+ int ifinscount;
+ char *ifdata;
+ char data[2048];
+};
+
+struct adsi_state {
+ char vname[40];
+ int id;
+};
+
+struct adsi_flag {
+ char vname[40];
+ int id;
+};
+
+struct adsi_display {
+ char vname[40];
+ int id;
+ char data[70];
+ int datalen;
+};
+
+struct adsi_script {
+ int state;
+ int numkeys;
+ int numsubs;
+ int numstates;
+ int numdisplays;
+ int numflags;
+ struct adsi_soft_key *key;
+ struct adsi_subscript *sub;
+ /* Pre-defined displays */
+ struct adsi_display displays[63];
+ /* ADSI States 1 (initial) - 254 */
+ struct adsi_state states[256];
+ /* Keys 2-63 */
+ struct adsi_soft_key keys[62];
+ /* Subscripts 0 (main) to 127 */
+ struct adsi_subscript subs[128];
+ /* Flags 1-7 */
+ struct adsi_flag flags[7];
+
+ /* Stuff from adsi script */
+ unsigned char sec[5];
+ char desc[19];
+ unsigned char fdn[5];
+ int ver;
+};
+
+
+static int process_token(void *out, char *src, int maxlen, int argtype)
+{
+ if ((strlen(src) > 1) && src[0] == '\"') {
+ /* This is a quoted string */
+ if (!(argtype & ARG_STRING))
+ return -1;
+ src++;
+ /* Don't take more than what's there */
+ if (maxlen > strlen(src) - 1)
+ maxlen = strlen(src) - 1;
+ memcpy(out, src, maxlen);
+ ((char *)out)[maxlen] = '\0';
+ } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
+ if (!(argtype & ARG_NUMBER))
+ return -1;
+ /* Octal value */
+ if (sscanf(src, "%o", (int *)out) != 1)
+ return -1;
+ if (argtype & ARG_STRING) {
+ /* Convert */
+ *((unsigned int *)out) = htonl(*((unsigned int *)out));
+ }
+ } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
+ if (!(argtype & ARG_NUMBER))
+ return -1;
+ /* Hex value */
+ if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
+ return -1;
+ if (argtype & ARG_STRING) {
+ /* Convert */
+ *((unsigned int *)out) = htonl(*((unsigned int *)out));
+ }
+ } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
+ if (!(argtype & ARG_NUMBER))
+ return -1;
+ /* Hex value */
+ if (sscanf(src, "%d", (int *)out) != 1)
+ return -1;
+ if (argtype & ARG_STRING) {
+ /* Convert */
+ *((unsigned int *)out) = htonl(*((unsigned int *)out));
+ }
+ } else
+ return -1;
+ return 0;
+}
+
+static char *get_token(char **buf, char *script, int lineno)
+{
+ char *tmp = *buf;
+ char *keyword;
+ int quoted = 0;
+ /* Advance past any white space */
+ while(*tmp && (*tmp < 33))
+ tmp++;
+ if (!*tmp)
+ return NULL;
+ keyword = tmp;
+ while(*tmp && ((*tmp > 32) || quoted)) {
+ if (*tmp == '\"') {
+ quoted = !quoted;
+ }
+ tmp++;
+ }
+ if (quoted) {
+ ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ *tmp = '\0';
+ tmp++;
+ while(*tmp && (*tmp < 33))
+ tmp++;
+ /* Note where we left off */
+ *buf = tmp;
+ return keyword;
+}
+
+static char *validdtmf = "123456789*0#ABCD";
+
+static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char dtmfstr[80];
+ char *a;
+ int bytes=0;
+ a = get_token(&args, script, lineno);
+ if (!a) {
+ ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ a = dtmfstr;
+ while(*a) {
+ if (strchr(validdtmf, *a)) {
+ *buf = *a;
+ buf++;
+ bytes++;
+ } else
+ ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
+ a++;
+ }
+ return bytes;
+}
+
+static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *page;
+ char *gline;
+ int line;
+ unsigned char cmd;
+ page = get_token(&args, script, lineno);
+ gline = get_token(&args, script, lineno);
+ if (!page || !gline) {
+ ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (!strcasecmp(page, "INFO")) {
+ cmd = 0;
+ } else if (!strcasecmp(page, "COMM")) {
+ cmd = 0x80;
+ } else {
+ ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
+ return 0;
+ }
+ if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
+ return 0;
+ }
+ cmd |= line;
+ buf[0] = 0x8b;
+ buf[1] = cmd;
+ return 2;
+}
+
+static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *dir;
+ char *gline;
+ int line;
+ unsigned char cmd;
+ dir = get_token(&args, script, lineno);
+ gline = get_token(&args, script, lineno);
+ if (!dir || !gline) {
+ ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (!strcasecmp(dir, "UP")) {
+ cmd = 0;
+ } else if (!strcasecmp(dir, "DOWN")) {
+ cmd = 0x20;
+ } else {
+ ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
+ return 0;
+ }
+ if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
+ return 0;
+ }
+ cmd |= line;
+ buf[0] = 0x8c;
+ buf[1] = cmd;
+ return 2;
+}
+
+static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *gtime;
+ int ms;
+ gtime = get_token(&args, script, lineno);
+ if (!gtime) {
+ ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
+ return 0;
+ }
+ buf[0] = 0x90;
+ if (id == 11)
+ buf[1] = ms / 100;
+ else
+ buf[1] = ms / 10;
+ return 2;
+}
+
+static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *gstate;
+ int state;
+ gstate = get_token(&args, script, lineno);
+ if (!gstate) {
+ ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = state;
+ return 2;
+}
+
+static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ /* For some reason the clear code is different slightly */
+ if (id == 7)
+ buf[1] = 0x10;
+ else
+ buf[1] = 0x00;
+ return 2;
+}
+
+static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
+{
+ int x;
+ for (x=0;x<state->numflags;x++)
+ if (!strcasecmp(state->flags[x].vname, name))
+ return &state->flags[x];
+ /* Return now if we're not allowed to create */
+ if (!create)
+ return NULL;
+ if (state->numflags > 6) {
+ ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
+ state->flags[state->numflags].id = state->numflags + 1;
+ state->numflags++;
+ return &state->flags[state->numflags-1];
+}
+
+static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char sname[80];
+ struct adsi_flag *flag;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ flag = getflagbyname(state, sname, script, lineno, 0);
+ if (!flag) {
+ ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = ((flag->id & 0x7) << 4) | 1;
+ return 2;
+}
+
+static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ struct adsi_flag *flag;
+ char sname[80];
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ flag = getflagbyname(state, sname, script, lineno, 0);
+ if (!flag) {
+ ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = ((flag->id & 0x7) << 4);
+ return 2;
+}
+
+static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ int secs;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = 0x1;
+ buf[2] = secs;
+ return 3;
+}
+
+static int geteventbyname(char *name)
+{
+ int x;
+ for (x=0;x<sizeof(events) / sizeof(events[0]); x++) {
+ if (!strcasecmp(events[x].name, name))
+ return events[x].id;
+ }
+ return 0;
+}
+
+static int getjustifybyname(char *name)
+{
+ int x;
+ for (x=0;x<sizeof(justify) / sizeof(justify[0]); x++) {
+ if (!strcasecmp(justify[x].name, name))
+ return justify[x].id;
+ }
+ return -1;
+}
+
+static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
+{
+ int x;
+ for (x=0;x<state->numkeys;x++)
+ if (!strcasecmp(state->keys[x].vname, name))
+ return &state->keys[x];
+ if (state->numkeys > 61) {
+ ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
+ state->keys[state->numkeys].id = state->numkeys + 2;
+ state->numkeys++;
+ return &state->keys[state->numkeys-1];
+}
+
+static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
+{
+ int x;
+ for (x=0;x<state->numsubs;x++)
+ if (!strcasecmp(state->subs[x].vname, name))
+ return &state->subs[x];
+ if (state->numsubs > 127) {
+ ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
+ state->subs[state->numsubs].id = state->numsubs;
+ state->numsubs++;
+ return &state->subs[state->numsubs-1];
+}
+
+static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
+{
+ int x;
+ for (x=0;x<state->numstates;x++)
+ if (!strcasecmp(state->states[x].vname, name))
+ return &state->states[x];
+ /* Return now if we're not allowed to create */
+ if (!create)
+ return NULL;
+ if (state->numstates > 253) {
+ ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
+ state->states[state->numstates].id = state->numstates + 1;
+ state->numstates++;
+ return &state->states[state->numstates-1];
+}
+
+static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
+{
+ int x;
+ for (x=0;x<state->numdisplays;x++)
+ if (!strcasecmp(state->displays[x].vname, name))
+ return &state->displays[x];
+ /* Return now if we're not allowed to create */
+ if (!create)
+ return NULL;
+ if (state->numdisplays > 61) {
+ ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
+ state->displays[state->numdisplays].id = state->numdisplays + 1;
+ state->numdisplays++;
+ return &state->displays[state->numdisplays-1];
+}
+
+static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char newkey[80];
+ int bytes;
+ unsigned char keyid[6];
+ int x;
+ int flagid=0;
+ struct adsi_soft_key *key;
+ struct adsi_flag *flag;
+
+ for (x=0;x<7;x++) {
+ /* Up to 6 key arguments */
+ tok = get_token(&args, script, lineno);
+ if (!tok)
+ break;
+ if (!strcasecmp(tok, "UNLESS")) {
+ /* Check for trailing UNLESS flag */
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
+ } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
+ } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) {
+ ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
+ } else
+ flagid = flag->id;
+ if ((tok = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
+ break;
+ }
+ if (x > 5) {
+ ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
+ break;
+ }
+ if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
+ continue;
+ }
+
+ key = getkeybyname(state, newkey, script, lineno);
+ if (!key)
+ break;
+ keyid[x] = key->id;
+ }
+ buf[0] = id;
+ buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
+ for (bytes=0;bytes<x;bytes++) {
+ buf[bytes + 2] = keyid[bytes];
+ }
+ return 2 + x;
+}
+
+static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char dispname[80];
+ int line=0;
+ int flag=0;
+ int cmd = 3;
+ struct adsi_display *disp;
+
+ /* Get display */
+ tok = get_token(&args, script, lineno);
+ if (!tok || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
+ return 0;
+ }
+ disp = getdisplaybyname(state, dispname, script, lineno, 0);
+ if (!disp) {
+ ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
+ return 0;
+ }
+
+ tok = get_token(&args, script, lineno);
+ if (!tok || strcasecmp(tok, "AT")) {
+ ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ /* Get line number */
+ tok = get_token(&args, script, lineno);
+ if (!tok || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
+ return 0;
+ }
+ tok = get_token(&args, script, lineno);
+ if (tok && !strcasecmp(tok, "NOUPDATE")) {
+ cmd = 1;
+ tok = get_token(&args, script, lineno);
+ }
+ if (tok && !strcasecmp(tok, "UNLESS")) {
+ /* Check for trailing UNLESS flag */
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
+ } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
+ }
+ if ((tok = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
+ }
+
+ buf[0] = id;
+ buf[1] = (cmd << 6) | (disp->id & 0x3f);
+ buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
+ return 3;
+}
+
+static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0x00;
+ return 2;
+}
+
+static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0x7;
+ return 2;
+}
+
+static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0;
+ return 2;
+}
+
+static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0xf;
+ return 2;
+}
+
+static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char subscript[80];
+ struct adsi_subscript *sub;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ sub = getsubbyname(state, subscript, script, lineno);
+ if (!sub)
+ return 0;
+ buf[0] = 0x9d;
+ buf[1] = sub->id;
+ return 2;
+}
+
+static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char subscript[80];
+ char sname[80];
+ int sawin=0;
+ int event;
+ int snums[8];
+ int scnt = 0;
+ int x;
+ struct adsi_subscript *sub;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ event = geteventbyname(tok);
+ if (event < 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
+ return 0;
+ }
+ tok = get_token(&args, script, lineno);
+ while ((!sawin && !strcasecmp(tok, "IN")) ||
+ (sawin && !strcasecmp(tok, "OR"))) {
+ sawin = 1;
+ if (scnt > 7) {
+ ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ /* Process 'in' things */
+ tok = get_token(&args, script, lineno);
+ if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
+ ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
+ return 0;
+ }
+ scnt++;
+ tok = get_token(&args, script, lineno);
+ if (!tok)
+ break;
+ }
+ if (!tok || strcasecmp(tok, "GOTO")) {
+ if (!tok)
+ tok = "<nothing>";
+ if (sawin)
+ ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
+ else
+ ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
+ }
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ sub = getsubbyname(state, subscript, script, lineno);
+ if (!sub)
+ return 0;
+ buf[0] = 8;
+ buf[1] = event;
+ buf[2] = sub->id | 0x80;
+ for (x=0;x<scnt;x++)
+ buf[3 + x] = snums[x];
+ return 3 + scnt;
+}
+
+struct adsi_key_cmd {
+ char *name;
+ int id;
+ int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
+};
+
+static struct adsi_key_cmd kcmds[] = {
+ { "SENDDTMF", 0, send_dtmf },
+ /* Encoded DTMF would go here */
+ { "ONHOOK", 0x81 },
+ { "OFFHOOK", 0x82 },
+ { "FLASH", 0x83 },
+ { "WAITDIALTONE", 0x84 },
+ /* Send line number */
+ { "BLANK", 0x86 },
+ { "SENDCHARS", 0x87 },
+ { "CLEARCHARS", 0x88 },
+ { "BACKSPACE", 0x89 },
+ /* Tab column */
+ { "GOTOLINE", 0x8b, goto_line },
+ { "GOTOLINEREL", 0x8c, goto_line_rel },
+ { "PAGEUP", 0x8d },
+ { "PAGEDOWN", 0x8e },
+ /* Extended DTMF */
+ { "DELAY", 0x90, send_delay },
+ { "DIALPULSEONE", 0x91 },
+ { "DATAMODE", 0x92 },
+ { "VOICEMODE", 0x93 },
+ /* Display call buffer 'n' */
+ /* Clear call buffer 'n' */
+ { "CLEARCB1", 0x95, clearcbone },
+ { "DIGITCOLLECT", 0x96, digitcollect },
+ { "DIGITDIRECT", 0x96, digitdirect },
+ { "CLEAR", 0x97 },
+ { "SHOWDISPLAY", 0x98, showdisplay },
+ { "CLEARDISPLAY", 0x98, cleardisplay },
+ { "SHOWKEYS", 0x99, showkeys },
+ { "SETSTATE", 0x9a, set_state },
+ { "TIMERSTART", 0x9b, starttimer },
+ { "TIMERCLEAR", 0x9b, cleartimer },
+ { "SETFLAG", 0x9c, setflag },
+ { "CLEARFLAG", 0x9c, clearflag },
+ { "GOTO", 0x9d, subscript },
+ { "EVENT22", 0x9e },
+ { "EVENT23", 0x9f },
+ { "EXIT", 0xa0 },
+};
+
+static struct adsi_key_cmd opcmds[] = {
+
+ /* 1 - Branch on event -- handled specially */
+ { "SHOWKEYS", 2, showkeys },
+ /* Display Control */
+ { "SHOWDISPLAY", 3, showdisplay },
+ { "CLEARDISPLAY", 3, cleardisplay },
+ { "CLEAR", 5 },
+ { "SETSTATE", 6, set_state },
+ { "TIMERSTART", 7, starttimer },
+ { "TIMERCLEAR", 7, cleartimer },
+ { "ONEVENT", 8, onevent },
+ /* 9 - Subroutine label, treated specially */
+ { "SETFLAG", 10, setflag },
+ { "CLEARFLAG", 10, clearflag },
+ { "DELAY", 11, send_delay },
+ { "EXIT", 12 },
+};
+
+
+static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ int x;
+ char *unused;
+ int res;
+ for (x=0;x<sizeof(kcmds) / sizeof(kcmds[0]);x++) {
+ if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
+ if (kcmds[x].add_args) {
+ res = kcmds[x].add_args(key->retstr + key->retstrlen,
+ code, kcmds[x].id, args, state, script, lineno);
+ if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
+ key->retstrlen += res;
+ else
+ ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script);
+ } else {
+ if ((unused = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
+ if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
+ key->retstr[key->retstrlen] = kcmds[x].id;
+ key->retstrlen++;
+ } else
+ ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script);
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ int x;
+ char *unused;
+ int res;
+ int max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
+ for (x=0;x<sizeof(opcmds) / sizeof(opcmds[0]);x++) {
+ if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
+ if (opcmds[x].add_args) {
+ res = opcmds[x].add_args(sub->data + sub->datalen,
+ code, opcmds[x].id, args, state, script, lineno);
+ if ((sub->datalen + res + 1) <= max)
+ sub->datalen += res;
+ else {
+ ast_log(LOG_WARNING, "No space for '%s' code in subscript '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script);
+ return -1;
+ }
+ } else {
+ if ((unused = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
+ if ((sub->datalen + 2) <= max) {
+ sub->data[sub->datalen] = opcmds[x].id;
+ sub->datalen++;
+ } else {
+ ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script);
+ return -1;
+ }
+ }
+ /* Separate commands with 0xff */
+ sub->data[sub->datalen] = 0xff;
+ sub->datalen++;
+ sub->inscount++;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
+{
+ char *keyword;
+ char *args;
+ char vname[256];
+ char tmp[80];
+ char tmp2[80];
+ int lrci;
+ int wi;
+ int event;
+ struct adsi_display *disp;
+ struct adsi_subscript *newsub;
+ /* Find the first keyword */
+ keyword = get_token(&buf, script, lineno);
+ if (!keyword)
+ return 0;
+ switch(state->state) {
+ case STATE_NORMAL:
+ if (!strcasecmp(keyword, "DESCRIPTION")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "VERSION")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "SECURITY")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "FDN")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "KEY")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ state->key = getkeybyname(state, vname, script, lineno);
+ if (!state->key) {
+ ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (state->key->defined) {
+ ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (strcasecmp(args, "OR")) {
+ ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ } else {
+ ast_copy_string(tmp2, tmp, sizeof(tmp2));
+ }
+ if (strlen(tmp2) > 18) {
+ ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
+ tmp2[18] = '\0';
+ }
+ if (strlen(tmp) > 7) {
+ ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
+ tmp[7] = '\0';
+ }
+ /* Setup initial stuff */
+ state->key->retstr[0] = 128;
+ /* 1 has the length */
+ state->key->retstr[2] = state->key->id;
+ /* Put the Full name in */
+ memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
+ /* Update length */
+ state->key->retstrlen = strlen(tmp2) + 3;
+ /* Put trailing 0xff */
+ state->key->retstr[state->key->retstrlen++] = 0xff;
+ /* Put the short name */
+ memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
+ /* Update length */
+ state->key->retstrlen += strlen(tmp);
+ /* Put trailing 0xff */
+ state->key->retstr[state->key->retstrlen++] = 0xff;
+ /* Record initial length */
+ state->key->initlen = state->key->retstrlen;
+ state->state = STATE_INKEY;
+ } else if (!strcasecmp(keyword, "SUB")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ state->sub = getsubbyname(state, vname, script, lineno);
+ if (!state->sub) {
+ ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (state->sub->defined) {
+ ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
+ break;
+ }
+ /* Setup sub */
+ state->sub->data[0] = 130;
+ /* 1 is the length */
+ state->sub->data[2] = 0x0; /* Clear extensibility bit */
+ state->sub->datalen = 3;
+ if (state->sub->id) {
+ /* If this isn't the main subroutine, make a subroutine label for it */
+ state->sub->data[3] = 9;
+ state->sub->data[4] = state->sub->id;
+ /* 5 is length */
+ state->sub->data[6] = 0xff;
+ state->sub->datalen = 7;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
+ break;
+ }
+ state->state = STATE_INSUB;
+ } else if (!strcasecmp(keyword, "STATE")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (getstatebyname(state, vname, script, lineno, 0)) {
+ ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
+ break;
+ }
+ getstatebyname(state, vname, script, lineno, 1);
+ } else if (!strcasecmp(keyword, "FLAG")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (getflagbyname(state, vname, script, lineno, 0)) {
+ ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
+ break;
+ }
+ getflagbyname(state, vname, script, lineno, 1);
+ } else if (!strcasecmp(keyword, "DISPLAY")) {
+ lrci = 0;
+ wi = 0;
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (getdisplaybyname(state, vname, script, lineno, 0)) {
+ ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
+ break;
+ }
+ disp = getdisplaybyname(state, vname, script, lineno, 1);
+ if (!disp)
+ break;
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (strlen(tmp) > 20) {
+ ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
+ tmp[20] = '\0';
+ }
+ memcpy(disp->data + 5, tmp, strlen(tmp));
+ disp->datalen = strlen(tmp) + 5;
+ disp->data[disp->datalen++] = 0xff;
+
+ args = get_token(&buf, script, lineno);
+ if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ /* Got a column two */
+ if (strlen(tmp) > 20) {
+ ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
+ tmp[20] = '\0';
+ }
+ memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
+ disp->datalen += strlen(tmp);
+ args = get_token(&buf, script, lineno);
+ }
+ while(args) {
+ if (!strcasecmp(args, "JUSTIFY")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
+ break;
+ }
+ lrci = getjustifybyname(args);
+ if (lrci < 0) {
+ ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ } else if (!strcasecmp(args, "WRAP")) {
+ wi = 0x80;
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ }
+ if (args) {
+ /* Something bad happened */
+ break;
+ }
+ disp->data[0] = 129;
+ disp->data[1] = disp->datalen - 2;
+ disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
+ disp->data[3] = wi;
+ disp->data[4] = 0xff;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
+ }
+ break;
+ case STATE_INKEY:
+ if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
+ if (!strcasecmp(keyword, "ENDKEY")) {
+ /* Return to normal operation and increment current key */
+ state->state = STATE_NORMAL;
+ state->key->defined = 1;
+ state->key->retstr[1] = state->key->retstrlen - 2;
+ state->key = NULL;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
+ }
+ }
+ break;
+ case STATE_INIF:
+ if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
+ if (!strcasecmp(keyword, "ENDIF")) {
+ /* Return to normal SUB operation and increment current key */
+ state->state = STATE_INSUB;
+ state->sub->defined = 1;
+ /* Store the proper number of instructions */
+ state->sub->ifdata[2] = state->sub->ifinscount;
+ } else if (!strcasecmp(keyword, "GOTO")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ newsub = getsubbyname(state, tmp, script, lineno);
+ if (!newsub)
+ break;
+ /* Somehow you use GOTO to go to another place */
+ state->sub->data[state->sub->datalen++] = 0x8;
+ state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
+ state->sub->data[state->sub->datalen++] = newsub->id;
+ /* Terminate */
+ state->sub->data[state->sub->datalen++] = 0xff;
+ /* Increment counters */
+ state->sub->inscount++;
+ state->sub->ifinscount++;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
+ }
+ } else
+ state->sub->ifinscount++;
+ break;
+ case STATE_INSUB:
+ if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
+ if (!strcasecmp(keyword, "ENDSUB")) {
+ /* Return to normal operation and increment current key */
+ state->state = STATE_NORMAL;
+ state->sub->defined = 1;
+ /* Store the proper length */
+ state->sub->data[1] = state->sub->datalen - 2;
+ if (state->sub->id) {
+ /* if this isn't main, store number of instructions, too */
+ state->sub->data[5] = state->sub->inscount;
+ }
+ state->sub = NULL;
+ } else if (!strcasecmp(keyword, "IFEVENT")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
+ break;
+ }
+ event = geteventbyname(args);
+ if (event < 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "THEN")) {
+ ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
+ break;
+ }
+ state->sub->ifinscount = 0;
+ state->sub->ifdata = state->sub->data +
+ state->sub->datalen;
+ /* Reserve header and insert op codes */
+ state->sub->ifdata[0] = 0x1;
+ state->sub->ifdata[1] = event;
+ /* 2 is for the number of instructions */
+ state->sub->ifdata[3] = 0xff;
+ state->sub->datalen += 4;
+ /* Update Subscript instruction count */
+ state->sub->inscount++;
+ state->state = STATE_INIF;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
+ }
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
+ }
+ return 0;
+}
+
+static struct adsi_script *compile_script(char *script)
+{
+ FILE *f;
+ char fn[256];
+ char buf[256];
+ char *c;
+ int lineno=0;
+ int x, err;
+ struct adsi_script *scr;
+ if (script[0] == '/')
+ ast_copy_string(fn, script, sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script);
+ f = fopen(fn, "r");
+ if (!f) {
+ ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
+ return NULL;
+ }
+ scr = malloc(sizeof(struct adsi_script));
+ if (!scr) {
+ fclose(f);
+ ast_log(LOG_WARNING, "Out of memory loading script '%s'\n", fn);
+ return NULL;
+ }
+ memset(scr, 0, sizeof(struct adsi_script));
+ /* Create "main" as first subroutine */
+ getsubbyname(scr, "main", NULL, 0);
+ while(!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f)) {
+ lineno++;
+ /* Trim off trailing return */
+ buf[strlen(buf) - 1] = '\0';
+ c = strchr(buf, ';');
+ /* Strip comments */
+ if (c)
+ *c = '\0';
+ if (!ast_strlen_zero(buf))
+ adsi_process(scr, buf, script, lineno);
+ }
+ }
+ fclose(f);
+ /* Make sure we're in the main routine again */
+ switch(scr->state) {
+ case STATE_NORMAL:
+ break;
+ case STATE_INSUB:
+ ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
+ free(scr);
+ return NULL;
+ case STATE_INKEY:
+ ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
+ free(scr);
+ return NULL;
+ }
+ err = 0;
+
+ /* Resolve all keys and record their lengths */
+ for (x=0;x<scr->numkeys;x++) {
+ if (!scr->keys[x].defined) {
+ ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
+ err++;
+ }
+ }
+
+ /* Resolve all subs */
+ for (x=0;x<scr->numsubs;x++) {
+ if (!scr->subs[x].defined) {
+ ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
+ err++;
+ }
+ if (x == (scr->numsubs - 1)) {
+ /* Clear out extension bit on last message */
+ scr->subs[x].data[2] = 0x80;
+ }
+ }
+
+ if (err) {
+ free(scr);
+ return NULL;
+ }
+ return scr;
+}
+
+#ifdef DUMP_MESSAGES
+static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
+{
+ int x;
+ printf("%s %s: [ ", type, vname);
+ for (x=0;x<buflen;x++)
+ printf("%02x ", buf[x]);
+ printf("]\n");
+}
+#endif
+
+static int adsi_prog(struct ast_channel *chan, char *script)
+{
+ struct adsi_script *scr;
+ int x;
+ unsigned char buf[1024];
+ int bytes;
+ scr = compile_script(script);
+ if (!scr)
+ return -1;
+
+ /* Start an empty ADSI Session */
+ if (adsi_load_session(chan, NULL, 0, 1) < 1)
+ return -1;
+
+ /* Now begin the download attempt */
+ if (adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
+ /* User rejected us for some reason */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n");
+ ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
+ free(scr);
+ return -1;
+ }
+
+ bytes = 0;
+ /* Start with key definitions */
+ for (x=0;x<scr->numkeys;x++) {
+ if (bytes + scr->keys[x].retstrlen > 253) {
+ /* Send what we've collected so far */
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ bytes =0;
+ }
+ memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
+ bytes += scr->keys[x].retstrlen;
+#ifdef DUMP_MESSAGES
+ dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
+#endif
+ }
+ if (bytes) {
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ }
+
+ bytes = 0;
+ /* Continue with the display messages */
+ for (x=0;x<scr->numdisplays;x++) {
+ if (bytes + scr->displays[x].datalen > 253) {
+ /* Send what we've collected so far */
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ bytes =0;
+ }
+ memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
+ bytes += scr->displays[x].datalen;
+#ifdef DUMP_MESSAGES
+ dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
+#endif
+ }
+ if (bytes) {
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ }
+
+ bytes = 0;
+ /* Send subroutines */
+ for (x=0;x<scr->numsubs;x++) {
+ if (bytes + scr->subs[x].datalen > 253) {
+ /* Send what we've collected so far */
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ bytes =0;
+ }
+ memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
+ bytes += scr->subs[x].datalen;
+#ifdef DUMP_MESSAGES
+ dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
+#endif
+ }
+ if (bytes) {
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ }
+
+
+ bytes = 0;
+ bytes += adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
+ bytes += adsi_set_line(buf, ADSI_INFO_PAGE, 1);
+ if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
+ return -1;
+ if (adsi_end_download(chan)) {
+ /* Download failed for some reason */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n");
+ ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
+ free(scr);
+ return -1;
+ }
+ free(scr);
+ adsi_unload_session(chan);
+ return 0;
+}
+
+static int adsi_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ if (ast_strlen_zero(data))
+ data = "asterisk.adsi";
+
+ if (!adsi_available(chan)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE. Not bothering to try.\n");
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE. Attempting Upload.\n");
+ res = adsi_prog(chan, data);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, adsi_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_alarmreceiver.c b/1.2-netsec/apps/app_alarmreceiver.c
new file mode 100644
index 000000000..7c36b60b8
--- /dev/null
+++ b/1.2-netsec/apps/app_alarmreceiver.c
@@ -0,0 +1,867 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005 Steve Rodgers
+ *
+ * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Central Station Alarm receiver for Ademco Contact ID
+ * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
+ *
+ * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
+ *
+ * Use at your own risk. Please consult the GNU GPL license document included with Asterisk details. *
+ *
+ * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/config.h"
+#include "asterisk/localtime.h"
+#include "asterisk/callerid.h"
+#include "asterisk/astdb.h"
+
+#define ALMRCV_CONFIG "alarmreceiver.conf"
+#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
+
+struct event_node{
+ char data[17];
+ struct event_node *next;
+};
+
+typedef struct event_node event_node_t;
+
+static char *tdesc = "Alarm Receiver for Asterisk";
+
+static char *app = "AlarmReceiver";
+
+static char *synopsis = "Provide support for receving alarm reports from a burglar or fire alarm panel";
+static char *descrip =
+" AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n"
+"Contact ID. This application should be called whenever there is an alarm\n"
+"panel calling in to dump its events. The application will handshake with the\n"
+"alarm panel, and receive events, validate them, handshake them, and store them\n"
+"until the panel hangs up. Once the panel hangs up, the application will run the\n"
+"system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n"
+"the events to the standard input of the application. The configuration file also\n"
+"contains settings for DTMF timing, and for the loudness of the acknowledgement\n"
+"tones.\n";
+
+/* Config Variables */
+
+static int fdtimeout = 2000;
+static int sdtimeout = 200;
+static int toneloudness = 4096;
+static int log_individual_events = 0;
+static char event_spool_dir[128] = {'\0'};
+static char event_app[128] = {'\0'};
+static char db_family[128] = {'\0'};
+static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
+
+
+/* Misc variables */
+
+
+static char event_file[14] = "/event-XXXXXX";
+
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+/*
+* Attempt to access a database variable and increment it,
+* provided that the user defined db-family in alarmreceiver.conf
+* The alarmreceiver app will write statistics to a few variables
+* in this family if it is defined. If the new key doesn't exist in the
+* family, then create it and set its value to 1.
+*/
+
+static void database_increment( char *key )
+{
+ int res = 0;
+ unsigned v;
+ char value[16];
+
+
+ if (ast_strlen_zero(db_family))
+ return; /* If not defined, don't do anything */
+
+ res = ast_db_get(db_family, key, value, sizeof(value) - 1);
+
+ if(res){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
+ /* Guess we have to create it */
+ res = ast_db_put(db_family, key, "1");
+ return;
+ }
+
+ sscanf(value, "%u", &v);
+ v++;
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: New value for %s: %u\n", key, v);
+
+ snprintf(value, sizeof(value), "%u", v);
+
+ res = ast_db_put(db_family, key, value);
+
+ if((res)&&(option_verbose >= 4))
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error");
+
+ return;
+}
+
+
+/*
+* Build a MuLaw data block for a single frequency tone
+*/
+
+static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
+{
+ int i;
+ float val;
+
+ for(i = 0; i < len; i++){
+ val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
+ data[i] = AST_LIN2MU((int)val);
+ }
+
+ /* wrap back around from 8000 */
+
+ if (*x >= 8000) *x = 0;
+ return;
+}
+
+/*
+* Send a single tone burst for a specifed duration and frequency.
+* Returns 0 if successful
+*/
+
+static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
+{
+ int res = 0;
+ int i = 0;
+ int x = 0;
+ struct ast_frame *f, wf;
+
+ struct {
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ unsigned char buf[640];
+ } tone_block;
+
+ for(;;)
+ {
+
+ if (ast_waitfor(chan, -1) < 0){
+ res = -1;
+ break;
+ }
+
+ f = ast_read(chan);
+ if (!f){
+ res = -1;
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_VOICE) {
+ wf.frametype = AST_FRAME_VOICE;
+ wf.subclass = AST_FORMAT_ULAW;
+ wf.offset = AST_FRIENDLY_OFFSET;
+ wf.mallocd = 0;
+ wf.data = tone_block.buf;
+ wf.datalen = f->datalen;
+ wf.samples = wf.datalen;
+
+ make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
+
+ i += wf.datalen / 8;
+ if (i > duration) {
+ break;
+ }
+ if (ast_write(chan, &wf)){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Failed to write frame on %s\n", chan->name);
+ ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
+ res = -1;
+ break;
+ }
+ }
+
+ ast_frfree(f);
+ }
+ return res;
+}
+
+/*
+* Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
+* treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
+* digits.
+*
+* Returns 0 if all digits successfully received.
+* Returns 1 if a digit time out occurred
+* Returns -1 if the caller hung up or there was a channel error.
+*
+*/
+
+static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
+{
+ int res = 0;
+ int i = 0;
+ int r;
+ struct ast_frame *f;
+ struct timeval lastdigittime;
+
+ lastdigittime = ast_tvnow();
+ for(;;){
+ /* if outa time, leave */
+ if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
+ ((i > 0) ? sdto : fdto)){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
+
+ ast_log(LOG_DEBUG,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
+
+ res = 1;
+ break;
+ }
+
+ if ((r = ast_waitfor(chan, -1) < 0)) {
+ ast_log(LOG_DEBUG, "Waitfor returned %d\n", r);
+ continue;
+ }
+
+ f = ast_read(chan);
+
+ if (f == NULL){
+ res = -1;
+ break;
+ }
+
+ /* If they hung up, leave */
+ if ((f->frametype == AST_FRAME_CONTROL) &&
+ (f->subclass == AST_CONTROL_HANGUP)){
+ ast_frfree(f);
+ res = -1;
+ break;
+ }
+
+ /* if not DTMF, just do it again */
+ if (f->frametype != AST_FRAME_DTMF){
+ ast_frfree(f);
+ continue;
+ }
+
+ digit_string[i++] = f->subclass; /* save digit */
+
+ ast_frfree(f);
+
+ /* If we have all the digits we expect, leave */
+ if(i >= length)
+ break;
+
+ lastdigittime = ast_tvnow();
+ }
+
+ digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
+ return res;
+
+}
+
+/*
+* Write the metadata to the log file
+*/
+
+static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
+{
+ int res = 0;
+ time_t t;
+ struct tm now;
+ char *cl,*cn;
+ char workstring[80];
+ char timestamp[80];
+
+ /* Extract the caller ID location */
+ if (chan->cid.cid_num)
+ ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
+ workstring[sizeof(workstring) - 1] = '\0';
+
+ ast_callerid_parse(workstring, &cn, &cl);
+ if (cl)
+ ast_shrink_phone_number(cl);
+
+
+ /* Get the current time */
+
+ time(&t);
+ ast_localtime(&t, &now, NULL);
+
+ /* Format the time */
+
+ strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
+
+
+ res = fprintf(logfile, "\n\n[metadata]\n\n");
+
+ if(res >= 0)
+ res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
+
+ if(res >= 0)
+ res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
+
+ if(res >- 0)
+ res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
+
+ if(res >= 0)
+ res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
+
+ if(res >= 0)
+ res = fprintf(logfile, "[events]\n\n");
+
+ if(res < 0){
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");
+
+ ast_log(LOG_DEBUG,"AlarmReceiver: can't write metadata\n");
+ }
+ else
+ res = 0;
+
+ return res;
+}
+
+/*
+* Write a single event to the log file
+*/
+
+static int write_event( FILE *logfile, event_node_t *event)
+{
+ int res = 0;
+
+ if( fprintf(logfile, "%s\n", event->data) < 0)
+ res = -1;
+
+ return res;
+}
+
+
+/*
+* If we are configured to log events, do so here.
+*
+*/
+
+static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
+{
+
+ int res = 0;
+ char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
+ int fd;
+ FILE *logfile;
+ event_node_t *elp = event;
+
+ if (!ast_strlen_zero(event_spool_dir)) {
+
+ /* Make a template */
+
+ ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
+ strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
+
+ /* Make the temporary file */
+
+ fd = mkstemp(workstring);
+
+ if(fd == -1){
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");
+ ast_log(LOG_DEBUG,"AlarmReceiver: can't make temporary file\n");
+ res = -1;
+ }
+
+ if(!res){
+ logfile = fdopen(fd, "w");
+ if(logfile){
+ /* Write the file */
+ res = write_metadata(logfile, signalling_type, chan);
+ if(!res)
+ while((!res) && (elp != NULL)){
+ res = write_event(logfile, elp);
+ elp = elp->next;
+ }
+ if(!res){
+ if(fflush(logfile) == EOF)
+ res = -1;
+ if(!res){
+ if(fclose(logfile) == EOF)
+ res = -1;
+ }
+ }
+ }
+ else
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+/*
+* This function implements the logic to receive the Ademco contact ID format.
+*
+* The function will return 0 when the caller hangs up, else a -1 if there was a problem.
+*/
+
+static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
+{
+ int i,j;
+ int res = 0;
+ int checksum;
+ char event[17];
+ event_node_t *enew, *elp;
+ int got_some_digits = 0;
+ int events_received = 0;
+ int ack_retries = 0;
+
+ static char digit_map[15] = "0123456789*#ABC";
+ static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
+
+ database_increment("calls-received");
+
+ /* Wait for first event */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n");
+
+ while(res >= 0){
+
+ if(got_some_digits == 0){
+
+ /* Send ACK tone sequence */
+
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
+
+
+ res = send_tone_burst(chan, 1400.0, 100, tldn);
+
+ if(!res)
+ res = ast_safe_sleep(chan, 100);
+
+ if(!res){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
+
+ res = send_tone_burst(chan, 2300.0, 100, tldn);
+ }
+
+ }
+
+ if( res >= 0)
+ res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
+
+ if (res < 0){
+
+ if(events_received == 0)
+ /* Hangup with no events received should be logged in the DB */
+ database_increment("no-events-received");
+ else{
+ if(ack_retries){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
+
+ database_increment("ack-retries");
+ }
+ }
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n");
+ res = -1;
+ break;
+ }
+
+ if(res != 0){
+ /* Didn't get all of the digits */
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
+
+ if(!got_some_digits){
+ got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
+ ack_retries++;
+ }
+ continue;
+ }
+
+ got_some_digits = 1;
+
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Received Event %s\n", event);
+ ast_log(LOG_DEBUG, "AlarmReceiver: Received event: %s\n", event);
+
+ /* Calculate checksum */
+
+ for(j = 0, checksum = 0; j < 16; j++){
+ for(i = 0 ; i < sizeof(digit_map) ; i++){
+ if(digit_map[i] == event[j])
+ break;
+ }
+
+ if(i == 16)
+ break;
+
+ checksum += digit_weights[i];
+ }
+
+ if(i == 16){
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
+ continue; /* Bad character */
+ }
+
+ /* Checksum is mod(15) of the total */
+
+ checksum = checksum % 15;
+
+ if(checksum){
+ database_increment("checksum-errors");
+ if(option_verbose >= 2){
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Nonzero checksum\n");
+ ast_log(LOG_DEBUG, "AlarmReceiver: Nonzero checksum\n");
+ continue;
+ }
+ }
+
+ /* Check the message type for correctness */
+
+ if(strncmp(event + 4, "18", 2)){
+ if(strncmp(event + 4, "98", 2)){
+ database_increment("format-errors");
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Wrong message type\n");
+ ast_log(LOG_DEBUG, "AlarmReceiver: Wrong message type\n");
+ continue;
+ }
+ }
+
+ events_received++;
+
+ /* Queue the Event */
+
+ if((enew = malloc(sizeof(event_node_t))) == NULL){
+ if(option_verbose >= 1)
+ ast_verbose(VERBOSE_PREFIX_1 "AlarmReceiver: Failed to allocate memory\n");
+ ast_log(LOG_WARNING, "AlarmReceiver Failed to allocate memory\n");
+ res = -1;
+ break;
+ }
+
+ memset(enew, 0, sizeof(event_node_t));
+
+ enew->next = NULL;
+ ast_copy_string(enew->data, event, sizeof(enew->data));
+
+ /*
+ * Insert event onto end of list
+ */
+
+ if(*ehead == NULL){
+ *ehead = enew;
+ }
+ else{
+ for(elp = *ehead; elp->next != NULL; elp = elp->next)
+ ;
+
+ elp->next = enew;
+ }
+
+ if(res > 0)
+ res = 0;
+
+ /* Let the user have the option of logging the single event before sending the kissoff tone */
+
+ if((res == 0) && (log_individual_events))
+ res = log_events(chan, ADEMCO_CONTACT_ID, enew);
+
+ /* Wait 200 msec before sending kissoff */
+
+ if(res == 0)
+ res = ast_safe_sleep(chan, 200);
+
+ /* Send the kissoff tone */
+
+ if(res == 0)
+ res = send_tone_burst(chan, 1400.0, 900, tldn);
+ }
+
+
+ return res;
+}
+
+
+/*
+* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
+* This function will always return 0.
+*/
+
+static int alarmreceiver_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ event_node_t *elp, *efree;
+ char signalling_type[64] = "";
+
+ event_node_t *event_head = NULL;
+
+ LOCAL_USER_ADD(u);
+
+ /* Set write and read formats to ULAW */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Setting read and write formats to ULAW\n");
+
+ if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
+ ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
+ ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ /* Set default values for this invokation of the application */
+
+ ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
+
+
+ /* Answer the channel if it is not already */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Answering channel\n");
+
+ if (chan->_state != AST_STATE_UP) {
+
+ res = ast_answer(chan);
+
+ if (res) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ /* Wait for the connection to settle post-answer */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for connection to stabilize\n");
+
+ res = ast_safe_sleep(chan, 1250);
+
+ /* Attempt to receive the events */
+
+ if(!res){
+
+ /* Determine the protocol to receive in advance */
+ /* Note: Ademco contact is the only one supported at this time */
+ /* Others may be added later */
+
+ if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
+ receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
+ else
+ res = -1;
+ }
+
+
+
+ /* Events queued by receiver, write them all out here if so configured */
+
+ if((!res) && (log_individual_events == 0)){
+ res = log_events(chan, signalling_type, event_head);
+
+ }
+
+ /*
+ * Do we exec a command line at the end?
+ */
+
+ if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
+ ast_log(LOG_DEBUG,"Alarmreceiver: executing: %s\n", event_app);
+ ast_safe_system(event_app);
+ }
+
+ /*
+ * Free up the data allocated in our linked list
+ */
+
+ for(elp = event_head; (elp != NULL);){
+ efree = elp;
+ elp = elp->next;
+ free(efree);
+ }
+
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+/*
+* Load the configuration from the configuration file
+*/
+
+static int load_config(void)
+{
+ struct ast_config *cfg;
+ char *p;
+
+ /* Read in the config file */
+
+ cfg = ast_config_load(ALMRCV_CONFIG);
+
+ if(!cfg){
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
+ }
+ else{
+
+
+ p = ast_variable_retrieve(cfg, "general", "eventcmd");
+
+ if(p){
+ ast_copy_string(event_app, p, sizeof(event_app));
+ event_app[sizeof(event_app) - 1] = '\0';
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "loudness");
+ if(p){
+ toneloudness = atoi(p);
+ if(toneloudness < 100)
+ toneloudness = 100;
+ if(toneloudness > 8192)
+ toneloudness = 8192;
+ }
+ p = ast_variable_retrieve(cfg, "general", "fdtimeout");
+ if(p){
+ fdtimeout = atoi(p);
+ if(fdtimeout < 1000)
+ fdtimeout = 1000;
+ if(fdtimeout > 10000)
+ fdtimeout = 10000;
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "sdtimeout");
+ if(p){
+ sdtimeout = atoi(p);
+ if(sdtimeout < 110)
+ sdtimeout = 110;
+ if(sdtimeout > 4000)
+ sdtimeout = 4000;
+
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "logindividualevents");
+ if(p){
+ log_individual_events = ast_true(p);
+
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "eventspooldir");
+
+ if(p){
+ ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
+ event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "timestampformat");
+
+ if(p){
+ ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
+ time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "db-family");
+
+ if(p){
+ ast_copy_string(db_family, p, sizeof(db_family));
+ db_family[sizeof(db_family) - 1] = '\0';
+ }
+ ast_config_destroy(cfg);
+ }
+ return 0;
+
+}
+
+/*
+* These functions are required to implement an Asterisk App.
+*/
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ load_config();
+ return ast_register_application(app, alarmreceiver_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_authenticate.c b/1.2-netsec/apps/app_authenticate.c
new file mode 100644
index 000000000..583c5f4d3
--- /dev/null
+++ b/1.2-netsec/apps/app_authenticate.c
@@ -0,0 +1,232 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Execute arbitrary authenticate commands
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/astdb.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Authentication Application";
+
+static char *app = "Authenticate";
+
+static char *synopsis = "Authenticate a user";
+
+static char *descrip =
+" Authenticate(password[|options]): This application asks the caller to enter a\n"
+"given password in order to continue dialplan execution. If the password begins\n"
+"with the '/' character, it is interpreted as a file which contains a list of\n"
+"valid passwords, listed 1 password per line in the file.\n"
+" When using a database key, the value associated with the key can be anything.\n"
+"Users have three attempts to authenticate before the channel is hung up. If the\n"
+"passsword is invalid, the 'j' option is specified, and priority n+101 exists,\n"
+"dialplan execution will continnue at this location.\n"
+" Options:\n"
+" a - Set the channels' account code to the password that is entered\n"
+" d - Interpret the given path as database key, not a literal file\n"
+" j - Support jumping to n+101 if authentication fails\n"
+" m - Interpret the given path as a file which contains a list of account\n"
+" codes and password hashes delimited with ':', listed one per line in\n"
+" the file. When one of the passwords is matched, the channel will have\n"
+" its account code set to the corresponding account code in the file.\n"
+" r - Remove the database key upon successful entry (valid with 'd' only)\n"
+;
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int auth_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int jump = 0;
+ int retries;
+ struct localuser *u;
+ char password[256]="";
+ char passwd[256];
+ char *opts;
+ char *prompt;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->_state != AST_STATE_UP) {
+ res = ast_answer(chan);
+ if (res) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ strncpy(password, data, sizeof(password) - 1);
+ opts=strchr(password, '|');
+ if (opts) {
+ *opts = 0;
+ opts++;
+ } else
+ opts = "";
+ if (strchr(opts, 'j'))
+ jump = 1;
+ /* Start asking for password */
+ prompt = "agent-pass";
+ for (retries = 0; retries < 3; retries++) {
+ res = ast_app_getdata(chan, prompt, passwd, sizeof(passwd) - 2, 0);
+ if (res < 0)
+ break;
+ res = 0;
+ if (password[0] == '/') {
+ if (strchr(opts, 'd')) {
+ char tmp[256];
+ /* Compare against a database key */
+ if (!ast_db_get(password + 1, passwd, tmp, sizeof(tmp))) {
+ /* It's a good password */
+ if (strchr(opts, 'r')) {
+ ast_db_del(password + 1, passwd);
+ }
+ break;
+ }
+ } else {
+ /* Compare against a file */
+ FILE *f;
+ f = fopen(password, "r");
+ if (f) {
+ char buf[256] = "";
+ char md5passwd[33] = "";
+ char *md5secret = NULL;
+
+ while (!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f) && !ast_strlen_zero(buf)) {
+ buf[strlen(buf) - 1] = '\0';
+ if (strchr(opts, 'm')) {
+ md5secret = strchr(buf, ':');
+ if (md5secret == NULL)
+ continue;
+ *md5secret = '\0';
+ md5secret++;
+ ast_md5_hash(md5passwd, passwd);
+ if (!strcmp(md5passwd, md5secret)) {
+ if (strchr(opts, 'a'))
+ ast_cdr_setaccount(chan, buf);
+ break;
+ }
+ } else {
+ if (!strcmp(passwd, buf)) {
+ if (strchr(opts, 'a'))
+ ast_cdr_setaccount(chan, buf);
+ break;
+ }
+ }
+ }
+ }
+ fclose(f);
+ if (!ast_strlen_zero(buf)) {
+ if (strchr(opts, 'm')) {
+ if (md5secret && !strcmp(md5passwd, md5secret))
+ break;
+ } else {
+ if (!strcmp(passwd, buf))
+ break;
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", password, strerror(errno));
+ }
+ } else {
+ /* Compare against a fixed password */
+ if (!strcmp(passwd, password))
+ break;
+ }
+ prompt="auth-incorrect";
+ }
+ if ((retries < 3) && !res) {
+ if (strchr(opts, 'a') && !strchr(opts, 'm'))
+ ast_cdr_setaccount(chan, passwd);
+ res = ast_streamfile(chan, "auth-thankyou", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ } else {
+ if (jump && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ res = 0;
+ } else {
+ if (!ast_streamfile(chan, "vm-goodbye", chan->language))
+ res = ast_waitstream(chan, "");
+ res = -1;
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, auth_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_cdr.c b/1.2-netsec/apps/app_cdr.c
new file mode 100644
index 000000000..501bfc79b
--- /dev/null
+++ b/1.2-netsec/apps/app_cdr.c
@@ -0,0 +1,97 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Martin Pycko <martinp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Applications connected with CDR engine
+ *
+ * \ingroup applications
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+
+static char *tdesc = "Tell Asterisk to not maintain a CDR for the current call";
+
+static char *nocdr_descrip =
+" NoCDR(): This application will tell Asterisk not to maintain a CDR for the\n"
+"current call.\n";
+
+static char *nocdr_app = "NoCDR";
+static char *nocdr_synopsis = "Tell Asterisk to not maintain a CDR for the current call";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int nocdr_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->cdr) {
+ ast_cdr_free(chan->cdr);
+ chan->cdr = NULL;
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(nocdr_app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_chanisavail.c b/1.2-netsec/apps/app_chanisavail.c
new file mode 100644
index 000000000..c55bb8ecb
--- /dev/null
+++ b/1.2-netsec/apps/app_chanisavail.c
@@ -0,0 +1,185 @@
+/*
+* Asterisk -- An open source telephony toolkit.
+*
+* Copyright (C) 1999 - 2005, Digium, Inc.
+*
+* Mark Spencer <markster@digium.com>
+* James Golovich <james@gnuinter.net>
+*
+* See http://www.asterisk.org for more information about
+* the Asterisk project. Please do not directly contact
+* any of the maintainers of this project for assistance;
+* the project provides a web site, mailing lists and IRC
+* channels for your use.
+*
+* This program is free software, distributed under the terms of
+* the GNU General Public License Version 2. See the LICENSE file
+* at the top of the source tree.
+*/
+
+/*! \file
+* \brief Check if Channel is Available
+*
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Check channel availability";
+
+static char *app = "ChanIsAvail";
+
+static char *synopsis = "Check channel availability";
+
+static char *descrip =
+" ChanIsAvail(Technology/resource[&Technology2/resource2...][|options]): \n"
+"This application will check to see if any of the specified channels are\n"
+"available. The following variables will be set by this application:\n"
+" ${AVAILCHAN} - the name of the available channel, if one exists\n"
+" ${AVAILORIGCHAN} - the canonical channel name that was used to create the channel\n"
+" ${AVAILSTATUS} - the status code for the available channel\n"
+" Options:\n"
+" s - Consider the channel unavailable if the channel is in use at all\n"
+" j - Support jumping to priority n+101 if no channel is available\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int chanavail_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1, inuse=-1, option_state=0, priority_jump=0;
+ int status;
+ struct localuser *u;
+ char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur, *options, *stringp;
+ struct ast_channel *tempchan;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ info = ast_strdupa(data);
+ stringp = info;
+ strsep(&stringp, "|");
+ options = strsep(&stringp, "|");
+ if (options) {
+ if (strchr(options, 's'))
+ option_state = 1;
+ if (strchr(options, 'j'))
+ priority_jump = 1;
+ }
+ peers = info;
+ if (peers) {
+ cur = peers;
+ do {
+ /* remember where to start next time */
+ rest = strchr(cur, '&');
+ if (rest) {
+ *rest = 0;
+ rest++;
+ }
+ tech = cur;
+ number = strchr(tech, '/');
+ if (!number) {
+ ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ *number = '\0';
+ number++;
+
+ if (option_state) {
+ /* If the pbx says in use then don't bother trying further.
+ This is to permit testing if someone's on a call, even if the
+ channel can permit more calls (ie callwaiting, sip calls, etc). */
+
+ snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
+ status = inuse = ast_device_state(trychan);
+ }
+ if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
+ pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
+ /* Store the originally used channel too */
+ snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
+ pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
+ snprintf(tmp, sizeof(tmp), "%d", status);
+ pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
+ ast_hangup(tempchan);
+ tempchan = NULL;
+ res = 1;
+ break;
+ } else {
+ snprintf(tmp, sizeof(tmp), "%d", status);
+ pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
+ }
+ cur = rest;
+ } while (cur);
+ }
+ if (res < 1) {
+ pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
+ pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res = 0;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, chanavail_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_chanspy.c b/1.2-netsec/apps/app_chanspy.c
new file mode 100644
index 000000000..299c58efc
--- /dev/null
+++ b/1.2-netsec/apps/app_chanspy.c
@@ -0,0 +1,587 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
+ *
+ * Disclaimed to Digium
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief ChanSpy: Listen in on any channel.
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/chanspy.h"
+#include "asterisk/features.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+#include "asterisk/say.h"
+#include "asterisk/pbx.h"
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+
+AST_MUTEX_DEFINE_STATIC(modlock);
+
+#define AST_NAME_STRLEN 256
+#define ALL_DONE(u, ret) LOCAL_USER_REMOVE(u); return ret;
+#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
+
+static const char *synopsis = "Listen to the audio of an active channel\n";
+static const char *app = "ChanSpy";
+static const char *desc =
+" ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
+"audio from an active Asterisk channel. This includes the audio coming in and\n"
+"out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
+"only channels beginning with this string will be spied upon.\n"
+" While Spying, the following actions may be performed:\n"
+" - Dialing # cycles the volume level.\n"
+" - Dialing * will stop spying and look for another channel to spy on.\n"
+" - Dialing a series of digits followed by # builds a channel name to append\n"
+" to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
+" the digits '1234#' while spying will begin spying on the channel,\n"
+" 'Agent/1234'.\n"
+" Options:\n"
+" b - Only spy on channels involved in a bridged call.\n"
+" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
+" 'grp'.\n"
+" q - Don't play a beep when beginning to spy on a channel.\n"
+" r[(basename)] - Record the session to the monitor spool directory. An\n"
+" optional base for the filename may be specified. The\n"
+" default is 'chanspy'.\n"
+" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
+" negative value refers to a quieter setting.\n"
+;
+
+static const char *chanspy_spy_type = "ChanSpy";
+
+enum {
+ OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
+ OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
+ OPTION_VOLUME = (1 << 2), /* Specify initial volume */
+ OPTION_GROUP = (1 << 3), /* Only look at channels in group */
+ OPTION_RECORD = (1 << 4), /* Record */
+} chanspy_opt_flags;
+
+enum {
+ OPT_ARG_VOLUME = 0,
+ OPT_ARG_GROUP,
+ OPT_ARG_RECORD,
+ OPT_ARG_ARRAY_SIZE,
+} chanspy_opt_args;
+
+AST_APP_OPTIONS(chanspy_opts, {
+ AST_APP_OPTION('q', OPTION_QUIET),
+ AST_APP_OPTION('b', OPTION_BRIDGED),
+ AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
+ AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
+ AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
+});
+
+STANDARD_LOCAL_USER;
+LOCAL_USER_DECL;
+
+struct chanspy_translation_helper {
+ /* spy data */
+ struct ast_channel_spy spy;
+ int fd;
+ int volfactor;
+};
+
+static struct ast_channel *local_channel_walk(struct ast_channel *chan)
+{
+ struct ast_channel *ret;
+ ast_mutex_lock(&modlock);
+ if ((ret = ast_channel_walk_locked(chan))) {
+ ast_mutex_unlock(&ret->lock);
+ }
+ ast_mutex_unlock(&modlock);
+ return ret;
+}
+
+static struct ast_channel *local_get_channel_begin_name(char *name)
+{
+ struct ast_channel *chan, *ret = NULL;
+ ast_mutex_lock(&modlock);
+ chan = local_channel_walk(NULL);
+ while (chan) {
+ if (!strncmp(chan->name, name, strlen(name))) {
+ ret = chan;
+ break;
+ }
+ chan = local_channel_walk(chan);
+ }
+ ast_mutex_unlock(&modlock);
+
+ return ret;
+}
+
+static void *spy_alloc(struct ast_channel *chan, void *data)
+{
+ /* just store the data pointer in the channel structure */
+ return data;
+}
+
+static void spy_release(struct ast_channel *chan, void *data)
+{
+ /* nothing to do */
+}
+
+static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct chanspy_translation_helper *csth = data;
+ struct ast_frame *f;
+
+ if (csth->spy.status != CHANSPY_RUNNING)
+ /* Channel is already gone more than likely */
+ return -1;
+
+ ast_mutex_lock(&csth->spy.lock);
+ f = ast_channel_spy_read_frame(&csth->spy, samples);
+ ast_mutex_unlock(&csth->spy.lock);
+
+ if (!f)
+ return 0;
+
+ if (ast_write(chan, f)) {
+ ast_frfree(f);
+ return -1;
+ }
+
+ if (csth->fd)
+ write(csth->fd, f->data, f->datalen);
+
+ ast_frfree(f);
+
+ return 0;
+}
+
+
+static struct ast_generator spygen = {
+ .alloc = spy_alloc,
+ .release = spy_release,
+ .generate = spy_generate,
+};
+
+static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy)
+{
+ int res;
+ struct ast_channel *peer;
+
+ ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
+
+ ast_mutex_lock(&chan->lock);
+ res = ast_channel_spy_add(chan, spy);
+ ast_mutex_unlock(&chan->lock);
+
+ if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
+ ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
+ }
+
+ return res;
+}
+
+static void stop_spying(struct ast_channel *chan, struct ast_channel_spy *spy)
+{
+ /* If our status has changed to DONE, then the channel we're spying on is gone....
+ DON'T TOUCH IT!!! RUN AWAY!!! */
+ if (spy->status == CHANSPY_DONE)
+ return;
+
+ if (!chan)
+ return;
+
+ ast_mutex_lock(&chan->lock);
+ ast_channel_spy_remove(chan, spy);
+ ast_mutex_unlock(&chan->lock);
+};
+
+/* Map 'volume' levels from -4 through +4 into
+ decibel (dB) settings for channel drivers
+*/
+static signed char volfactor_map[] = {
+ -24,
+ -18,
+ -12,
+ -6,
+ 0,
+ 6,
+ 12,
+ 18,
+ 24,
+};
+
+/* attempt to set the desired gain adjustment via the channel driver;
+ if successful, clear it out of the csth structure so the
+ generator will not attempt to do the adjustment itself
+*/
+static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
+{
+ signed char volume_adjust = volfactor_map[csth->volfactor + 4];
+
+ if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0))
+ csth->volfactor = 0;
+}
+
+static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd)
+{
+ struct chanspy_translation_helper csth;
+ int running, res = 0, x = 0;
+ char inp[24];
+ char *name=NULL;
+ struct ast_frame *f;
+
+ running = (chan && !ast_check_hangup(chan) && spyee && !ast_check_hangup(spyee));
+
+ if (running) {
+ memset(inp, 0, sizeof(inp));
+ name = ast_strdupa(spyee->name);
+ if (option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
+
+ memset(&csth, 0, sizeof(csth));
+ ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
+ ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
+ ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
+ csth.spy.type = chanspy_spy_type;
+ csth.spy.status = CHANSPY_RUNNING;
+ csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
+ csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
+ ast_mutex_init(&csth.spy.lock);
+ csth.volfactor = *volfactor;
+ set_volume(chan, &csth);
+ csth.spy.read_vol_adjustment = csth.volfactor;
+ csth.spy.write_vol_adjustment = csth.volfactor;
+ csth.fd = fd;
+
+ if (start_spying(spyee, chan, &csth.spy))
+ running = 0;
+ }
+
+ if (running) {
+ running = 1;
+ ast_activate_generator(chan, &spygen, &csth);
+
+ while (csth.spy.status == CHANSPY_RUNNING &&
+ chan && !ast_check_hangup(chan) &&
+ spyee &&
+ !ast_check_hangup(spyee) &&
+ running == 1 &&
+ (res = ast_waitfor(chan, -1) > -1)) {
+ if ((f = ast_read(chan))) {
+ res = 0;
+ if (f->frametype == AST_FRAME_DTMF) {
+ res = f->subclass;
+ }
+ ast_frfree(f);
+ if (!res) {
+ continue;
+ }
+ } else {
+ break;
+ }
+ if (x == sizeof(inp)) {
+ x = 0;
+ }
+ if (res < 0) {
+ running = -1;
+ }
+ if (res == 0) {
+ continue;
+ } else if (res == '*') {
+ running = 0;
+ } else if (res == '#') {
+ if (!ast_strlen_zero(inp)) {
+ running = x ? atoi(inp) : -1;
+ break;
+ } else {
+ (*volfactor)++;
+ if (*volfactor > 4) {
+ *volfactor = -4;
+ }
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
+ }
+ csth.volfactor = *volfactor;
+ set_volume(chan, &csth);
+ csth.spy.read_vol_adjustment = csth.volfactor;
+ csth.spy.write_vol_adjustment = csth.volfactor;
+ }
+ } else if (res >= 48 && res <= 57) {
+ inp[x++] = res;
+ }
+ }
+ ast_deactivate_generator(chan);
+ stop_spying(spyee, &csth.spy);
+
+ if (option_verbose >= 2) {
+ ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
+ }
+ } else {
+ running = 0;
+ }
+
+ ast_mutex_destroy(&csth.spy.lock);
+
+ return running;
+}
+
+static int chanspy_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ struct ast_channel *peer=NULL, *prev=NULL;
+ char name[AST_NAME_STRLEN],
+ peer_name[AST_NAME_STRLEN + 5],
+ *args,
+ *ptr = NULL,
+ *options = NULL,
+ *spec = NULL,
+ *argv[5],
+ *mygroup = NULL,
+ *recbase = NULL;
+ int res = -1,
+ volfactor = 0,
+ silent = 0,
+ argc = 0,
+ bronly = 0,
+ chosen = 0,
+ count=0,
+ waitms = 100,
+ num = 0,
+ oldrf = 0,
+ oldwf = 0,
+ fd = 0;
+ struct ast_flags flags;
+ signed char zero_volume = 0;
+
+ if (!(args = ast_strdupa((char *)data))) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ oldrf = chan->readformat;
+ oldwf = chan->writeformat;
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ ast_answer(chan);
+
+ ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
+
+ if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+ spec = argv[0];
+ if ( argc > 1) {
+ options = argv[1];
+ }
+ if (ast_strlen_zero(spec) || !strcmp(spec, "all")) {
+ spec = NULL;
+ }
+ }
+
+ if (options) {
+ char *opts[OPT_ARG_ARRAY_SIZE];
+ ast_app_parse_options(chanspy_opts, &flags, opts, options);
+ if (ast_test_flag(&flags, OPTION_GROUP)) {
+ mygroup = opts[1];
+ }
+ if (ast_test_flag(&flags, OPTION_RECORD)) {
+ if (!(recbase = opts[2])) {
+ recbase = "chanspy";
+ }
+ }
+ silent = ast_test_flag(&flags, OPTION_QUIET);
+ bronly = ast_test_flag(&flags, OPTION_BRIDGED);
+ if (ast_test_flag(&flags, OPTION_VOLUME) && opts[1]) {
+ int vol;
+
+ if ((sscanf(opts[0], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
+ ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
+ else
+ volfactor = vol;
+ }
+ }
+
+ if (recbase) {
+ char filename[512];
+ snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL));
+ if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC, 0644)) <= 0) {
+ ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename);
+ fd = 0;
+ }
+ }
+
+ for(;;) {
+ if (!silent) {
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ if (res < 0) {
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+ break;
+ }
+ }
+
+ count = 0;
+ res = ast_waitfordigit(chan, waitms);
+ if (res < 0) {
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+ break;
+ }
+
+ peer = local_channel_walk(NULL);
+ prev=NULL;
+ while(peer) {
+ if (peer != chan) {
+ char *group = NULL;
+ int igrp = 1;
+
+ if (peer == prev && !chosen) {
+ break;
+ }
+ chosen = 0;
+ group = pbx_builtin_getvar_helper(peer, "SPYGROUP");
+ if (mygroup) {
+ if (!group || strcmp(mygroup, group)) {
+ igrp = 0;
+ }
+ }
+
+ if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) &&
+ !strncasecmp(peer->name, spec, strlen(spec)))))) {
+ if (peer && (!bronly || ast_bridged_channel(peer)) &&
+ !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
+ int x = 0;
+ strncpy(peer_name, "spy-", 5);
+ strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN);
+ ptr = strchr(peer_name, '/');
+ *ptr = '\0';
+ ptr++;
+ for (x = 0 ; x < strlen(peer_name) ; x++) {
+ if (peer_name[x] == '/') {
+ break;
+ }
+ peer_name[x] = tolower(peer_name[x]);
+ }
+
+ if (!silent) {
+ if (ast_fileexists(peer_name, NULL, NULL) != -1) {
+ res = ast_streamfile(chan, peer_name, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ if (res)
+ break;
+ } else
+ res = ast_say_character_str(chan, peer_name, "", chan->language);
+ if ((num=atoi(ptr)))
+ ast_say_digits(chan, atoi(ptr), "", chan->language);
+ }
+ count++;
+ prev = peer;
+ res = channel_spy(chan, peer, &volfactor, fd);
+ if (res == -1) {
+ break;
+ } else if (res > 1 && spec) {
+ snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res);
+ if ((peer = local_get_channel_begin_name(name))) {
+ chosen = 1;
+ }
+ continue;
+ }
+ }
+ }
+ }
+ if ((peer = local_channel_walk(peer)) == NULL) {
+ break;
+ }
+ }
+ waitms = count ? 100 : 5000;
+ }
+
+
+ if (fd > 0) {
+ close(fd);
+ }
+
+ if (oldrf && ast_set_read_format(chan, oldrf) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
+ }
+
+ if (oldwf && ast_set_write_format(chan, oldwf) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+ }
+
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+
+ ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
+
+ ALL_DONE(u, res);
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, chanspy_exec, synopsis, desc);
+}
+
+char *description(void)
+{
+ return (char *) synopsis;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_controlplayback.c b/1.2-netsec/apps/app_controlplayback.c
new file mode 100644
index 000000000..55a46ceb5
--- /dev/null
+++ b/1.2-netsec/apps/app_controlplayback.c
@@ -0,0 +1,190 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Trivial application to control playback of a sound file
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+
+static const char *tdesc = "Control Playback Application";
+
+static const char *app = "ControlPlayback";
+
+static const char *synopsis = "Play a file with fast forward and rewind";
+
+static const char *descrip =
+" ControlPlayback(file[|skipms[|ff[|rew[|stop[|pause[|restart|options]]]]]]]):\n"
+"This application will play back the given filename. By default, the '*' key\n"
+"can be used to rewind, and the '#' key can be used to fast-forward.\n"
+"Parameters:\n"
+" skipms - This is number of milliseconds to skip when rewinding or\n"
+" fast-forwarding.\n"
+" ff - Fast-forward when this DTMF digit is received.\n"
+" rew - Rewind when this DTMF digit is received.\n"
+" stop - Stop playback when this DTMF digit is received.\n"
+" pause - Pause playback when this DTMF digit is received.\n"
+" restart - Restart playback when this DTMF digit is received.\n"
+"Options:\n"
+" j - Jump to priority n+101 if the requested file is not found.\n"
+"This application sets the following channel variable upon completion:\n"
+" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n"
+" string, one of: SUCCESS | USERSTOPPED | ERROR\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int is_on_phonepad(char key)
+{
+ return key == 35 || key == 42 || (key >= 48 && key <= 57);
+}
+
+static int controlplayback_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0, priority_jump = 0;
+ int skipms = 0;
+ struct localuser *u;
+ char *tmp;
+ int argc;
+ char *argv[8];
+ enum arg_ids {
+ arg_file = 0,
+ arg_skip = 1,
+ arg_fwd = 2,
+ arg_rev = 3,
+ arg_stop = 4,
+ arg_pause = 5,
+ arg_restart = 6,
+ options = 7,
+ };
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ tmp = ast_strdupa(data);
+ memset(argv, 0, sizeof(argv));
+
+ argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (argc < 1) {
+ ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ skipms = argv[arg_skip] ? atoi(argv[arg_skip]) : 3000;
+ if (!skipms)
+ skipms = 3000;
+
+ if (!argv[arg_fwd] || !is_on_phonepad(*argv[arg_fwd]))
+ argv[arg_fwd] = "#";
+ if (!argv[arg_rev] || !is_on_phonepad(*argv[arg_rev]))
+ argv[arg_rev] = "*";
+ if (argv[arg_stop] && !is_on_phonepad(*argv[arg_stop]))
+ argv[arg_stop] = NULL;
+ if (argv[arg_pause] && !is_on_phonepad(*argv[arg_pause]))
+ argv[arg_pause] = NULL;
+ if (argv[arg_restart] && !is_on_phonepad(*argv[arg_restart]))
+ argv[arg_restart] = NULL;
+
+ if (argv[options]) {
+ if (strchr(argv[options], 'j'))
+ priority_jump = 1;
+ }
+
+ res = ast_control_streamfile(chan, argv[arg_file], argv[arg_fwd], argv[arg_rev], argv[arg_stop], argv[arg_pause], argv[arg_restart], skipms);
+
+ /* If we stopped on one of our stop keys, return 0 */
+ if (argv[arg_stop] && strchr(argv[arg_stop], res)) {
+ res = 0;
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
+ } else {
+ if (res < 0) {
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ ast_log(LOG_WARNING, "ControlPlayback tried to jump to priority n+101 as requested, but priority didn't exist\n");
+ }
+ }
+ res = 0;
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
+ } else
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, controlplayback_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_curl.c b/1.2-netsec/apps/app_curl.c
new file mode 100644
index 000000000..5cf4b167d
--- /dev/null
+++ b/1.2-netsec/apps/app_curl.c
@@ -0,0 +1,256 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Tilghman Lesher
+ *
+ * Tilghman Lesher <curl-20050919@the-tilghman.com>
+ * and Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
+ *
+ * app_curl.c is distributed with no restrictions on usage or
+ * redistribution.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ * \brief Curl - App to load a URL
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cli.h"
+#include "asterisk/options.h"
+#include "asterisk/module.h"
+
+static char *tdesc = "Load external URL";
+
+static char *app = "Curl";
+
+static char *synopsis = "Load an external URL";
+
+static char *descrip =
+" Curl(URL[|postdata]): This application will request the specified URL.\n"
+"It is mainly used for signalling external applications of an event.\n"
+"Parameters:\n"
+" URL - This is the external URL to request.\n"
+" postdata - This information will be treated as POST data.\n"
+"This application will set the following variable:\n"
+" CURL - This variable will contain the resulting page.\n"
+"This application has been deprecated in favor of the CURL function.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+static void *myrealloc(void *ptr, size_t size)
+{
+ /* There might be a realloc() out there that doesn't like reallocing
+ NULL pointers, so we take care of it here */
+ if (ptr)
+ return realloc(ptr, size);
+ else
+ return malloc(size);
+}
+
+static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = size * nmemb;
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+static int curl_internal(struct MemoryStruct *chunk, char *url, char *post)
+{
+ CURL *curl;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl = curl_easy_init();
+
+ if (!curl) {
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "asterisk-libcurl-agent/1.0");
+
+ if (post) {
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
+ }
+
+ curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+ return 0;
+}
+
+static int curl_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *info, *post_data=NULL, *url;
+ struct MemoryStruct chunk = { NULL, 0 };
+ static int dep_warning = 0;
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "The application Curl is deprecated. Please use the CURL() function instead.\n");
+ dep_warning = 1;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Curl requires an argument (URL)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if ((info = ast_strdupa(data))) {
+ url = strsep(&info, "|");
+ post_data = info;
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (! curl_internal(&chunk, url, post_data)) {
+ if (chunk.memory) {
+ chunk.memory[chunk.size] = '\0';
+ if (chunk.memory[chunk.size - 1] == 10)
+ chunk.memory[chunk.size - 1] = '\0';
+
+ pbx_builtin_setvar_helper(chan, "CURL", chunk.memory);
+
+ free(chunk.memory);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+ res = -1;
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static char *acf_curl_exec(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+ struct localuser *u;
+ char *info, *post_data=NULL, *url;
+ struct MemoryStruct chunk = { NULL, 0 };
+
+ *buf = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+ return buf;
+ }
+
+ LOCAL_USER_ACF_ADD(u);
+
+ info = ast_strdupa(data);
+ if (!info) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return buf;
+ }
+
+ url = strsep(&info, "|");
+ post_data = info;
+
+ if (! curl_internal(&chunk, url, post_data)) {
+ if (chunk.memory) {
+ chunk.memory[chunk.size] = '\0';
+ if (chunk.memory[chunk.size - 1] == 10)
+ chunk.memory[chunk.size - 1] = '\0';
+
+ ast_copy_string(buf, chunk.memory, len);
+ free(chunk.memory);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return buf;
+}
+
+struct ast_custom_function acf_curl = {
+ .name = "CURL",
+ .synopsis = "Retrieves the contents of a URL",
+ .syntax = "CURL(url[|post-data])",
+ .desc =
+ " url - URL to retrieve\n"
+ " post-data - Optional data to send as a POST (GET is default action)\n",
+ .read = acf_curl_exec,
+};
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&acf_curl);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_custom_function_register(&acf_curl);
+ res |= ast_register_application(app, curl_exec, synopsis, descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_cut.c b/1.2-netsec/apps/app_cut.c
new file mode 100644
index 000000000..46f934b89
--- /dev/null
+++ b/1.2-netsec/apps/app_cut.c
@@ -0,0 +1,473 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2003 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_cut__v003@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ * \brief Cut application
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+
+/* Maximum length of any variable */
+#define MAXRESULT 1024
+
+static char *tdesc = "Cut out information from a string";
+
+static char *app_cut = "Cut";
+
+static char *cut_synopsis = "Splits a variable's contents using the specified delimiter";
+
+static char *cut_descrip =
+" Cut(newvar=varname,delimiter,fieldspec): This applicaiton will split the\n"
+"contents of a variable based on the given delimeter and store the result in\n"
+"a new variable.\n"
+"Parameters:\n"
+" newvar - new variable created from result string\n"
+" varname - variable you want cut\n"
+" delimiter - defaults to '-'\n"
+" fieldspec - number of the field you want (1-based offset)\n"
+" may also be specified as a range (with -)\n"
+" or group of ranges and fields (with &)\n"
+"This application has been deprecated in favor of the CUT function.\n";
+
+static char *app_sort = "Sort";
+static char *app_sort_synopsis = "Sorts a list of keywords and values";
+static char *app_sort_descrip =
+" Sort(newvar=key1:val1[,key2:val2[[...],keyN:valN]]): This application will\n"
+"sort the list provided in ascending order. The result will be stored in the\n"
+"specified variable name.\n"
+" This applicaiton has been deprecated in favor of the SORT function.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+struct sortable_keys {
+ char *key;
+ float value;
+};
+
+static int sort_subroutine(const void *arg1, const void *arg2)
+{
+ const struct sortable_keys *one=arg1, *two=arg2;
+ if (one->value < two->value) {
+ return -1;
+ } else if (one->value == two->value) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+#define ERROR_NOARG (-1)
+#define ERROR_NOMEM (-2)
+#define ERROR_USAGE (-3)
+
+static int sort_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
+{
+ char *strings, *ptrkey, *ptrvalue;
+ int count=1, count2, element_count=0;
+ struct sortable_keys *sortable_keys;
+
+ memset(buffer, 0, buflen);
+
+ if (!data) {
+ return ERROR_NOARG;
+ }
+
+ strings = ast_strdupa((char *)data);
+ if (!strings) {
+ return ERROR_NOMEM;
+ }
+
+ for (ptrkey = strings; *ptrkey; ptrkey++) {
+ if (*ptrkey == '|') {
+ count++;
+ }
+ }
+
+ sortable_keys = alloca(count * sizeof(struct sortable_keys));
+ if (!sortable_keys) {
+ return ERROR_NOMEM;
+ }
+
+ memset(sortable_keys, 0, count * sizeof(struct sortable_keys));
+
+ /* Parse each into a struct */
+ count2 = 0;
+ while ((ptrkey = strsep(&strings, "|"))) {
+ ptrvalue = index(ptrkey, ':');
+ if (!ptrvalue) {
+ count--;
+ continue;
+ }
+ *ptrvalue = '\0';
+ ptrvalue++;
+ sortable_keys[count2].key = ptrkey;
+ sscanf(ptrvalue, "%f", &sortable_keys[count2].value);
+ count2++;
+ }
+
+ /* Sort the structs */
+ qsort(sortable_keys, count, sizeof(struct sortable_keys), sort_subroutine);
+
+ for (count2 = 0; count2 < count; count2++) {
+ int blen = strlen(buffer);
+ if (element_count++) {
+ strncat(buffer + blen, ",", buflen - blen - 1);
+ blen++;
+ }
+ strncat(buffer + blen, sortable_keys[count2].key, buflen - blen - 1);
+ }
+
+ return 0;
+}
+
+static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
+{
+ char *s, *args[3], *varname=NULL, *delimiter=NULL, *field=NULL;
+ int args_okay = 0;
+
+ memset(buffer, 0, buflen);
+
+ /* Check and parse arguments */
+ if (data) {
+ s = ast_strdupa((char *)data);
+ if (s) {
+ ast_app_separate_args(s, '|', args, 3);
+ varname = args[0];
+ delimiter = args[1];
+ field = args[2];
+
+ if (field) {
+ args_okay = 1;
+ }
+ } else {
+ return ERROR_NOMEM;
+ }
+ }
+
+ if (args_okay) {
+ char d, ds[2];
+ char *tmp = alloca(strlen(varname) + 4);
+ char varvalue[MAXRESULT], *tmp2=varvalue;
+
+ if (tmp) {
+ snprintf(tmp, strlen(varname) + 4, "${%s}", varname);
+ memset(varvalue, 0, sizeof(varvalue));
+ } else {
+ return ERROR_NOMEM;
+ }
+
+ if (delimiter[0])
+ d = delimiter[0];
+ else
+ d = '-';
+
+ /* String form of the delimiter, for use with strsep(3) */
+ snprintf(ds, sizeof(ds), "%c", d);
+
+ pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
+
+ if (tmp2) {
+ int curfieldnum = 1;
+ while ((tmp2 != NULL) && (field != NULL)) {
+ char *nextgroup = strsep(&field, "&");
+ int num1 = 0, num2 = MAXRESULT;
+ char trashchar;
+
+ if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
+ /* range with both start and end */
+ } else if (sscanf(nextgroup, "-%d", &num2) == 1) {
+ /* range with end */
+ num1 = 0;
+ } else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
+ /* range with start */
+ num2 = MAXRESULT;
+ } else if (sscanf(nextgroup, "%d", &num1) == 1) {
+ /* single number */
+ num2 = num1;
+ } else {
+ return ERROR_USAGE;
+ }
+
+ /* Get to start, if any */
+ if (num1 > 0) {
+ while ((tmp2 != (char *)NULL + 1) && (curfieldnum < num1)) {
+ tmp2 = index(tmp2, d) + 1;
+ curfieldnum++;
+ }
+ }
+
+ /* Most frequent problem is the expectation of reordering fields */
+ if ((num1 > 0) && (curfieldnum > num1)) {
+ ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
+ }
+
+ /* Re-null tmp2 if we added 1 to NULL */
+ if (tmp2 == (char *)NULL + 1)
+ tmp2 = NULL;
+
+ /* Output fields until we either run out of fields or num2 is reached */
+ while ((tmp2 != NULL) && (curfieldnum <= num2)) {
+ char *tmp3 = strsep(&tmp2, ds);
+ int curlen = strlen(buffer);
+
+ if (curlen) {
+ snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3);
+ } else {
+ snprintf(buffer, buflen, "%s", tmp3);
+ }
+
+ curfieldnum++;
+ }
+ }
+ }
+ } else {
+ return ERROR_NOARG;
+ }
+ return 0;
+}
+
+static int sort_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *varname, *strings, result[512] = "";
+ static int dep_warning=0;
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "The application Sort is deprecated. Please use the SORT() function instead.\n");
+ dep_warning=1;
+ }
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Sort() requires an argument\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ strings = ast_strdupa((char *)data);
+ if (!strings) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ varname = strsep(&strings, "=");
+ switch (sort_internal(chan, strings, result, sizeof(result))) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "Sort() requires an argument\n");
+ res = 0;
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ res = -1;
+ break;
+ case 0:
+ pbx_builtin_setvar_helper(chan, varname, result);
+ res = 0;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ res = -1;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int cut_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *s, *newvar=NULL, result[512];
+ static int dep_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "The application Cut is deprecated. Please use the CUT() function instead.\n");
+ dep_warning=1;
+ }
+
+ /* Check and parse arguments */
+ if (data) {
+ s = ast_strdupa((char *)data);
+ if (s) {
+ newvar = strsep(&s, "=");
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ switch (cut_internal(chan, s, result, sizeof(result))) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "Cut() requires an argument\n");
+ res = 0;
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ res = -1;
+ break;
+ case ERROR_USAGE:
+ ast_log(LOG_ERROR, "Usage: %s\n", cut_synopsis);
+ res = 0;
+ break;
+ case 0:
+ pbx_builtin_setvar_helper(chan, newvar, result);
+ res = 0;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ res = -1;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static char *acf_sort_exec(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+ struct localuser *u;
+
+ LOCAL_USER_ACF_ADD(u);
+
+ switch (sort_internal(chan, data, buf, len)) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "SORT() requires an argument\n");
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ case 0:
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ }
+ LOCAL_USER_REMOVE(u);
+ return buf;
+}
+
+static char *acf_cut_exec(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+ struct localuser *u;
+
+ LOCAL_USER_ACF_ADD(u);
+
+ switch (cut_internal(chan, data, buf, len)) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "CUT() requires an argument\n");
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ case ERROR_USAGE:
+ ast_log(LOG_ERROR, "Usage: %s\n", cut_synopsis);
+ break;
+ case 0:
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ }
+ LOCAL_USER_REMOVE(u);
+ return buf;
+}
+
+struct ast_custom_function acf_sort = {
+ .name = "SORT",
+ .synopsis = "Sorts a list of key/vals into a list of keys, based upon the vals",
+ .syntax = "SORT(key1:val1[...][,keyN:valN])",
+ .desc =
+"Takes a comma-separated list of keys and values, each separated by a colon, and returns a\n"
+"comma-separated list of the keys, sorted by their values. Values will be evaluated as\n"
+"floating-point numbers.\n",
+ .read = acf_sort_exec,
+};
+
+struct ast_custom_function acf_cut = {
+ .name = "CUT",
+ .synopsis = "Slices and dices strings, based upon a named delimiter.",
+ .syntax = "CUT(<varname>,<char-delim>,<range-spec>)",
+ .desc =
+" varname - variable you want cut\n"
+" char-delim - defaults to '-'\n"
+" range-spec - number of the field you want (1-based offset)\n"
+" may also be specified as a range (with -)\n"
+" or group of ranges and fields (with &)\n",
+ .read = acf_cut_exec,
+};
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&acf_cut);
+ res |= ast_custom_function_unregister(&acf_sort);
+ res |= ast_unregister_application(app_sort);
+ res |= ast_unregister_application(app_cut);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_custom_function_register(&acf_cut);
+ res |= ast_custom_function_register(&acf_sort);
+ res |= ast_register_application(app_sort, sort_exec, app_sort_synopsis, app_sort_descrip);
+ res |= ast_register_application(app_cut, cut_exec, cut_synopsis, cut_descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_datetime.c b/1.2-netsec/apps/app_datetime.c
new file mode 100644
index 000000000..b10ad97e6
--- /dev/null
+++ b/1.2-netsec/apps/app_datetime.c
@@ -0,0 +1,101 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Time of day - Report the time of day
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/say.h"
+
+static char *tdesc = "Date and Time";
+
+static char *app = "DateTime";
+
+static char *synopsis = "Say the date and time";
+
+static char *descrip =
+" DateTime(): This application will say the current date and time.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int datetime_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ time_t t;
+ struct localuser *u;
+ LOCAL_USER_ADD(u);
+ time(&t);
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ if (!res)
+ res = ast_say_datetime(chan, t, "", chan->language);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, datetime_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_db.c b/1.2-netsec/apps/app_db.c
new file mode 100644
index 000000000..245c8d3ca
--- /dev/null
+++ b/1.2-netsec/apps/app_db.c
@@ -0,0 +1,323 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ * Copyright (C) 2003, Jefferson Noxon
+ *
+ * Mark Spencer <markster@digium.com>
+ * Jefferson Noxon <jeff@debian.org>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Database access functions
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/options.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/astdb.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Database Access Functions";
+
+static char *g_descrip =
+" DBget(varname=family/key[|options]): This application will retrieve a value\n"
+"from the Asterisk database and store it in the given variable.\n"
+" Options:\n"
+" j - Jump to priority n+101 if the requested family/key isn't found.\n"
+" This application sets the following channel variable upon completion:\n"
+" DBGETSTATUS - This variable will contain the status of the attempt\n"
+" FOUND | NOTFOUND \n"
+" This application has been deprecated in favor of the DB function.\n";
+
+static char *p_descrip =
+" DBput(family/key=value): This application will store the given value in the\n"
+"specified location in the Asterisk database.\n"
+" This application has been deprecated in favor of the DB function.\n";
+
+static char *d_descrip =
+" DBdel(family/key): This applicaiton will delete a key from the Asterisk\n"
+"database.\n";
+
+static char *dt_descrip =
+" DBdeltree(family[/keytree]): This application will delete a family or keytree\n"
+"from the Asterisk database\n";
+
+static char *g_app = "DBget";
+static char *p_app = "DBput";
+static char *d_app = "DBdel";
+static char *dt_app = "DBdeltree";
+
+static char *g_synopsis = "Retrieve a value from the database";
+static char *p_synopsis = "Store a value in the database";
+static char *d_synopsis = "Delete a key from the database";
+static char *dt_synopsis = "Delete a family or keytree from the database";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int deltree_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *family, *keytree;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ argv = ast_strdupa(data);
+ if (!argv) {
+ ast_log(LOG_ERROR, "Memory allocation failed\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (strchr(argv, '/')) {
+ family = strsep(&argv, "/");
+ keytree = strsep(&argv, "\0");
+ if (!family || !keytree) {
+ ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ if (ast_strlen_zero(keytree))
+ keytree = 0;
+ } else {
+ family = argv;
+ keytree = 0;
+ }
+
+ if (option_verbose > 2) {
+ if (keytree)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s, keytree=%s\n", family, keytree);
+ else
+ ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s\n", family);
+ }
+
+ if (ast_db_deltree(family, keytree)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: Error deleting key from database.\n");
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static int del_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *family, *key;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ argv = ast_strdupa(data);
+ if (!argv) {
+ ast_log (LOG_ERROR, "Memory allocation failed\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (strchr(argv, '/')) {
+ family = strsep(&argv, "/");
+ key = strsep(&argv, "\0");
+ if (!family || !key) {
+ ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdel: family=%s, key=%s\n", family, key);
+ if (ast_db_del(family, key)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdel: Error deleting key from database.\n");
+ }
+ } else {
+ ast_log(LOG_DEBUG, "Ignoring, no parameters\n");
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static int put_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *value, *family, *key;
+ static int dep_warning = 0;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "This application has been deprecated, please use the ${DB(family/key)} function instead.\n");
+ dep_warning = 1;
+ }
+
+ argv = ast_strdupa(data);
+ if (!argv) {
+ ast_log(LOG_ERROR, "Memory allocation failed\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (strchr(argv, '/') && strchr(argv, '=')) {
+ family = strsep(&argv, "/");
+ key = strsep(&argv, "=");
+ value = strsep(&argv, "\0");
+ if (!value || !family || !key) {
+ ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBput: family=%s, key=%s, value=%s\n", family, key, value);
+ if (ast_db_put(family, key, value)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBput: Error writing value to database.\n");
+ }
+
+ } else {
+ ast_log (LOG_DEBUG, "Ignoring, no parameters\n");
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static int get_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *varname, *family, *key, *options = NULL;
+ char dbresult[256];
+ static int dep_warning = 0;
+ int priority_jump = 0;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "This application has been deprecated, please use the ${DB(family/key)} function instead.\n");
+ dep_warning = 1;
+ }
+
+ argv = ast_strdupa(data);
+ if (!argv) {
+ ast_log(LOG_ERROR, "Memory allocation failed\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (strchr(argv, '=') && strchr(argv, '/')) {
+ varname = strsep(&argv, "=");
+ family = strsep(&argv, "/");
+ if (strchr((void *)&argv, '|')) {
+ key = strsep(&argv, "|");
+ options = strsep(&argv, "\0");
+ } else
+ key = strsep(&argv, "\0");
+
+ if (!varname || !family || !key) {
+ ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (options) {
+ if (strchr(options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBget: varname=%s, family=%s, key=%s\n", varname, family, key);
+ if (!ast_db_get(family, key, dbresult, sizeof (dbresult) - 1)) {
+ pbx_builtin_setvar_helper(chan, varname, dbresult);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBget: set variable %s to %s\n", varname, dbresult);
+ pbx_builtin_setvar_helper(chan, "DBGETSTATUS", "FOUND");
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBget: Value not found in database.\n");
+ if (priority_jump || option_priority_jumping) {
+ /* Send the call to n+101 priority, where n is the current priority */
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ }
+ pbx_builtin_setvar_helper(chan, "DBGETSTATUS", "NOTFOUND");
+ }
+ } else {
+ ast_log(LOG_DEBUG, "Ignoring, no parameters\n");
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module(void)
+{
+ int retval;
+
+ retval = ast_unregister_application(dt_app);
+ retval |= ast_unregister_application(d_app);
+ retval |= ast_unregister_application(p_app);
+ retval |= ast_unregister_application(g_app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return retval;
+}
+
+int load_module(void)
+{
+ int retval;
+
+ retval = ast_register_application(g_app, get_exec, g_synopsis, g_descrip);
+ retval |= ast_register_application(p_app, put_exec, p_synopsis, p_descrip);
+ retval |= ast_register_application(d_app, del_exec, d_synopsis, d_descrip);
+ retval |= ast_register_application(dt_app, deltree_exec, dt_synopsis, dt_descrip);
+
+ return retval;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_dial.c b/1.2-netsec/apps/app_dial.c
new file mode 100644
index 000000000..5ec606062
--- /dev/null
+++ b/1.2-netsec/apps/app_dial.c
@@ -0,0 +1,1748 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/say.h"
+#include "asterisk/config.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/causes.h"
+#include "asterisk/manager.h"
+#include "asterisk/privacy.h"
+
+static char *tdesc = "Dialing Application";
+
+static char *app = "Dial";
+
+static char *synopsis = "Place a call and connect to the current channel";
+
+static char *descrip =
+" Dial(Technology/resource[&Tech2/resource2...][|timeout][|options][|URL]):\n"
+"This applicaiton will place calls to one or more specified channels. As soon\n"
+"as one of the requested channels answers, the originating channel will be\n"
+"answered, if it has not already been answered. These two channels will then\n"
+"be active in a bridged call. All other channels that were requested will then\n"
+"be hung up.\n"
+" Unless there is a timeout specified, the Dial application will wait\n"
+"indefinitely until one of the called channels answers, the user hangs up, or\n"
+"if all of the called channels are busy or unavailable. Dialplan executing will\n"
+"continue if no requested channels can be called, or if the timeout expires.\n\n"
+" This application sets the following channel variables upon completion:\n"
+" DIALEDTIME - This is the time from dialing a channel until when it\n"
+" is disconnected.\n"
+" ANSWEREDTIME - This is the amount of time for actual call.\n"
+" DIALSTATUS - This is the status of the call:\n"
+" CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n"
+" DONTCALL | TORTURE\n"
+" For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n"
+"DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n"
+"script. The DIALSTATUS variable will be set to TORTURE if the called party\n"
+"wants to send the caller to the 'torture' script.\n"
+" This application will report normal termination if the originating channel\n"
+"hangs up, or if the call is bridged and either of the parties in the bridge\n"
+"ends the call.\n"
+" The optional URL will be sent to the called party if the channel supports it.\n"
+" If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
+"application will be put into that group (as in Set(GROUP()=...).\n\n"
+" Options:\n"
+" A(x) - Play an announcement to the called party, using 'x' as the file.\n"
+" C - Reset the CDR for this call.\n"
+" d - Allow the calling user to dial a 1 digit extension while waiting for\n"
+" a call to be answered. Exit to that extension if it exists in the\n"
+" current context, or the context defined in the EXITCONTEXT variable,\n"
+" if it exists.\n"
+" D([called][:calling]) - Send the specified DTMF strings *after* the called\n"
+" party has answered, but before the call gets bridged. The 'called'\n"
+" DTMF string is sent to the called party, and the 'calling' DTMF\n"
+" string is sent to the calling party. Both parameters can be used\n"
+" alone.\n"
+" f - Force the callerid of the *calling* channel to be set as the\n"
+" extension associated with the channel using a dialplan 'hint'.\n"
+" For example, some PSTNs do not allow CallerID to be set to anything\n"
+" other than the number assigned to the caller.\n"
+" g - Proceed with dialplan execution at the current extension if the\n"
+" destination channel hangs up.\n"
+" G(context^exten^pri) - If the call is answered, transfer the calling party to\n"
+" the specified priority and the called party to the specified priority+1.\n"
+" Optionally, an extension, or extension and context may be specified. \n"
+" Otherwise, the current extension is used.\n"
+" h - Allow the called party to hang up by sending the '*' DTMF digit.\n"
+" H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
+" j - Jump to priority n+101 if all of the requested channels were busy.\n"
+" L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n"
+" left. Repeat the warning every 'z' ms. The following special\n"
+" variables can be used with this option:\n"
+" * LIMIT_PLAYAUDIO_CALLER yes|no (default yes)\n"
+" Play sounds to the caller.\n"
+" * LIMIT_PLAYAUDIO_CALLEE yes|no\n"
+" Play sounds to the callee.\n"
+" * LIMIT_TIMEOUT_FILE File to play when time is up.\n"
+" * LIMIT_CONNECT_FILE File to play when call begins.\n"
+" * LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
+" The default is to say the time remaining.\n"
+" m([class]) - Provide hold music to the calling party until a requested\n"
+" channel answers. A specific MusicOnHold class can be\n"
+" specified.\n"
+" M(x[^arg]) - Execute the Macro for the *called* channel before connecting\n"
+" to the calling channel. Arguments can be specified to the Macro\n"
+" using '^' as a delimeter. The Macro can set the variable\n"
+" MACRO_RESULT to specify the following actions after the Macro is\n"
+" finished executing.\n"
+" * ABORT Hangup both legs of the call.\n"
+" * CONGESTION Behave as if line congestion was encountered.\n"
+" * BUSY Behave as if a busy signal was encountered. This will also\n"
+" have the application jump to priority n+101 if the\n"
+" 'j' option is set.\n"
+" * CONTINUE Hangup the called party and allow the calling party\n"
+" to continue dialplan execution at the next priority.\n"
+" * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
+" specified priority. Optionally, an extension, or\n"
+" extension and priority can be specified.\n"
+" n - This option is a modifier for the screen/privacy mode. It specifies\n"
+" that no introductions are to be saved in the priv-callerintros\n"
+" directory.\n"
+" N - This option is a modifier for the screen/privacy mode. It specifies\n"
+" that if callerID is present, do not screen the call.\n"
+" o - Specify that the CallerID that was present on the *calling* channel\n"
+" be set as the CallerID on the *called* channel. This was the\n"
+" behavior of Asterisk 1.0 and earlier.\n"
+" p - This option enables screening mode. This is basically Privacy mode\n"
+" without memory.\n"
+" P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if\n"
+" it is provided. The current extension is used if a database\n"
+" family/key is not specified.\n"
+" r - Indicate ringing to the calling party. Pass no audio to the calling\n"
+" party until the called channel has answered.\n"
+" S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
+" answered the call.\n"
+" t - Allow the called party to transfer the calling party by sending the\n"
+" DTMF sequence defined in features.conf.\n"
+" T - Allow the calling party to transfer the called party by sending the\n"
+" DTMF sequence defined in features.conf.\n"
+" w - Allow the called party to enable recording of the call by sending\n"
+" the DTMF sequence defined for one-touch recording in features.conf.\n"
+" W - Allow the calling party to enable recording of the call by sending\n"
+" the DTMF sequence defined for one-touch recording in features.conf.\n";
+
+/* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
+static char *rapp = "RetryDial";
+static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension.";
+static char *rdescrip =
+" RetryDial(announce|sleep|retries|dialargs): This application will attempt to\n"
+"place a call using the normal Dial application. If no channel can be reached,\n"
+"the 'announce' file will be played. Then, it will wait 'sleep' number of\n"
+"seconds before retying the call. After 'retires' number of attempts, the\n"
+"calling channel will continue at the next priority in the dialplan. If the\n"
+"'retries' setting is set to 0, this application will retry endlessly.\n"
+" While waiting to retry a call, a 1 digit extension may be dialed. If that\n"
+"extension exists in either the context defined in ${EXITCONTEXT} or the current\n"
+"one, The call will jump to that extension immediately.\n"
+" The 'dialargs' are specified in the same format that arguments are provided\n"
+"to the Dial application.\n";
+
+enum {
+ OPT_ANNOUNCE = (1 << 0),
+ OPT_RESETCDR = (1 << 1),
+ OPT_DTMF_EXIT = (1 << 2),
+ OPT_SENDDTMF = (1 << 3),
+ OPT_FORCECLID = (1 << 4),
+ OPT_GO_ON = (1 << 5),
+ OPT_CALLEE_HANGUP = (1 << 6),
+ OPT_CALLER_HANGUP = (1 << 7),
+ OPT_PRIORITY_JUMP = (1 << 8),
+ OPT_DURATION_LIMIT = (1 << 9),
+ OPT_MUSICBACK = (1 << 10),
+ OPT_CALLEE_MACRO = (1 << 11),
+ OPT_SCREEN_NOINTRO = (1 << 12),
+ OPT_SCREEN_NOCLID = (1 << 13),
+ OPT_ORIGINAL_CLID = (1 << 14),
+ OPT_SCREENING = (1 << 15),
+ OPT_PRIVACY = (1 << 16),
+ OPT_RINGBACK = (1 << 17),
+ OPT_DURATION_STOP = (1 << 18),
+ OPT_CALLEE_TRANSFER = (1 << 19),
+ OPT_CALLER_TRANSFER = (1 << 20),
+ OPT_CALLEE_MONITOR = (1 << 21),
+ OPT_CALLER_MONITOR = (1 << 22),
+ OPT_GOTO = (1 << 23),
+} dial_exec_option_flags;
+
+#define DIAL_STILLGOING (1 << 30)
+#define DIAL_NOFORWARDHTML (1 << 31)
+
+enum {
+ OPT_ARG_ANNOUNCE = 0,
+ OPT_ARG_SENDDTMF,
+ OPT_ARG_GOTO,
+ OPT_ARG_DURATION_LIMIT,
+ OPT_ARG_MUSICBACK,
+ OPT_ARG_CALLEE_MACRO,
+ OPT_ARG_PRIVACY,
+ OPT_ARG_DURATION_STOP,
+ /* note: this entry _MUST_ be the last one in the enum */
+ OPT_ARG_ARRAY_SIZE,
+} dial_exec_option_args;
+
+AST_APP_OPTIONS(dial_exec_options, {
+ AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
+ AST_APP_OPTION('C', OPT_RESETCDR),
+ AST_APP_OPTION('d', OPT_DTMF_EXIT),
+ AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
+ AST_APP_OPTION('f', OPT_FORCECLID),
+ AST_APP_OPTION('g', OPT_GO_ON),
+ AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO),
+ AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
+ AST_APP_OPTION('H', OPT_CALLER_HANGUP),
+ AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
+ AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
+ AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
+ AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
+ AST_APP_OPTION('n', OPT_SCREEN_NOINTRO),
+ AST_APP_OPTION('N', OPT_SCREEN_NOCLID),
+ AST_APP_OPTION('o', OPT_ORIGINAL_CLID),
+ AST_APP_OPTION('p', OPT_SCREENING),
+ AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
+ AST_APP_OPTION('r', OPT_RINGBACK),
+ AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
+ AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
+ AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+ AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
+ AST_APP_OPTION('W', OPT_CALLER_MONITOR),
+});
+
+/* We define a custom "local user" structure because we
+ use it not only for keeping track of what is in use but
+ also for keeping track of who we're dialing. */
+
+struct localuser {
+ struct ast_channel *chan;
+ unsigned int flags;
+ int forwards;
+ struct localuser *next;
+};
+
+LOCAL_USER_DECL;
+
+static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
+{
+ /* Hang up a tree of stuff */
+ struct localuser *oo;
+ while (outgoing) {
+ /* Hangup any existing lines we have open */
+ if (outgoing->chan && (outgoing->chan != exception))
+ ast_hangup(outgoing->chan);
+ oo = outgoing;
+ outgoing=outgoing->next;
+ free(oo);
+ }
+}
+
+#define AST_MAX_FORWARDS 8
+
+#define AST_MAX_WATCHERS 256
+
+#define HANDLE_CAUSE(cause, chan) do { \
+ switch(cause) { \
+ case AST_CAUSE_BUSY: \
+ if (chan->cdr) \
+ ast_cdr_busy(chan->cdr); \
+ numbusy++; \
+ break; \
+ case AST_CAUSE_CONGESTION: \
+ if (chan->cdr) \
+ ast_cdr_failed(chan->cdr); \
+ numcongestion++; \
+ break; \
+ case AST_CAUSE_UNREGISTERED: \
+ if (chan->cdr) \
+ ast_cdr_failed(chan->cdr); \
+ numnochan++; \
+ break; \
+ default: \
+ numnochan++; \
+ break; \
+ } \
+} while (0)
+
+
+static int onedigit_goto(struct ast_channel *chan, char *context, char exten, int pri)
+{
+ char rexten[2] = { exten, '\0' };
+
+ if (context) {
+ if (!ast_goto_if_exists(chan, context, rexten, pri))
+ return 1;
+ } else {
+ if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
+ return 1;
+ else if (!ast_strlen_zero(chan->macrocontext)) {
+ if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
+{
+ char *context;
+ char *exten;
+ if (!ast_strlen_zero(chan->macrocontext))
+ context = chan->macrocontext;
+ else
+ context = chan->context;
+
+ if (!ast_strlen_zero(chan->macroexten))
+ exten = chan->macroexten;
+ else
+ exten = chan->exten;
+
+ if (ast_get_hint(NULL, 0, name, namelen, chan, context, exten))
+ return name;
+ else
+ return "";
+}
+
+static void senddialevent(struct ast_channel *src, struct ast_channel *dst)
+{
+ manager_event(EVENT_FLAG_CALL, "Dial",
+ "Source: %s\r\n"
+ "Destination: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "SrcUniqueID: %s\r\n"
+ "DestUniqueID: %s\r\n",
+ src->name, dst->name, src->cid.cid_num ? src->cid.cid_num : "<unknown>",
+ src->cid.cid_name ? src->cid.cid_name : "<unknown>", src->uniqueid,
+ dst->uniqueid);
+}
+
+static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, struct ast_flags *peerflags, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart, int priority_jump, int *result)
+{
+ struct localuser *o;
+ int found;
+ int numlines;
+ int numbusy = busystart;
+ int numcongestion = congestionstart;
+ int numnochan = nochanstart;
+ int prestart = busystart + congestionstart + nochanstart;
+ int cause;
+ int orig = *to;
+ struct ast_frame *f;
+ struct ast_channel *peer = NULL;
+ struct ast_channel *watchers[AST_MAX_WATCHERS];
+ int pos;
+ int single;
+ struct ast_channel *winner;
+ char *context = NULL;
+ char cidname[AST_MAX_EXTENSION];
+
+ single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK));
+
+ if (single) {
+ /* Turn off hold music, etc */
+ ast_deactivate_generator(in);
+ /* If we are calling a single channel, make them compatible for in-band tone purpose */
+ ast_channel_make_compatible(outgoing->chan, in);
+ }
+
+
+ while (*to && !peer) {
+ o = outgoing;
+ found = -1;
+ pos = 1;
+ numlines = prestart;
+ watchers[0] = in;
+ while (o) {
+ /* Keep track of important channels */
+ if (ast_test_flag(o, DIAL_STILLGOING) && o->chan) {
+ watchers[pos++] = o->chan;
+ found = 1;
+ }
+ o = o->next;
+ numlines++;
+ }
+ if (found < 0) {
+ if (numlines == (numbusy + numcongestion + numnochan)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
+ if (numbusy)
+ strcpy(status, "BUSY");
+ else if (numcongestion)
+ strcpy(status, "CONGESTION");
+ else if (numnochan)
+ strcpy(status, "CHANUNAVAIL");
+ if (option_priority_jumping || priority_jump)
+ ast_goto_if_exists(in, in->context, in->exten, in->priority + 101);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
+ }
+ *to = 0;
+ return NULL;
+ }
+ winner = ast_waitfor_n(watchers, pos, to);
+ o = outgoing;
+ while (o) {
+ if (ast_test_flag(o, DIAL_STILLGOING) && o->chan && (o->chan->_state == AST_STATE_UP)) {
+ if (!peer) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
+ peer = o->chan;
+ ast_copy_flags(peerflags, o,
+ OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ DIAL_NOFORWARDHTML);
+ }
+ } else if (o->chan && (o->chan == winner)) {
+ if (!ast_strlen_zero(o->chan->call_forward)) {
+ char tmpchan[256];
+ char *stuff;
+ char *tech;
+ ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
+ if ((stuff = strchr(tmpchan, '/'))) {
+ *stuff = '\0';
+ stuff++;
+ tech = tmpchan;
+ } else {
+ snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
+ stuff = tmpchan;
+ tech = "Local";
+ }
+ /* Before processing channel, go ahead and check for forwarding */
+ o->forwards++;
+ if (o->forwards < AST_MAX_FORWARDS) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
+ /* Setup parameters */
+ o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
+ if (!o->chan)
+ ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", o->chan->name);
+ cause = AST_CAUSE_CONGESTION;
+ o->chan = NULL;
+ }
+ if (!o->chan) {
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(cause, in);
+ } else {
+ if (o->chan->cid.cid_num)
+ free(o->chan->cid.cid_num);
+ o->chan->cid.cid_num = NULL;
+ if (o->chan->cid.cid_name)
+ free(o->chan->cid.cid_name);
+ o->chan->cid.cid_name = NULL;
+
+ if (ast_test_flag(o, OPT_FORCECLID)) {
+ char *newcid = NULL;
+
+ if (!ast_strlen_zero(in->macroexten))
+ newcid = in->macroexten;
+ else
+ newcid = in->exten;
+ o->chan->cid.cid_num = strdup(newcid);
+ ast_copy_string(o->chan->accountcode, winner->accountcode, sizeof(o->chan->accountcode));
+ o->chan->cdrflags = winner->cdrflags;
+ if (!o->chan->cid.cid_num)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ } else {
+ if (in->cid.cid_num) {
+ o->chan->cid.cid_num = strdup(in->cid.cid_num);
+ if (!o->chan->cid.cid_num)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ if (in->cid.cid_name) {
+ o->chan->cid.cid_name = strdup(in->cid.cid_name);
+ if (!o->chan->cid.cid_name)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
+ o->chan->cdrflags = in->cdrflags;
+ }
+
+ if (in->cid.cid_ani) {
+ if (o->chan->cid.cid_ani)
+ free(o->chan->cid.cid_ani);
+ o->chan->cid.cid_ani = strdup(in->cid.cid_ani);
+ if (!o->chan->cid.cid_ani)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ if (o->chan->cid.cid_rdnis)
+ free(o->chan->cid.cid_rdnis);
+ if (!ast_strlen_zero(in->macroexten))
+ o->chan->cid.cid_rdnis = strdup(in->macroexten);
+ else
+ o->chan->cid.cid_rdnis = strdup(in->exten);
+ if (ast_call(o->chan, tmpchan, 0)) {
+ ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
+ ast_clear_flag(o, DIAL_STILLGOING);
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ numnochan++;
+ } else {
+ senddialevent(in, o->chan);
+ /* After calling, set callerid to extension */
+ if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID))
+ ast_set_callerid(o->chan, ast_strlen_zero(in->macroexten) ? in->exten : in->macroexten, get_cid_name(cidname, sizeof(cidname), in), NULL);
+ }
+ }
+ /* Hangup the original channel now, in case we needed it */
+ ast_hangup(winner);
+ continue;
+ }
+ f = ast_read(winner);
+ if (f) {
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch(f->subclass) {
+ case AST_CONTROL_ANSWER:
+ /* This is our guy if someone answered. */
+ if (!peer) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
+ peer = o->chan;
+ ast_copy_flags(peerflags, o,
+ OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ DIAL_NOFORWARDHTML);
+ }
+ /* If call has been answered, then the eventual hangup is likely to be normal hangup */
+ in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ o->chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ break;
+ case AST_CONTROL_BUSY:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
+ in->hangupcause = o->chan->hangupcause;
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(AST_CAUSE_BUSY, in);
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
+ in->hangupcause = o->chan->hangupcause;
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
+ break;
+ case AST_CONTROL_RINGING:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
+ if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) {
+ ast_indicate(in, AST_CONTROL_RINGING);
+ (*sentringing)++;
+ }
+ break;
+ case AST_CONTROL_PROGRESS:
+ if (option_verbose > 2)
+ ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", o->chan->name,in->name);
+ if (!ast_test_flag(outgoing, OPT_RINGBACK))
+ ast_indicate(in, AST_CONTROL_PROGRESS);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+ if (option_verbose > 2)
+ ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", o->chan->name,in->name);
+ ast_indicate(in, AST_CONTROL_VIDUPDATE);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ if (option_verbose > 2)
+ ast_verbose ( VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", o->chan->name,in->name);
+ if (!ast_test_flag(outgoing, OPT_RINGBACK))
+ ast_indicate(in, AST_CONTROL_PROCEEDING);
+ break;
+ case AST_CONTROL_HOLD:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", o->chan->name);
+ ast_indicate(in, AST_CONTROL_HOLD);
+ break;
+ case AST_CONTROL_UNHOLD:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", o->chan->name);
+ ast_indicate(in, AST_CONTROL_UNHOLD);
+ break;
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_FLASH:
+ /* Ignore going off hook and flash */
+ break;
+ case -1:
+ if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", o->chan->name);
+ ast_indicate(in, -1);
+ (*sentringing) = 0;
+ }
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
+ }
+ } else if (single && (f->frametype == AST_FRAME_VOICE) &&
+ !(ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK))) {
+ if (ast_write(in, f))
+ ast_log(LOG_DEBUG, "Unable to forward frame\n");
+ } else if (single && (f->frametype == AST_FRAME_IMAGE) &&
+ !(ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK))) {
+ if (ast_write(in, f))
+ ast_log(LOG_DEBUG, "Unable to forward image\n");
+ } else if (single && (f->frametype == AST_FRAME_TEXT) &&
+ !(ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK))) {
+ if (ast_write(in, f))
+ ast_log(LOG_DEBUG, "Unable to text\n");
+ } else if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML))
+ ast_channel_sendhtml(in, f->subclass, f->data, f->datalen);
+
+ ast_frfree(f);
+ } else {
+ in->hangupcause = o->chan->hangupcause;
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(in->hangupcause, in);
+ }
+ }
+ o = o->next;
+ }
+ if (winner == in) {
+ f = ast_read(in);
+#if 0
+ if (f && (f->frametype != AST_FRAME_VOICE))
+ printf("Frame type: %d, %d\n", f->frametype, f->subclass);
+ else if (!f || (f->frametype != AST_FRAME_VOICE))
+ printf("Hangup received on %s\n", in->name);
+#endif
+ if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
+ /* Got hung up */
+ *to=-1;
+ strcpy(status, "CANCEL");
+ if (f)
+ ast_frfree(f);
+ return NULL;
+ }
+
+ if (f && (f->frametype == AST_FRAME_DTMF)) {
+ if (ast_test_flag(peerflags, OPT_DTMF_EXIT)) {
+ context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
+ if (onedigit_goto(in, context, (char) f->subclass, 1)) {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
+ *to=0;
+ *result = f->subclass;
+ strcpy(status, "CANCEL");
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+
+ if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) &&
+ (f->subclass == '*')) { /* hmm it it not guarenteed to be '*' anymore. */
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
+ *to=0;
+ strcpy(status, "CANCEL");
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+
+ /* Forward HTML stuff */
+ if (single && f && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML))
+ ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen);
+
+
+ if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF))) {
+ if (ast_write(outgoing->chan, f))
+ ast_log(LOG_WARNING, "Unable to forward voice\n");
+ }
+ if (single && (f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_VIDUPDATE)) {
+ if (option_verbose > 2)
+ ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", in->name,outgoing->chan->name);
+ ast_indicate(outgoing->chan, AST_CONTROL_VIDUPDATE);
+ }
+ ast_frfree(f);
+ }
+ if (!*to && (option_verbose > 2))
+ ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+ }
+
+ return peer;
+
+}
+
+static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags)
+{
+ int res=-1;
+ struct localuser *u;
+ char *tech, *number, *rest, *cur;
+ char privcid[256];
+ char privintro[1024];
+ struct localuser *outgoing=NULL, *tmp;
+ struct ast_channel *peer;
+ int to;
+ int numbusy = 0;
+ int numcongestion = 0;
+ int numnochan = 0;
+ int cause;
+ char numsubst[AST_MAX_EXTENSION];
+ char restofit[AST_MAX_EXTENSION];
+ char cidname[AST_MAX_EXTENSION];
+ char toast[80];
+ char *newnum;
+ char *l;
+ int privdb_val=0;
+ unsigned int calldurationlimit=0;
+ struct ast_bridge_config config;
+ long timelimit = 0;
+ long play_warning = 0;
+ long warning_freq=0;
+ char *warning_sound=NULL;
+ char *end_sound=NULL;
+ char *start_sound=NULL;
+ char *dtmfcalled=NULL, *dtmfcalling=NULL;
+ char *var;
+ char status[256];
+ int play_to_caller=0,play_to_callee=0;
+ int sentringing=0, moh=0;
+ char *outbound_group = NULL;
+ char *macro_result = NULL, *macro_transfer_dest = NULL;
+ int digit = 0, result = 0;
+ time_t start_time, answer_time, end_time;
+ struct ast_app *app = NULL;
+
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(peers);
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(options);
+ AST_APP_ARG(url);
+ );
+ struct ast_flags opts = { 0, };
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory allocation failure\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options)) {
+ if (ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ if (ast_strlen_zero(args.peers)) {
+ ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (ast_test_flag(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) {
+ calldurationlimit = atoi(opt_args[OPT_ARG_DURATION_STOP]);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n",calldurationlimit);
+ }
+
+ if (ast_test_flag(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
+ parse = opt_args[OPT_ARG_SENDDTMF];
+ dtmfcalled = strsep(&parse, ":");
+ dtmfcalling = parse;
+ }
+
+ if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
+ char *limit_str, *warning_str, *warnfreq_str;
+
+ parse = opt_args[OPT_ARG_DURATION_LIMIT];
+ limit_str = strsep(&parse, ":");
+ warning_str = strsep(&parse, ":");
+ warnfreq_str = parse;
+
+ timelimit = atol(limit_str);
+ if (warning_str)
+ play_warning = atol(warning_str);
+ if (warnfreq_str)
+ warning_freq = atol(warnfreq_str);
+
+ if (!timelimit) {
+ timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0;
+ warning_sound = NULL;
+ }
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLER");
+ play_to_caller = var ? ast_true(var) : 1;
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLEE");
+ play_to_callee = var ? ast_true(var) : 0;
+
+ if (!play_to_caller && !play_to_callee)
+ play_to_caller=1;
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_WARNING_FILE");
+ warning_sound = var ? var : "timeleft";
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE");
+ end_sound = var ? var : NULL;
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
+ start_sound = var ? var : NULL;
+
+ /* undo effect of S(x) in case they are both used */
+ calldurationlimit = 0;
+ /* more efficient do it like S(x) does since no advanced opts*/
+ if (!play_warning && !start_sound && !end_sound && timelimit) {
+ calldurationlimit = timelimit/1000;
+ timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0;
+ } else if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "Limit Data for this call:\n");
+ ast_verbose(VERBOSE_PREFIX_3 "- timelimit = %ld\n", timelimit);
+ ast_verbose(VERBOSE_PREFIX_3 "- play_warning = %ld\n", play_warning);
+ ast_verbose(VERBOSE_PREFIX_3 "- play_to_caller= %s\n", play_to_caller ? "yes" : "no");
+ ast_verbose(VERBOSE_PREFIX_3 "- play_to_callee= %s\n", play_to_callee ? "yes" : "no");
+ ast_verbose(VERBOSE_PREFIX_3 "- warning_freq = %ld\n", warning_freq);
+ ast_verbose(VERBOSE_PREFIX_3 "- start_sound = %s\n", start_sound ? start_sound : "UNDEF");
+ ast_verbose(VERBOSE_PREFIX_3 "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
+ ast_verbose(VERBOSE_PREFIX_3 "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
+ }
+ }
+
+ if (ast_test_flag(&opts, OPT_RESETCDR) && chan->cdr)
+ ast_cdr_reset(chan->cdr, NULL);
+ if (ast_test_flag(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
+ opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten);
+ if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) {
+ char callerid[60];
+
+ l = chan->cid.cid_num;
+ if (!ast_strlen_zero(l)) {
+ ast_shrink_phone_number(l);
+ if( ast_test_flag(&opts, OPT_PRIVACY) ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Privacy DB is '%s', clid is '%s'\n",
+ opt_args[OPT_ARG_PRIVACY], l);
+ privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
+ }
+ else {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Privacy Screening, clid is '%s'\n", l);
+ privdb_val = AST_PRIVACY_UNKNOWN;
+ }
+ } else {
+ char *tnam, *tn2;
+
+ tnam = ast_strdupa(chan->name);
+ /* clean the channel name so slashes don't try to end up in disk file name */
+ for(tn2 = tnam; *tn2; tn2++) {
+ if( *tn2=='/')
+ *tn2 = '='; /* any other chars to be afraid of? */
+ }
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Privacy-- callerid is empty\n");
+
+ snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
+ l = callerid;
+ privdb_val = AST_PRIVACY_UNKNOWN;
+ }
+
+ ast_copy_string(privcid,l,sizeof(privcid));
+
+ if( strncmp(privcid,"NOCALLERID",10) != 0 && ast_test_flag(&opts, OPT_SCREEN_NOCLID) ) { /* if callerid is set, and ast_test_flag(&opts, OPT_SCREEN_NOCLID) is set also */
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "CallerID set (%s); N option set; Screening should be off\n", privcid);
+ privdb_val = AST_PRIVACY_ALLOW;
+ }
+ else if( ast_test_flag(&opts, OPT_SCREEN_NOCLID) && strncmp(privcid,"NOCALLERID",10) == 0 ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "CallerID blank; N option set; Screening should happen; dbval is %d\n", privdb_val);
+ }
+
+ if( privdb_val == AST_PRIVACY_DENY ) {
+ ast_verbose( VERBOSE_PREFIX_3 "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
+ res=0;
+ goto out;
+ }
+ else if( privdb_val == AST_PRIVACY_KILL ) {
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201);
+ res = 0;
+ goto out; /* Is this right? */
+ }
+ else if( privdb_val == AST_PRIVACY_TORTURE ) {
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 301);
+ res = 0;
+ goto out; /* is this right??? */
+
+ }
+ else if( privdb_val == AST_PRIVACY_UNKNOWN ) {
+ /* Get the user's intro, store it in priv-callerintros/$CID,
+ unless it is already there-- this should be done before the
+ call is actually dialed */
+
+ /* make sure the priv-callerintros dir exists? */
+
+ snprintf(privintro,sizeof(privintro),"priv-callerintros/%s", privcid);
+ if( ast_fileexists(privintro,NULL,NULL ) > 0 && strncmp(privcid,"NOCALLERID",10) != 0) {
+ /* the DELUX version of this code would allow this caller the
+ option to hear and retape their previously recorded intro.
+ */
+ }
+ else {
+ int duration; /* for feedback from play_and_wait */
+ /* the file doesn't exist yet. Let the caller submit his
+ vocal intro for posterity */
+ /* priv-recordintro script:
+
+ "At the tone, please say your name:"
+
+ */
+ ast_play_and_record(chan, "priv-recordintro", privintro, 4, "gsm", &duration, 128, 2000, 0); /* NOTE: I've reduced the total time to 4 sec */
+ /* don't think we'll need a lock removed, we took care of
+ conflicts by naming the privintro file */
+ }
+ }
+ }
+
+ /* If a channel group has been specified, get it for use when we create peer channels */
+ outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
+
+ ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP);
+ cur = args.peers;
+ do {
+ /* Remember where to start next time */
+ rest = strchr(cur, '&');
+ if (rest) {
+ *rest = 0;
+ rest++;
+ }
+ /* Get a technology/[device:]number pair */
+ tech = cur;
+ number = strchr(tech, '/');
+ if (!number) {
+ ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
+ goto out;
+ }
+ *number = '\0';
+ number++;
+ tmp = malloc(sizeof(struct localuser));
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ goto out;
+ }
+ memset(tmp, 0, sizeof(struct localuser));
+ if (opts.flags) {
+ ast_copy_flags(tmp, &opts,
+ OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
+ ast_set2_flag(tmp, args.url, DIAL_NOFORWARDHTML);
+ }
+ ast_copy_string(numsubst, number, sizeof(numsubst));
+ /* If we're dialing by extension, look at the extension to know what to dial */
+ if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
+ /* strlen("BYEXTENSION") == 11 */
+ ast_copy_string(restofit, newnum + 11, sizeof(restofit));
+ snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
+ }
+ /* Request the peer */
+ tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
+ if (!tmp->chan) {
+ /* If we can't, just go on to the next call */
+ ast_log(LOG_NOTICE, "Unable to create channel of type '%s' (cause %d - %s)\n", tech, cause, ast_cause2str(cause));
+ HANDLE_CAUSE(cause, chan);
+ cur = rest;
+ if (!cur)
+ chan->hangupcause = cause;
+ continue;
+ }
+ pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst);
+ if (!ast_strlen_zero(tmp->chan->call_forward)) {
+ char tmpchan[256];
+ char *stuff;
+ char *tech;
+ ast_copy_string(tmpchan, tmp->chan->call_forward, sizeof(tmpchan));
+ if ((stuff = strchr(tmpchan, '/'))) {
+ *stuff = '\0';
+ stuff++;
+ tech = tmpchan;
+ } else {
+ snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context);
+ stuff = tmpchan;
+ tech = "Local";
+ }
+ tmp->forwards++;
+ if (tmp->forwards < AST_MAX_FORWARDS) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
+ ast_hangup(tmp->chan);
+ /* Setup parameters */
+ tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause);
+ if (!tmp->chan)
+ ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", tmp->chan->name);
+ ast_hangup(tmp->chan);
+ tmp->chan = NULL;
+ cause = AST_CAUSE_CONGESTION;
+ }
+ if (!tmp->chan) {
+ HANDLE_CAUSE(cause, chan);
+ cur = rest;
+ continue;
+ }
+ }
+
+ /* Inherit specially named variables from parent channel */
+ ast_channel_inherit_variables(chan, tmp->chan);
+
+ tmp->chan->appl = "AppDial";
+ tmp->chan->data = "(Outgoing Line)";
+ tmp->chan->whentohangup = 0;
+ if (tmp->chan->cid.cid_num)
+ free(tmp->chan->cid.cid_num);
+ tmp->chan->cid.cid_num = NULL;
+ if (tmp->chan->cid.cid_name)
+ free(tmp->chan->cid.cid_name);
+ tmp->chan->cid.cid_name = NULL;
+ if (tmp->chan->cid.cid_ani)
+ free(tmp->chan->cid.cid_ani);
+ tmp->chan->cid.cid_ani = NULL;
+
+ if (chan->cid.cid_num)
+ tmp->chan->cid.cid_num = strdup(chan->cid.cid_num);
+ if (chan->cid.cid_name)
+ tmp->chan->cid.cid_name = strdup(chan->cid.cid_name);
+ if (chan->cid.cid_ani)
+ tmp->chan->cid.cid_ani = strdup(chan->cid.cid_ani);
+
+ /* Copy language from incoming to outgoing */
+ ast_copy_string(tmp->chan->language, chan->language, sizeof(tmp->chan->language));
+ ast_copy_string(tmp->chan->accountcode, chan->accountcode, sizeof(tmp->chan->accountcode));
+ tmp->chan->cdrflags = chan->cdrflags;
+ if (ast_strlen_zero(tmp->chan->musicclass))
+ ast_copy_string(tmp->chan->musicclass, chan->musicclass, sizeof(tmp->chan->musicclass));
+ if (chan->cid.cid_rdnis)
+ tmp->chan->cid.cid_rdnis = strdup(chan->cid.cid_rdnis);
+ /* Pass callingpres setting */
+ tmp->chan->cid.cid_pres = chan->cid.cid_pres;
+ /* Pass type of number */
+ tmp->chan->cid.cid_ton = chan->cid.cid_ton;
+ /* Pass type of tns */
+ tmp->chan->cid.cid_tns = chan->cid.cid_tns;
+ /* Presense of ADSI CPE on outgoing channel follows ours */
+ tmp->chan->adsicpe = chan->adsicpe;
+ /* Pass the transfer capability */
+ tmp->chan->transfercapability = chan->transfercapability;
+
+ /* If we have an outbound group, set this peer channel to it */
+ if (outbound_group)
+ ast_app_group_set_channel(tmp->chan, outbound_group);
+
+ /* Place the call, but don't wait on the answer */
+ res = ast_call(tmp->chan, numsubst, 0);
+
+ /* Save the info in cdr's that we called them */
+ if (chan->cdr)
+ ast_cdr_setdestchan(chan->cdr, tmp->chan->name);
+
+ /* check the results of ast_call */
+ if (res) {
+ /* Again, keep going even if there's an error */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
+ else if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
+ ast_hangup(tmp->chan);
+ tmp->chan = NULL;
+ cur = rest;
+ continue;
+ } else {
+ senddialevent(chan, tmp->chan);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
+ if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID))
+ ast_set_callerid(tmp->chan, ast_strlen_zero(chan->macroexten) ? chan->exten : chan->macroexten, get_cid_name(cidname, sizeof(cidname), chan), NULL);
+ }
+ /* Put them in the list of outgoing thingies... We're ready now.
+ XXX If we're forcibly removed, these outgoing calls won't get
+ hung up XXX */
+ ast_set_flag(tmp, DIAL_STILLGOING);
+ tmp->next = outgoing;
+ outgoing = tmp;
+ /* If this line is up, don't try anybody else */
+ if (outgoing->chan->_state == AST_STATE_UP)
+ break;
+ cur = rest;
+ } while (cur);
+
+ if (!ast_strlen_zero(args.timeout)) {
+ to = atoi(args.timeout);
+ if (to > 0)
+ to *= 1000;
+ else
+ ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
+ } else
+ to = -1;
+
+ if (outgoing) {
+ /* Our status will at least be NOANSWER */
+ strcpy(status, "NOANSWER");
+ if (ast_test_flag(outgoing, OPT_MUSICBACK)) {
+ moh=1;
+ ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]);
+ } else if (ast_test_flag(outgoing, OPT_RINGBACK)) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ sentringing++;
+ }
+ } else
+ strcpy(status, "CHANUNAVAIL");
+
+ time(&start_time);
+ peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result);
+
+ if (!peer) {
+ if (result) {
+ res = result;
+ } else if (to)
+ /* Musta gotten hung up */
+ res = -1;
+ else
+ /* Nobody answered, next please? */
+ res = 0;
+
+ goto out;
+ }
+ if (peer) {
+ time(&answer_time);
+#ifdef OSP_SUPPORT
+ /* Once call is answered, ditch the OSP Handle */
+ pbx_builtin_setvar_helper(chan, "_OSPHANDLE", "");
+#endif
+ strcpy(status, "ANSWER");
+ /* Ah ha! Someone answered within the desired timeframe. Of course after this
+ we will always return with -1 so that it is hung up properly after the
+ conversation. */
+ hanguptree(outgoing, peer);
+ outgoing = NULL;
+ /* If appropriate, log that we have a destination channel */
+ if (chan->cdr)
+ ast_cdr_setdestchan(chan->cdr, peer->name);
+ if (peer->name)
+ pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
+
+ number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
+ if (!number)
+ number = numsubst;
+ pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
+ if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
+ ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", args.url);
+ ast_channel_sendurl( peer, args.url );
+ }
+ if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) {
+ int res2;
+ int loopcount = 0;
+ if( privdb_val == AST_PRIVACY_UNKNOWN ) {
+
+ /* Get the user's intro, store it in priv-callerintros/$CID,
+ unless it is already there-- this should be done before the
+ call is actually dialed */
+
+ /* all ring indications and moh for the caller has been halted as soon as the
+ target extension was picked up. We are going to have to kill some
+ time and make the caller believe the peer hasn't picked up yet */
+
+ if (ast_test_flag(&opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
+ ast_indicate(chan, -1);
+ ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ sentringing++;
+ }
+
+ /* Start autoservice on the other chan ?? */
+ res2 = ast_autoservice_start(chan);
+ /* Now Stream the File */
+ if (!res2) {
+ do {
+ if (!res2)
+ res2 = ast_play_and_wait(peer,"priv-callpending");
+ if( res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2>'5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4') ) /* uh, interrupting with a bad answer is ... ignorable! */
+ res2 = 0;
+
+ /* priv-callpending script:
+ "I have a caller waiting, who introduces themselves as:"
+ */
+ if (!res2)
+ res2 = ast_play_and_wait(peer,privintro);
+ if( res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2>'5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4') ) /* uh, interrupting with a bad answer is ... ignorable! */
+ res2 = 0;
+ /* now get input from the called party, as to their choice */
+ if( !res2 ) {
+ if( ast_test_flag(&opts, OPT_PRIVACY) )
+ res2 = ast_play_and_wait(peer,"priv-callee-options");
+ if( ast_test_flag(&opts, OPT_SCREENING) )
+ res2 = ast_play_and_wait(peer,"screen-callee-options");
+ }
+ /* priv-callee-options script:
+ "Dial 1 if you wish this caller to reach you directly in the future,
+ and immediately connect to their incoming call
+ Dial 2 if you wish to send this caller to voicemail now and
+ forevermore.
+ Dial 3 to send this callerr to the torture menus, now and forevermore.
+ Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
+ Dial 5 to allow this caller to come straight thru to you in the future,
+ but right now, just this once, send them to voicemail."
+ */
+
+ /* screen-callee-options script:
+ "Dial 1 if you wish to immediately connect to the incoming call
+ Dial 2 if you wish to send this caller to voicemail.
+ Dial 3 to send this callerr to the torture menus.
+ Dial 4 to send this caller to a simple "go away" menu.
+ */
+ if( !res2 || res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2 > '5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4') ) {
+ /* invalid option */
+ res2 = ast_play_and_wait(peer,"vm-sorry");
+ }
+ loopcount++; /* give the callee a couple chances to make a choice */
+ } while( (!res2 || res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2 > '5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4')) && loopcount < 2 );
+ }
+
+ switch(res2) {
+ case '1':
+ if( ast_test_flag(&opts, OPT_PRIVACY) ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n",
+ opt_args[OPT_ARG_PRIVACY], privcid);
+ ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW);
+ }
+ break;
+ case '2':
+ if( ast_test_flag(&opts, OPT_PRIVACY) ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to DENY\n",
+ opt_args[OPT_ARG_PRIVACY], privcid);
+ ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_DENY);
+ }
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+ res2 = ast_autoservice_stop(chan);
+ ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
+ res=0;
+ goto out;
+ case '3':
+ if( ast_test_flag(&opts, OPT_PRIVACY) ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to TORTURE\n",
+ opt_args[OPT_ARG_PRIVACY], privcid);
+ ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_TORTURE);
+ }
+ ast_copy_string(status, "TORTURE", sizeof(status));
+
+ res = 0;
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+ res2 = ast_autoservice_stop(chan);
+ ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
+ goto out; /* Is this right? */
+ case '4':
+ if( ast_test_flag(&opts, OPT_PRIVACY) ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to KILL\n",
+ opt_args[OPT_ARG_PRIVACY], privcid);
+ ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_KILL);
+ }
+
+ ast_copy_string(status, "DONTCALL", sizeof(status));
+ res = 0;
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+ res2 = ast_autoservice_stop(chan);
+ ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
+ goto out; /* Is this right? */
+ case '5':
+ if( ast_test_flag(&opts, OPT_PRIVACY) ) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n",
+ opt_args[OPT_ARG_PRIVACY], privcid);
+ ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW);
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+ res2 = ast_autoservice_stop(chan);
+ ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
+ res=0;
+ goto out;
+ } /* if not privacy, then 5 is the same as "default" case */
+ default:
+ /* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */
+ /* well, there seems basically two choices. Just patch the caller thru immediately,
+ or,... put 'em thru to voicemail. */
+ /* since the callee may have hung up, let's do the voicemail thing, no database decision */
+ if (option_verbose > 2)
+ ast_log(LOG_NOTICE,"privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n");
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+ res2 = ast_autoservice_stop(chan);
+ ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
+ res=0;
+ goto out;
+ }
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+ res2 = ast_autoservice_stop(chan);
+ /* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll
+ just clog things up, and it's not useful information, not being tied to a CID */
+ if( strncmp(privcid,"NOCALLERID",10) == 0 || ast_test_flag(&opts, OPT_SCREEN_NOINTRO) ) {
+ ast_filedelete(privintro, NULL);
+ if( ast_fileexists(privintro,NULL,NULL ) > 0 )
+ ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", privintro);
+ else if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro);
+ }
+ }
+ }
+ if (ast_test_flag(&opts, OPT_ANNOUNCE) && !ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
+ /* Start autoservice on the other chan */
+ res = ast_autoservice_start(chan);
+ /* Now Stream the File */
+ if (!res)
+ res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language);
+ if (!res) {
+ digit = ast_waitstream(peer, AST_DIGIT_ANY);
+ }
+ /* Ok, done. stop autoservice */
+ res = ast_autoservice_stop(chan);
+ if (digit > 0 && !res)
+ res = ast_senddigit(chan, digit);
+ else
+ res = digit;
+
+ } else
+ res = 0;
+
+ if (chan && peer && ast_test_flag(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
+ char *ch;
+
+ for (ch = opt_args[OPT_ARG_GOTO]; *ch; ch++) {
+ if (*ch == '^')
+ *ch = '|';
+ }
+ ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
+ ast_parseable_goto(peer, opt_args[OPT_ARG_GOTO]);
+ peer->priority++;
+ ast_pbx_start(peer);
+ hanguptree(outgoing, NULL);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (ast_test_flag(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
+ char *ch;
+
+ res = ast_autoservice_start(chan);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+ res = -1;
+ }
+
+ app = pbx_findapp("Macro");
+
+ if (app && !res) {
+ for (ch = opt_args[OPT_ARG_CALLEE_MACRO]; *ch; ch++) {
+ if (*ch == '^')
+ *ch = '|';
+ }
+ res = pbx_exec(peer, app, opt_args[OPT_ARG_CALLEE_MACRO], 1);
+ ast_log(LOG_DEBUG, "Macro exited with status %d\n", res);
+ res = 0;
+ } else {
+ ast_log(LOG_ERROR, "Could not find application Macro\n");
+ res = -1;
+ }
+
+ if (ast_autoservice_stop(chan) < 0) {
+ ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+ res = -1;
+ }
+
+ if (!res) {
+ if ((macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
+ if (!strcasecmp(macro_result, "BUSY")) {
+ ast_copy_string(status, macro_result, sizeof(status));
+ if (option_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
+ if (!ast_goto_if_exists(chan, NULL, NULL, chan->priority + 101)) {
+ ast_set_flag(peerflags, OPT_GO_ON);
+ }
+ } else
+ ast_set_flag(peerflags, OPT_GO_ON);
+ res = -1;
+ }
+ else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
+ ast_copy_string(status, macro_result, sizeof(status));
+ ast_set_flag(peerflags, OPT_GO_ON);
+ res = -1;
+ }
+ else if (!strcasecmp(macro_result, "CONTINUE")) {
+ /* hangup peer and keep chan alive assuming the macro has changed
+ the context / exten / priority or perhaps
+ the next priority in the current exten is desired.
+ */
+ ast_set_flag(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(macro_result, "ABORT")) {
+ /* Hangup both ends unless the caller has the g flag */
+ res = -1;
+ } else if (!strncasecmp(macro_result, "GOTO:",5) && (macro_transfer_dest = ast_strdupa(macro_result + 5))) {
+ res = -1;
+ /* perform a transfer to a new extension */
+ if (strchr(macro_transfer_dest,'^')) { /* context^exten^priority*/
+ /* no brainer mode... substitute ^ with | and feed it to builtin goto */
+ for (res=0;res<strlen(macro_transfer_dest);res++)
+ if (macro_transfer_dest[res] == '^')
+ macro_transfer_dest[res] = '|';
+
+ if (!ast_parseable_goto(chan, macro_transfer_dest))
+ ast_set_flag(peerflags, OPT_GO_ON);
+
+ }
+ }
+ }
+ }
+ }
+
+ if (!res) {
+ if (calldurationlimit > 0) {
+ time_t now;
+
+ time(&now);
+ chan->whentohangup = now + calldurationlimit;
+ }
+ if (!ast_strlen_zero(dtmfcalled)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the called party.\n",dtmfcalled);
+ res = ast_dtmf_stream(peer,chan,dtmfcalled,250);
+ }
+ if (!ast_strlen_zero(dtmfcalling)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the calling party.\n",dtmfcalling);
+ res = ast_dtmf_stream(chan,peer,dtmfcalling,250);
+ }
+ }
+
+ if (!res) {
+ memset(&config,0,sizeof(struct ast_bridge_config));
+ if (play_to_caller)
+ ast_set_flag(&(config.features_caller), AST_FEATURE_PLAY_WARNING);
+ if (play_to_callee)
+ ast_set_flag(&(config.features_callee), AST_FEATURE_PLAY_WARNING);
+ if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
+ if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
+ if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag(peerflags, OPT_CALLER_HANGUP))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
+ if (ast_test_flag(peerflags, OPT_CALLER_MONITOR))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
+
+ config.timelimit = timelimit;
+ config.play_warning = play_warning;
+ config.warning_freq = warning_freq;
+ config.warning_sound = warning_sound;
+ config.end_sound = end_sound;
+ config.start_sound = start_sound;
+ if (moh) {
+ moh = 0;
+ ast_moh_stop(chan);
+ } else if (sentringing) {
+ sentringing = 0;
+ ast_indicate(chan, -1);
+ }
+ /* Be sure no generators are left on it */
+ ast_deactivate_generator(chan);
+ /* Make sure channels are compatible */
+ res = ast_channel_make_compatible(chan, peer);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
+ ast_hangup(peer);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ res = ast_bridge_call(chan,peer,&config);
+ time(&end_time);
+ snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
+ pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast);
+
+ } else {
+ time(&end_time);
+ res = -1;
+ }
+ snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
+ pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
+
+ if (res != AST_PBX_NO_HANGUP_PEER) {
+ if (!chan->_softhangup)
+ chan->hangupcause = peer->hangupcause;
+ ast_hangup(peer);
+ }
+ }
+out:
+ if (moh) {
+ moh = 0;
+ ast_moh_stop(chan);
+ } else if (sentringing) {
+ sentringing = 0;
+ ast_indicate(chan, -1);
+ }
+ hanguptree(outgoing, NULL);
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
+ ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status);
+
+ if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE))
+ res=0;
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int dial_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_flags peerflags;
+ memset(&peerflags, 0, sizeof(peerflags));
+ return dial_exec_full(chan, data, &peerflags);
+}
+
+static int retrydial_exec(struct ast_channel *chan, void *data)
+{
+ char *announce = NULL, *context = NULL, *dialdata = NULL;
+ int sleep = 0, loops = 0, res = 0;
+ struct localuser *u;
+ struct ast_flags peerflags;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ announce = ast_strdupa(data);
+ if (!announce) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ memset(&peerflags, 0, sizeof(peerflags));
+
+ if ((dialdata = strchr(announce, '|'))) {
+ *dialdata = '\0';
+ dialdata++;
+ if ((sleep = atoi(dialdata))) {
+ sleep *= 1000;
+ } else {
+ ast_log(LOG_ERROR, "%s requires the numerical argument <sleep>\n",rapp);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if ((dialdata = strchr(dialdata, '|'))) {
+ *dialdata = '\0';
+ dialdata++;
+ if (!(loops = atoi(dialdata))) {
+ ast_log(LOG_ERROR, "%s requires the numerical argument <loops>\n",rapp);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+ }
+
+ if ((dialdata = strchr(dialdata, '|'))) {
+ *dialdata = '\0';
+ dialdata++;
+ } else {
+ ast_log(LOG_ERROR, "%s requires more arguments\n",rapp);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (sleep < 1000)
+ sleep = 10000;
+
+ if (!loops)
+ loops = -1;
+
+ context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
+
+ while (loops) {
+ chan->data = "Retrying";
+ if (ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_stop(chan);
+
+ if ((res = dial_exec_full(chan, dialdata, &peerflags)) == 0) {
+ if (ast_test_flag(&peerflags, OPT_DTMF_EXIT)) {
+ if (!(res = ast_streamfile(chan, announce, chan->language)))
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (!res && sleep) {
+ if (!ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_start(chan, NULL);
+ res = ast_waitfordigit(chan, sleep);
+ }
+ } else {
+ if (!(res = ast_streamfile(chan, announce, chan->language)))
+ res = ast_waitstream(chan, "");
+ if (sleep) {
+ if (!ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_start(chan, NULL);
+ if (!res)
+ res = ast_waitfordigit(chan, sleep);
+ }
+ }
+ }
+
+ if (res < 0)
+ break;
+ else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
+ if (onedigit_goto(chan, context, (char) res, 1)) {
+ res = 0;
+ break;
+ }
+ }
+ loops--;
+ }
+
+ if (ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_stop(chan);
+
+ LOCAL_USER_REMOVE(u);
+ return loops ? res : 0;
+
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+ res |= ast_unregister_application(rapp);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app, dial_exec, synopsis, descrip);
+ res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_dictate.c b/1.2-netsec/apps/app_dictate.c
new file mode 100644
index 000000000..91a1650ea
--- /dev/null
+++ b/1.2-netsec/apps/app_dictate.c
@@ -0,0 +1,358 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II
+ *
+ * Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * Donated by Sangoma Technologies <http://www.samgoma.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Virtual Dictation Machine Application For Asterisk
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h> /* for mkdir */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Virtual Dictation Machine";
+static char *app = "Dictate";
+static char *synopsis = "Virtual Dictation Machine";
+static char *desc = " Dictate([<base_dir>])\n"
+"Start dictation machine using optional base dir for files.\n";
+
+
+STANDARD_LOCAL_USER;
+LOCAL_USER_DECL;
+
+typedef enum {
+ DFLAG_RECORD = (1 << 0),
+ DFLAG_PLAY = (1 << 1),
+ DFLAG_TRUNC = (1 << 2),
+ DFLAG_PAUSE = (1 << 3),
+} dflags;
+
+typedef enum {
+ DMODE_INIT,
+ DMODE_RECORD,
+ DMODE_PLAY
+} dmodes;
+
+#define ast_toggle_flag(it,flag) if(ast_test_flag(it, flag)) ast_clear_flag(it, flag); else ast_set_flag(it, flag)
+
+static int play_and_wait(struct ast_channel *chan, char *file, char *digits)
+{
+ int res = -1;
+ if (!ast_streamfile(chan, file, chan->language)) {
+ res = ast_waitstream(chan, digits);
+ }
+ return res;
+}
+
+static int dictate_exec(struct ast_channel *chan, void *data)
+{
+ char *mydata, *argv[2], *path = NULL, filein[256];
+ char dftbase[256];
+ char *base;
+ struct ast_flags flags = {0};
+ struct ast_filestream *fs;
+ struct ast_frame *f = NULL;
+ struct localuser *u;
+ int ffactor = 320 * 80,
+ res = 0,
+ argc = 0,
+ done = 0,
+ oldr = 0,
+ lastop = 0,
+ samples = 0,
+ speed = 1,
+ digit = 0,
+ len = 0,
+ maxlen = 0,
+ mode = 0;
+
+ LOCAL_USER_ADD(u);
+
+ snprintf(dftbase, sizeof(dftbase), "%s/dictate", ast_config_AST_SPOOL_DIR);
+ if (!ast_strlen_zero(data) && (mydata = ast_strdupa(data))) {
+ argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+ }
+
+ if (argc) {
+ base = argv[0];
+ } else {
+ base = dftbase;
+ }
+
+ oldr = chan->readformat;
+ if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)) < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode.\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ ast_answer(chan);
+ ast_safe_sleep(chan, 200);
+ for(res = 0; !res;) {
+ if (ast_app_getdata(chan, "dictate/enter_filename", filein, sizeof(filein), 0) ||
+ ast_strlen_zero(filein)) {
+ res = -1;
+ break;
+ }
+
+ mkdir(base, 0755);
+ len = strlen(base) + strlen(filein) + 2;
+ if (!path || len > maxlen) {
+ path = alloca(len);
+ memset(path, 0, len);
+ maxlen = len;
+ } else {
+ memset(path, 0, maxlen);
+ }
+
+ snprintf(path, len, "%s/%s", base, filein);
+ fs = ast_writefile(path, "raw", NULL, O_CREAT|O_APPEND, 0, 0700);
+ mode = DMODE_PLAY;
+ memset(&flags, 0, sizeof(flags));
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ digit = play_and_wait(chan, "dictate/forhelp", AST_DIGIT_ANY);
+ done = 0;
+ speed = 1;
+ res = 0;
+ lastop = 0;
+ samples = 0;
+ while (!done && ((res = ast_waitfor(chan, -1)) > -1) && fs && (f = ast_read(chan))) {
+ if (digit) {
+ struct ast_frame fr = {AST_FRAME_DTMF, digit};
+ ast_queue_frame(chan, &fr);
+ digit = 0;
+ }
+ if ((f->frametype == AST_FRAME_DTMF)) {
+ int got = 1;
+ switch(mode) {
+ case DMODE_PLAY:
+ switch(f->subclass) {
+ case '1':
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ mode = DMODE_RECORD;
+ break;
+ case '2':
+ speed++;
+ if (speed > 4) {
+ speed = 1;
+ }
+ res = ast_say_number(chan, speed, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ break;
+ case '7':
+ samples -= ffactor;
+ if(samples < 0) {
+ samples = 0;
+ }
+ ast_seekstream(fs, samples, SEEK_SET);
+ break;
+ case '8':
+ samples += ffactor;
+ ast_seekstream(fs, samples, SEEK_SET);
+ break;
+
+ default:
+ got = 0;
+ }
+ break;
+ case DMODE_RECORD:
+ switch(f->subclass) {
+ case '1':
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ mode = DMODE_PLAY;
+ break;
+ case '8':
+ ast_toggle_flag(&flags, DFLAG_TRUNC);
+ lastop = 0;
+ break;
+ default:
+ got = 0;
+ }
+ break;
+ default:
+ got = 0;
+ }
+ if (!got) {
+ switch(f->subclass) {
+ case '#':
+ done = 1;
+ continue;
+ break;
+ case '*':
+ ast_toggle_flag(&flags, DFLAG_PAUSE);
+ if (ast_test_flag(&flags, DFLAG_PAUSE)) {
+ digit = play_and_wait(chan, "dictate/pause", AST_DIGIT_ANY);
+ } else {
+ digit = play_and_wait(chan, mode == DMODE_PLAY ? "dictate/playback" : "dictate/record", AST_DIGIT_ANY);
+ }
+ break;
+ case '0':
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
+ switch(mode) {
+ case DMODE_PLAY:
+ digit = play_and_wait(chan, "dictate/play_help", AST_DIGIT_ANY);
+ break;
+ case DMODE_RECORD:
+ digit = play_and_wait(chan, "dictate/record_help", AST_DIGIT_ANY);
+ break;
+ }
+ if (digit == 0) {
+ digit = play_and_wait(chan, "dictate/both_help", AST_DIGIT_ANY);
+ } else if (digit < 0) {
+ done = 1;
+ break;
+ }
+ break;
+ }
+ }
+
+ } else if (f->frametype == AST_FRAME_VOICE) {
+ switch(mode) {
+ struct ast_frame *fr;
+ int x;
+ case DMODE_PLAY:
+ if (lastop != DMODE_PLAY) {
+ if (ast_test_flag(&flags, DFLAG_PAUSE)) {
+ digit = play_and_wait(chan, "dictate/playback_mode", AST_DIGIT_ANY);
+ if (digit == 0) {
+ digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
+ } else if (digit < 0) {
+ break;
+ }
+ }
+ if (lastop != DFLAG_PLAY) {
+ lastop = DFLAG_PLAY;
+ ast_closestream(fs);
+ fs = ast_openstream(chan, path, chan->language);
+ ast_seekstream(fs, samples, SEEK_SET);
+ chan->stream = NULL;
+ }
+ lastop = DMODE_PLAY;
+ }
+
+ if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
+ for (x = 0; x < speed; x++) {
+ if ((fr = ast_readframe(fs))) {
+ ast_write(chan, fr);
+ samples += fr->samples;
+ ast_frfree(fr);
+ fr = NULL;
+ } else {
+ samples = 0;
+ ast_seekstream(fs, 0, SEEK_SET);
+ }
+ }
+ }
+ break;
+ case DMODE_RECORD:
+ if (lastop != DMODE_RECORD) {
+ int oflags = O_CREAT | O_WRONLY;
+ if (ast_test_flag(&flags, DFLAG_PAUSE)) {
+ digit = play_and_wait(chan, "dictate/record_mode", AST_DIGIT_ANY);
+ if (digit == 0) {
+ digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
+ } else if (digit < 0) {
+ break;
+ }
+ }
+ lastop = DMODE_RECORD;
+ ast_closestream(fs);
+ if ( ast_test_flag(&flags, DFLAG_TRUNC)) {
+ oflags |= O_TRUNC;
+ digit = play_and_wait(chan, "dictate/truncating_audio", AST_DIGIT_ANY);
+ } else {
+ oflags |= O_APPEND;
+ }
+ fs = ast_writefile(path, "raw", NULL, oflags, 0, 0700);
+ if (ast_test_flag(&flags, DFLAG_TRUNC)) {
+ ast_seekstream(fs, 0, SEEK_SET);
+ ast_clear_flag(&flags, DFLAG_TRUNC);
+ } else {
+ ast_seekstream(fs, 0, SEEK_END);
+ }
+ }
+ if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
+ res = ast_writestream(fs, f);
+ }
+ break;
+ }
+
+ }
+
+ ast_frfree(f);
+ }
+ }
+ if (oldr) {
+ ast_set_read_format(chan, oldr);
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, dictate_exec, synopsis, desc);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_directed_pickup.c b/1.2-netsec/apps/app_directed_pickup.c
new file mode 100644
index 000000000..9e7d3cc84
--- /dev/null
+++ b/1.2-netsec/apps/app_directed_pickup.c
@@ -0,0 +1,169 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Joshua Colp
+ *
+ * Joshua Colp <jcolp@asterlink.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Directed Call Pickup Support
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static const char *tdesc = "Directed Call Pickup Application";
+static const char *app = "Pickup";
+static const char *synopsis = "Directed Call Pickup";
+static const char *descrip =
+" Pickup(extension[@context]): This application can pickup any ringing channel\n"
+"that is calling the specified extension. If no context is specified, the current\n"
+"context will be used.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int pickup_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u = NULL;
+ struct ast_channel *origin = NULL, *target = NULL;
+ char *tmp = NULL, *exten = NULL, *context = NULL;
+ char workspace[256] = "";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Pickup requires an argument (extension) !\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Get the extension and context if present */
+ exten = data;
+ context = strchr(data, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+
+ /* Find a channel to pickup */
+ origin = ast_get_channel_by_exten_locked(exten, context);
+ if (origin && origin->cdr) {
+ ast_cdr_getvar(origin->cdr, "dstchannel", &tmp, workspace,
+ sizeof(workspace), 0);
+ if (tmp) {
+ /* We have a possible channel... now we need to find it! */
+ target = ast_get_channel_by_name_locked(tmp);
+ } else {
+ ast_log(LOG_DEBUG, "No target channel found.\n");
+ res = -1;
+ }
+ ast_mutex_unlock(&origin->lock);
+ } else {
+ if (origin)
+ ast_mutex_unlock(&origin->lock);
+ ast_log(LOG_DEBUG, "No originating channel found.\n");
+ }
+
+ if (res)
+ goto out;
+
+ if (target && (!target->pbx) && ((target->_state == AST_STATE_RINGING) || (target->_state == AST_STATE_RING))) {
+ ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n", target->name,
+ chan->name);
+ res = ast_answer(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+ res = -1;
+ goto out;
+ }
+ res = ast_queue_control(chan, AST_CONTROL_ANSWER);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n",
+ chan->name);
+ res = -1;
+ goto out;
+ }
+ res = ast_channel_masquerade(target, chan);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
+ res = -1;
+ goto out;
+ }
+ } else {
+ ast_log(LOG_DEBUG, "No call pickup possible...\n");
+ res = -1;
+ }
+ /* Done */
+ out:
+ if (target)
+ ast_mutex_unlock(&target->lock);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, pickup_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_directory.c b/1.2-netsec/apps/app_directory.c
new file mode 100644
index 000000000..dc1181b6d
--- /dev/null
+++ b/1.2-netsec/apps/app_directory.c
@@ -0,0 +1,516 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Provide a directory of extensions
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Extension Directory";
+static char *app = "Directory";
+
+static char *synopsis = "Provide directory of voicemail extensions";
+static char *descrip =
+" Directory(vm-context[|dial-context[|options]]): This application will present\n"
+"the calling channel with a directory of extensions from which they can search\n"
+"by name. The list of names and corresponding extensions is retrieved from the\n"
+"voicemail configuration file, voicemail.conf.\n"
+" This applicaiton will immediate exit if one of the following DTMF digits are\n"
+"received and the extension to jump to exists:\n"
+" 0 - Jump to the 'o' extension, if it exists.\n"
+" * - Jump to the 'a' extension, if it exists.\n\n"
+" Parameters:\n"
+" vm-context - This is the context within voicemail.conf to use for the\n"
+" Directory.\n"
+" dial-context - This is the dialplan context to use when looking for an\n"
+" extension that the user has selected, or when jumping to the\n"
+" 'o' or 'a' extension.\n\n"
+" Options:\n"
+" f - Allow the caller to enter the first name of a user in the directory\n"
+" instead of using the last name.\n";
+
+/* For simplicity, I'm keeping the format compatible with the voicemail config,
+ but i'm open to suggestions for isolating it */
+
+#define VOICEMAIL_CONFIG "voicemail.conf"
+
+/* How many digits to read in */
+#define NUMDIGITS 3
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char *convert(char *lastname)
+{
+ char *tmp;
+ int lcount = 0;
+ tmp = malloc(NUMDIGITS + 1);
+ if (tmp) {
+ while((*lastname > 32) && lcount < NUMDIGITS) {
+ switch(toupper(*lastname)) {
+ case '1':
+ tmp[lcount++] = '1';
+ break;
+ case '2':
+ case 'A':
+ case 'B':
+ case 'C':
+ tmp[lcount++] = '2';
+ break;
+ case '3':
+ case 'D':
+ case 'E':
+ case 'F':
+ tmp[lcount++] = '3';
+ break;
+ case '4':
+ case 'G':
+ case 'H':
+ case 'I':
+ tmp[lcount++] = '4';
+ break;
+ case '5':
+ case 'J':
+ case 'K':
+ case 'L':
+ tmp[lcount++] = '5';
+ break;
+ case '6':
+ case 'M':
+ case 'N':
+ case 'O':
+ tmp[lcount++] = '6';
+ break;
+ case '7':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ tmp[lcount++] = '7';
+ break;
+ case '8':
+ case 'T':
+ case 'U':
+ case 'V':
+ tmp[lcount++] = '8';
+ break;
+ case '9':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ tmp[lcount++] = '9';
+ break;
+ }
+ lastname++;
+ }
+ tmp[lcount] = '\0';
+ }
+ return tmp;
+}
+
+/* play name of mailbox owner.
+ * returns: -1 for bad or missing extension
+ * '1' for selected entry from directory
+ * '*' for skipped entry from directory
+ */
+static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
+ int res = 0;
+ int loop = 3;
+ char fn[256];
+ char fn2[256];
+
+ /* Check for the VoiceMail2 greeting first */
+ snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
+ (char *)ast_config_AST_SPOOL_DIR, context, ext);
+
+ /* Otherwise, check for an old-style Voicemail greeting */
+ snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
+ (char *)ast_config_AST_SPOOL_DIR, ext);
+
+ if (ast_fileexists(fn, NULL, chan->language) > 0) {
+ res = ast_streamfile(chan, fn, chan->language);
+ if (!res) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+ ast_stopstream(chan);
+ } else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
+ res = ast_streamfile(chan, fn2, chan->language);
+ if (!res) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+ ast_stopstream(chan);
+ } else {
+ res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
+ AST_DIGIT_ANY, chan->language);
+ }
+
+ while (loop) {
+ if (!res) {
+ res = ast_streamfile(chan, "dir-instr", chan->language);
+ }
+ if (!res) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+ if (!res) {
+ res = ast_waitfordigit(chan, 3000);
+ }
+ ast_stopstream(chan);
+
+ if (res > -1) {
+ switch (res) {
+ case '1':
+ /* Name selected */
+ loop = 0;
+ if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
+ ast_log(LOG_WARNING,
+ "Can't find extension '%s' in context '%s'. "
+ "Did you pass the wrong context to Directory?\n",
+ ext, dialcontext);
+ res = -1;
+ }
+ break;
+
+ case '*':
+ /* Skip to next match in list */
+ loop = 0;
+ break;
+
+ default:
+ /* Not '1', or '*', so decrement number of tries */
+ res = 0;
+ loop--;
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* User hungup, so jump out now */
+ loop = 0;
+ }
+ } /* end while */
+
+ return(res);
+}
+
+static struct ast_config *realtime_directory(char *context)
+{
+ struct ast_config *cfg;
+ struct ast_config *rtdata;
+ struct ast_category *cat;
+ struct ast_variable *var;
+ char *mailbox;
+ char *fullname;
+ char *hidefromdir;
+ char tmp[100];
+
+ /* Load flat file config. */
+ cfg = ast_config_load(VOICEMAIL_CONFIG);
+
+ if (!cfg) {
+ /* Loading config failed. */
+ ast_log(LOG_WARNING, "Loading config failed.\n");
+ return NULL;
+ }
+
+ /* Get realtime entries, categorized by their mailbox number
+ and present in the requested context */
+ rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
+
+ /* if there are no results, just return the entries from the config file */
+ if (!rtdata)
+ return cfg;
+
+ /* Does the context exist within the config file? If not, make one */
+ cat = ast_category_get(cfg, context);
+ if (!cat) {
+ cat = ast_category_new(context);
+ if (!cat) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ ast_config_destroy(cfg);
+ return NULL;
+ }
+ ast_category_append(cfg, cat);
+ }
+
+ mailbox = ast_category_browse(rtdata, NULL);
+ while (mailbox) {
+ fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
+ hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
+ snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
+ fullname ? fullname : "",
+ hidefromdir ? hidefromdir : "no");
+ var = ast_variable_new(mailbox, tmp);
+ if (var)
+ ast_variable_append(cat, var);
+ else
+ ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
+ mailbox = ast_category_browse(rtdata, mailbox);
+ }
+ ast_config_destroy(rtdata);
+
+ return cfg;
+}
+
+static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit, int last)
+{
+ /* Read in the first three digits.. "digit" is the first digit, already read */
+ char ext[NUMDIGITS + 1];
+ char name[80] = "";
+ struct ast_variable *v;
+ int res;
+ int found=0;
+ int lastuserchoice = 0;
+ char *start, *pos, *conv,*stringp=NULL;
+
+ if (ast_strlen_zero(context)) {
+ ast_log(LOG_WARNING,
+ "Directory must be called with an argument "
+ "(context in which to interpret extensions)\n");
+ return -1;
+ }
+ if (digit == '0') {
+ if (!ast_goto_if_exists(chan, chan->context, "o", 1) ||
+ (!ast_strlen_zero(chan->macrocontext) &&
+ !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Can't find extension 'o' in current context. "
+ "Not Exiting the Directory!\n");
+ res = 0;
+ }
+ }
+ if (digit == '*') {
+ if (!ast_goto_if_exists(chan, chan->context, "a", 1) ||
+ (!ast_strlen_zero(chan->macrocontext) &&
+ !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Can't find extension 'a' in current context. "
+ "Not Exiting the Directory!\n");
+ res = 0;
+ }
+ }
+ memset(ext, 0, sizeof(ext));
+ ext[0] = digit;
+ res = 0;
+ if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
+ if (!res) {
+ /* Search for all names which start with those digits */
+ v = ast_variable_browse(cfg, context);
+ while(v && !res) {
+ /* Find all candidate extensions */
+ while(v) {
+ /* Find a candidate extension */
+ start = strdup(v->value);
+ if (start && !strcasestr(start, "hidefromdir=yes")) {
+ stringp=start;
+ strsep(&stringp, ",");
+ pos = strsep(&stringp, ",");
+ if (pos) {
+ ast_copy_string(name, pos, sizeof(name));
+ /* Grab the last name */
+ if (last && strrchr(pos,' '))
+ pos = strrchr(pos, ' ') + 1;
+ conv = convert(pos);
+ if (conv) {
+ if (!strcmp(conv, ext)) {
+ /* Match! */
+ found++;
+ free(conv);
+ free(start);
+ break;
+ }
+ free(conv);
+ }
+ }
+ free(start);
+ }
+ v = v->next;
+ }
+
+ if (v) {
+ /* We have a match -- play a greeting if they have it */
+ res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
+ switch (res) {
+ case -1:
+ /* user pressed '1' but extension does not exist, or
+ * user hungup
+ */
+ lastuserchoice = 0;
+ break;
+ case '1':
+ /* user pressed '1' and extensions exists;
+ play_mailbox_owner will already have done
+ a goto() on the channel
+ */
+ lastuserchoice = res;
+ break;
+ case '*':
+ /* user pressed '*' to skip something found */
+ lastuserchoice = res;
+ res = 0;
+ break;
+ default:
+ break;
+ }
+ v = v->next;
+ }
+ }
+
+ if (lastuserchoice != '1') {
+ if (found)
+ res = ast_streamfile(chan, "dir-nomore", chan->language);
+ else
+ res = ast_streamfile(chan, "dir-nomatch", chan->language);
+ if (!res)
+ res = 1;
+ return res;
+ }
+ return 0;
+ }
+ return res;
+}
+
+static int directory_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ struct ast_config *cfg;
+ int last = 1;
+ char *context, *dialcontext, *dirintro, *options;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ context = ast_strdupa(data);
+ dialcontext = strchr(context, '|');
+ if (dialcontext) {
+ *dialcontext = '\0';
+ dialcontext++;
+ options = strchr(dialcontext, '|');
+ if (options) {
+ *options = '\0';
+ options++;
+ if (strchr(options, 'f'))
+ last = 0;
+ }
+ } else
+ dialcontext = context;
+
+ cfg = realtime_directory(context);
+ if (!cfg) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
+ if (ast_strlen_zero(dirintro))
+ dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
+ if (ast_strlen_zero(dirintro)) {
+ if (last)
+ dirintro = "dir-intro";
+ else
+ dirintro = "dir-intro-fn";
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ for (;;) {
+ if (!res)
+ res = ast_streamfile(chan, dirintro, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (!res)
+ res = ast_waitfordigit(chan, 5000);
+ if (res > 0) {
+ res = do_directory(chan, cfg, context, dialcontext, res, last);
+ if (res > 0) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (res >= 0) {
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ ast_config_destroy(cfg);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, directory_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_disa.c b/1.2-netsec/apps/app_disa.c
new file mode 100644
index 000000000..2ac124525
--- /dev/null
+++ b/1.2-netsec/apps/app_disa.c
@@ -0,0 +1,412 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ *
+ * Made only slightly more sane by Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief DISA -- Direct Inward System Access Application
+ *
+ * \author Jim Dixon <jim@lambdatel.com>
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/indications.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/callerid.h"
+
+static char *tdesc = "DISA (Direct Inward System Access) Application";
+
+static char *app = "DISA";
+
+static char *synopsis = "DISA (Direct Inward System Access)";
+
+static char *descrip =
+ "DISA(<numeric passcode>[|<context>]) or disa(<filename>)\n"
+ "The DISA, Direct Inward System Access, application allows someone from \n"
+ "outside the telephone switch (PBX) to obtain an \"internal\" system \n"
+ "dialtone and to place calls from it as if they were placing a call from \n"
+ "within the switch.\n"
+ "DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
+ "the pound sign (#). If the passcode is correct, the user is then given\n"
+ "system dialtone on which a call may be placed. Obviously, this type\n"
+ "of access has SERIOUS security implications, and GREAT care must be\n"
+ "taken NOT to compromise your security.\n\n"
+ "There is a possibility of accessing DISA without password. Simply\n"
+ "exchange your password with \"no-password\".\n\n"
+ " Example: exten => s,1,DISA(no-password|local)\n\n"
+ "Be aware that using this compromises the security of your PBX.\n\n"
+ "The arguments to this application (in extensions.conf) allow either\n"
+ "specification of a single global passcode (that everyone uses), or\n"
+ "individual passcodes contained in a file. It also allow specification\n"
+ "of the context on which the user will be dialing. If no context is\n"
+ "specified, the DISA application defaults the context to \"disa\".\n"
+ "Presumably a normal system will have a special context set up\n"
+ "for DISA use with some or a lot of restrictions. \n\n"
+ "The file that contains the passcodes (if used) allows specification\n"
+ "of either just a passcode (defaulting to the \"disa\" context, or\n"
+ "passcode|context on each line of the file. The file may contain blank\n"
+ "lines, or comments starting with \"#\" or \";\". In addition, the\n"
+ "above arguments may have |new-callerid-string appended to them, to\n"
+ "specify a new (different) callerid to be used for this call, for\n"
+ "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
+ "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Last\n"
+ "but not least, |mailbox[@context] may be appended, which will cause\n"
+ "a stutter-dialtone (indication \"dialrecall\") to be used, if the\n"
+ "specified mailbox contains any new messages, for example:\n"
+ "numeric-passcode|context||1234 (w/a changing callerid). Note that\n"
+ "in the case of specifying the numeric-passcode, the context must be\n"
+ "specified if the callerid is specified also.\n\n"
+ "If login is successful, the application looks up the dialed number in\n"
+ "the specified (or default) context, and executes it if found.\n"
+ "If the user enters an invalid extension and extension \"i\" (invalid) \n"
+ "exists in the context, it will be used.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static void play_dialtone(struct ast_channel *chan, char *mailbox)
+{
+ const struct tone_zone_sound *ts = NULL;
+ if(ast_app_has_voicemail(mailbox, NULL))
+ ts = ast_get_indication_tone(chan->zone, "dialrecall");
+ else
+ ts = ast_get_indication_tone(chan->zone, "dial");
+ if (ts)
+ ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ ast_tonepair_start(chan, 350, 440, 0, 0);
+}
+
+static int disa_exec(struct ast_channel *chan, void *data)
+{
+ int i,j,k,x,did_ignore;
+ int firstdigittimeout = 20000;
+ int digittimeout = 10000;
+ struct localuser *u;
+ char *tmp, exten[AST_MAX_EXTENSION],acctcode[20]="";
+ char pwline[256];
+ char ourcidname[256],ourcidnum[256];
+ struct ast_frame *f;
+ struct timeval lastdigittime;
+ int res;
+ time_t rstart;
+ FILE *fp;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(passcode);
+ AST_APP_ARG(context);
+ AST_APP_ARG(cid);
+ AST_APP_ARG(mailbox);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->pbx) {
+ firstdigittimeout = chan->pbx->rtimeout*1000;
+ digittimeout = chan->pbx->dtimeout*1000;
+ }
+
+ if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
+ ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
+ ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
+ ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
+
+ tmp = ast_strdupa(data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (ast_strlen_zero(args.context))
+ args.context = "disa";
+ if (ast_strlen_zero(args.mailbox))
+ args.mailbox = "";
+
+ ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
+
+ if (chan->_state != AST_STATE_UP) {
+ /* answer */
+ ast_answer(chan);
+ }
+ i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
+ did_ignore = 0;
+ exten[0] = 0;
+ acctcode[0] = 0;
+ /* can we access DISA without password? */
+
+ ast_log(LOG_DEBUG, "Context: %s\n",args.context);
+
+ if (!strcasecmp(args.passcode, "no-password")) {
+ k |= 1; /* We have the password */
+ ast_log(LOG_DEBUG, "DISA no-password login success\n");
+ }
+ lastdigittime = ast_tvnow();
+
+ play_dialtone(chan, args.mailbox);
+
+ for (;;) {
+ /* if outa time, give em reorder */
+ if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
+ ((k&2) ? digittimeout : firstdigittimeout))
+ {
+ ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
+ ((k&1) ? "extension" : "password"),chan->name);
+ break;
+ }
+ if ((res = ast_waitfor(chan, -1) < 0)) {
+ ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
+ continue;
+ }
+
+ f = ast_read(chan);
+ if (f == NULL)
+ {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if ((f->frametype == AST_FRAME_CONTROL) &&
+ (f->subclass == AST_CONTROL_HANGUP))
+ {
+ ast_frfree(f);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ ast_frfree(f);
+ continue;
+ }
+ /* if not DTMF, just do it again */
+ if (f->frametype != AST_FRAME_DTMF)
+ {
+ ast_frfree(f);
+ continue;
+ }
+
+ j = f->subclass; /* save digit */
+ ast_frfree(f);
+ if (i == 0)
+ {
+ k|=2; /* We have the first digit */
+ ast_playtones_stop(chan);
+ }
+ lastdigittime = ast_tvnow();
+ /* got a DTMF tone */
+ if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
+ {
+ if (!(k&1)) /* if in password state */
+ {
+ if (j == '#') /* end of password */
+ {
+ /* see if this is an integer */
+ if (sscanf(args.passcode,"%d",&j) < 1)
+ { /* nope, it must be a filename */
+ fp = fopen(args.passcode,"r");
+ if (!fp)
+ {
+ ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ pwline[0] = 0;
+ while(fgets(pwline,sizeof(pwline) - 1,fp))
+ {
+ if (!pwline[0]) continue;
+ if (pwline[strlen(pwline) - 1] == '\n')
+ pwline[strlen(pwline) - 1] = 0;
+ if (!pwline[0]) continue;
+ /* skip comments */
+ if (pwline[0] == '#') continue;
+ if (pwline[0] == ';') continue;
+
+ AST_STANDARD_APP_ARGS(args, pwline);
+
+ ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
+
+ /* password must be in valid format (numeric) */
+ if (sscanf(args.passcode,"%d",&j) < 1) continue;
+ /* if we got it */
+ if (!strcmp(exten,args.passcode)) {
+ if (ast_strlen_zero(args.context))
+ args.context = "disa";
+ if (ast_strlen_zero(args.mailbox))
+ args.mailbox = "";
+ break;
+ }
+ }
+ fclose(fp);
+ }
+ /* compare the two */
+ if (strcmp(exten,args.passcode))
+ {
+ ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
+ goto reorder;
+
+ }
+ /* password good, set to dial state */
+ ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
+ play_dialtone(chan, args.mailbox);
+
+ k|=1; /* In number mode */
+ i = 0; /* re-set buffer pointer */
+ exten[sizeof(acctcode)] = 0;
+ ast_copy_string(acctcode, exten, sizeof(acctcode));
+ exten[0] = 0;
+ ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
+ continue;
+ }
+ }
+
+ exten[i++] = j; /* save digit */
+ exten[i] = 0;
+ if (!(k&1)) continue; /* if getting password, continue doing it */
+ /* if this exists */
+
+ if (ast_ignore_pattern(args.context, exten)) {
+ play_dialtone(chan, "");
+ did_ignore = 1;
+ } else
+ if (did_ignore) {
+ ast_playtones_stop(chan);
+ did_ignore = 0;
+ }
+
+ /* if can do some more, do it */
+ if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
+ break;
+ }
+ }
+ }
+
+ if (k == 3) {
+ int recheck = 0;
+ struct ast_flags flags = { AST_CDR_FLAG_POSTED };
+
+ if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
+ pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
+ exten[0] = 'i';
+ exten[1] = '\0';
+ recheck = 1;
+ }
+ if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
+ ast_playtones_stop(chan);
+ /* We're authenticated and have a target extension */
+ if (!ast_strlen_zero(args.cid))
+ {
+ ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
+ ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
+ }
+
+ if (!ast_strlen_zero(acctcode))
+ ast_copy_string(chan->accountcode, acctcode, sizeof(chan->accountcode));
+
+ ast_cdr_reset(chan->cdr, &flags);
+ ast_explicit_goto(chan, args.context, exten, 1);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ }
+
+ /* Received invalid, but no "i" extension exists in the given context */
+
+reorder:
+
+ ast_indicate(chan,AST_CONTROL_CONGESTION);
+ /* something is invalid, give em reorder for several seconds */
+ time(&rstart);
+ while(time(NULL) < rstart + 10)
+ {
+ if (ast_waitfor(chan, -1) < 0)
+ break;
+ f = ast_read(chan);
+ if (!f)
+ break;
+ ast_frfree(f);
+ }
+ ast_playtones_stop(chan);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, disa_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key(void)
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_dumpchan.c b/1.2-netsec/apps/app_dumpchan.c
new file mode 100644
index 000000000..4802dfee1
--- /dev/null
+++ b/1.2-netsec/apps/app_dumpchan.c
@@ -0,0 +1,192 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Anthony Minessale II.
+ *
+ * Anthony Minessale <anthmct@yahoo.com>
+ *
+ * disclaimed to Digium
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Application to dump channel variables
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Dump Info About The Calling Channel";
+static char *app = "DumpChan";
+static char *synopsis = "Dump Info About The Calling Channel";
+static char *desc =
+" DumpChan([<min_verbose_level>])\n"
+"Displays information on channel and listing of all channel\n"
+"variables. If min_verbose_level is specified, output is only\n"
+"displayed when the verbose level is currently set to that number\n"
+"or greater. \n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int ast_serialize_showchan(struct ast_channel *c, char *buf, size_t size)
+{
+ struct timeval now;
+ long elapsed_seconds=0;
+ int hour=0, min=0, sec=0;
+ char cgrp[256];
+ char pgrp[256];
+
+ now = ast_tvnow();
+ memset(buf,0,size);
+ if (!c)
+ return 0;
+
+ if (c->cdr) {
+ elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
+ hour = elapsed_seconds / 3600;
+ min = (elapsed_seconds % 3600) / 60;
+ sec = elapsed_seconds % 60;
+ }
+
+ snprintf(buf,size,
+ "Name= %s\n"
+ "Type= %s\n"
+ "UniqueID= %s\n"
+ "CallerID= %s\n"
+ "CallerIDName= %s\n"
+ "DNIDDigits= %s\n"
+ "State= %s (%d)\n"
+ "Rings= %d\n"
+ "NativeFormat= %d\n"
+ "WriteFormat= %d\n"
+ "ReadFormat= %d\n"
+ "1stFileDescriptor= %d\n"
+ "Framesin= %d %s\n"
+ "Framesout= %d %s\n"
+ "TimetoHangup= %ld\n"
+ "ElapsedTime= %dh%dm%ds\n"
+ "Context= %s\n"
+ "Extension= %s\n"
+ "Priority= %d\n"
+ "CallGroup= %s\n"
+ "PickupGroup= %s\n"
+ "Application= %s\n"
+ "Data= %s\n"
+ "Blocking_in= %s\n",
+ c->name,
+ c->type,
+ c->uniqueid,
+ (c->cid.cid_num ? c->cid.cid_num : "(N/A)"),
+ (c->cid.cid_name ? c->cid.cid_name : "(N/A)"),
+ (c->cid.cid_dnid ? c->cid.cid_dnid : "(N/A)" ),
+ ast_state2str(c->_state),
+ c->_state,
+ c->rings,
+ c->nativeformats,
+ c->writeformat,
+ c->readformat,
+ c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "",
+ c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", (long)c->whentohangup,
+ hour,
+ min,
+ sec,
+ c->context,
+ c->exten,
+ c->priority,
+ ast_print_group(cgrp, sizeof(cgrp), c->callgroup),
+ ast_print_group(pgrp, sizeof(pgrp), c->pickupgroup),
+ ( c->appl ? c->appl : "(N/A)" ),
+ ( c-> data ? (!ast_strlen_zero(c->data) ? c->data : "(Empty)") : "(None)"),
+ (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
+
+ return 0;
+}
+
+static int dumpchan_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char vars[1024];
+ char info[1024];
+ int level = 0;
+ static char *line = "================================================================================";
+
+ LOCAL_USER_ADD(u);
+
+ if (!ast_strlen_zero(data)) {
+ level = atoi(data);
+ }
+
+ pbx_builtin_serialize_variables(chan, vars, sizeof(vars));
+ ast_serialize_showchan(chan, info, sizeof(info));
+ if (option_verbose >= level)
+ ast_verbose("\nDumping Info For Channel: %s:\n%s\nInfo:\n%s\nVariables:\n%s%s\n",chan->name, line, info, vars, line);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, dumpchan_exec, synopsis, desc);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_echo.c b/1.2-netsec/apps/app_echo.c
new file mode 100644
index 000000000..2b7c20079
--- /dev/null
+++ b/1.2-netsec/apps/app_echo.c
@@ -0,0 +1,123 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Echo application -- play back what you hear to evaluate latency
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+static char *tdesc = "Simple Echo Application";
+
+static char *app = "Echo";
+
+static char *synopsis = "Echo audio read back to the user";
+
+static char *descrip =
+" Echo(): Echo audio read from channel back to the channel. \n"
+"User can exit the application by either pressing the '#' key, \n"
+"or hanging up.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int echo_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ struct ast_frame *f;
+ LOCAL_USER_ADD(u);
+ ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
+ ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
+ /* Do our thing here */
+ while(ast_waitfor(chan, -1) > -1) {
+ f = ast_read(chan);
+ if (!f)
+ break;
+ f->delivery.tv_sec = 0;
+ f->delivery.tv_usec = 0;
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (ast_write(chan, f))
+ break;
+ } else if (f->frametype == AST_FRAME_VIDEO) {
+ if (ast_write(chan, f))
+ break;
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ if (f->subclass == '#') {
+ res = 0;
+ break;
+ } else
+ if (ast_write(chan, f))
+ break;
+ }
+ ast_frfree(f);
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, echo_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_enumlookup.c b/1.2-netsec/apps/app_enumlookup.c
new file mode 100644
index 000000000..bd209c47b
--- /dev/null
+++ b/1.2-netsec/apps/app_enumlookup.c
@@ -0,0 +1,270 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Enumlookup - lookup entry in ENUM
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/enum.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "ENUM Lookup";
+
+static char *app = "EnumLookup";
+
+static char *synopsis = "Lookup number in ENUM";
+
+static char *descrip =
+" EnumLookup(exten[|option]): Looks up an extension via ENUM and sets\n"
+"the variable 'ENUM'. For VoIP URIs this variable will \n"
+"look like 'TECHNOLOGY/URI' with the appropriate technology.\n"
+"Currently, the enumservices SIP, H323, IAX, IAX2 and TEL are recognized. \n"
+"\nReturns status in the ENUMSTATUS channel variable:\n"
+" ERROR Failed to do a lookup\n"
+" <tech> Technology of the successful lookup: SIP, H323, IAX, IAX2 or TEL\n"
+" BADURI Got URI Asterisk does not understand.\n"
+" The option string may contain zero or the following character:\n"
+" 'j' -- jump to +101 priority if the lookup isn't successful.\n"
+" and jump to +51 priority on a TEL entry.\n";
+
+#define ENUM_CONFIG "enum.conf"
+
+static char h323driver[80] = "";
+#define H323DRIVERDEFAULT "H323"
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+/*--- enumlookup_exec: Look up number in ENUM and return result */
+static int enumlookup_exec(struct ast_channel *chan, void *data)
+{
+ int res=0,priority_jump=0;
+ char tech[80];
+ char dest[80];
+ char tmp[256];
+ char *c,*t = NULL;
+ static int dep_warning=0;
+ struct localuser *u;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(d);
+ AST_APP_ARG(o);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "EnumLookup requires an argument (extension)\n");
+ return -1;
+ }
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "The application EnumLookup is deprecated. Please use the ENUMLOOKUP() function instead.\n");
+ dep_warning = 1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ parse = ast_strdupa(data);
+ if (!parse) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ tech[0] = '\0';
+ dest[0] = '\0';
+
+ if (args.o) {
+ if (strchr(args.o, 'j'))
+ priority_jump = 1;
+ }
+
+ res = ast_get_enum(chan, args.d, dest, sizeof(dest), tech, sizeof(tech), NULL, NULL);
+
+ if (!res) { /* Failed to do a lookup */
+ if (priority_jump || option_priority_jumping) {
+ /* Look for a "busy" place */
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ }
+ pbx_builtin_setvar_helper(chan, "ENUMSTATUS", "ERROR");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ pbx_builtin_setvar_helper(chan, "ENUMSTATUS", tech);
+ /* Parse it out */
+ if (res > 0) {
+ if (!strcasecmp(tech, "SIP")) {
+ c = dest;
+ if (!strncmp(c, "sip:", 4))
+ c += 4;
+ snprintf(tmp, sizeof(tmp), "SIP/%s", c);
+ pbx_builtin_setvar_helper(chan, "ENUM", tmp);
+ } else if (!strcasecmp(tech, "h323")) {
+ c = dest;
+ if (!strncmp(c, "h323:", 5))
+ c += 5;
+ snprintf(tmp, sizeof(tmp), "%s/%s", h323driver, c);
+/* do a s!;.*!! on the H323 URI */
+ t = strchr(c,';');
+ if (t)
+ *t = 0;
+ pbx_builtin_setvar_helper(chan, "ENUM", tmp);
+ } else if (!strcasecmp(tech, "iax")) {
+ c = dest;
+ if (!strncmp(c, "iax:", 4))
+ c += 4;
+ snprintf(tmp, sizeof(tmp), "IAX/%s", c);
+ pbx_builtin_setvar_helper(chan, "ENUM", tmp);
+ } else if (!strcasecmp(tech, "iax2")) {
+ c = dest;
+ if (!strncmp(c, "iax2:", 5))
+ c += 5;
+ snprintf(tmp, sizeof(tmp), "IAX2/%s", c);
+ pbx_builtin_setvar_helper(chan, "ENUM", tmp);
+ } else if (!strcasecmp(tech, "tel")) {
+ c = dest;
+ if (!strncmp(c, "tel:", 4))
+ c += 4;
+
+ if (c[0] != '+') {
+ ast_log(LOG_NOTICE, "tel: uri must start with a \"+\" (got '%s')\n", c);
+ res = 0;
+ } else {
+/* now copy over the number, skipping all non-digits and stop at ; or NULL */
+ t = tmp;
+ while( *c && (*c != ';') && (t - tmp < (sizeof(tmp) - 1))) {
+ if (isdigit(*c))
+ *t++ = *c;
+ c++;
+ }
+ *t = 0;
+ pbx_builtin_setvar_helper(chan, "ENUM", tmp);
+ ast_log(LOG_NOTICE, "tel: ENUM set to \"%s\"\n", tmp);
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 51))
+ res = 0;
+ }
+ }
+ } else if (!ast_strlen_zero(tech)) {
+ ast_log(LOG_NOTICE, "Don't know how to handle technology '%s'\n", tech);
+ pbx_builtin_setvar_helper(chan, "ENUMSTATUS", "BADURI");
+ res = 0;
+ }
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+/*--- load_config: Load enum.conf and find out how to handle H.323 */
+static int load_config(void)
+{
+ struct ast_config *cfg;
+ char *s;
+
+ cfg = ast_config_load(ENUM_CONFIG);
+ if (cfg) {
+ if (!(s=ast_variable_retrieve(cfg, "general", "h323driver"))) {
+ strncpy(h323driver, H323DRIVERDEFAULT, sizeof(h323driver) - 1);
+ } else {
+ strncpy(h323driver, s, sizeof(h323driver) - 1);
+ }
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ ast_log(LOG_NOTICE, "No ENUM Config file, using defaults\n");
+ return 0;
+}
+
+
+/*--- unload_module: Unload this application from PBX */
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+/*--- load_module: Load this application into PBX */
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app, enumlookup_exec, synopsis, descrip);
+
+ if (!res)
+ res = load_config();
+
+ return res;
+}
+
+/*--- reload: Reload configuration file */
+int reload(void)
+{
+ return load_config();
+}
+
+
+/*--- description: Describe module */
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_eval.c b/1.2-netsec/apps/app_eval.c
new file mode 100644
index 000000000..13ab0ddbc
--- /dev/null
+++ b/1.2-netsec/apps/app_eval.c
@@ -0,0 +1,127 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_eval__v001@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ * \brief Eval application
+ *
+ * \author Tilghman Lesher <app_eval__v001@the-tilghman.com>
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+/* Maximum length of any variable */
+#define MAXRESULT 1024
+
+static char *tdesc = "Reevaluates strings";
+
+static char *app_eval = "Eval";
+
+static char *eval_synopsis = "Evaluates a string";
+
+static char *eval_descrip =
+"Usage: Eval(newvar=somestring)\n"
+" Normally Asterisk evaluates variables inline. But what if you want to\n"
+"store variable offsets in a database, to be evaluated later? Eval is\n"
+"the answer, by allowing a string to be evaluated twice in the dialplan,\n"
+"the first time as part of the normal dialplan, and the second using Eval.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int eval_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *s, *newvar=NULL, tmp[MAXRESULT];
+ static int dep_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "This application has been deprecated in favor of the dialplan function, EVAL\n");
+ dep_warning = 1;
+ }
+
+ /* Check and parse arguments */
+ if (data) {
+ s = ast_strdupa((char *)data);
+ if (s) {
+ newvar = strsep(&s, "=");
+ if (newvar && (newvar[0] != '\0')) {
+ memset(tmp, 0, MAXRESULT);
+ pbx_substitute_variables_helper(chan, s, tmp, MAXRESULT - 1);
+ pbx_builtin_setvar_helper(chan, newvar, tmp);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ res = -1;
+ }
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_eval);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app_eval, eval_exec, eval_synopsis, eval_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_exec.c b/1.2-netsec/apps/app_exec.c
new file mode 100644
index 000000000..9759455ac
--- /dev/null
+++ b/1.2-netsec/apps/app_exec.c
@@ -0,0 +1,135 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_exec__v001@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Exec application
+ *
+ * \author Tilghman Lesher <app_exec__v001@the-tilghman.com>
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+/* Maximum length of any variable */
+#define MAXRESULT 1024
+
+static char *tdesc = "Executes applications";
+
+static char *app_exec = "Exec";
+
+static char *exec_synopsis = "Executes internal application";
+
+static char *exec_descrip =
+"Usage: Exec(appname(arguments))\n"
+" Allows an arbitrary application to be invoked even when not\n"
+"hardcoded into the dialplan. To invoke external applications\n"
+"see the application System. Returns whatever value the\n"
+"app returns or a non-zero value if the app cannot be found.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int exec_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *s, *appname, *endargs, args[MAXRESULT];
+ struct ast_app *app;
+
+ LOCAL_USER_ADD(u);
+
+ memset(args, 0, MAXRESULT);
+
+ /* Check and parse arguments */
+ if (data) {
+ s = ast_strdupa((char *)data);
+ if (s) {
+ appname = strsep(&s, "(");
+ if (s) {
+ endargs = strrchr(s, ')');
+ if (endargs)
+ *endargs = '\0';
+ pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
+ }
+ if (appname) {
+ app = pbx_findapp(appname);
+ if (app) {
+ res = pbx_exec(chan, app, args, 1);
+ } else {
+ ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
+ res = -1;
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ res = -1;
+ }
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_exec);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app_exec, exec_exec, exec_synopsis, exec_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_externalivr.c b/1.2-netsec/apps/app_externalivr.c
new file mode 100644
index 000000000..a53a59532
--- /dev/null
+++ b/1.2-netsec/apps/app_externalivr.c
@@ -0,0 +1,583 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * Portions taken from the file-based music-on-hold work
+ * created by Anthony Minessale II in res_musiconhold.c
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief External IVR application interface
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/app.h"
+
+static const char *tdesc = "External IVR Interface Application";
+
+static const char *app = "ExternalIVR";
+
+static const char *synopsis = "Interfaces with an external IVR application";
+
+static const char *descrip =
+" ExternalIVR(command[|arg[|arg...]]): Forks an process to run the supplied command,\n"
+"and starts a generator on the channel. The generator's play list is\n"
+"controlled by the external application, which can add and clear entries\n"
+"via simple commands issued over its stdout. The external application\n"
+"will receive all DTMF events received on the channel, and notification\n"
+"if the channel is hung up. The application will not be forcibly terminated\n"
+"when the channel is hung up.\n"
+"See doc/README.externalivr for a protocol specification.\n";
+
+/* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
+#define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
+
+struct playlist_entry {
+ AST_LIST_ENTRY(playlist_entry) list;
+ char filename[1];
+};
+
+struct localuser {
+ struct ast_channel *chan;
+ struct localuser *next;
+ AST_LIST_HEAD(playlist, playlist_entry) playlist;
+ AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
+ int abort_current_sound;
+ int playing_silence;
+ int option_autoclear;
+};
+
+LOCAL_USER_DECL;
+
+struct gen_state {
+ struct localuser *u;
+ struct ast_filestream *stream;
+ struct playlist_entry *current;
+ int sample_queue;
+};
+
+static void send_child_event(FILE *handle, const char event, const char *data,
+ const struct ast_channel *chan)
+{
+ char tmp[256];
+
+ if (!data) {
+ snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
+ } else {
+ snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
+ }
+
+ fprintf(handle, "%s\n", tmp);
+ ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
+}
+
+static void *gen_alloc(struct ast_channel *chan, void *params)
+{
+ struct localuser *u = params;
+ struct gen_state *state;
+
+ state = calloc(1, sizeof(*state));
+
+ if (!state)
+ return NULL;
+
+ state->u = u;
+
+ return state;
+}
+
+static void gen_closestream(struct gen_state *state)
+{
+ if (!state->stream)
+ return;
+
+ ast_closestream(state->stream);
+ state->u->chan->stream = NULL;
+ state->stream = NULL;
+}
+
+static void gen_release(struct ast_channel *chan, void *data)
+{
+ struct gen_state *state = data;
+
+ gen_closestream(state);
+ free(data);
+}
+
+/* caller has the playlist locked */
+static int gen_nextfile(struct gen_state *state)
+{
+ struct localuser *u = state->u;
+ char *file_to_stream;
+
+ u->abort_current_sound = 0;
+ u->playing_silence = 0;
+ gen_closestream(state);
+
+ while (!state->stream) {
+ state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
+ if (state->current) {
+ file_to_stream = state->current->filename;
+ } else {
+ file_to_stream = "silence-10";
+ u->playing_silence = 1;
+ }
+
+ if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
+ ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
+ if (!u->playing_silence) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return (!state->stream);
+}
+
+static struct ast_frame *gen_readframe(struct gen_state *state)
+{
+ struct ast_frame *f = NULL;
+ struct localuser *u = state->u;
+
+ if (u->abort_current_sound ||
+ (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
+ gen_closestream(state);
+ AST_LIST_LOCK(&u->playlist);
+ gen_nextfile(state);
+ AST_LIST_UNLOCK(&u->playlist);
+ }
+
+ if (!(state->stream && (f = ast_readframe(state->stream)))) {
+ if (state->current) {
+ AST_LIST_LOCK(&u->finishlist);
+ AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
+ AST_LIST_UNLOCK(&u->finishlist);
+ state->current = NULL;
+ }
+ if (!gen_nextfile(state))
+ f = ast_readframe(state->stream);
+ }
+
+ return f;
+}
+
+static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct gen_state *state = data;
+ struct ast_frame *f = NULL;
+ int res = 0;
+
+ state->sample_queue += samples;
+
+ while (state->sample_queue > 0) {
+ if (!(f = gen_readframe(state)))
+ return -1;
+
+ res = ast_write(chan, f);
+ ast_frfree(f);
+ if (res < 0) {
+ ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
+ return -1;
+ }
+ state->sample_queue -= f->samples;
+ }
+
+ return res;
+}
+
+static struct ast_generator gen =
+{
+ alloc: gen_alloc,
+ release: gen_release,
+ generate: gen_generate,
+};
+
+static struct playlist_entry *make_entry(const char *filename)
+{
+ struct playlist_entry *entry;
+
+ entry = calloc(1, sizeof(*entry) + strlen(filename) + 10);
+
+ if (!entry)
+ return NULL;
+
+ strcpy(entry->filename, filename);
+
+ return entry;
+}
+
+static int app_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u = NULL;
+ struct playlist_entry *entry;
+ const char *args = data;
+ int child_stdin[2] = { 0,0 };
+ int child_stdout[2] = { 0,0 };
+ int child_stderr[2] = { 0,0 };
+ int res = -1;
+ int gen_active = 0;
+ int pid;
+ char *argv[32];
+ int argc = 1;
+ char *buf, *command;
+ FILE *child_commands = NULL;
+ FILE *child_errors = NULL;
+ FILE *child_events = NULL;
+
+ LOCAL_USER_ADD(u);
+
+ AST_LIST_HEAD_INIT(&u->playlist);
+ AST_LIST_HEAD_INIT(&u->finishlist);
+ u->abort_current_sound = 0;
+
+ if (ast_strlen_zero(args)) {
+ ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
+ goto exit;
+ }
+
+ buf = ast_strdupa(data);
+ if (!buf) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (pipe(child_stdin)) {
+ ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (pipe(child_stdout)) {
+ ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (pipe(child_stderr)) {
+ ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+
+ if (ast_activate_generator(chan, &gen, u) < 0) {
+ ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
+ goto exit;
+ } else
+ gen_active = 1;
+
+ pid = fork();
+ if (pid < 0) {
+ ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (!pid) {
+ /* child process */
+ int i;
+
+ dup2(child_stdin[0], STDIN_FILENO);
+ dup2(child_stdout[1], STDOUT_FILENO);
+ dup2(child_stderr[1], STDERR_FILENO);
+ for (i = STDERR_FILENO + 1; i < 1024; i++)
+ close(i);
+ execv(argv[0], argv);
+ fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno));
+ exit(1);
+ } else {
+ /* parent process */
+ int child_events_fd = child_stdin[1];
+ int child_commands_fd = child_stdout[0];
+ int child_errors_fd = child_stderr[0];
+ struct ast_frame *f;
+ int ms;
+ int exception;
+ int ready_fd;
+ int waitfds[2] = { child_errors_fd, child_commands_fd };
+ struct ast_channel *rchan;
+
+ close(child_stdin[0]);
+ child_stdin[0] = 0;
+ close(child_stdout[1]);
+ child_stdout[1] = 0;
+ close(child_stderr[1]);
+ child_stderr[1] = 0;
+
+ if (!(child_events = fdopen(child_events_fd, "w"))) {
+ ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
+ goto exit;
+ }
+
+ if (!(child_commands = fdopen(child_commands_fd, "r"))) {
+ ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
+ goto exit;
+ }
+
+ if (!(child_errors = fdopen(child_errors_fd, "r"))) {
+ ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
+ goto exit;
+ }
+
+ setvbuf(child_events, NULL, _IONBF, 0);
+ setvbuf(child_commands, NULL, _IONBF, 0);
+ setvbuf(child_errors, NULL, _IONBF, 0);
+
+ res = 0;
+
+ while (1) {
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
+ ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
+ res = -1;
+ break;
+ }
+
+ if (ast_check_hangup(chan)) {
+ ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
+ send_child_event(child_events, 'H', NULL, chan);
+ res = -1;
+ break;
+ }
+
+ ready_fd = 0;
+ ms = 100;
+ errno = 0;
+ exception = 0;
+
+ rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);
+
+ if (!AST_LIST_EMPTY(&u->finishlist)) {
+ AST_LIST_LOCK(&u->finishlist);
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
+ send_child_event(child_events, 'F', entry->filename, chan);
+ free(entry);
+ }
+ AST_LIST_UNLOCK(&u->finishlist);
+ }
+
+ if (rchan) {
+ /* the channel has something */
+ f = ast_read(chan);
+ if (!f) {
+ ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
+ send_child_event(child_events, 'H', NULL, chan);
+ res = -1;
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_DTMF) {
+ send_child_event(child_events, f->subclass, NULL, chan);
+ if (u->option_autoclear) {
+ if (!u->abort_current_sound && !u->playing_silence)
+ send_child_event(child_events, 'T', NULL, chan);
+ AST_LIST_LOCK(&u->playlist);
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
+ send_child_event(child_events, 'D', entry->filename, chan);
+ free(entry);
+ }
+ if (!u->playing_silence)
+ u->abort_current_sound = 1;
+ AST_LIST_UNLOCK(&u->playlist);
+ }
+ } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
+ ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
+ send_child_event(child_events, 'H', NULL, chan);
+ ast_frfree(f);
+ res = -1;
+ break;
+ }
+ ast_frfree(f);
+ } else if (ready_fd == child_commands_fd) {
+ char input[1024];
+
+ if (exception || feof(child_commands)) {
+ ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
+ res = -1;
+ break;
+ }
+
+ if (!fgets(input, sizeof(input), child_commands))
+ continue;
+
+ command = ast_strip(input);
+
+ ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);
+
+ if (strlen(input) < 4)
+ continue;
+
+ if (input[0] == 'S') {
+ if (ast_fileexists(&input[2], NULL, NULL) == -1) {
+ ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
+ send_child_event(child_events, 'Z', NULL, chan);
+ strcpy(&input[2], "exception");
+ }
+ if (!u->abort_current_sound && !u->playing_silence)
+ send_child_event(child_events, 'T', NULL, chan);
+ AST_LIST_LOCK(&u->playlist);
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
+ send_child_event(child_events, 'D', entry->filename, chan);
+ free(entry);
+ }
+ if (!u->playing_silence)
+ u->abort_current_sound = 1;
+ entry = make_entry(&input[2]);
+ if (entry)
+ AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
+ AST_LIST_UNLOCK(&u->playlist);
+ } else if (input[0] == 'A') {
+ if (ast_fileexists(&input[2], NULL, NULL) == -1) {
+ ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
+ send_child_event(child_events, 'Z', NULL, chan);
+ strcpy(&input[2], "exception");
+ }
+ entry = make_entry(&input[2]);
+ if (entry) {
+ AST_LIST_LOCK(&u->playlist);
+ AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
+ AST_LIST_UNLOCK(&u->playlist);
+ }
+ } else if (input[0] == 'H') {
+ ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
+ send_child_event(child_events, 'H', NULL, chan);
+ break;
+ } else if (input[0] == 'O') {
+ if (!strcasecmp(&input[2], "autoclear"))
+ u->option_autoclear = 1;
+ else if (!strcasecmp(&input[2], "noautoclear"))
+ u->option_autoclear = 0;
+ else
+ ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
+ }
+ } else if (ready_fd == child_errors_fd) {
+ char input[1024];
+
+ if (exception || feof(child_errors)) {
+ ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
+ res = -1;
+ break;
+ }
+
+ if (fgets(input, sizeof(input), child_errors)) {
+ command = ast_strip(input);
+ ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
+ }
+ } else if ((ready_fd < 0) && ms) {
+ if (errno == 0 || errno == EINTR)
+ continue;
+
+ ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
+ break;
+ }
+ }
+ }
+
+ exit:
+ if (gen_active)
+ ast_deactivate_generator(chan);
+
+ if (child_events)
+ fclose(child_events);
+
+ if (child_commands)
+ fclose(child_commands);
+
+ if (child_errors)
+ fclose(child_errors);
+
+ if (child_stdin[0])
+ close(child_stdin[0]);
+
+ if (child_stdin[1])
+ close(child_stdin[1]);
+
+ if (child_stdout[0])
+ close(child_stdout[0]);
+
+ if (child_stdout[1])
+ close(child_stdout[1]);
+
+ if (child_stderr[0])
+ close(child_stderr[0]);
+
+ if (child_stderr[1])
+ close(child_stderr[1]);
+
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
+ free(entry);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, app_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_festival.c b/1.2-netsec/apps/app_festival.c
new file mode 100644
index 000000000..d3bd1e9b9
--- /dev/null
+++ b/1.2-netsec/apps/app_festival.c
@@ -0,0 +1,532 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002, Christos Ricudis
+ *
+ * Christos Ricudis <ricudis@itc.auth.gr>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Connect to festival
+ *
+ * \ingroup applications
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/md5.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+#define FESTIVAL_CONFIG "festival.conf"
+
+static char *tdesc = "Simple Festival Interface";
+
+static char *app = "Festival";
+
+static char *synopsis = "Say text to the user";
+
+static char *descrip =
+" Festival(text[|intkeys]): Connect to Festival, send the argument, get back the waveform,"
+"play it to the user, allowing any given interrupt keys to immediately terminate and return\n"
+"the value, or 'any' to allow any number back (useful in dialplan)\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char *socket_receive_file_to_buff(int fd,int *size)
+{
+ /* Receive file (probably a waveform file) from socket using */
+ /* Festival key stuff technique, but long winded I know, sorry */
+ /* but will receive any file without closeing the stream or */
+ /* using OOB data */
+ static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
+ char *buff;
+ int bufflen;
+ int n,k,i;
+ char c;
+
+ bufflen = 1024;
+ buff = (char *)malloc(bufflen);
+ *size=0;
+
+ for (k=0; file_stuff_key[k] != '\0';)
+ {
+ n = read(fd,&c,1);
+ if (n==0) break; /* hit stream eof before end of file */
+ if ((*size)+k+1 >= bufflen)
+ { /* +1 so you can add a NULL if you want */
+ bufflen += bufflen/4;
+ buff = (char *)realloc(buff,bufflen);
+ }
+ if (file_stuff_key[k] == c)
+ k++;
+ else if ((c == 'X') && (file_stuff_key[k+1] == '\0'))
+ { /* It looked like the key but wasn't */
+ for (i=0; i < k; i++,(*size)++)
+ buff[*size] = file_stuff_key[i];
+ k=0;
+ /* omit the stuffed 'X' */
+ }
+ else
+ {
+ for (i=0; i < k; i++,(*size)++)
+ buff[*size] = file_stuff_key[i];
+ k=0;
+ buff[*size] = c;
+ (*size)++;
+ }
+
+ }
+
+ return buff;
+}
+
+static int send_waveform_to_fd(char *waveform, int length, int fd) {
+
+ int res;
+ int x;
+#ifdef __PPC__
+ char c;
+#endif
+
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res)
+ return res;
+ for (x=0;x<256;x++) {
+ if (x != fd)
+ close(x);
+ }
+/*IAS */
+#ifdef __PPC__
+ for( x=0; x<length; x+=2)
+ {
+ c = *(waveform+x+1);
+ *(waveform+x+1)=*(waveform+x);
+ *(waveform+x)=c;
+ }
+#endif
+
+ write(fd,waveform,length);
+ close(fd);
+ exit(0);
+}
+
+
+static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys) {
+ int res=0;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int needed = 0;
+ int owriteformat;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ char offset[AST_FRIENDLY_OFFSET];
+ char frdata[2048];
+ } myf;
+
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Unable to create pipe\n");
+ return -1;
+ }
+
+ /* Answer if it's not already going */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+ ast_stopstream(chan);
+ ast_indicate(chan, -1);
+
+ owriteformat = chan->writeformat;
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ return -1;
+ }
+
+ res=send_waveform_to_fd(waveform,length,fds[1]);
+ if (res >= 0) {
+ pid = res;
+ /* Order is important -- there's almost always going to be mp3... we want to prioritize the
+ user */
+ for (;;) {
+ ms = 1000;
+ res = ast_waitfor(chan, ms);
+ if (res < 1) {
+ res = -1;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_log(LOG_DEBUG, "User pressed a key\n");
+ if (intkeys && strchr(intkeys, f->subclass)) {
+ res = f->subclass;
+ ast_frfree(f);
+ break;
+ }
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ /* Treat as a generator */
+ needed = f->samples * 2;
+ if (needed > sizeof(myf.frdata)) {
+ ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
+ (int)sizeof(myf.frdata) / 2, needed/2);
+ needed = sizeof(myf.frdata);
+ }
+ res = read(fds[0], myf.frdata, needed);
+ if (res > 0) {
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_SLINEAR;
+ myf.f.datalen = res;
+ myf.f.samples = res / 2;
+ myf.f.mallocd = 0;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.data = myf.frdata;
+ if (ast_write(chan, &myf.f) < 0) {
+ res = -1;
+ break;
+ }
+ if (res < needed) { /* last frame */
+ ast_log(LOG_DEBUG, "Last frame\n");
+ res=0;
+ break;
+ }
+ } else {
+ ast_log(LOG_DEBUG, "No more waveform\n");
+ res = 0;
+ }
+ }
+ ast_frfree(f);
+ }
+ }
+ close(fds[0]);
+ close(fds[1]);
+
+/* if (pid > -1) */
+/* kill(pid, SIGKILL); */
+ if (!res && owriteformat)
+ ast_set_write_format(chan, owriteformat);
+ return res;
+}
+
+#define MAXLEN 180
+#define MAXFESTLEN 2048
+
+
+
+
+static int festival_exec(struct ast_channel *chan, void *vdata)
+{
+ int usecache;
+ int res=0;
+ struct localuser *u;
+ struct sockaddr_in serv_addr;
+ struct hostent *serverhost;
+ struct ast_hostent ahp;
+ int fd;
+ FILE *fs;
+ char *host;
+ char *cachedir;
+ char *temp;
+ char *festivalcommand;
+ int port=1314;
+ int n;
+ char ack[4];
+ char *waveform;
+ int filesize;
+ int wave;
+ char bigstring[MAXFESTLEN];
+ int i;
+ struct MD5Context md5ctx;
+ unsigned char MD5Res[16];
+ char MD5Hex[33] = "";
+ char koko[4] = "";
+ char cachefile[MAXFESTLEN]="";
+ int readcache=0;
+ int writecache=0;
+ int strln;
+ int fdesc = -1;
+ char buffer[16384];
+ int seekpos = 0;
+ char *data;
+ char *intstr;
+ struct ast_config *cfg;
+
+ if (ast_strlen_zero(vdata)) {
+ ast_log(LOG_WARNING, "festival requires an argument (text)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ cfg = ast_config_load(FESTIVAL_CONFIG);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
+ host = "localhost";
+ }
+ if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
+ port = 1314;
+ } else {
+ port = atoi(temp);
+ }
+ if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
+ usecache=0;
+ } else {
+ usecache = ast_true(temp);
+ }
+ if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
+ cachedir = "/tmp/";
+ }
+ if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
+ festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n";
+ }
+
+ data = ast_strdupa(vdata);
+ if (!data) {
+ ast_log(LOG_ERROR, "Out of memery\n");
+ ast_config_destroy(cfg);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ intstr = strchr(data, '|');
+ if (intstr) {
+ *intstr = '\0';
+ intstr++;
+ if (!strcasecmp(intstr, "any"))
+ intstr = AST_DIGIT_ANY;
+ }
+
+ ast_log(LOG_DEBUG, "Text passed to festival server : %s\n",(char *)data);
+ /* Connect to local festival server */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ ast_log(LOG_WARNING,"festival_client: can't get socket\n");
+ ast_config_destroy(cfg);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
+ /* its a name rather than an ipnum */
+ serverhost = ast_gethostbyname(host, &ahp);
+ if (serverhost == (struct hostent *)0) {
+ ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n");
+ ast_config_destroy(cfg);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length);
+ }
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(port);
+
+ if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
+ ast_log(LOG_WARNING,"festival_client: connect to server failed\n");
+ ast_config_destroy(cfg);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ /* Compute MD5 sum of string */
+ MD5Init(&md5ctx);
+ MD5Update(&md5ctx,(unsigned char const *)data,strlen(data));
+ MD5Final(MD5Res,&md5ctx);
+ MD5Hex[0] = '\0';
+
+ /* Convert to HEX and look if there is any matching file in the cache
+ directory */
+ for (i=0;i<16;i++) {
+ snprintf(koko, sizeof(koko), "%X",MD5Res[i]);
+ strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
+ }
+ readcache=0;
+ writecache=0;
+ if (strlen(cachedir)+strlen(MD5Hex)+1<=MAXFESTLEN && (usecache==-1)) {
+ snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
+ fdesc=open(cachefile,O_RDWR);
+ if (fdesc==-1) {
+ fdesc=open(cachefile,O_CREAT|O_RDWR,0777);
+ if (fdesc!=-1) {
+ writecache=1;
+ strln=strlen((char *)data);
+ ast_log(LOG_DEBUG,"line length : %d\n",strln);
+ write(fdesc,&strln,sizeof(int));
+ write(fdesc,data,strln);
+ seekpos=lseek(fdesc,0,SEEK_CUR);
+ ast_log(LOG_DEBUG,"Seek position : %d\n",seekpos);
+ }
+ } else {
+ read(fdesc,&strln,sizeof(int));
+ ast_log(LOG_DEBUG,"Cache file exists, strln=%d, strlen=%d\n",strln,(int)strlen((char *)data));
+ if (strlen((char *)data)==strln) {
+ ast_log(LOG_DEBUG,"Size OK\n");
+ read(fdesc,&bigstring,strln);
+ bigstring[strln] = 0;
+ if (strcmp(bigstring,data)==0) {
+ readcache=1;
+ } else {
+ ast_log(LOG_WARNING,"Strings do not match\n");
+ }
+ } else {
+ ast_log(LOG_WARNING,"Size mismatch\n");
+ }
+ }
+ }
+
+ if (readcache==1) {
+ close(fd);
+ fd=fdesc;
+ ast_log(LOG_DEBUG,"Reading from cache...\n");
+ } else {
+ ast_log(LOG_DEBUG,"Passing text to festival...\n");
+ fs=fdopen(dup(fd),"wb");
+ fprintf(fs,festivalcommand,(char *)data);
+ fflush(fs);
+ fclose(fs);
+ }
+
+ /* Write to cache and then pass it down */
+ if (writecache==1) {
+ ast_log(LOG_DEBUG,"Writing result to cache...\n");
+ while ((strln=read(fd,buffer,16384))!=0) {
+ write(fdesc,buffer,strln);
+ }
+ close(fd);
+ close(fdesc);
+ fd=open(cachefile,O_RDWR);
+ lseek(fd,seekpos,SEEK_SET);
+ }
+
+ ast_log(LOG_DEBUG,"Passing data to channel...\n");
+
+ /* Read back info from server */
+ /* This assumes only one waveform will come back, also LP is unlikely */
+ wave = 0;
+ do {
+ int read_data;
+ for (n=0; n < 3; )
+ {
+ read_data = read(fd,ack+n,3-n);
+ /* this avoids falling in infinite loop
+ * in case that festival server goes down
+ * */
+ if ( read_data == -1 )
+ {
+ ast_log(LOG_WARNING,"Unable to read from cache/festival fd");
+ return -1;
+ }
+ n += read_data;
+ }
+ ack[3] = '\0';
+ if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */
+ ast_log(LOG_DEBUG,"Festival WV command\n");
+ waveform = socket_receive_file_to_buff(fd,&filesize);
+ res = send_waveform_to_channel(chan,waveform,filesize, intstr);
+ free(waveform);
+ break;
+ }
+ else if (strcmp(ack,"LP\n") == 0) { /* receive an s-expr */
+ ast_log(LOG_DEBUG,"Festival LP command\n");
+ waveform = socket_receive_file_to_buff(fd,&filesize);
+ waveform[filesize]='\0';
+ ast_log(LOG_WARNING,"Festival returned LP : %s\n",waveform);
+ free(waveform);
+ } else if (strcmp(ack,"ER\n") == 0) { /* server got an error */
+ ast_log(LOG_WARNING,"Festival returned ER\n");
+ res=-1;
+ break;
+ }
+ } while (strcmp(ack,"OK\n") != 0);
+ close(fd);
+ ast_config_destroy(cfg);
+ LOCAL_USER_REMOVE(u);
+ return res;
+
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+
+ return ast_register_application(app, festival_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_flash.c b/1.2-netsec/apps/app_flash.c
new file mode 100644
index 000000000..a9aae22b8
--- /dev/null
+++ b/1.2-netsec/apps/app_flash.c
@@ -0,0 +1,141 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to flash a zap trunk
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#ifdef __linux__
+#include <linux/zaptel.h>
+#else
+#include <zaptel.h>
+#endif /* __linux__ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Flash zap trunk application";
+
+static char *app = "Flash";
+
+static char *synopsis = "Flashes a Zap Trunk";
+
+static char *descrip =
+" Flash(): Sends a flash on a zap trunk. This is only a hack for\n"
+"people who want to perform transfers and such via AGI and is generally\n"
+"quite useless oths application will only work on Zap trunks.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static inline int zt_wait_event(int fd)
+{
+ /* Avoid the silly zt_waitevent which ignores a bunch of events */
+ int i,j=0;
+ i = ZT_IOMUX_SIGEVENT;
+ if (ioctl(fd, ZT_IOMUX, &i) == -1) return -1;
+ if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
+ return j;
+}
+
+static int flash_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ int x;
+ struct localuser *u;
+ struct zt_params ztp;
+ LOCAL_USER_ADD(u);
+ if (!strcasecmp(chan->type, "Zap")) {
+ memset(&ztp, 0, sizeof(ztp));
+ res = ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp);
+ if (!res) {
+ if (ztp.sigtype & __ZT_SIG_FXS) {
+ x = ZT_FLASH;
+ res = ioctl(chan->fds[0], ZT_HOOK, &x);
+ if (!res || (errno == EINPROGRESS)) {
+ if (res) {
+ /* Wait for the event to finish */
+ zt_wait_event(chan->fds[0]);
+ }
+ res = ast_safe_sleep(chan, 1000);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Flashed channel %s\n", chan->name);
+ } else
+ ast_log(LOG_WARNING, "Unable to flash channel %s: %s\n", chan->name, strerror(errno));
+ } else
+ ast_log(LOG_WARNING, "%s is not an FXO Channel\n", chan->name);
+ } else
+ ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", chan->name, strerror(errno));
+ } else
+ ast_log(LOG_WARNING, "%s is not a Zap channel\n", chan->name);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, flash_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_forkcdr.c b/1.2-netsec/apps/app_forkcdr.c
new file mode 100644
index 000000000..290c16850
--- /dev/null
+++ b/1.2-netsec/apps/app_forkcdr.c
@@ -0,0 +1,131 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
+ * Development of this app Sponsered/Funded by TAAN Softworks Corp
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Fork CDR application
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+
+static char *tdesc = "Fork The CDR into 2 separate entities.";
+static char *app = "ForkCDR";
+static char *synopsis =
+"Forks the Call Data Record";
+static char *descrip =
+" ForkCDR([options]): Causes the Call Data Record to fork an additional\n"
+ "cdr record starting from the time of the fork call\n"
+"If the option 'v' is passed all cdr variables will be passed along also.\n"
+"";
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static void ast_cdr_fork(struct ast_channel *chan)
+{
+ struct ast_cdr *cdr;
+ struct ast_cdr *newcdr;
+ struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
+
+ cdr = chan->cdr;
+
+ while (cdr->next)
+ cdr = cdr->next;
+
+ if (!(newcdr = ast_cdr_dup(cdr)))
+ return;
+
+ ast_cdr_append(cdr, newcdr);
+ ast_cdr_reset(newcdr, &flags);
+
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
+ ast_cdr_free_vars(cdr, 0);
+
+ ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
+}
+
+static int forkcdr_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+
+ if (!chan->cdr) {
+ ast_log(LOG_WARNING, "Channel does not have a CDR\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!ast_strlen_zero(data))
+ ast_set2_flag(chan->cdr, strchr(data, 'v'), AST_CDR_FLAG_KEEP_VARS);
+
+ ast_cdr_fork(chan);
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, forkcdr_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_getcpeid.c b/1.2-netsec/apps/app_getcpeid.c
new file mode 100644
index 000000000..b2d883bc1
--- /dev/null
+++ b/1.2-netsec/apps/app_getcpeid.c
@@ -0,0 +1,167 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Get ADSI CPE ID
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Get ADSI CPE ID";
+
+static char *app = "GetCPEID";
+
+static char *synopsis = "Get ADSI CPE ID";
+
+static char *descrip =
+" GetCPEID: Obtains and displays ADSI CPE ID and other information in order\n"
+"to properly setup zapata.conf for on-hook operations.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
+{
+ int justify[5] = { ADSI_JUST_CENT, ADSI_JUST_LEFT, ADSI_JUST_LEFT, ADSI_JUST_LEFT };
+ char *tmp[5];
+ int x;
+ for (x=0;x<4;x++)
+ tmp[x] = stuff[x];
+ tmp[4] = NULL;
+ return adsi_print(chan, tmp, justify, voice);
+}
+
+static int cpeid_exec(struct ast_channel *chan, void *idata)
+{
+ int res=0;
+ struct localuser *u;
+ unsigned char cpeid[4];
+ int gotgeometry = 0;
+ int gotcpeid = 0;
+ int width, height, buttons;
+ char data[4][80];
+ char *stuff[4];
+
+ LOCAL_USER_ADD(u);
+ stuff[0] = data[0];
+ stuff[1] = data[1];
+ stuff[2] = data[2];
+ stuff[3] = data[3];
+ memset(data, 0, sizeof(data));
+ strncpy(stuff[0], "** CPE Info **", sizeof(data[0]) - 1);
+ strncpy(stuff[1], "Identifying CPE...", sizeof(data[1]) - 1);
+ strncpy(stuff[2], "Please wait...", sizeof(data[2]) - 1);
+ res = adsi_load_session(chan, NULL, 0, 1);
+ if (res > 0) {
+ cpeid_setstatus(chan, stuff, 0);
+ res = adsi_get_cpeid(chan, cpeid, 0);
+ if (res > 0) {
+ gotcpeid = 1;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n", cpeid[0], cpeid[1], cpeid[2], cpeid[3], chan->name);
+ }
+ if (res > -1) {
+ strncpy(stuff[1], "Measuring CPE...", sizeof(data[1]) - 1);
+ strncpy(stuff[2], "Please wait...", sizeof(data[2]) - 1);
+ cpeid_setstatus(chan, stuff, 0);
+ res = adsi_get_cpeinfo(chan, &width, &height, &buttons, 0);
+ if (res > -1) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "CPE has %d lines, %d columns, and %d buttons on '%s'\n", height, width, buttons, chan->name);
+ gotgeometry = 1;
+ }
+ }
+ if (res > -1) {
+ if (gotcpeid)
+ snprintf(stuff[1], sizeof(data[1]), "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
+ else
+ strncpy(stuff[1], "CPEID Unknown", sizeof(data[1]) - 1);
+ if (gotgeometry)
+ snprintf(stuff[2], sizeof(data[2]), "Geom: %dx%d, %d buttons", width, height, buttons);
+ else
+ strncpy(stuff[2], "Geometry unknown", sizeof(data[2]) - 1);
+ strncpy(stuff[3], "Press # to exit", sizeof(data[3]) - 1);
+ cpeid_setstatus(chan, stuff, 1);
+ for(;;) {
+ res = ast_waitfordigit(chan, 1000);
+ if (res < 0)
+ break;
+ if (res == '#') {
+ res = 0;
+ break;
+ }
+ }
+ adsi_unload_session(chan);
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, cpeid_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_groupcount.c b/1.2-netsec/apps/app_groupcount.c
new file mode 100644
index 000000000..aa848c2d5
--- /dev/null
+++ b/1.2-netsec/apps/app_groupcount.c
@@ -0,0 +1,340 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Group Manipulation Applications
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int group_count_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int count;
+ struct localuser *u;
+ char group[80] = "";
+ char category[80] = "";
+ char ret[80] = "";
+ char *grp;
+ static int deprecation_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "The GetGroupCount application has been deprecated, please use the GROUP_COUNT function.\n");
+ deprecation_warning = 1;
+ }
+
+ ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category));
+
+ if (ast_strlen_zero(group)) {
+ grp = pbx_builtin_getvar_helper(chan, category);
+ strncpy(group, grp, sizeof(group) - 1);
+ }
+
+ count = ast_app_group_get_count(group, category);
+ snprintf(ret, sizeof(ret), "%d", count);
+ pbx_builtin_setvar_helper(chan, "GROUPCOUNT", ret);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int group_match_count_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int count;
+ struct localuser *u;
+ char group[80] = "";
+ char category[80] = "";
+ char ret[80] = "";
+ static int deprecation_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "The GetGroupMatchCount application has been deprecated, please use the GROUP_MATCH_COUNT function.\n");
+ deprecation_warning = 1;
+ }
+
+ ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category));
+
+ if (!ast_strlen_zero(group)) {
+ count = ast_app_group_match_get_count(group, category);
+ snprintf(ret, sizeof(ret), "%d", count);
+ pbx_builtin_setvar_helper(chan, "GROUPCOUNT", ret);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int group_set_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ static int deprecation_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "The SetGroup application has been deprecated, please use the GROUP() function.\n");
+ deprecation_warning = 1;
+ }
+
+ if (ast_app_group_set_channel(chan, data))
+ ast_log(LOG_WARNING, "SetGroup requires an argument (group name)\n");
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int group_check_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int max, count;
+ struct localuser *u;
+ char limit[80]="";
+ char category[80]="";
+ static int deprecation_warning = 0;
+ char *parse;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(max);
+ AST_APP_ARG(options);
+ );
+
+ LOCAL_USER_ADD(u);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "The CheckGroup application has been deprecated, please use a combination of the GotoIf application and the GROUP_COUNT() function.\n");
+ deprecation_warning = 1;
+ }
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (ast_strlen_zero(args.max)) {
+ ast_log(LOG_WARNING, "CheckGroup requires an argument(max[@category][|options])\n");
+ return res;
+ }
+
+ ast_app_group_split_group(args.max, limit, sizeof(limit), category, sizeof(category));
+
+ if ((sscanf(limit, "%d", &max) == 1) && (max > -1)) {
+ count = ast_app_group_get_count(pbx_builtin_getvar_helper(chan, category), category);
+ if (count > max) {
+ pbx_builtin_setvar_helper(chan, "CHECKGROUPSTATUS", "OVERMAX");
+ if (priority_jump || option_priority_jumping) {
+ if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
+ res = -1;
+ }
+ } else
+ pbx_builtin_setvar_helper(chan, "CHECKGROUPSTATUS", "OK");
+ } else
+ ast_log(LOG_WARNING, "CheckGroup requires a positive integer argument (max)\n");
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int group_show_channels(int fd, int argc, char *argv[])
+{
+#define FORMAT_STRING "%-25s %-20s %-20s\n"
+
+ struct ast_channel *c = NULL;
+ int numchans = 0;
+ struct ast_var_t *current;
+ struct varshead *headp;
+ regex_t regexbuf;
+ int havepattern = 0;
+
+ if (argc < 3 || argc > 4)
+ return RESULT_SHOWUSAGE;
+
+ if (argc == 4) {
+ if (regcomp(&regexbuf, argv[3], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ havepattern = 1;
+ }
+
+ ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
+ while ( (c = ast_channel_walk_locked(c)) != NULL) {
+ headp=&c->varshead;
+ AST_LIST_TRAVERSE(headp,current,entries) {
+ if (!strncmp(ast_var_name(current), GROUP_CATEGORY_PREFIX "_", strlen(GROUP_CATEGORY_PREFIX) + 1)) {
+ if (!havepattern || !regexec(&regexbuf, ast_var_value(current), 0, NULL, 0)) {
+ ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current),
+ (ast_var_name(current) + strlen(GROUP_CATEGORY_PREFIX) + 1));
+ numchans++;
+ }
+ } else if (!strcmp(ast_var_name(current), GROUP_CATEGORY_PREFIX)) {
+ if (!havepattern || !regexec(&regexbuf, ast_var_value(current), 0, NULL, 0)) {
+ ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current), "(default)");
+ numchans++;
+ }
+ }
+ }
+ numchans++;
+ ast_mutex_unlock(&c->lock);
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return RESULT_SUCCESS;
+#undef FORMAT_STRING
+}
+
+static char *tdesc = "Group Management Routines";
+
+static char *app_group_count = "GetGroupCount";
+static char *app_group_set = "SetGroup";
+static char *app_group_check = "CheckGroup";
+static char *app_group_match_count = "GetGroupMatchCount";
+
+static char *group_count_synopsis = "Get the channel count of a group";
+static char *group_set_synopsis = "Set the channel's group";
+static char *group_check_synopsis = "Check the channel count of a group against a limit";
+static char *group_match_count_synopsis = "Get the channel count of all groups that match a pattern";
+
+static char *group_count_descrip =
+"Usage: GetGroupCount([groupname][@category])\n"
+" Calculates the group count for the specified group, or uses\n"
+"the current channel's group if not specifed (and non-empty).\n"
+"Stores result in GROUPCOUNT. \n"
+"This application has been deprecated, please use the function\n"
+"GroupCount.\n";
+
+static char *group_set_descrip =
+"Usage: SetGroup(groupname[@category])\n"
+" Sets the channel group to the specified value. Equivalent to\n"
+"Set(GROUP=group). Always returns 0.\n";
+
+static char *group_check_descrip =
+"Usage: CheckGroup(max[@category][|options])\n"
+" Checks that the current number of total channels in the\n"
+"current channel's group does not exceed 'max'. If the number\n"
+"does not exceed 'max', we continue to the next step. \n"
+" The option string may contain zero of the following character:\n"
+" 'j' -- jump to n+101 priority if the number does in fact exceed max,\n"
+" and priority n+101 exists. Execuation then continues at that\n"
+" step, otherwise -1 is returned.\n"
+" This application sets the following channel variable upon successful completion:\n"
+" CHECKGROUPSTATUS The status of the check that the current channel's\n"
+" group does not exceed 'max'. It's value is one of\n"
+" OK | OVERMAX \n";
+
+static char *group_match_count_descrip =
+"Usage: GetGroupMatchCount(groupmatch[@category])\n"
+" Calculates the group count for all groups that match the specified\n"
+"pattern. Uses standard regular expression matching (see regex(7)).\n"
+"Stores result in GROUPCOUNT. Always returns 0.\n"
+"This application has been deprecated, please use the function\n"
+"GroupMatchCount.\n";
+
+static char show_channels_usage[] =
+"Usage: group show channels [pattern]\n"
+" Lists all currently active channels with channel group(s) specified.\n Optional regular expression pattern is matched to group names for each channel.\n";
+
+static struct ast_cli_entry cli_show_channels =
+ { { "group", "show", "channels", NULL }, group_show_channels, "Show active channels with group(s)", show_channels_usage};
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_cli_unregister(&cli_show_channels);
+ res |= ast_unregister_application(app_group_count);
+ res |= ast_unregister_application(app_group_set);
+ res |= ast_unregister_application(app_group_check);
+ res |= ast_unregister_application(app_group_match_count);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app_group_count, group_count_exec, group_count_synopsis, group_count_descrip);
+ res |= ast_register_application(app_group_set, group_set_exec, group_set_synopsis, group_set_descrip);
+ res |= ast_register_application(app_group_check, group_check_exec, group_check_synopsis, group_check_descrip);
+ res |= ast_register_application(app_group_match_count, group_match_count_exec, group_match_count_synopsis, group_match_count_descrip);
+ res |= ast_cli_register(&cli_show_channels);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_hasnewvoicemail.c b/1.2-netsec/apps/app_hasnewvoicemail.c
new file mode 100644
index 000000000..d5e7664f6
--- /dev/null
+++ b/1.2-netsec/apps/app_hasnewvoicemail.c
@@ -0,0 +1,264 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Changes Copyright (c) 2004 - 2005 Todd Freeman <freeman@andrews.edu>
+ *
+ * 95% based on HasNewVoicemail by:
+ *
+ * Copyright (c) 2003 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <asterisk-hasnewvoicemail-app@the-tilghman.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief HasVoicemail application
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Indicator for whether a voice mailbox has messages in a given folder.";
+static char *app_hasvoicemail = "HasVoicemail";
+static char *hasvoicemail_synopsis = "Conditionally branches to priority + 101 with the right options set";
+static char *hasvoicemail_descrip =
+"HasVoicemail(vmbox[/folder][@context][|varname[|options]])\n"
+" Optionally sets <varname> to the number of messages in that folder."
+" Assumes folder of INBOX if not specified.\n"
+" The option string may contain zero or the following character:\n"
+" 'j' -- jump to priority n+101, if there is voicemail in the folder indicated.\n"
+" This application sets the following channel variable upon completion:\n"
+" HASVMSTATUS The result of the voicemail check returned as a text string as follows\n"
+" <# of messages in the folder, 0 for NONE>\n";
+
+static char *app_hasnewvoicemail = "HasNewVoicemail";
+static char *hasnewvoicemail_synopsis = "Conditionally branches to priority + 101 with the right options set";
+static char *hasnewvoicemail_descrip =
+"HasNewVoicemail(vmbox[/folder][@context][|varname[|options]])\n"
+"Assumes folder 'INBOX' if folder is not specified. Optionally sets <varname> to the number of messages\n"
+"in that folder.\n"
+" The option string may contain zero of the following character:\n"
+" 'j' -- jump to priority n+101, if there is new voicemail in folder 'folder' or INBOX\n"
+" This application sets the following channel variable upon completion:\n"
+" HASVMSTATUS The result of the new voicemail check returned as a text string as follows\n"
+" <# of messages in the folder, 0 for NONE>\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int hasvoicemail_internal(char *context, char *box, char *folder)
+{
+ char vmpath[256];
+ DIR *vmdir;
+ struct dirent *vment;
+ int count=0;
+
+ snprintf(vmpath,sizeof(vmpath), "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR, context, box, folder);
+ if ((vmdir = opendir(vmpath))) {
+ /* No matter what the format of VM, there will always be a .txt file for each message. */
+ while ((vment = readdir(vmdir))) {
+ if (!strncmp(vment->d_name + 7, ".txt", 4)) {
+ count++;
+ }
+ }
+ closedir(vmdir);
+ }
+ return count;
+}
+
+static int hasvoicemail_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *input, *varname = NULL, *vmbox, *context = "default";
+ char *vmfolder;
+ int vmcount = 0;
+ static int dep_warning = 0;
+ int priority_jump = 0;
+ char tmp[12];
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vmbox);
+ AST_APP_ARG(varname);
+ AST_APP_ARG(options);
+ );
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "The applications HasVoicemail and HasNewVoicemail have been deprecated. Please use the VMCOUNT() function instead.\n");
+ dep_warning = 1;
+ }
+
+ if (!data) {
+ ast_log(LOG_WARNING, "HasVoicemail requires an argument (vm-box[/folder][@context][|varname[|options]])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ input = ast_strdupa((char *)data);
+ if (! input) {
+ ast_log(LOG_ERROR, "Out of memory error\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, input);
+
+ if ((vmbox = strsep(&args.vmbox, "@")))
+ if (!ast_strlen_zero(args.vmbox))
+ context = args.vmbox;
+ if (!vmbox)
+ vmbox = args.vmbox;
+
+ vmfolder = strchr(vmbox, '/');
+ if (vmfolder) {
+ *vmfolder = '\0';
+ vmfolder++;
+ } else {
+ vmfolder = "INBOX";
+ }
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ vmcount = hasvoicemail_internal(context, vmbox, vmfolder);
+ /* Set the count in the channel variable */
+ if (varname) {
+ snprintf(tmp, sizeof(tmp), "%d", vmcount);
+ pbx_builtin_setvar_helper(chan, varname, tmp);
+ }
+
+ if (vmcount > 0) {
+ /* Branch to the next extension */
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
+ ast_log(LOG_WARNING, "VM box %s@%s has new voicemail, but extension %s, priority %d doesn't exist\n", vmbox, context, chan->exten, chan->priority + 101);
+ }
+ }
+
+ snprintf(tmp, sizeof(tmp), "%d", vmcount);
+ pbx_builtin_setvar_helper(chan, "HASVMSTATUS", tmp);
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static char *acf_vmcount_exec(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+ struct localuser *u;
+ char *args, *context, *box, *folder;
+
+ LOCAL_USER_ACF_ADD(u);
+
+ buf[0] = '\0';
+
+ args = ast_strdupa(data);
+ if (!args) {
+ ast_log(LOG_ERROR, "Out of memory");
+ LOCAL_USER_REMOVE(u);
+ return buf;
+ }
+
+ box = strsep(&args, "|");
+ if (strchr(box, '@')) {
+ context = box;
+ box = strsep(&context, "@");
+ } else {
+ context = "default";
+ }
+
+ if (args) {
+ folder = args;
+ } else {
+ folder = "INBOX";
+ }
+
+ snprintf(buf, len, "%d", hasvoicemail_internal(context, box, folder));
+
+ LOCAL_USER_REMOVE(u);
+
+ return buf;
+}
+
+struct ast_custom_function acf_vmcount = {
+ .name = "VMCOUNT",
+ .synopsis = "Counts the voicemail in a specified mailbox",
+ .syntax = "VMCOUNT(vmbox[@context][|folder])",
+ .desc =
+ " context - defaults to \"default\"\n"
+ " folder - defaults to \"INBOX\"\n",
+ .read = acf_vmcount_exec,
+};
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&acf_vmcount);
+ res |= ast_unregister_application(app_hasvoicemail);
+ res |= ast_unregister_application(app_hasnewvoicemail);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_custom_function_register(&acf_vmcount);
+ res |= ast_register_application(app_hasvoicemail, hasvoicemail_exec, hasvoicemail_synopsis, hasvoicemail_descrip);
+ res |= ast_register_application(app_hasnewvoicemail, hasvoicemail_exec, hasnewvoicemail_synopsis, hasnewvoicemail_descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_ices.c b/1.2-netsec/apps/app_ices.c
new file mode 100644
index 000000000..4c1505584
--- /dev/null
+++ b/1.2-netsec/apps/app_ices.c
@@ -0,0 +1,225 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#define ICES "/usr/bin/ices"
+#define LOCAL_ICES "/usr/local/bin/ices"
+
+static char *tdesc = "Encode and Stream via icecast and ices";
+
+static char *app = "ICES";
+
+static char *synopsis = "Encode and stream using 'ices'";
+
+static char *descrip =
+" ICES(config.xml) Streams to an icecast server using ices\n"
+"(available separately). A configuration file must be supplied\n"
+"for ices (see examples/asterisk-ices.conf). \n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int icesencode(char *filename, int fd)
+{
+ int res;
+ int x;
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res)
+ return res;
+ dup2(fd, STDIN_FILENO);
+ for (x=STDERR_FILENO + 1;x<256;x++) {
+ if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
+ close(x);
+ }
+ /* Most commonly installed in /usr/local/bin */
+ execl(ICES, "ices", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(LOCAL_ICES, "ices", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("ices", "ices", filename, (char *)NULL);
+ ast_log(LOG_WARNING, "Execute of ices failed\n");
+ return -1;
+}
+
+static int ices_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int flags;
+ int oreadformat;
+ struct timeval last;
+ struct ast_frame *f;
+ char filename[256]="";
+ char *c;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ last = ast_tv(0, 0);
+
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Unable to create pipe\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ flags = fcntl(fds[1], F_GETFL);
+ fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
+
+ ast_stopstream(chan);
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ if (res) {
+ close(fds[0]);
+ close(fds[1]);
+ ast_log(LOG_WARNING, "Answer failed!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ oreadformat = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ close(fds[0]);
+ close(fds[1]);
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if (((char *)data)[0] == '/')
+ strncpy(filename, (char *)data, sizeof(filename) - 1);
+ else
+ snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, (char *)data);
+ /* Placeholder for options */
+ c = strchr(filename, '|');
+ if (c)
+ *c = '\0';
+ res = icesencode(filename, fds[0]);
+ close(fds[0]);
+ if (res >= 0) {
+ pid = res;
+ for (;;) {
+ /* Wait for audio, and stream */
+ ms = ast_waitfor(chan, -1);
+ if (ms < 0) {
+ ast_log(LOG_DEBUG, "Hangup detected\n");
+ res = -1;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ res = write(fds[1], f->data, f->datalen);
+ if (res < 0) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
+ res = -1;
+ break;
+ }
+ }
+ }
+ ast_frfree(f);
+ }
+ }
+ close(fds[1]);
+
+ if (pid > -1)
+ kill(pid, SIGKILL);
+ if (!res && oreadformat)
+ ast_set_read_format(chan, oreadformat);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, ices_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_image.c b/1.2-netsec/apps/app_image.c
new file mode 100644
index 000000000..e23b274a9
--- /dev/null
+++ b/1.2-netsec/apps/app_image.c
@@ -0,0 +1,147 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to transmit an image
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Image Transmission Application";
+
+static char *app = "SendImage";
+
+static char *synopsis = "Send an image file";
+
+static char *descrip =
+" SendImage(filename): Sends an image on a channel. \n"
+"If the channel supports image transport but the image send\n"
+"fails, the channel will be hung up. Otherwise, the dialplan\n"
+"continues execution.\n"
+"The option string may contain the following character:\n"
+" 'j' -- jump to priority n+101 if the channel doesn't support image transport\n"
+"This application sets the following channel variable upon completion:\n"
+" SENDIMAGESTATUS The status is the result of the attempt as a text string, one of\n"
+" OK | NOSUPPORT \n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int sendimage_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *parse;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(options);
+ );
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.filename)) {
+ ast_log(LOG_WARNING, "SendImage requires an argument (filename[|options])\n");
+ return -1;
+ }
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (!ast_supports_images(chan)) {
+ /* Does not support transport */
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "NOSUPPORT");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ res = ast_send_image(chan, args.filename);
+
+ if (!res)
+ pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "OK");
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, sendimage_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_intercom.c b/1.2-netsec/apps/app_intercom.c
new file mode 100644
index 000000000..1ae78c6ab
--- /dev/null
+++ b/1.2-netsec/apps/app_intercom.c
@@ -0,0 +1,234 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Use /dev/dsp as an intercom.
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#if defined(__linux__)
+#include <linux/soundcard.h>
+#elif defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#else
+#include <soundcard.h>
+#endif
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/frame.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#ifdef __OpenBSD__
+#define DEV_DSP "/dev/audio"
+#else
+#define DEV_DSP "/dev/dsp"
+#endif
+
+/* Number of 32 byte buffers -- each buffer is 2 ms */
+#define BUFFER_SIZE 32
+
+static char *tdesc = "Intercom using /dev/dsp for output";
+
+static char *app = "Intercom";
+
+static char *synopsis = "(Obsolete) Send to Intercom";
+static char *descrip =
+" Intercom(): Sends the user to the intercom (i.e. /dev/dsp). This program\n"
+"is generally considered obselete by the chan_oss module. User can terminate\n"with a DTMF tone, or by hangup.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+AST_MUTEX_DEFINE_STATIC(sound_lock);
+static int sound = -1;
+
+static int write_audio(short *data, int len)
+{
+ int res;
+ struct audio_buf_info info;
+ ast_mutex_lock(&sound_lock);
+ if (sound < 0) {
+ ast_log(LOG_WARNING, "Sound device closed?\n");
+ ast_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ if (ioctl(sound, SNDCTL_DSP_GETOSPACE, &info)) {
+ ast_log(LOG_WARNING, "Unable to read output space\n");
+ ast_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ res = write(sound, data, len);
+ ast_mutex_unlock(&sound_lock);
+ return res;
+}
+
+static int create_audio(void)
+{
+ int fmt, desired, res, fd;
+ fd = open(DEV_DSP, O_WRONLY);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s: %s\n", DEV_DSP, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ fmt = AFMT_S16_LE;
+ res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
+ close(fd);
+ return -1;
+ }
+ fmt = 0;
+ res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ close(fd);
+ return -1;
+ }
+ /* 8000 Hz desired */
+ desired = 8000;
+ fmt = desired;
+ res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ close(fd);
+ return -1;
+ }
+ if (fmt != desired) {
+ ast_log(LOG_WARNING, "Requested %d Hz, got %d Hz -- sound may be choppy\n", desired, fmt);
+ }
+#if 1
+ /* 2 bytes * 15 units of 2^5 = 32 bytes per buffer */
+ fmt = ((BUFFER_SIZE) << 16) | (0x0005);
+ res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n");
+ }
+#endif
+ sound = fd;
+ return 0;
+}
+
+static int intercom_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ struct ast_frame *f;
+ int oreadformat;
+ LOCAL_USER_ADD(u);
+ /* Remember original read format */
+ oreadformat = chan->readformat;
+ /* Set mode to signed linear */
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set format to signed linear on channel %s\n", chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ /* Read packets from the channel */
+ while(!res) {
+ res = ast_waitfor(chan, -1);
+ if (res > 0) {
+ res = 0;
+ f = ast_read(chan);
+ if (f) {
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_frfree(f);
+ break;
+ } else {
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass == AST_FORMAT_SLINEAR) {
+ res = write_audio(f->data, f->datalen);
+ if (res > 0)
+ res = 0;
+ } else
+ ast_log(LOG_DEBUG, "Unable to handle non-signed linear frame (%d)\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else
+ res = -1;
+ }
+ }
+
+ if (!res)
+ ast_set_read_format(chan, oreadformat);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ if (sound > -1)
+ close(sound);
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ if (create_audio())
+ return -1;
+ return ast_register_application(app, intercom_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_ivrdemo.c b/1.2-netsec/apps/app_ivrdemo.c
new file mode 100644
index 000000000..198cfda2a
--- /dev/null
+++ b/1.2-netsec/apps/app_ivrdemo.c
@@ -0,0 +1,144 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief IVR Demo application
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "IVR Demo Application";
+static char *app = "IVRDemo";
+static char *synopsis =
+" This is a skeleton application that shows you the basic structure to create your\n"
+"own asterisk applications and demonstrates the IVR demo.\n";
+
+static int ivr_demo_func(struct ast_channel *chan, void *data)
+{
+ ast_verbose("IVR Demo, data is %s!\n", (char *)data);
+ return 0;
+}
+
+AST_IVR_DECLARE_MENU(ivr_submenu, "IVR Demo Sub Menu", 0,
+{
+ { "s", AST_ACTION_BACKGROUND, "demo-abouttotry" },
+ { "s", AST_ACTION_WAITOPTION },
+ { "1", AST_ACTION_PLAYBACK, "digits/1" },
+ { "1", AST_ACTION_PLAYBACK, "digits/1" },
+ { "1", AST_ACTION_RESTART },
+ { "2", AST_ACTION_PLAYLIST, "digits/2;digits/3" },
+ { "3", AST_ACTION_CALLBACK, ivr_demo_func },
+ { "4", AST_ACTION_TRANSFER, "demo|s|1" },
+ { "*", AST_ACTION_REPEAT },
+ { "#", AST_ACTION_UPONE },
+ { NULL }
+});
+
+AST_IVR_DECLARE_MENU(ivr_demo, "IVR Demo Main Menu", 0,
+{
+ { "s", AST_ACTION_BACKGROUND, "demo-congrats" },
+ { "g", AST_ACTION_BACKGROUND, "demo-instruct" },
+ { "g", AST_ACTION_WAITOPTION },
+ { "1", AST_ACTION_PLAYBACK, "digits/1" },
+ { "1", AST_ACTION_RESTART },
+ { "2", AST_ACTION_MENU, &ivr_submenu },
+ { "2", AST_ACTION_RESTART },
+ { "i", AST_ACTION_PLAYBACK, "invalid" },
+ { "i", AST_ACTION_REPEAT, (void *)(unsigned long)2 },
+ { "#", AST_ACTION_EXIT },
+ { NULL },
+});
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int skel_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Do our thing here */
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ if (!res)
+ res = ast_ivr_menu_run(chan, &ivr_demo, data);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, skel_exec, tdesc, synopsis);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_lookupblacklist.c b/1.2-netsec/apps/app_lookupblacklist.c
new file mode 100644
index 000000000..1f7e30fd8
--- /dev/null
+++ b/1.2-netsec/apps/app_lookupblacklist.c
@@ -0,0 +1,141 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to lookup the callerid number, and see if it is blacklisted
+ *
+ * \ingroup applications
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/astdb.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Look up Caller*ID name/number from blacklist database";
+
+static char *app = "LookupBlacklist";
+
+static char *synopsis = "Look up Caller*ID name/number from blacklist database";
+
+static char *descrip =
+ " LookupBlacklist(options): Looks up the Caller*ID number on the active\n"
+ "channel in the Asterisk database (family 'blacklist'). \n"
+ "The option string may contain the following character:\n"
+ " 'j' -- jump to n+101 priority if the number/name is found in the blacklist\n"
+ "This application sets the following channel variable upon completion:\n"
+ " LOOKUPBLSTATUS The status of the Blacklist lookup as a text string, one of\n"
+ " FOUND | NOTFOUND\n"
+ "Example: exten => 1234,1,LookupBlacklist()\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int
+lookupblacklist_exec (struct ast_channel *chan, void *data)
+{
+ char blacklist[1];
+ struct localuser *u;
+ int bl = 0;
+ int priority_jump = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!ast_strlen_zero(data)) {
+ if (strchr(data, 'j'))
+ priority_jump = 1;
+ }
+
+ if (chan->cid.cid_num) {
+ if (!ast_db_get("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist))) {
+ if (option_verbose > 2)
+ ast_log(LOG_NOTICE, "Blacklisted number %s found\n",chan->cid.cid_num);
+ bl = 1;
+ }
+ }
+ if (chan->cid.cid_name) {
+ if (!ast_db_get("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist))) {
+ if (option_verbose > 2)
+ ast_log (LOG_NOTICE,"Blacklisted name \"%s\" found\n",chan->cid.cid_name);
+ bl = 1;
+ }
+ }
+
+ if (bl) {
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "LOOKUPBLSTATUS", "FOUND");
+ } else
+ pbx_builtin_setvar_helper(chan, "LOOKUPBLSTATUS", "NOTFOUND");
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module (void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module (void)
+{
+ return ast_register_application (app, lookupblacklist_exec, synopsis,descrip);
+}
+
+char *description (void)
+{
+ return tdesc;
+}
+
+int usecount (void)
+{
+ int res;
+ STANDARD_USECOUNT (res);
+ return res;
+}
+
+char *key ()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_lookupcidname.c b/1.2-netsec/apps/app_lookupcidname.c
new file mode 100644
index 000000000..52587b03a
--- /dev/null
+++ b/1.2-netsec/apps/app_lookupcidname.c
@@ -0,0 +1,120 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to set callerid name from database, based on directory number
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/astdb.h"
+
+static char *tdesc = "Look up CallerID Name from local database";
+
+static char *app = "LookupCIDName";
+
+static char *synopsis = "Look up CallerID Name from local database";
+
+static char *descrip =
+ " LookupCIDName: Looks up the Caller*ID number on the active\n"
+ "channel in the Asterisk database (family 'cidname') and sets the\n"
+ "Caller*ID name. Does nothing if no Caller*ID was received on the\n"
+ "channel. This is useful if you do not subscribe to Caller*ID\n"
+ "name delivery, or if you want to change the names on some incoming\n"
+ "calls.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int
+lookupcidname_exec (struct ast_channel *chan, void *data)
+{
+ char dbname[64];
+ struct localuser *u;
+
+ LOCAL_USER_ADD (u);
+ if (chan->cid.cid_num) {
+ if (!ast_db_get ("cidname", chan->cid.cid_num, dbname, sizeof (dbname))) {
+ ast_set_callerid (chan, NULL, dbname, NULL);
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID name to %s\n",
+ dbname);
+ }
+ }
+ LOCAL_USER_REMOVE (u);
+ return 0;
+}
+
+int
+unload_module (void)
+{
+ int res;
+
+ res = ast_unregister_application (app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int
+load_module (void)
+{
+ return ast_register_application (app, lookupcidname_exec, synopsis,
+ descrip);
+}
+
+char *
+description (void)
+{
+ return tdesc;
+}
+
+int
+usecount (void)
+{
+ int res;
+ STANDARD_USECOUNT (res);
+ return res;
+}
+
+char *
+key ()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_macro.c b/1.2-netsec/apps/app_macro.c
new file mode 100644
index 000000000..7e7c9141a
--- /dev/null
+++ b/1.2-netsec/apps/app_macro.c
@@ -0,0 +1,396 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Dial plan macro Implementation
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+#define MAX_ARGS 80
+
+/* special result value used to force macro exit */
+#define MACRO_EXIT_RESULT 1024
+
+static char *tdesc = "Extension Macros";
+
+static char *descrip =
+" Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
+"'macro-<macroname>', jumping to the 's' extension of that context and\n"
+"executing each step, then returning when the steps end. \n"
+"The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
+"${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
+"${ARG1}, ${ARG2}, etc in the macro context.\n"
+"If you Goto out of the Macro context, the Macro will terminate and control\n"
+"will be returned at the location of the Goto.\n"
+"If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
+"at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n";
+
+static char *if_descrip =
+" MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
+"Executes macro defined in <macroname_a> if <expr> is true\n"
+"(otherwise <macroname_b> if provided)\n"
+"Arguments and return values as in application macro()\n";
+
+static char *exit_descrip =
+" MacroExit():\n"
+"Causes the currently running macro to exit as if it had\n"
+"ended normally by running out of priorities to execute.\n"
+"If used outside a macro, will likely cause unexpected\n"
+"behavior.\n";
+
+static char *app = "Macro";
+static char *if_app = "MacroIf";
+static char *exit_app = "MacroExit";
+
+static char *synopsis = "Macro Implementation";
+static char *if_synopsis = "Conditional Macro Implementation";
+static char *exit_synopsis = "Exit From Macro";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int macro_exec(struct ast_channel *chan, void *data)
+{
+ char *tmp;
+ char *cur, *rest;
+ char *macro;
+ char fullmacro[80];
+ char varname[80];
+ char *oldargs[MAX_ARGS + 1] = { NULL, };
+ int argc, x;
+ int res=0;
+ char oldexten[256]="";
+ int oldpriority;
+ char pc[80], depthc[12];
+ char oldcontext[AST_MAX_CONTEXT] = "";
+ char *offsets;
+ int offset, depth;
+ int setmacrocontext=0;
+ int autoloopflag, dead = 0;
+
+ char *save_macro_exten;
+ char *save_macro_context;
+ char *save_macro_priority;
+ char *save_macro_offset;
+ struct localuser *u;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Count how many levels deep the rabbit hole goes */
+ tmp = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
+ if (tmp) {
+ sscanf(tmp, "%d", &depth);
+ } else {
+ depth = 0;
+ }
+
+ if (depth >= 7) {
+ ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ snprintf(depthc, sizeof(depthc), "%d", depth + 1);
+ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
+
+ tmp = ast_strdupa(data);
+ rest = tmp;
+ macro = strsep(&rest, "|");
+ if (ast_strlen_zero(macro)) {
+ ast_log(LOG_WARNING, "Invalid macro name specified\n");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
+ if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
+ if (!ast_context_find(fullmacro))
+ ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
+ else
+ ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ /* Save old info */
+ oldpriority = chan->priority;
+ ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
+ ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
+ if (ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
+ ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
+ chan->macropriority = chan->priority;
+ setmacrocontext=1;
+ }
+ argc = 1;
+ /* Save old macro variables */
+ save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
+ if (save_macro_exten)
+ save_macro_exten = strdup(save_macro_exten);
+ pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
+
+ save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
+ if (save_macro_context)
+ save_macro_context = strdup(save_macro_context);
+ pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
+
+ save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
+ if (save_macro_priority)
+ save_macro_priority = strdup(save_macro_priority);
+ snprintf(pc, sizeof(pc), "%d", oldpriority);
+ pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
+
+ save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
+ if (save_macro_offset)
+ save_macro_offset = strdup(save_macro_offset);
+ pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
+
+ /* Setup environment for new run */
+ chan->exten[0] = 's';
+ chan->exten[1] = '\0';
+ ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
+ chan->priority = 1;
+
+ while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
+ /* Save copy of old arguments if we're overwriting some, otherwise
+ let them pass through to the other macro */
+ snprintf(varname, sizeof(varname), "ARG%d", argc);
+ oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
+ if (oldargs[argc])
+ oldargs[argc] = strdup(oldargs[argc]);
+ pbx_builtin_setvar_helper(chan, varname, cur);
+ argc++;
+ }
+ autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
+ ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
+ while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
+ /* Reset the macro depth, if it was changed in the last iteration */
+ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
+ if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
+ (res == '*') || (res == '#')) {
+ /* Just return result as to the previous application as if it had been dialed */
+ ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
+ break;
+ }
+ switch(res) {
+ case MACRO_EXIT_RESULT:
+ res = 0;
+ goto out;
+ case AST_PBX_KEEPALIVE:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
+ else if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
+ goto out;
+ break;
+ default:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
+ else if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
+ dead = 1;
+ goto out;
+ }
+ }
+ if (strcasecmp(chan->context, fullmacro)) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
+ break;
+ }
+ /* don't stop executing extensions when we're in "h" */
+ if (chan->_softhangup && strcasecmp(oldexten,"h")) {
+ ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
+ chan->exten, chan->priority);
+ goto out;
+ }
+ chan->priority++;
+ }
+ out:
+ /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
+ snprintf(depthc, sizeof(depthc), "%d", depth);
+ if (!dead) {
+ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
+
+ ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
+ }
+
+ for (x = 1; x < argc; x++) {
+ /* Restore old arguments and delete ours */
+ snprintf(varname, sizeof(varname), "ARG%d", x);
+ if (oldargs[x]) {
+ if (!dead)
+ pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
+ free(oldargs[x]);
+ } else if (!dead) {
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+ }
+
+ /* Restore macro variables */
+ if (!dead) {
+ pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
+ pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
+ pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
+ }
+ if (save_macro_exten)
+ free(save_macro_exten);
+ if (save_macro_context)
+ free(save_macro_context);
+ if (save_macro_priority)
+ free(save_macro_priority);
+
+ if (!dead && setmacrocontext) {
+ chan->macrocontext[0] = '\0';
+ chan->macroexten[0] = '\0';
+ chan->macropriority = 0;
+ }
+
+ if (!dead && !strcasecmp(chan->context, fullmacro)) {
+ /* If we're leaving the macro normally, restore original information */
+ chan->priority = oldpriority;
+ ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
+ if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
+ /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
+ ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
+ if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
+ /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
+ normally if there is any problem */
+ if (sscanf(offsets, "%d", &offset) == 1) {
+ if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
+ chan->priority += offset;
+ }
+ }
+ }
+ }
+ }
+
+ if (!dead)
+ pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
+ if (save_macro_offset)
+ free(save_macro_offset);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int macroif_exec(struct ast_channel *chan, void *data)
+{
+ char *expr = NULL, *label_a = NULL, *label_b = NULL;
+ int res = 0;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ expr = ast_strdupa(data);
+ if (!expr) {
+ ast_log(LOG_ERROR, "Out of Memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if ((label_a = strchr(expr, '?'))) {
+ *label_a = '\0';
+ label_a++;
+ if ((label_b = strchr(label_a, ':'))) {
+ *label_b = '\0';
+ label_b++;
+ }
+ if (ast_true(expr))
+ macro_exec(chan, label_a);
+ else if (label_b)
+ macro_exec(chan, label_b);
+ } else
+ ast_log(LOG_WARNING, "Invalid Syntax.\n");
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int macro_exit_exec(struct ast_channel *chan, void *data)
+{
+ return MACRO_EXIT_RESULT;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(if_app);
+ res |= ast_unregister_application(exit_app);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
+ res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
+ res |= ast_register_application(app, macro_exec, synopsis, descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_math.c b/1.2-netsec/apps/app_math.c
new file mode 100644
index 000000000..f1be8d4d1
--- /dev/null
+++ b/1.2-netsec/apps/app_math.c
@@ -0,0 +1,297 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Andy Powell
+ *
+ * Updated by Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief A simple math application
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/file.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/localtime.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+static char *tdesc = "Basic Math Functions";
+
+static char *app_math = "Math";
+
+static char *math_synopsis = "Performs Mathematical Functions";
+
+static char *math_descrip =
+"Math(returnvar,<number1><op><number 2>\n\n"
+"Perform floating point calculation on number 1 to number 2 and \n"
+"store the result in returnvar. Valid ops are: \n"
+" +,-,/,*,%,<,>,>=,<=,==\n"
+"and behave as their C equivalents.\n";
+
+#define ADDFUNCTION 0
+#define DIVIDEFUNCTION 1
+#define MULTIPLYFUNCTION 2
+#define SUBTRACTFUNCTION 3
+#define MODULUSFUNCTION 4
+
+#define GTFUNCTION 5
+#define LTFUNCTION 6
+#define GTEFUNCTION 7
+#define LTEFUNCTION 8
+#define EQFUNCTION 9
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int math_exec(struct ast_channel *chan, void *data)
+{
+ float fnum1;
+ float fnum2;
+ float ftmp = 0;
+ char *op;
+ int iaction=-1;
+ static int deprecation_warning = 0;
+
+ /* dunno, big calulations :D */
+ char user_result[30];
+
+ char *s;
+ char *mvar, *mvalue1, *mvalue2=NULL;
+
+ struct localuser *u;
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "Math() is deprecated, please use Set(var=${MATH(...)} instead.\n");
+ deprecation_warning = 1;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "No parameters passed. !\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ s = ast_strdupa(data);
+ if (!s) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ mvar = strsep(&s, "|");
+ mvalue1 = strsep(&s, "|");
+
+ if ((op = strchr(mvalue1, '+'))) {
+ iaction = ADDFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '-'))) {
+ iaction = SUBTRACTFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '*'))) {
+ iaction = MULTIPLYFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '/'))) {
+ iaction = DIVIDEFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '>'))) {
+ iaction = GTFUNCTION;
+ *op = '\0';
+ if (*(op+1) == '=') {
+ op++;
+ *op = '\0';
+ iaction = GTEFUNCTION;
+ }
+ } else if ((op = strchr(mvalue1, '<'))) {
+ iaction = LTFUNCTION;
+ *op = '\0';
+ if (*(op+1) == '=') {
+ op++;
+ *op = '\0';
+ iaction = LTEFUNCTION;
+ }
+ } else if ((op = strchr(mvalue1, '='))) {
+ iaction = GTFUNCTION;
+ *op = '\0';
+ if (*(op+1) == '=') {
+ op++;
+ *op = '\0';
+ iaction = EQFUNCTION;
+ } else
+ op = NULL;
+ }
+
+ if (op)
+ mvalue2 = op + 1;
+
+ if (!mvar || !mvalue1 || !mvalue2) {
+ ast_log(LOG_WARNING, "Supply all the parameters - just this once, please\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (!strcmp(mvar,"")) {
+ ast_log(LOG_WARNING, "No return variable set.\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (sscanf(mvalue1, "%f", &fnum1) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (sscanf(mvalue2, "%f", &fnum2) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ switch (iaction) {
+ case ADDFUNCTION :
+ ftmp = fnum1 + fnum2;
+ break;
+ case DIVIDEFUNCTION :
+ if (fnum2 <=0)
+ ftmp = 0; /* can't do a divide by 0 */
+ else
+ ftmp = (fnum1 / fnum2);
+ break;
+ case MULTIPLYFUNCTION :
+ ftmp = (fnum1 * fnum2);
+ break;
+ case SUBTRACTFUNCTION :
+ ftmp = (fnum1 - fnum2);
+ break;
+ case MODULUSFUNCTION : {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 % inum2);
+
+ break;
+ }
+ case GTFUNCTION :
+ if (fnum1 > fnum2)
+ strcpy(user_result, "TRUE");
+ else
+ strcpy(user_result, "FALSE");
+ break;
+ case LTFUNCTION :
+ if (fnum1 < fnum2)
+ strcpy(user_result, "TRUE");
+ else
+ strcpy(user_result, "FALSE");
+ break;
+ case GTEFUNCTION :
+ if (fnum1 >= fnum2)
+ strcpy(user_result, "TRUE");
+ else
+ strcpy(user_result, "FALSE");
+ break;
+ case LTEFUNCTION :
+ if (fnum1 <= fnum2)
+ strcpy(user_result, "TRUE");
+ else
+ strcpy(user_result, "FALSE");
+ break;
+ case EQFUNCTION :
+ if (fnum1 == fnum2)
+ strcpy(user_result, "TRUE");
+ else
+ strcpy(user_result, "FALSE");
+ break;
+ default :
+ ast_log(LOG_WARNING, "Something happened that neither of us should be proud of %d\n", iaction);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (iaction < GTFUNCTION || iaction > EQFUNCTION)
+ snprintf(user_result,sizeof(user_result),"%f",ftmp);
+
+ pbx_builtin_setvar_helper(chan, mvar, user_result);
+
+ LOCAL_USER_REMOVE(u);
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_math);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app_math, math_exec, math_synopsis, math_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
+/* Fading everything to black and blue... */
diff --git a/1.2-netsec/apps/app_md5.c b/1.2-netsec/apps/app_md5.c
new file mode 100644
index 000000000..4b018b33e
--- /dev/null
+++ b/1.2-netsec/apps/app_md5.c
@@ -0,0 +1,205 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Olle E. Johansson, Edvina.net
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+
+/*! \file
+ *
+ * \brief MD5 checksum application
+ *
+ * \todo Remove this deprecated application in 1.3dev
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *tdesc_md5 = "MD5 checksum applications";
+static char *app_md5 = "MD5";
+static char *desc_md5 = "Calculate MD5 checksum";
+static char *synopsis_md5 =
+" MD5(<var>=<string>): Calculates a MD5 checksum on <string>.\n"
+"Returns hash value in a channel variable. \n";
+
+static char *app_md5check = "MD5Check";
+static char *desc_md5check = "Check MD5 checksum";
+static char *synopsis_md5check =
+" MD5Check(<md5hash>|<string>[|options]): Calculates a MD5 checksum on <string>\n"
+"and compares it with the hash. Returns 0 if <md5hash> is correct for <string>.\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'j' -- jump to priority n+101 if the hash and string do not match \n"
+"This application sets the following channel variable upon completion:\n"
+" CHECKMD5STATUS The status of the MD5 check, one of the following\n"
+" MATCH | NOMATCH\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+/*--- md5_exec: Calculate MD5 checksum (hash) on given string and
+ return it in channel variable ---*/
+static int md5_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *varname= NULL; /* Variable to set */
+ char *string = NULL; /* String to calculate on */
+ char retvar[50]; /* Return value */
+ static int dep_warning = 0;
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "This application has been deprecated, please use the MD5 function instead.\n");
+ dep_warning = 1;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: md5(<varname>=<string>) - missing argument!\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ memset(retvar,0, sizeof(retvar));
+ string = ast_strdupa(data);
+ varname = strsep(&string,"=");
+ if (ast_strlen_zero(varname)) {
+ ast_log(LOG_WARNING, "Syntax: md5(<varname>=<string>) - missing argument!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ ast_md5_hash(retvar, string);
+ pbx_builtin_setvar_helper(chan, varname, retvar);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+/*--- md5check_exec: Calculate MD5 checksum and compare it with
+ existing checksum. ---*/
+static int md5check_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *string = NULL; /* String to calculate on */
+ char newhash[50]; /* Return value */
+ static int dep_warning = 0;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(md5hash);
+ AST_APP_ARG(string);
+ AST_APP_ARG(options);
+ );
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "This application has been deprecated, please use the CHECK_MD5 function instead.\n");
+ dep_warning = 1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(string = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, string);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (ast_strlen_zero(args.md5hash) || ast_strlen_zero(args.string)) {
+ ast_log(LOG_WARNING, "Syntax: MD5Check(<md5hash>|<string>[|options]) - missing argument!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ memset(newhash,0, sizeof(newhash));
+
+ ast_md5_hash(newhash, args.string);
+ if (!strcmp(newhash, args.md5hash)) { /* Verification ok */
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "MD5 verified ok: %s -- %s\n", args.md5hash, args.string);
+ pbx_builtin_setvar_helper(chan, "CHECKMD5STATUS", "MATCH");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "ERROR: MD5 not verified: %s -- %s\n", args.md5hash, args.string);
+ pbx_builtin_setvar_helper(chan, "CHECKMD5STATUS", "NOMATCH");
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Can't jump to exten+101 (e%s,p%d), sorry\n", chan->exten,chan->priority+101);
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_md5);
+ res |= ast_unregister_application(app_md5check);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app_md5check, md5check_exec, desc_md5check, synopsis_md5check);
+ res |= ast_register_application(app_md5, md5_exec, desc_md5, synopsis_md5);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc_md5;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_meetme.c b/1.2-netsec/apps/app_meetme.c
new file mode 100644
index 000000000..9bfbc8892
--- /dev/null
+++ b/1.2-netsec/apps/app_meetme.c
@@ -0,0 +1,2251 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Meet me conference bridge
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#ifdef __linux__
+#include <linux/zaptel.h>
+#else
+#include <zaptel.h>
+#endif /* __linux__ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+
+static const char *tdesc = "MeetMe conference bridge";
+
+static const char *app = "MeetMe";
+static const char *app2 = "MeetMeCount";
+static const char *app3 = "MeetMeAdmin";
+
+static const char *synopsis = "MeetMe conference bridge";
+static const char *synopsis2 = "MeetMe participant count";
+static const char *synopsis3 = "MeetMe conference Administration";
+
+static const char *descrip =
+" MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
+"If the conference number is omitted, the user will be prompted to enter\n"
+"one. \n"
+"User can exit the conference by hangup, or if the 'p' option is specified, by pressing '#'.\n"
+"Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
+
+"The option string may contain zero or more of the following characters:\n"
+" 'a' -- set admin mode\n"
+" 'A' -- set marked mode\n"
+" 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
+" Default: conf-background.agi\n"
+" (Note: This does not work with non-Zap channels in the same conference)\n"
+" 'c' -- announce user(s) count on joining a conference\n"
+" 'd' -- dynamically add conference\n"
+" 'D' -- dynamically add conference, prompting for a PIN\n"
+" 'e' -- select an empty conference\n"
+" 'E' -- select an empty pinless conference\n"
+" 'i' -- announce user join/leave\n"
+" 'm' -- set monitor only mode (Listen only, no talking)\n"
+" 'M' -- enable music on hold when the conference has a single caller\n"
+" 'p' -- allow user to exit the conference by pressing '#'\n"
+" 'P' -- always prompt for the pin even if it is specified\n"
+" 'q' -- quiet mode (don't play enter/leave sounds)\n"
+" 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
+" using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
+" meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
+" 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
+" 't' -- set talk only mode. (Talk only, no listening)\n"
+" 'T' -- set talker detection (sent to manager interface and meetme list)\n"
+" 'v' -- video mode\n"
+" 'w' -- wait until the marked user enters the conference\n"
+" 'x' -- close the conference when last marked user exits\n"
+" 'X' -- allow user to exit the conference by entering a valid single\n"
+" digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
+" if that variable is not defined.\n";
+
+static const char *descrip2 =
+" MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
+"MeetMe conference. If var is specified, playback will be skipped and the value\n"
+"will be returned in the variable. Upon app completion, MeetMeCount will hangup the\n"
+"channel, unless priority n+1 exists, in which case priority progress will continue.\n"
+"A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
+
+static const char *descrip3 =
+" MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
+" 'e' -- Eject last user that joined\n"
+" 'k' -- Kick one user out of conference\n"
+" 'K' -- Kick all users out of conference\n"
+" 'l' -- Unlock conference\n"
+" 'L' -- Lock conference\n"
+" 'm' -- Unmute conference\n"
+" 'M' -- Mute conference\n"
+" 'n' -- Unmute entire conference (except admin)\n"
+" 'N' -- Mute entire conference (except admin)\n"
+"";
+
+#define CONFIG_FILE_NAME "meetme.conf"
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static struct ast_conference {
+ char confno[AST_MAX_EXTENSION]; /* Conference */
+ struct ast_channel *chan; /* Announcements channel */
+ int fd; /* Announcements fd */
+ int zapconf; /* Zaptel Conf # */
+ int users; /* Number of active users */
+ int markedusers; /* Number of marked users */
+ struct ast_conf_user *firstuser; /* Pointer to the first user struct */
+ struct ast_conf_user *lastuser; /* Pointer to the last user struct */
+ time_t start; /* Start time (s) */
+ int recording; /* recording status */
+ int isdynamic; /* Created on the fly? */
+ int locked; /* Is the conference locked? */
+ pthread_t recordthread; /* thread for recording */
+ pthread_attr_t attr; /* thread attribute */
+ char *recordingfilename; /* Filename to record the Conference into */
+ char *recordingformat; /* Format to record the Conference in */
+ char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
+ char pinadmin[AST_MAX_EXTENSION]; /* If protected by a admin PIN */
+ struct ast_conference *next;
+} *confs;
+
+struct volume {
+ int desired; /* Desired volume adjustment */
+ int actual; /* Actual volume adjustment (for channels that can't adjust) */
+};
+
+struct ast_conf_user {
+ int user_no; /* User Number */
+ struct ast_conf_user *prevuser; /* Pointer to the previous user */
+ struct ast_conf_user *nextuser; /* Pointer to the next user */
+ int userflags; /* Flags as set in the conference */
+ int adminflags; /* Flags set by the Admin */
+ struct ast_channel *chan; /* Connected channel */
+ int talking; /* Is user talking */
+ int zapchannel; /* Is a Zaptel channel */
+ char usrvalue[50]; /* Custom User Value */
+ char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
+ time_t jointime; /* Time the user joined the conference */
+ struct volume talk;
+ struct volume listen;
+};
+
+static int audio_buffers; /* The number of audio buffers to be allocated on pseudo channels
+ when in a conference
+ */
+
+#define DEFAULT_AUDIO_BUFFERS 32 /* each buffer is 20ms, so this is 640ms total */
+
+#define ADMINFLAG_MUTED (1 << 1) /* User is muted */
+#define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
+#define MEETME_DELAYDETECTTALK 300
+#define MEETME_DELAYDETECTENDTALK 1000
+
+enum volume_action {
+ VOL_UP,
+ VOL_DOWN,
+};
+
+AST_MUTEX_DEFINE_STATIC(conflock);
+
+static int admin_exec(struct ast_channel *chan, void *data);
+
+static void *recordthread(void *args);
+
+#include "enter.h"
+#include "leave.h"
+
+#define ENTER 0
+#define LEAVE 1
+
+#define MEETME_RECORD_OFF 0
+#define MEETME_RECORD_ACTIVE 1
+#define MEETME_RECORD_TERMINATE 2
+
+#define CONF_SIZE 320
+
+#define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
+#define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
+#define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
+#define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user when '*' is pressed */
+#define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
+#define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
+#define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
+#define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
+#define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
+#define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
+#define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
+#define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
+#define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
+#define CONFFLAG_INTROUSER (1 << 14) /* If set, user will be ask record name on entry of conference */
+#define CONFFLAG_RECORDCONF (1<< 15) /* If set, the MeetMe will be recorded */
+#define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
+#define CONFFLAG_DYNAMIC (1 << 17)
+#define CONFFLAG_DYNAMICPIN (1 << 18)
+#define CONFFLAG_EMPTY (1 << 19)
+#define CONFFLAG_EMPTYNOPIN (1 << 20)
+#define CONFFLAG_ALWAYSPROMPT (1 << 21)
+#define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 22) /* If set, when user joins the conference, they will be told the number of users that are already in */
+
+
+AST_APP_OPTIONS(meetme_opts, {
+ AST_APP_OPTION('a', CONFFLAG_ADMIN ),
+ AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
+ AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
+ AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
+ AST_APP_OPTION('m', CONFFLAG_MONITOR ),
+ AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
+ AST_APP_OPTION('s', CONFFLAG_STARMENU ),
+ AST_APP_OPTION('t', CONFFLAG_TALKER ),
+ AST_APP_OPTION('q', CONFFLAG_QUIET ),
+ AST_APP_OPTION('M', CONFFLAG_MOH ),
+ AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
+ AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
+ AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
+ AST_APP_OPTION('b', CONFFLAG_AGI ),
+ AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
+ AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
+ AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
+ AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
+ AST_APP_OPTION('e', CONFFLAG_EMPTY ),
+ AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
+ AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
+});
+
+static char *istalking(int x)
+{
+ if (x > 0)
+ return "(talking)";
+ else if (x < 0)
+ return "(unmonitored)";
+ else
+ return "(not talking)";
+}
+
+static int careful_write(int fd, unsigned char *data, int len, int block)
+{
+ int res;
+ int x;
+
+ while (len) {
+ if (block) {
+ x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
+ res = ioctl(fd, ZT_IOMUX, &x);
+ } else
+ res = 0;
+ if (res >= 0)
+ res = write(fd, data, len);
+ if (res < 1) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
+ return -1;
+ } else
+ return 0;
+ }
+ len -= res;
+ data += res;
+ }
+
+ return 0;
+}
+
+/* Map 'volume' levels from -5 through +5 into
+ decibel (dB) settings for channel drivers
+ Note: these are not a straight linear-to-dB
+ conversion... the numbers have been modified
+ to give the user a better level of adjustability
+*/
+static signed char gain_map[] = {
+ -15,
+ -13,
+ -10,
+ -6,
+ 0,
+ 0,
+ 0,
+ 6,
+ 10,
+ 13,
+ 15,
+};
+
+static int set_talk_volume(struct ast_conf_user *user, int volume)
+{
+ signed char gain_adjust;
+
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ gain_adjust = gain_map[volume + 5];
+
+ return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
+}
+
+static int set_listen_volume(struct ast_conf_user *user, int volume)
+{
+ signed char gain_adjust;
+
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ gain_adjust = gain_map[volume + 5];
+
+ return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
+}
+
+static void tweak_volume(struct volume *vol, enum volume_action action)
+{
+ switch (action) {
+ case VOL_UP:
+ switch (vol->desired) {
+ case 5:
+ break;
+ case 0:
+ vol->desired = 2;
+ break;
+ case -2:
+ vol->desired = 0;
+ break;
+ default:
+ vol->desired++;
+ break;
+ }
+ break;
+ case VOL_DOWN:
+ switch (vol->desired) {
+ case -5:
+ break;
+ case 2:
+ vol->desired = 0;
+ break;
+ case 0:
+ vol->desired = -2;
+ break;
+ default:
+ vol->desired--;
+ break;
+ }
+ }
+}
+
+static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
+{
+ tweak_volume(&user->talk, action);
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ if (!set_talk_volume(user, user->talk.desired))
+ user->talk.actual = 0;
+ else
+ user->talk.actual = user->talk.desired;
+}
+
+static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
+{
+ tweak_volume(&user->listen, action);
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ if (!set_listen_volume(user, user->listen.desired))
+ user->listen.actual = 0;
+ else
+ user->listen.actual = user->listen.desired;
+}
+
+static void reset_volumes(struct ast_conf_user *user)
+{
+ signed char zero_volume = 0;
+
+ ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
+ ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
+}
+
+static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
+{
+ unsigned char *data;
+ int len;
+ int res = -1;
+
+ if (!chan->_softhangup)
+ res = ast_autoservice_start(chan);
+
+ ast_mutex_lock(&conflock);
+
+ switch(sound) {
+ case ENTER:
+ data = enter;
+ len = sizeof(enter);
+ break;
+ case LEAVE:
+ data = leave;
+ len = sizeof(leave);
+ break;
+ default:
+ data = NULL;
+ len = 0;
+ }
+ if (data)
+ careful_write(conf->fd, data, len, 1);
+
+ ast_mutex_unlock(&conflock);
+
+ if (!res)
+ ast_autoservice_stop(chan);
+}
+
+static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
+{
+ struct ast_conference *cnf;
+ struct zt_confinfo ztc;
+
+ ast_mutex_lock(&conflock);
+
+ for (cnf = confs; cnf; cnf = cnf->next) {
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+
+ if (!cnf && (make || dynamic)) {
+ /* Make a new one */
+ cnf = calloc(1, sizeof(*cnf));
+ if (cnf) {
+ ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
+ ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
+ ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
+ cnf->markedusers = 0;
+ cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
+ if (cnf->chan) {
+ cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
+ } else {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
+ cnf->fd = open("/dev/zap/pseudo", O_RDWR);
+ if (cnf->fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo device\n");
+ free(cnf);
+ cnf = NULL;
+ goto cnfout;
+ }
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Setup a new zap conference */
+ ztc.chan = 0;
+ ztc.confno = -1;
+ ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
+ if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ if (cnf->chan)
+ ast_hangup(cnf->chan);
+ else
+ close(cnf->fd);
+ free(cnf);
+ cnf = NULL;
+ goto cnfout;
+ }
+ /* Fill the conference struct */
+ cnf->start = time(NULL);
+ cnf->zapconf = ztc.confno;
+ cnf->isdynamic = dynamic;
+ cnf->firstuser = NULL;
+ cnf->lastuser = NULL;
+ cnf->locked = 0;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
+ cnf->next = confs;
+ confs = cnf;
+ } else
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ cnfout:
+ ast_mutex_unlock(&conflock);
+ return cnf;
+}
+
+static int confs_show(int fd, int argc, char **argv)
+{
+ ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
+
+ return RESULT_SUCCESS;
+}
+
+static char show_confs_usage[] =
+"Deprecated! Please use 'meetme' instead.\n";
+
+static struct ast_cli_entry cli_show_confs = {
+ { "show", "conferences", NULL }, confs_show,
+ "Show status of conferences", show_confs_usage, NULL };
+
+static int conf_cmd(int fd, int argc, char **argv) {
+ /* Process the command */
+ struct ast_conference *cnf;
+ struct ast_conf_user *user;
+ int hr, min, sec;
+ int i = 0, total = 0;
+ time_t now;
+ char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
+ char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
+ char cmdline[1024] = "";
+
+ if (argc > 8)
+ ast_cli(fd, "Invalid Arguments.\n");
+ /* Check for length so no buffer will overflow... */
+ for (i = 0; i < argc; i++) {
+ if (strlen(argv[i]) > 100)
+ ast_cli(fd, "Invalid Arguments.\n");
+ }
+ if (argc == 1) {
+ /* 'MeetMe': List all the conferences */
+ now = time(NULL);
+ cnf = confs;
+ if (!cnf) {
+ ast_cli(fd, "No active MeetMe conferences.\n");
+ return RESULT_SUCCESS;
+ }
+ ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
+ while(cnf) {
+ if (cnf->markedusers == 0)
+ strcpy(cmdline, "N/A ");
+ else
+ snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
+ hr = (now - cnf->start) / 3600;
+ min = ((now - cnf->start) % 3600) / 60;
+ sec = (now - cnf->start) % 60;
+
+ ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
+
+ total += cnf->users;
+ cnf = cnf->next;
+ }
+ ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
+ return RESULT_SUCCESS;
+ }
+ if (argc < 3)
+ return RESULT_SHOWUSAGE;
+ ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
+ if (strstr(argv[1], "lock")) {
+ if (strcmp(argv[1], "lock") == 0) {
+ /* Lock */
+ strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ /* Unlock */
+ strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ } else if (strstr(argv[1], "mute")) {
+ if (argc < 4)
+ return RESULT_SHOWUSAGE;
+ if (strcmp(argv[1], "mute") == 0) {
+ /* Mute */
+ if (strcmp(argv[3], "all") == 0) {
+ strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
+ strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ } else {
+ /* Unmute */
+ if (strcmp(argv[3], "all") == 0) {
+ strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
+ strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ }
+ } else if (strcmp(argv[1], "kick") == 0) {
+ if (argc < 4)
+ return RESULT_SHOWUSAGE;
+ if (strcmp(argv[3], "all") == 0) {
+ /* Kick all */
+ strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ /* Kick a single user */
+ strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
+ strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ } else if(strcmp(argv[1], "list") == 0) {
+ /* List all the users in a conference */
+ if (!confs) {
+ ast_cli(fd, "No active conferences.\n");
+ return RESULT_SUCCESS;
+ }
+ cnf = confs;
+ /* Find the right conference */
+ while(cnf) {
+ if (strcmp(cnf->confno, argv[2]) == 0)
+ break;
+ if (cnf->next) {
+ cnf = cnf->next;
+ } else {
+ ast_cli(fd, "No such conference: %s.\n",argv[2]);
+ return RESULT_SUCCESS;
+ }
+ }
+ /* Show all the users */
+ for (user = cnf->firstuser; user; user = user->nextuser)
+ ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s\n",
+ user->user_no,
+ user->chan->cid.cid_num ? user->chan->cid.cid_num : "<unknown>",
+ user->chan->cid.cid_name ? user->chan->cid.cid_name : "<no name>",
+ user->chan->name,
+ user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
+ user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
+ user->adminflags & ADMINFLAG_MUTED ? "(Admn Muted)" : "",
+ istalking(user->talking));
+ ast_cli(fd,"%d users in that conference.\n",cnf->users);
+
+ return RESULT_SUCCESS;
+ } else
+ return RESULT_SHOWUSAGE;
+ ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
+ admin_exec(NULL, cmdline);
+
+ return 0;
+}
+
+static char *complete_confcmd(char *line, char *word, int pos, int state) {
+#define CONF_COMMANDS 6
+ int which = 0, x = 0;
+ struct ast_conference *cnf = NULL;
+ struct ast_conf_user *usr = NULL;
+ char *confno = NULL;
+ char usrno[50] = "";
+ char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
+ char *myline;
+
+ if (pos == 1) {
+ /* Command */
+ for (x = 0;x < CONF_COMMANDS; x++) {
+ if (!strncasecmp(cmds[x], word, strlen(word))) {
+ if (++which > state) {
+ return strdup(cmds[x]);
+ }
+ }
+ }
+ } else if (pos == 2) {
+ /* Conference Number */
+ ast_mutex_lock(&conflock);
+ cnf = confs;
+ while(cnf) {
+ if (!strncasecmp(word, cnf->confno, strlen(word))) {
+ if (++which > state)
+ break;
+ }
+ cnf = cnf->next;
+ }
+ ast_mutex_unlock(&conflock);
+ return cnf ? strdup(cnf->confno) : NULL;
+ } else if (pos == 3) {
+ /* User Number || Conf Command option*/
+ if (strstr(line, "mute") || strstr(line, "kick")) {
+ if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
+ return strdup("all");
+ }
+ which++;
+ ast_mutex_lock(&conflock);
+
+ /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
+ myline = ast_strdupa(line);
+ if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
+ while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
+ ;
+ }
+
+ for (cnf = confs; cnf; cnf = cnf->next) {
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+
+ if (cnf) {
+ /* Search for the user */
+ for (usr = cnf->firstuser; usr; usr = usr->nextuser) {
+ snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
+ if (!strncasecmp(word, usrno, strlen(word))) {
+ if (++which > state)
+ break;
+ }
+ }
+ }
+ ast_mutex_unlock(&conflock);
+ return usr ? strdup(usrno) : NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static char conf_usage[] =
+"Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
+" Executes a command for the conference or on a conferee\n";
+
+static struct ast_cli_entry cli_conf = {
+ {"meetme", NULL, NULL }, conf_cmd,
+ "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
+
+static void conf_flush(int fd, struct ast_channel *chan)
+{
+ int x;
+
+ /* read any frames that may be waiting on the channel
+ and throw them away
+ */
+ if (chan) {
+ struct ast_frame *f;
+
+ /* when no frames are available, this will wait
+ for 1 millisecond maximum
+ */
+ while (ast_waitfor(chan, 1)) {
+ f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ else /* channel was hung up or something else happened */
+ break;
+ }
+ }
+
+ /* flush any data sitting in the pseudo channel */
+ x = ZT_FLUSH_ALL;
+ if (ioctl(fd, ZT_FLUSH, &x))
+ ast_log(LOG_WARNING, "Error flushing channel\n");
+
+}
+
+/* Remove the conference from the list and free it.
+ We assume that this was called while holding conflock. */
+static int conf_free(struct ast_conference *conf)
+{
+ struct ast_conference *prev = NULL, *cur = confs;
+
+ while (cur) {
+ if (cur == conf) {
+ if (prev)
+ prev->next = conf->next;
+ else
+ confs = conf->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (!cur)
+ ast_log(LOG_WARNING, "Conference not found\n");
+
+ if (conf->recording == MEETME_RECORD_ACTIVE) {
+ conf->recording = MEETME_RECORD_TERMINATE;
+ ast_mutex_unlock(&conflock);
+ while (1) {
+ ast_mutex_lock(&conflock);
+ if (conf->recording == MEETME_RECORD_OFF)
+ break;
+ ast_mutex_unlock(&conflock);
+ }
+ }
+
+ if (conf->chan)
+ ast_hangup(conf->chan);
+ else
+ close(conf->fd);
+
+ free(conf);
+
+ return 0;
+}
+
+static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
+{
+ struct ast_conf_user *user = calloc(1, sizeof(*user));
+ struct ast_conf_user *usr = NULL;
+ int fd;
+ struct zt_confinfo ztc, ztc_empty;
+ struct ast_frame *f;
+ struct ast_channel *c;
+ struct ast_frame fr;
+ int outfd;
+ int ms;
+ int nfds;
+ int res;
+ int flags;
+ int retryzap;
+ int origfd;
+ int musiconhold = 0;
+ int firstpass = 0;
+ int lastmarked = 0;
+ int currentmarked = 0;
+ int ret = -1;
+ int x;
+ int menu_active = 0;
+ int using_pseudo = 0;
+ int duration=20;
+ struct ast_dsp *dsp=NULL;
+ struct ast_app *app;
+ char *agifile;
+ char *agifiledefault = "conf-background.agi";
+ char meetmesecs[30] = "";
+ char exitcontext[AST_MAX_CONTEXT] = "";
+ char recordingtmp[AST_MAX_EXTENSION] = "";
+ int dtmf;
+ ZT_BUFFERINFO bi;
+ char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+
+ if (!user) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return ret;
+ }
+
+ if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
+ conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
+ if (!conf->recordingfilename) {
+ snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
+ conf->recordingfilename = ast_strdupa(recordingtmp);
+ }
+ conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
+ if (!conf->recordingformat) {
+ snprintf(recordingtmp, sizeof(recordingtmp), "wav");
+ conf->recordingformat = ast_strdupa(recordingtmp);
+ }
+ pthread_attr_init(&conf->attr);
+ pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
+ ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
+ conf->confno, conf->recordingfilename, conf->recordingformat);
+ ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
+ }
+
+ time(&user->jointime);
+
+ if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
+ /* Sorry, but this confernce is locked! */
+ if (!ast_streamfile(chan, "conf-locked", chan->language))
+ ast_waitstream(chan, "");
+ goto outrun;
+ }
+
+ if (confflags & CONFFLAG_MARKEDUSER)
+ conf->markedusers++;
+
+ ast_mutex_lock(&conflock);
+ if (!conf->firstuser) {
+ /* Fill the first new User struct */
+ user->user_no = 1;
+ conf->firstuser = user;
+ conf->lastuser = user;
+ } else {
+ /* Fill the new user struct */
+ user->user_no = conf->lastuser->user_no + 1;
+ user->prevuser = conf->lastuser;
+ if (conf->lastuser->nextuser) {
+ ast_log(LOG_WARNING, "Error in User Management!\n");
+ ast_mutex_unlock(&conflock);
+ goto outrun;
+ } else {
+ conf->lastuser->nextuser = user;
+ conf->lastuser = user;
+ }
+ }
+
+ user->chan = chan;
+ user->userflags = confflags;
+ user->adminflags = 0;
+ user->talking = -1;
+ conf->users++;
+ ast_mutex_unlock(&conflock);
+
+ if (confflags & CONFFLAG_EXIT_CONTEXT) {
+ if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
+ ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
+ else if (!ast_strlen_zero(chan->macrocontext))
+ ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
+ else
+ ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
+ }
+
+ if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
+ snprintf(user->namerecloc, sizeof(user->namerecloc),
+ "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
+ conf->confno, user->user_no);
+ res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
+ if (res == -1)
+ goto outrun;
+ }
+
+ if (!(confflags & CONFFLAG_QUIET)) {
+ if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
+ if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
+ ast_waitstream(chan, "");
+ if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
+ if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
+ ast_waitstream(chan, "");
+ }
+
+ if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
+ int keepplaying = 1;
+
+ if (conf->users == 2) {
+ if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (res > 0)
+ keepplaying=0;
+ else if (res == -1)
+ goto outrun;
+ }
+ } else {
+ if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (res > 0)
+ keepplaying=0;
+ else if (res == -1)
+ goto outrun;
+ }
+ if (keepplaying) {
+ res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ if (res > 0)
+ keepplaying=0;
+ else if (res == -1)
+ goto outrun;
+ }
+ if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (res > 0)
+ keepplaying=0;
+ else if (res == -1)
+ goto outrun;
+ }
+ }
+ }
+
+ ast_indicate(chan, -1);
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
+ goto outrun;
+ }
+
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
+ goto outrun;
+ }
+
+ retryzap = strcasecmp(chan->type, "Zap");
+ user->zapchannel = !retryzap;
+
+ zapretry:
+ origfd = chan->fds[0];
+ if (retryzap) {
+ fd = open("/dev/zap/pseudo", O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ goto outrun;
+ }
+ using_pseudo = 1;
+ /* Make non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ /* Setup buffering information */
+ memset(&bi, 0, sizeof(bi));
+ bi.bufsize = CONF_SIZE/2;
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = audio_buffers;
+ if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
+ ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ x = 1;
+ if (ioctl(fd, ZT_SETLINEAR, &x)) {
+ ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ nfds = 1;
+ } else {
+ /* XXX Make sure we're not running on a pseudo channel XXX */
+ fd = chan->fds[0];
+ nfds = 0;
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ memset(&ztc_empty, 0, sizeof(ztc_empty));
+ /* Check to see if we're in a conference... */
+ ztc.chan = 0;
+ if (ioctl(fd, ZT_GETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error getting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (ztc.confmode) {
+ /* Whoa, already in a conference... Retry... */
+ if (!retryzap) {
+ ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
+ retryzap = 1;
+ goto zapretry;
+ }
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = conf->zapconf;
+
+ ast_mutex_lock(&conflock);
+
+ if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
+ if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
+ if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
+ ast_waitstream(conf->chan, "");
+ if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
+ ast_waitstream(conf->chan, "");
+ }
+ }
+
+ if (confflags & CONFFLAG_MONITOR)
+ ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+ else if (confflags & CONFFLAG_TALKER)
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+ else
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ ast_mutex_unlock(&conflock);
+ goto outrun;
+ }
+ ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
+
+ manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+
+ if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
+ firstpass = 1;
+ if (!(confflags & CONFFLAG_QUIET))
+ if (!(confflags & CONFFLAG_WAITMARKED) || (conf->markedusers >= 1))
+ conf_play(chan, conf, ENTER);
+ }
+
+ ast_mutex_unlock(&conflock);
+
+ conf_flush(fd, chan);
+
+ if (confflags & CONFFLAG_AGI) {
+ /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
+ or use default filename of conf-background.agi */
+
+ agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
+ if (!agifile)
+ agifile = agifiledefault;
+
+ if (user->zapchannel) {
+ /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
+ x = 1;
+ ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
+ }
+ /* Find a pointer to the agi app and execute the script */
+ app = pbx_findapp("agi");
+ if (app) {
+ ret = pbx_exec(chan, app, agifile, 1);
+ } else {
+ ast_log(LOG_WARNING, "Could not find application (agi)\n");
+ ret = -2;
+ }
+ if (user->zapchannel) {
+ /* Remove CONFMUTE mode on Zap channel */
+ x = 0;
+ ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
+ }
+ } else {
+ if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
+ /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
+ x = 1;
+ ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
+ }
+ if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
+ ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
+ res = -1;
+ }
+ for(;;) {
+ int menu_was_active = 0;
+
+ outfd = -1;
+ ms = -1;
+
+ /* if we have just exited from the menu, and the user had a channel-driver
+ volume adjustment, restore it
+ */
+ if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
+ set_talk_volume(user, user->listen.desired);
+
+ menu_was_active = menu_active;
+
+ currentmarked = conf->markedusers;
+ if (!(confflags & CONFFLAG_QUIET) &&
+ (confflags & CONFFLAG_MARKEDUSER) &&
+ (confflags & CONFFLAG_WAITMARKED) &&
+ lastmarked == 0) {
+ if (currentmarked == 1 && conf->users > 1) {
+ ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ if (conf->users - 1 == 1) {
+ if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
+ ast_waitstream(chan, "");
+ }
+ }
+ if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
+ if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
+ ast_waitstream(chan, "");
+ }
+
+ c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+
+ /* Update the struct with the actual confflags */
+ user->userflags = confflags;
+
+ if (confflags & CONFFLAG_WAITMARKED) {
+ if(currentmarked == 0) {
+ if (lastmarked != 0) {
+ if (!(confflags & CONFFLAG_QUIET))
+ if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
+ ast_waitstream(chan, "");
+ if(confflags & CONFFLAG_MARKEDEXIT)
+ break;
+ else {
+ ztc.confmode = ZT_CONF_CONF;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ }
+ }
+ if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
+ ast_moh_start(chan, NULL);
+ musiconhold = 1;
+ } else {
+ ztc.confmode = ZT_CONF_CONF;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ }
+ } else if(currentmarked >= 1 && lastmarked == 0) {
+ if (confflags & CONFFLAG_MONITOR)
+ ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+ else if (confflags & CONFFLAG_TALKER)
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+ else
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (musiconhold && (confflags & CONFFLAG_MOH)) {
+ ast_moh_stop(chan);
+ musiconhold = 0;
+ }
+ if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
+ if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
+ ast_waitstream(chan, "");
+ conf_play(chan, conf, ENTER);
+ }
+ }
+ }
+
+ /* trying to add moh for single person conf */
+ if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
+ if (conf->users == 1) {
+ if (musiconhold == 0) {
+ ast_moh_start(chan, NULL);
+ musiconhold = 1;
+ }
+ } else {
+ if (musiconhold) {
+ ast_moh_stop(chan);
+ musiconhold = 0;
+ }
+ }
+ }
+
+ /* Leave if the last marked user left */
+ if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
+ ret = -1;
+ break;
+ }
+
+ /* Check if the admin changed my modes */
+ if (user->adminflags) {
+ /* Set the new modes */
+ if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
+ ztc.confmode ^= ZT_CONF_TALKER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+ }
+ if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
+ ztc.confmode |= ZT_CONF_TALKER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+ }
+ if (user->adminflags & ADMINFLAG_KICKME) {
+ /* You have been kicked. */
+ if (!ast_streamfile(chan, "conf-kicked", chan->language))
+ ast_waitstream(chan, "");
+ ret = 0;
+ break;
+ }
+ } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
+ ztc.confmode |= ZT_CONF_TALKER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+ }
+
+ if (c) {
+ if (c->fds[0] != origfd) {
+ if (using_pseudo) {
+ /* Kill old pseudo */
+ close(fd);
+ using_pseudo = 0;
+ }
+ ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
+ retryzap = strcasecmp(c->type, "Zap");
+ user->zapchannel = !retryzap;
+ goto zapretry;
+ }
+ f = ast_read(c);
+ if (!f)
+ break;
+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
+ if (user->talk.actual)
+ ast_frame_adjust_volume(f, user->talk.actual);
+
+ if (confflags & CONFFLAG_MONITORTALKER) {
+ int totalsilence;
+
+ if (user->talking == -1)
+ user->talking = 0;
+
+ res = ast_dsp_silence(dsp, f, &totalsilence);
+ if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
+ user->talking = 1;
+ manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+ if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
+ user->talking = 0;
+ manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+ }
+ if (using_pseudo) {
+ /* Absolutely do _not_ use careful_write here...
+ it is important that we read data from the channel
+ as fast as it arrives, and feed it into the conference.
+ The buffering in the pseudo channel will take care of any
+ timing differences, unless they are so drastic as to lose
+ audio frames (in which case carefully writing would only
+ have delayed the audio even further).
+ */
+ /* As it turns out, we do want to use careful write. We just
+ don't want to block, but we do want to at least *try*
+ to write out all the samples.
+ */
+ careful_write(fd, f->data, f->datalen, 0);
+ }
+ } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
+ char tmp[2];
+
+ tmp[0] = f->subclass;
+ tmp[1] = '\0';
+ if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
+ ret = 0;
+ break;
+ } else if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
+ } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
+ ret = 0;
+ break;
+ } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
+ if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+
+ /* if we are entering the menu, and the user has a channel-driver
+ volume adjustment, clear it
+ */
+ if (!menu_active && user->talk.desired && !user->talk.actual)
+ set_talk_volume(user, 0);
+
+ if (musiconhold) {
+ ast_moh_stop(chan);
+ }
+ if ((confflags & CONFFLAG_ADMIN)) {
+ /* Admin menu */
+ if (!menu_active) {
+ menu_active = 1;
+ /* Record this sound! */
+ if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
+ dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ } else
+ dtmf = 0;
+ } else
+ dtmf = f->subclass;
+ if (dtmf) {
+ switch(dtmf) {
+ case '1': /* Un/Mute */
+ menu_active = 0;
+ if (ztc.confmode & ZT_CONF_TALKER) {
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
+ confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
+ } else {
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+ confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
+ }
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+ if (ztc.confmode & ZT_CONF_TALKER) {
+ if (!ast_streamfile(chan, "conf-unmuted", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "conf-muted", chan->language))
+ ast_waitstream(chan, "");
+ }
+ break;
+ case '2': /* Un/Lock the Conference */
+ menu_active = 0;
+ if (conf->locked) {
+ conf->locked = 0;
+ if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ conf->locked = 1;
+ if (!ast_streamfile(chan, "conf-lockednow", chan->language))
+ ast_waitstream(chan, "");
+ }
+ break;
+ case '3': /* Eject last user */
+ menu_active = 0;
+ usr = conf->lastuser;
+ if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
+ if(!ast_streamfile(chan, "conf-errormenu", chan->language))
+ ast_waitstream(chan, "");
+ } else
+ usr->adminflags |= ADMINFLAG_KICKME;
+ ast_stopstream(chan);
+ break;
+ case '4':
+ tweak_listen_volume(user, VOL_DOWN);
+ break;
+ case '6':
+ tweak_listen_volume(user, VOL_UP);
+ break;
+ case '7':
+ tweak_talk_volume(user, VOL_DOWN);
+ break;
+ case '8':
+ menu_active = 0;
+ break;
+ case '9':
+ tweak_talk_volume(user, VOL_UP);
+ break;
+ default:
+ menu_active = 0;
+ /* Play an error message! */
+ if (!ast_streamfile(chan, "conf-errormenu", chan->language))
+ ast_waitstream(chan, "");
+ break;
+ }
+ }
+ } else {
+ /* User menu */
+ if (!menu_active) {
+ menu_active = 1;
+ if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
+ dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ } else
+ dtmf = 0;
+ } else
+ dtmf = f->subclass;
+ if (dtmf) {
+ switch(dtmf) {
+ case '1': /* Un/Mute */
+ menu_active = 0;
+ if (ztc.confmode & ZT_CONF_TALKER) {
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
+ confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
+ } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+ confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
+ }
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+ if (ztc.confmode & ZT_CONF_TALKER) {
+ if (!ast_streamfile(chan, "conf-unmuted", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "conf-muted", chan->language))
+ ast_waitstream(chan, "");
+ }
+ break;
+ case '4':
+ tweak_listen_volume(user, VOL_DOWN);
+ break;
+ case '6':
+ tweak_listen_volume(user, VOL_UP);
+ break;
+ case '7':
+ tweak_talk_volume(user, VOL_DOWN);
+ break;
+ case '8':
+ menu_active = 0;
+ break;
+ case '9':
+ tweak_talk_volume(user, VOL_UP);
+ break;
+ default:
+ menu_active = 0;
+ if (!ast_streamfile(chan, "conf-errormenu", chan->language))
+ ast_waitstream(chan, "");
+ break;
+ }
+ }
+ }
+ if (musiconhold)
+ ast_moh_start(chan, NULL);
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ ast_mutex_unlock(&conflock);
+ goto outrun;
+ }
+
+ conf_flush(fd, chan);
+ } else if (option_debug) {
+ ast_log(LOG_DEBUG,
+ "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
+ chan->name, f->frametype, f->subclass);
+ }
+ ast_frfree(f);
+ } else if (outfd > -1) {
+ res = read(outfd, buf, CONF_SIZE);
+ if (res > 0) {
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_SLINEAR;
+ fr.datalen = res;
+ fr.samples = res/2;
+ fr.data = buf;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ if (user->listen.actual)
+ ast_frame_adjust_volume(&fr, user->listen.actual);
+ if (ast_write(chan, &fr) < 0) {
+ ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+ }
+ } else
+ ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+ }
+ lastmarked = currentmarked;
+ }
+ }
+
+ if (musiconhold)
+ ast_moh_stop(chan);
+
+ if (using_pseudo)
+ close(fd);
+ else {
+ /* Take out of conference */
+ ztc.chan = 0;
+ ztc.confno = 0;
+ ztc.confmode = 0;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ }
+ }
+
+ reset_volumes(user);
+
+ ast_mutex_lock(&conflock);
+ if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
+ conf_play(chan, conf, LEAVE);
+
+ if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
+ if (ast_fileexists(user->namerecloc, NULL, NULL)) {
+ if ((conf->chan) && (conf->users > 1)) {
+ if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
+ ast_waitstream(conf->chan, "");
+ if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
+ ast_waitstream(conf->chan, "");
+ }
+ ast_filedelete(user->namerecloc, NULL);
+ }
+ }
+ ast_mutex_unlock(&conflock);
+
+ outrun:
+ ast_mutex_lock(&conflock);
+
+ if (confflags & CONFFLAG_MONITORTALKER && dsp)
+ ast_dsp_free(dsp);
+
+ if (user->user_no) { /* Only cleanup users who really joined! */
+ manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ conf->users--;
+ if (confflags & CONFFLAG_MARKEDUSER)
+ conf->markedusers--;
+ if (!conf->users) {
+ /* No more users -- close this one out */
+ conf_free(conf);
+ } else {
+ /* Remove the user struct */
+ if (user == conf->firstuser) {
+ if (user->nextuser) {
+ /* There is another entry */
+ user->nextuser->prevuser = NULL;
+ } else {
+ /* We are the only entry */
+ conf->lastuser = NULL;
+ }
+ /* In either case */
+ conf->firstuser = user->nextuser;
+ } else if (user == conf->lastuser){
+ if (user->prevuser)
+ user->prevuser->nextuser = NULL;
+ else
+ ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
+ conf->lastuser = user->prevuser;
+ } else {
+ if (user->nextuser)
+ user->nextuser->prevuser = user->prevuser;
+ else
+ ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
+ if (user->prevuser)
+ user->prevuser->nextuser = user->nextuser;
+ else
+ ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
+ }
+ }
+ /* Return the number of seconds the user was in the conf */
+ snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
+ pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
+ }
+ free(user);
+ ast_mutex_unlock(&conflock);
+
+ return ret;
+}
+
+static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ struct ast_conference *cnf;
+
+ /* Check first in the conference list */
+ ast_mutex_lock(&conflock);
+ for (cnf = confs; cnf; cnf = cnf->next) {
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+ ast_mutex_unlock(&conflock);
+
+ if (!cnf) {
+ if (dynamic) {
+ /* No need to parse meetme.conf */
+ ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
+ if (dynamic_pin) {
+ if (dynamic_pin[0] == 'q') {
+ /* Query the user to enter a PIN */
+ if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
+ return NULL;
+ }
+ cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
+ } else {
+ cnf = build_conf(confno, "", "", make, dynamic);
+ }
+ } else {
+ /* Check the config */
+ cfg = ast_config_load(CONFIG_FILE_NAME);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
+ return NULL;
+ }
+ var = ast_variable_browse(cfg, "rooms");
+ while (var) {
+ if (!strcasecmp(var->name, "conf")) {
+ /* Separate the PIN */
+ char *pin, *pinadmin, *conf;
+
+ if ((pinadmin = ast_strdupa(var->value))) {
+ conf = strsep(&pinadmin, "|,");
+ pin = strsep(&pinadmin, "|,");
+ if (!strcasecmp(conf, confno)) {
+ /* Bingo it's a valid conference */
+ if (pin)
+ if (pinadmin)
+ cnf = build_conf(confno, pin, pinadmin, make, dynamic);
+ else
+ cnf = build_conf(confno, pin, "", make, dynamic);
+ else
+ if (pinadmin)
+ cnf = build_conf(confno, "", pinadmin, make, dynamic);
+ else
+ cnf = build_conf(confno, "", "", make, dynamic);
+ break;
+ }
+ }
+ }
+ var = var->next;
+ }
+ if (!var) {
+ ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
+ }
+ ast_config_destroy(cfg);
+ }
+ } else if (dynamic_pin) {
+ /* Correct for the user selecting 'D' instead of 'd' to have
+ someone join into a conference that has already been created
+ with a pin. */
+ if (dynamic_pin[0] == 'q')
+ dynamic_pin[0] = '\0';
+ }
+
+ return cnf;
+}
+
+/*--- count_exec: The MeetmeCount application */
+static int count_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int res = 0;
+ struct ast_conference *conf;
+ int count;
+ char *confnum, *localdata;
+ char val[80] = "0";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ localdata = ast_strdupa(data);
+ if (!localdata) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ confnum = strsep(&localdata,"|");
+ conf = find_conf(chan, confnum, 0, 0, NULL);
+ if (conf)
+ count = conf->users;
+ else
+ count = 0;
+
+ if (!ast_strlen_zero(localdata)){
+ /* have var so load it and exit */
+ snprintf(val, sizeof(val), "%d",count);
+ pbx_builtin_setvar_helper(chan, localdata, val);
+ } else {
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+ res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
+ }
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+/*--- conf_exec: The meetme() application */
+static int conf_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ char confno[AST_MAX_EXTENSION] = "";
+ int allowretry = 0;
+ int retrycnt = 0;
+ struct ast_conference *cnf;
+ struct ast_flags confflags = {0};
+ int dynamic = 0;
+ int empty = 0, empty_no_pin = 0;
+ int always_prompt = 0;
+ char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
+
+ LOCAL_USER_ADD(u);
+
+ if (ast_strlen_zero(data)) {
+ allowretry = 1;
+ notdata = "";
+ } else {
+ notdata = data;
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ info = ast_strdupa(notdata);
+
+ if (info) {
+ char *tmp = strsep(&info, "|");
+ ast_copy_string(confno, tmp, sizeof(confno));
+ if (ast_strlen_zero(confno)) {
+ allowretry = 1;
+ }
+ }
+ if (info)
+ inflags = strsep(&info, "|");
+ if (info)
+ inpin = strsep(&info, "|");
+ if (inpin)
+ ast_copy_string(the_pin, inpin, sizeof(the_pin));
+
+ if (inflags) {
+ ast_app_parse_options(meetme_opts, &confflags, NULL, inflags);
+ dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
+ if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !inpin)
+ strcpy(the_pin, "q");
+
+ empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
+ empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
+ always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
+ }
+
+ do {
+ if (retrycnt > 3)
+ allowretry = 0;
+ if (empty) {
+ int i, map[1024] = { 0, };
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ int confno_int;
+
+ ast_mutex_lock(&conflock);
+ for (cnf = confs; cnf; cnf = cnf->next) {
+ if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
+ /* Disqualify in use conference */
+ if (confno_int >= 0 && confno_int < 1024)
+ map[confno_int]++;
+ }
+ }
+ ast_mutex_unlock(&conflock);
+
+ /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
+ if ((empty_no_pin) || (!dynamic)) {
+ cfg = ast_config_load(CONFIG_FILE_NAME);
+ if (cfg) {
+ var = ast_variable_browse(cfg, "rooms");
+ while (var) {
+ if (!strcasecmp(var->name, "conf")) {
+ char *stringp = ast_strdupa(var->value);
+ if (stringp) {
+ char *confno_tmp = strsep(&stringp, "|,");
+ int found = 0;
+ if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
+ if ((confno_int >= 0) && (confno_int < 1024)) {
+ if (stringp && empty_no_pin) {
+ map[confno_int]++;
+ }
+ }
+ }
+ if (!dynamic) {
+ /* For static: run through the list and see if this conference is empty */
+ ast_mutex_lock(&conflock);
+ cnf = confs;
+ while (cnf) {
+ if (!strcmp(confno_tmp, cnf->confno)) {
+ /* The conference exists, therefore it's not empty */
+ found = 1;
+ break;
+ }
+ cnf = cnf->next;
+ }
+ ast_mutex_unlock(&conflock);
+ if (!found) {
+ /* At this point, we have a confno_tmp (static conference) that is empty */
+ if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
+ /* Case 1: empty_no_pin and pin is nonexistent (NULL)
+ * Case 2: empty_no_pin and pin is blank (but not NULL)
+ * Case 3: not empty_no_pin
+ */
+ ast_copy_string(confno, confno_tmp, sizeof(confno));
+ break;
+ /* XXX the map is not complete (but we do have a confno) */
+ }
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ }
+ }
+ var = var->next;
+ }
+ ast_config_destroy(cfg);
+ }
+ }
+
+ /* Select first conference number not in use */
+ if (ast_strlen_zero(confno) && dynamic) {
+ for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+ if (!map[i]) {
+ snprintf(confno, sizeof(confno), "%d", i);
+ break;
+ }
+ }
+ }
+
+ /* Not found? */
+ if (ast_strlen_zero(confno)) {
+ res = ast_streamfile(chan, "conf-noempty", chan->language);
+ if (!res)
+ ast_waitstream(chan, "");
+ } else {
+ if (sscanf(confno, "%d", &confno_int) == 1) {
+ res = ast_streamfile(chan, "conf-enteringno", chan->language);
+ if (!res) {
+ ast_waitstream(chan, "");
+ res = ast_say_digits(chan, confno_int, "", chan->language);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
+ }
+ }
+ }
+
+ while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
+ /* Prompt user for conference number */
+ res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
+ if (res < 0) {
+ /* Don't try to validate when we catch an error */
+ confno[0] = '\0';
+ allowretry = 0;
+ break;
+ }
+ }
+ if (!ast_strlen_zero(confno)) {
+ /* Check the validity of the conference */
+ cnf = find_conf(chan, confno, 1, dynamic, the_pin);
+ if (!cnf) {
+ res = ast_streamfile(chan, "conf-invalid", chan->language);
+ if (!res)
+ ast_waitstream(chan, "");
+ res = -1;
+ if (allowretry)
+ confno[0] = '\0';
+ } else {
+ if ((!ast_strlen_zero(cnf->pin) &&
+ !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
+ (!ast_strlen_zero(cnf->pinadmin) &&
+ ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
+ char pin[AST_MAX_EXTENSION]="";
+ int j;
+
+ /* Allow the pin to be retried up to 3 times */
+ for (j = 0; j < 3; j++) {
+ if (*the_pin && (always_prompt == 0)) {
+ ast_copy_string(pin, the_pin, sizeof(pin));
+ res = 0;
+ } else {
+ /* Prompt user for pin if pin is required */
+ res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
+ }
+ if (res >= 0) {
+ if (!strcasecmp(pin, cnf->pin) ||
+ (!ast_strlen_zero(cnf->pinadmin) &&
+ !strcasecmp(pin, cnf->pinadmin))) {
+ /* Pin correct */
+ allowretry = 0;
+ if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
+ ast_set_flag(&confflags, CONFFLAG_ADMIN);
+ /* Run the conference */
+ res = conf_run(chan, cnf, confflags.flags);
+ break;
+ } else {
+ /* Pin invalid */
+ res = ast_streamfile(chan, "conf-invalidpin", chan->language);
+ if (!res)
+ ast_waitstream(chan, AST_DIGIT_ANY);
+ if (res < 0)
+ break;
+ pin[0] = res;
+ pin[1] = '\0';
+ res = -1;
+ if (allowretry)
+ confno[0] = '\0';
+ }
+ } else {
+ /* failed when getting the pin */
+ res = -1;
+ allowretry = 0;
+ /* see if we need to get rid of the conference */
+ ast_mutex_lock(&conflock);
+ if (!cnf->users) {
+ conf_free(cnf);
+ }
+ ast_mutex_unlock(&conflock);
+ break;
+ }
+
+ /* Don't retry pin with a static pin */
+ if (*the_pin && (always_prompt==0)) {
+ break;
+ }
+ }
+ } else {
+ /* No pin required */
+ allowretry = 0;
+
+ /* Run the conference */
+ res = conf_run(chan, cnf, confflags.flags);
+ }
+ }
+ }
+ } while (allowretry);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
+ struct ast_conf_user *user = NULL;
+ char usrno[1024] = "";
+
+ if (conf && callerident) {
+ user = conf->firstuser;
+ while (user) {
+ snprintf(usrno, sizeof(usrno), "%d", user->user_no);
+ if (strcmp(usrno, callerident) == 0)
+ return user;
+ user = user->nextuser;
+ }
+ }
+ return NULL;
+}
+
+/*--- admin_exec: The MeetMeadmin application */
+/* MeetMeAdmin(confno, command, caller) */
+static int admin_exec(struct ast_channel *chan, void *data) {
+ char *params, *command = NULL, *caller = NULL, *conf = NULL;
+ struct ast_conference *cnf;
+ struct ast_conf_user *user = NULL;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ ast_mutex_lock(&conflock);
+ /* The param has the conference number the user and the command to execute */
+ if (!ast_strlen_zero(data)) {
+ params = ast_strdupa((char *) data);
+ conf = strsep(&params, "|");
+ command = strsep(&params, "|");
+ caller = strsep(&params, "|");
+
+ if (!command) {
+ ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
+ ast_mutex_unlock(&conflock);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ for (cnf = confs; cnf; cnf = cnf->next) {
+ if (!strcmp(cnf->confno, conf))
+ break;
+ }
+
+ if (caller)
+ user = find_user(cnf, caller);
+
+ if (cnf) {
+ switch((int) (*command)) {
+ case 76: /* L: Lock */
+ cnf->locked = 1;
+ break;
+ case 108: /* l: Unlock */
+ cnf->locked = 0;
+ break;
+ case 75: /* K: kick all users*/
+ user = cnf->firstuser;
+ while(user) {
+ user->adminflags |= ADMINFLAG_KICKME;
+ if (user->nextuser) {
+ user = user->nextuser;
+ } else {
+ break;
+ }
+ }
+ break;
+ case 101: /* e: Eject last user*/
+ user = cnf->lastuser;
+ if (!(user->userflags & CONFFLAG_ADMIN)) {
+ user->adminflags |= ADMINFLAG_KICKME;
+ break;
+ } else
+ ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
+ break;
+ case 77: /* M: Mute */
+ if (user) {
+ user->adminflags |= ADMINFLAG_MUTED;
+ } else {
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ }
+ break;
+ case 78: /* N: Mute all users */
+ user = cnf->firstuser;
+ while(user) {
+ if (user && !(user->userflags & CONFFLAG_ADMIN))
+ user->adminflags |= ADMINFLAG_MUTED;
+ if (user->nextuser) {
+ user = user->nextuser;
+ } else {
+ break;
+ }
+ }
+ break;
+ case 109: /* m: Unmute */
+ if (user && (user->adminflags & ADMINFLAG_MUTED)) {
+ user->adminflags ^= ADMINFLAG_MUTED;
+ } else {
+ ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
+ }
+ break;
+ case 110: /* n: Unmute all users */
+ user = cnf->firstuser;
+ while(user) {
+ if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
+ user->adminflags ^= ADMINFLAG_MUTED;
+ }
+ if (user->nextuser) {
+ user = user->nextuser;
+ } else {
+ break;
+ }
+ }
+ break;
+ case 107: /* k: Kick user */
+ if (user) {
+ user->adminflags |= ADMINFLAG_KICKME;
+ } else {
+ ast_log(LOG_NOTICE, "Specified User not found!");
+ }
+ break;
+ }
+ } else {
+ ast_log(LOG_NOTICE, "Conference Number not found\n");
+ }
+ }
+ ast_mutex_unlock(&conflock);
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static void *recordthread(void *args)
+{
+ struct ast_conference *cnf = args;
+ struct ast_frame *f=NULL;
+ int flags;
+ struct ast_filestream *s;
+ int res=0;
+
+ if (!cnf || !cnf->chan) {
+ pthread_exit(0);
+ }
+ ast_stopstream(cnf->chan);
+ flags = O_CREAT|O_TRUNC|O_WRONLY;
+ s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
+
+ if (s) {
+ cnf->recording = MEETME_RECORD_ACTIVE;
+ while (ast_waitfor(cnf->chan, -1) > -1) {
+ f = ast_read(cnf->chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ res = ast_writestream(s, f);
+ if (res)
+ break;
+ }
+ ast_frfree(f);
+ if (cnf->recording == MEETME_RECORD_TERMINATE) {
+ ast_mutex_lock(&conflock);
+ ast_mutex_unlock(&conflock);
+ break;
+ }
+ }
+ cnf->recording = MEETME_RECORD_OFF;
+ ast_closestream(s);
+ }
+ pthread_exit(0);
+}
+
+static void load_config(void)
+{
+ struct ast_config *cfg;
+ char *val;
+
+ audio_buffers = DEFAULT_AUDIO_BUFFERS;
+
+ if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
+ return;
+
+ if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
+ if ((sscanf(val, "%d", &audio_buffers) != 1)) {
+ ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
+ audio_buffers = DEFAULT_AUDIO_BUFFERS;
+ } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
+ ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
+ ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
+ audio_buffers = DEFAULT_AUDIO_BUFFERS;
+ }
+ if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
+ ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
+ }
+
+ ast_config_destroy(cfg);
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_cli_unregister(&cli_show_confs);
+ res |= ast_cli_unregister(&cli_conf);
+ res |= ast_unregister_application(app3);
+ res |= ast_unregister_application(app2);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ load_config();
+
+ res = ast_cli_register(&cli_show_confs);
+ res |= ast_cli_register(&cli_conf);
+ res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
+ res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
+ res |= ast_register_application(app, conf_exec, synopsis, descrip);
+
+ return res;
+}
+
+int reload(void)
+{
+ load_config();
+
+ return 0;
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_milliwatt.c b/1.2-netsec/apps/app_milliwatt.c
new file mode 100644
index 000000000..9c46689f4
--- /dev/null
+++ b/1.2-netsec/apps/app_milliwatt.c
@@ -0,0 +1,170 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Digital Milliwatt Test
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+static char *tdesc = "Digital Milliwatt (mu-law) Test Application";
+
+static char *app = "Milliwatt";
+
+static char *synopsis = "Generate a Constant 1000Hz tone at 0dbm (mu-law)";
+
+static char *descrip =
+"Milliwatt(): Generate a Constant 1000Hz tone at 0dbm (mu-law)\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char digital_milliwatt[] = {0x1e,0x0b,0x0b,0x1e,0x9e,0x8b,0x8b,0x9e} ;
+
+static void *milliwatt_alloc(struct ast_channel *chan, void *params)
+{
+int *indexp;
+ indexp = malloc(sizeof(int));
+ if (indexp == NULL) return(NULL);
+ *indexp = 0;
+ return(indexp);
+}
+
+static void milliwatt_release(struct ast_channel *chan, void *data)
+{
+ free(data);
+ return;
+}
+
+static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct ast_frame wf;
+ unsigned char buf[AST_FRIENDLY_OFFSET + 640];
+ int i,*indexp = (int *) data;
+
+ if (len + AST_FRIENDLY_OFFSET > sizeof(buf))
+ {
+ ast_log(LOG_WARNING,"Only doing %d bytes (%d bytes requested)\n",(int)(sizeof(buf) - AST_FRIENDLY_OFFSET),len);
+ len = sizeof(buf) - AST_FRIENDLY_OFFSET;
+ }
+ wf.frametype = AST_FRAME_VOICE;
+ wf.subclass = AST_FORMAT_ULAW;
+ wf.offset = AST_FRIENDLY_OFFSET;
+ wf.mallocd = 0;
+ wf.data = buf + AST_FRIENDLY_OFFSET;
+ wf.datalen = len;
+ wf.samples = wf.datalen;
+ wf.src = "app_milliwatt";
+ wf.delivery.tv_sec = 0;
+ wf.delivery.tv_usec = 0;
+ wf.prev = wf.next = NULL;
+ /* create a buffer containing the digital milliwatt pattern */
+ for(i = 0; i < len; i++)
+ {
+ buf[AST_FRIENDLY_OFFSET + i] = digital_milliwatt[(*indexp)++];
+ *indexp &= 7;
+ }
+ if (ast_write(chan,&wf) < 0)
+ {
+ ast_log(LOG_WARNING,"Failed to write frame to '%s': %s\n",chan->name,strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_generator milliwattgen =
+{
+ alloc: milliwatt_alloc,
+ release: milliwatt_release,
+ generate: milliwatt_generate,
+} ;
+
+static int milliwatt_exec(struct ast_channel *chan, void *data)
+{
+
+ struct localuser *u;
+ LOCAL_USER_ADD(u);
+ ast_set_write_format(chan, AST_FORMAT_ULAW);
+ ast_set_read_format(chan, AST_FORMAT_ULAW);
+ if (chan->_state != AST_STATE_UP)
+ {
+ ast_answer(chan);
+ }
+ if (ast_activate_generator(chan,&milliwattgen,"milliwatt") < 0)
+ {
+ ast_log(LOG_WARNING,"Failed to activate generator on '%s'\n",chan->name);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ while(!ast_safe_sleep(chan, 10000));
+ ast_deactivate_generator(chan);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, milliwatt_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_mixmonitor.c b/1.2-netsec/apps/app_mixmonitor.c
new file mode 100644
index 000000000..0f5d101df
--- /dev/null
+++ b/1.2-netsec/apps/app_mixmonitor.c
@@ -0,0 +1,466 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II
+ * Copyright (C) 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * Based on app_muxmon.c provided by
+ * Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief MixMonitor() - Record a call and mix the audio during the recording
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/chanspy.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/linkedlists.h"
+
+#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
+
+static const char *tdesc = "Mixed Audio Monitoring Application";
+static const char *app = "MixMonitor";
+static const char *synopsis = "Record a call and mix the audio during the recording";
+static const char *desc = ""
+" MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
+"Records the audio on the current channel to the specified file.\n"
+"If the filename is an absolute path, uses that path, otherwise\n"
+"creates the file in the configured monitoring directory from\n"
+"asterisk.conf.\n\n"
+"Valid options:\n"
+" a - Append to the file instead of overwriting it.\n"
+" b - Only save audio to the file while the channel is bridged.\n"
+" Note: does not include conferences.\n"
+" v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
+" V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
+" W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
+" (range -4 to 4)\n\n"
+"<command> will be executed when the recording is over\n"
+"Any strings matching ^{X} will be unescaped to ${X} and \n"
+"all variables will be evaluated at that time.\n"
+"The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
+"";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static const char *mixmonitor_spy_type = "MixMonitor";
+
+struct mixmonitor {
+ struct ast_channel *chan;
+ char *filename;
+ char *post_process;
+ unsigned int flags;
+ int readvol;
+ int writevol;
+};
+
+enum {
+ MUXFLAG_APPEND = (1 << 1),
+ MUXFLAG_BRIDGED = (1 << 2),
+ MUXFLAG_VOLUME = (1 << 3),
+ MUXFLAG_READVOLUME = (1 << 4),
+ MUXFLAG_WRITEVOLUME = (1 << 5),
+} mixmonitor_flags;
+
+enum {
+ OPT_ARG_READVOLUME = 0,
+ OPT_ARG_WRITEVOLUME,
+ OPT_ARG_VOLUME,
+ OPT_ARG_ARRAY_SIZE,
+} mixmonitor_args;
+
+AST_APP_OPTIONS(mixmonitor_opts, {
+ AST_APP_OPTION('a', MUXFLAG_APPEND),
+ AST_APP_OPTION('b', MUXFLAG_BRIDGED),
+ AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
+ AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
+ AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
+});
+
+static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy)
+{
+ /* If our status has changed to DONE, then the channel we're spying on is gone....
+ DON'T TOUCH IT!!! RUN AWAY!!! */
+ if (spy->status == CHANSPY_DONE)
+ return;
+
+ if (!chan)
+ return;
+
+ ast_mutex_lock(&chan->lock);
+ ast_channel_spy_remove(chan, spy);
+ ast_mutex_unlock(&chan->lock);
+}
+
+static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
+{
+ struct ast_channel *peer;
+ int res;
+
+ if (!chan)
+ return -1;
+
+ ast_mutex_lock(&chan->lock);
+ res = ast_channel_spy_add(chan, spy);
+ ast_mutex_unlock(&chan->lock);
+
+ if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
+ ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
+
+ return res;
+}
+
+#define SAMPLES_PER_FRAME 160
+
+static void *mixmonitor_thread(void *obj)
+{
+ struct mixmonitor *mixmonitor = obj;
+ struct ast_channel_spy spy;
+ struct ast_filestream *fs = NULL;
+ char *ext, *name;
+ unsigned int oflags;
+ struct ast_frame *f;
+ char post_process[1024] = "";
+
+ STANDARD_INCREMENT_USECOUNT;
+
+ name = ast_strdupa(mixmonitor->chan->name);
+
+ oflags = O_CREAT|O_WRONLY;
+ oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
+
+ if ((ext = strrchr(mixmonitor->filename, '.'))) {
+ *(ext++) = '\0';
+ } else {
+ ext = "raw";
+ }
+
+ fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644);
+ if (!fs) {
+ ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
+ goto out;
+ }
+
+ if (ast_test_flag(mixmonitor, MUXFLAG_APPEND))
+ ast_seekstream(fs, 0, SEEK_END);
+
+ memset(&spy, 0, sizeof(spy));
+ ast_set_flag(&spy, CHANSPY_FORMAT_AUDIO);
+ ast_set_flag(&spy, CHANSPY_MIXAUDIO);
+ spy.type = mixmonitor_spy_type;
+ spy.status = CHANSPY_RUNNING;
+ spy.read_queue.format = AST_FORMAT_SLINEAR;
+ spy.write_queue.format = AST_FORMAT_SLINEAR;
+ if (mixmonitor->readvol) {
+ ast_set_flag(&spy, CHANSPY_READ_VOLADJUST);
+ spy.read_vol_adjustment = mixmonitor->readvol;
+ }
+ if (mixmonitor->writevol) {
+ ast_set_flag(&spy, CHANSPY_WRITE_VOLADJUST);
+ spy.write_vol_adjustment = mixmonitor->writevol;
+ }
+ ast_mutex_init(&spy.lock);
+
+ if (startmon(mixmonitor->chan, &spy)) {
+ ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
+ spy.type, mixmonitor->chan->name);
+ goto out2;
+ }
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", name);
+
+ if (mixmonitor->post_process) {
+ char *p;
+
+ for (p = mixmonitor->post_process; *p ; p++) {
+ if (*p == '^' && *(p+1) == '{') {
+ *p = '$';
+ }
+ }
+ pbx_substitute_variables_helper(mixmonitor->chan, mixmonitor->post_process, post_process, sizeof(post_process) - 1);
+ }
+
+ while (1) {
+ struct ast_frame *next;
+ int write;
+
+ ast_mutex_lock(&spy.lock);
+
+ ast_channel_spy_trigger_wait(&spy);
+
+ if (ast_check_hangup(mixmonitor->chan) || spy.status != CHANSPY_RUNNING) {
+ ast_mutex_unlock(&spy.lock);
+ break;
+ }
+
+ while (1) {
+ if (!(f = ast_channel_spy_read_frame(&spy, SAMPLES_PER_FRAME)))
+ break;
+
+ write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
+ ast_bridged_channel(mixmonitor->chan));
+
+ /* it is possible for ast_channel_spy_read_frame() to return a chain
+ of frames if a queue flush was necessary, so process them
+ */
+ for (; f; f = next) {
+ next = f->next;
+ if (write)
+ ast_writestream(fs, f);
+ ast_frfree(f);
+ }
+ }
+
+ ast_mutex_unlock(&spy.lock);
+ }
+
+ stopmon(mixmonitor->chan, &spy);
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", name);
+
+ if (!ast_strlen_zero(post_process)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", post_process);
+ ast_safe_system(post_process);
+ }
+
+out2:
+ ast_mutex_destroy(&spy.lock);
+
+ if (fs)
+ ast_closestream(fs);
+
+out:
+ free(mixmonitor);
+
+ STANDARD_DECREMENT_USECOUNT;
+
+ return NULL;
+}
+
+static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
+ int readvol, int writevol, const char *post_process)
+{
+ pthread_attr_t attr;
+ pthread_t thread;
+ struct mixmonitor *mixmonitor;
+ int len;
+
+ len = sizeof(*mixmonitor) + strlen(filename) + 1;
+ if (!ast_strlen_zero(post_process))
+ len += strlen(post_process) + 1;
+
+ if (!(mixmonitor = calloc(1, len))) {
+ ast_log(LOG_ERROR, "Memory Error!\n");
+ return;
+ }
+
+ mixmonitor->chan = chan;
+ mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor);
+ strcpy(mixmonitor->filename, filename);
+ if (!ast_strlen_zero(post_process)) {
+ mixmonitor->post_process = mixmonitor->filename + strlen(filename) + 1;
+ strcpy(mixmonitor->post_process, post_process);
+ }
+ mixmonitor->readvol = readvol;
+ mixmonitor->writevol = writevol;
+ mixmonitor->flags = flags;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&thread, &attr, mixmonitor_thread, mixmonitor);
+ pthread_attr_destroy(&attr);
+}
+
+static int mixmonitor_exec(struct ast_channel *chan, void *data)
+{
+ int x, readvol = 0, writevol = 0;
+ struct localuser *u;
+ struct ast_flags flags = {0};
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(options);
+ AST_APP_ARG(post_process);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.filename)) {
+ ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (args.options) {
+ char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+
+ ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
+
+ if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
+ if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
+ ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
+ } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+ ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
+ } else {
+ readvol = get_volfactor(x);
+ }
+ }
+
+ if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
+ if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
+ ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
+ } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+ ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
+ } else {
+ writevol = get_volfactor(x);
+ }
+ }
+
+ if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
+ if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
+ ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
+ } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+ ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
+ } else {
+ readvol = writevol = get_volfactor(x);
+ }
+ }
+ }
+
+ /* if not provided an absolute path, use the system-configured monitoring directory */
+ if (args.filename[0] != '/') {
+ char *build;
+
+ build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
+ sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
+ args.filename = build;
+ }
+
+ pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
+ launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static int mixmonitor_cli(int fd, int argc, char **argv)
+{
+ struct ast_channel *chan;
+
+ if (argc < 3)
+ return RESULT_SHOWUSAGE;
+
+ if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
+ ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
+ return RESULT_SUCCESS;
+ }
+
+ if (!strcasecmp(argv[1], "start"))
+ mixmonitor_exec(chan, argv[3]);
+ else if (!strcasecmp(argv[1], "stop"))
+ ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
+
+ ast_mutex_unlock(&chan->lock);
+
+ return RESULT_SUCCESS;
+}
+
+
+static struct ast_cli_entry cli_mixmonitor = {
+ { "mixmonitor", NULL, NULL },
+ mixmonitor_cli,
+ "Execute a MixMonitor command",
+ "mixmonitor <start|stop> <chan_name> [<args>]\n"
+};
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_cli_unregister(&cli_mixmonitor);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_cli_register(&cli_mixmonitor);
+ res |= ast_register_application(app, mixmonitor_exec, synopsis, desc);
+
+ return res;
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_mp3.c b/1.2-netsec/apps/app_mp3.c
new file mode 100644
index 000000000..ee78840ec
--- /dev/null
+++ b/1.2-netsec/apps/app_mp3.c
@@ -0,0 +1,260 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Silly application to play an MP3 file -- uses mpg123
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
+#define MPG_123 "/usr/bin/mpg123"
+
+static char *tdesc = "Silly MP3 Application";
+
+static char *app = "MP3Player";
+
+static char *synopsis = "Play an MP3 file or stream";
+
+static char *descrip =
+" MP3Player(location) Executes mpg123 to play the given location,\n"
+"which typically would be a filename or a URL. User can exit by pressing\n"
+"any key on the dialpad, or by hanging up.";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int mp3play(char *filename, int fd)
+{
+ int res;
+ int x;
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res)
+ return res;
+ dup2(fd, STDOUT_FILENO);
+ for (x=0;x<256;x++) {
+ if (x != STDOUT_FILENO)
+ close(x);
+ }
+ /* Execute mpg123, but buffer if it's a net connection */
+ if (!strncasecmp(filename, "http://", 7)) {
+ /* Most commonly installed in /usr/local/bin */
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ }
+ else {
+ /* Most commonly installed in /usr/local/bin */
+ execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ }
+ ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
+ return -1;
+}
+
+static int timed_read(int fd, void *data, int datalen, int timeout)
+{
+ int res;
+ struct pollfd fds[1];
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ res = poll(fds, 1, timeout);
+ if (res < 1) {
+ ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
+ return -1;
+ }
+ return read(fd, data, datalen);
+
+}
+
+static int mp3_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int owriteformat;
+ int timeout = 2000;
+ struct timeval next;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ char offset[AST_FRIENDLY_OFFSET];
+ short frdata[160];
+ } myf;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Unable to create pipe\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ ast_stopstream(chan);
+
+ owriteformat = chan->writeformat;
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ res = mp3play((char *)data, fds[1]);
+ if (!strncasecmp((char *)data, "http://", 7)) {
+ timeout = 10000;
+ }
+ /* Wait 1000 ms first */
+ next = ast_tvnow();
+ next.tv_sec += 1;
+ if (res >= 0) {
+ pid = res;
+ /* Order is important -- there's almost always going to be mp3... we want to prioritize the
+ user */
+ for (;;) {
+ ms = ast_tvdiff_ms(next, ast_tvnow());
+ if (ms <= 0) {
+ res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
+ if (res > 0) {
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_SLINEAR;
+ myf.f.datalen = res;
+ myf.f.samples = res / 2;
+ myf.f.mallocd = 0;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.delivery.tv_sec = 0;
+ myf.f.delivery.tv_usec = 0;
+ myf.f.data = myf.frdata;
+ if (ast_write(chan, &myf.f) < 0) {
+ res = -1;
+ break;
+ }
+ } else {
+ ast_log(LOG_DEBUG, "No more mp3\n");
+ res = 0;
+ break;
+ }
+ next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
+ } else {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ ast_log(LOG_DEBUG, "Hangup detected\n");
+ res = -1;
+ break;
+ }
+ if (ms) {
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_log(LOG_DEBUG, "User pressed a key\n");
+ ast_frfree(f);
+ res = 0;
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+ }
+ }
+ close(fds[0]);
+ close(fds[1]);
+
+ if (pid > -1)
+ kill(pid, SIGKILL);
+ if (!res && owriteformat)
+ ast_set_write_format(chan, owriteformat);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, mp3_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_nbscat.c b/1.2-netsec/apps/app_nbscat.c
new file mode 100644
index 000000000..e1fb921c4
--- /dev/null
+++ b/1.2-netsec/apps/app_nbscat.c
@@ -0,0 +1,241 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Silly application to play an NBScat file -- uses nbscat8k
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
+#define NBSCAT "/usr/bin/nbscat8k"
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+static char *tdesc = "Silly NBS Stream Application";
+
+static char *app = "NBScat";
+
+static char *synopsis = "Play an NBS local stream";
+
+static char *descrip =
+" NBScat: Executes nbscat to listen to the local NBS stream.\n"
+"User can exit by pressing any key\n.";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int NBScatplay(int fd)
+{
+ int res;
+ int x;
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res)
+ return res;
+ dup2(fd, STDOUT_FILENO);
+ for (x=0;x<256;x++) {
+ if (x != STDOUT_FILENO)
+ close(x);
+ }
+ /* Most commonly installed in /usr/local/bin */
+ execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
+ execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
+ ast_log(LOG_WARNING, "Execute of nbscat8k failed\n");
+ return -1;
+}
+
+static int timed_read(int fd, void *data, int datalen)
+{
+ int res;
+ struct pollfd fds[1];
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ res = poll(fds, 1, 2000);
+ if (res < 1) {
+ ast_log(LOG_NOTICE, "Selected timed out/errored out with %d\n", res);
+ return -1;
+ }
+ return read(fd, data, datalen);
+
+}
+
+static int NBScat_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int owriteformat;
+ struct timeval next;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ char offset[AST_FRIENDLY_OFFSET];
+ short frdata[160];
+ } myf;
+
+ LOCAL_USER_ADD(u);
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) {
+ ast_log(LOG_WARNING, "Unable to create socketpair\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ ast_stopstream(chan);
+
+ owriteformat = chan->writeformat;
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ res = NBScatplay(fds[1]);
+ /* Wait 1000 ms first */
+ next = ast_tvnow();
+ next.tv_sec += 1;
+ if (res >= 0) {
+ pid = res;
+ /* Order is important -- there's almost always going to be mp3... we want to prioritize the
+ user */
+ for (;;) {
+ ms = ast_tvdiff_ms(next, ast_tvnow());
+ if (ms <= 0) {
+ res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata));
+ if (res > 0) {
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_SLINEAR;
+ myf.f.datalen = res;
+ myf.f.samples = res / 2;
+ myf.f.mallocd = 0;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.delivery.tv_sec = 0;
+ myf.f.delivery.tv_usec = 0;
+ myf.f.data = myf.frdata;
+ if (ast_write(chan, &myf.f) < 0) {
+ res = -1;
+ break;
+ }
+ } else {
+ ast_log(LOG_DEBUG, "No more mp3\n");
+ res = 0;
+ break;
+ }
+ next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
+ } else {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ ast_log(LOG_DEBUG, "Hangup detected\n");
+ res = -1;
+ break;
+ }
+ if (ms) {
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_log(LOG_DEBUG, "User pressed a key\n");
+ ast_frfree(f);
+ res = 0;
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+ }
+ }
+ close(fds[0]);
+ close(fds[1]);
+
+ if (pid > -1)
+ kill(pid, SIGKILL);
+ if (!res && owriteformat)
+ ast_set_write_format(chan, owriteformat);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, NBScat_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_osplookup.c b/1.2-netsec/apps/app_osplookup.c
new file mode 100644
index 000000000..2fe2d016a
--- /dev/null
+++ b/1.2-netsec/apps/app_osplookup.c
@@ -0,0 +1,375 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Open Settlement Protocol Lookup
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astosp.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "OSP Lookup";
+
+static char *app = "OSPLookup";
+static char *app2 = "OSPNext";
+static char *app3 = "OSPFinish";
+
+static char *synopsis = "Lookup number in OSP";
+static char *synopsis2 = "Lookup next OSP entry";
+static char *synopsis3 = "Record OSP entry";
+
+static char *descrip =
+" OSPLookup(exten[|provider[|options]]): Looks up an extension via OSP and sets\n"
+"the variables, where 'n' is the number of the result beginning with 1:\n"
+" ${OSPTECH}: The technology to use for the call\n"
+" ${OSPDEST}: The destination to use for the call\n"
+" ${OSPTOKEN}: The actual OSP token as a string\n"
+" ${OSPHANDLE}: The OSP Handle for anything remaining\n"
+" ${OSPRESULTS}: The number of OSP results total remaining\n"
+"\n"
+"The option string may contain the following character:\n"
+" 'j' -- jump to n+101 priority if the lookup was NOT successful\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPLOOKUPSTATUS The status of the OSP Lookup attempt as a text string, one of\n"
+" SUCCESS | FAILED \n";
+
+
+static char *descrip2 =
+" OSPNext(cause[|options]): Looks up the next OSP Destination for ${OSPHANDLE}\n"
+"See OSPLookup for more information\n"
+"\n"
+"The option string may contain the following character:\n"
+" 'j' -- jump to n+101 priority if the lookup was NOT successful\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPNEXTSTATUS The status of the OSP Next attempt as a text string, one of\n"
+" SUCCESS | FAILED \n";
+
+static char *descrip3 =
+" OSPFinish(status[|options]): Records call state for ${OSPHANDLE}, according to\n"
+"status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or CHANUNAVAIL\n"
+"or coincidentally, just what the Dial application stores in its ${DIALSTATUS}.\n"
+"\n"
+"The option string may contain the following character:\n"
+" 'j' -- jump to n+101 priority if the finish attempt was NOT successful\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPFINISHSTATUS The status of the OSP Finish attempt as a text string, one of\n"
+" SUCCESS | FAILED \n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int str2cause(char *cause)
+{
+ if (!strcasecmp(cause, "BUSY"))
+ return AST_CAUSE_BUSY;
+ if (!strcasecmp(cause, "CONGESTION"))
+ return AST_CAUSE_CONGESTION;
+ if (!strcasecmp(cause, "ANSWER"))
+ return AST_CAUSE_NORMAL;
+ if (!strcasecmp(cause, "CANCEL"))
+ return AST_CAUSE_NORMAL;
+ if (!strcasecmp(cause, "NOANSWER"))
+ return AST_CAUSE_NOANSWER;
+ if (!strcasecmp(cause, "NOCHANAVAIL"))
+ return AST_CAUSE_CONGESTION;
+ ast_log(LOG_WARNING, "Unknown cause '%s', using NORMAL\n", cause);
+ return AST_CAUSE_NORMAL;
+}
+
+static int osplookup_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *temp;
+ struct ast_osp_result result;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(extension);
+ AST_APP_ARG(provider);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "OSPLookup requires an argument OSPLookup(exten[|provider[|options]])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ temp = ast_strdupa(data);
+ if (!temp) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, temp);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ ast_log(LOG_DEBUG, "Whoo hoo, looking up OSP on '%s' via '%s'\n", args.extension, args.provider ? args.provider : "<default>");
+ if ((res = ast_osp_lookup(chan, args.provider, args.extension, chan->cid.cid_num, &result)) > 0) {
+ char tmp[80];
+ snprintf(tmp, sizeof(tmp), "%d", result.handle);
+ pbx_builtin_setvar_helper(chan, "_OSPHANDLE", tmp);
+ pbx_builtin_setvar_helper(chan, "_OSPTECH", result.tech);
+ pbx_builtin_setvar_helper(chan, "_OSPDEST", result.dest);
+ pbx_builtin_setvar_helper(chan, "_OSPTOKEN", result.token);
+ snprintf(tmp, sizeof(tmp), "%d", result.numresults);
+ pbx_builtin_setvar_helper(chan, "_OSPRESULTS", tmp);
+ pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", "SUCCESS");
+
+ } else {
+ if (!res) {
+ ast_log(LOG_NOTICE, "OSP Lookup failed for '%s' (provider '%s')\n", args.extension, args.provider ? args.provider : "<default>");
+ pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", "FAILED");
+ } else
+ ast_log(LOG_DEBUG, "Got hangup on '%s' while doing OSP Lookup for '%s' (provider '%s')!\n", chan->name, args.extension, args.provider ? args.provider : "<default>" );
+ }
+ if (!res) {
+ /* Look for a "busy" place */
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ } else if (res > 0)
+ res = 0;
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int ospnext_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *temp;
+ int cause;
+ struct ast_osp_result result;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(cause);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "OSPNext should have an argument (cause[|options])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ temp = ast_strdupa(data);
+ if (!temp) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, temp);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ cause = str2cause(args.cause);
+ temp = pbx_builtin_getvar_helper(chan, "OSPHANDLE");
+ result.handle = -1;
+ if (!ast_strlen_zero(temp) && (sscanf(temp, "%d", &result.handle) == 1) && (result.handle > -1)) {
+ temp = pbx_builtin_getvar_helper(chan, "OSPRESULTS");
+ if (ast_strlen_zero(temp) || (sscanf(temp, "%d", &result.numresults) != 1)) {
+ result.numresults = 0;
+ }
+ if ((res = ast_osp_next(&result, cause)) > 0) {
+ char tmp[80];
+ snprintf(tmp, sizeof(tmp), "%d", result.handle);
+ pbx_builtin_setvar_helper(chan, "_OSPHANDLE", tmp);
+ pbx_builtin_setvar_helper(chan, "_OSPTECH", result.tech);
+ pbx_builtin_setvar_helper(chan, "_OSPDEST", result.dest);
+ pbx_builtin_setvar_helper(chan, "_OSPTOKEN", result.token);
+ snprintf(tmp, sizeof(tmp), "%d", result.numresults);
+ pbx_builtin_setvar_helper(chan, "_OSPRESULTS", tmp);
+ pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", "SUCCESS");
+ }
+ } else {
+ if (!res) {
+ if (result.handle < 0)
+ ast_log(LOG_NOTICE, "OSP Lookup Next failed for handle '%d'\n", result.handle);
+ else
+ ast_log(LOG_DEBUG, "No OSP handle specified\n");
+ pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", "FAILED");
+ } else
+ ast_log(LOG_DEBUG, "Got hangup on '%s' while doing OSP Next!\n", chan->name);
+ }
+ if (!res) {
+ /* Look for a "busy" place */
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ } else if (res > 0)
+ res = 0;
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int ospfinished_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *temp;
+ int cause;
+ time_t start=0, duration=0;
+ struct ast_osp_result result;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(status);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "OSPFinish should have an argument (status[|options])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ temp = ast_strdupa(data);
+ if (!temp) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, temp);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (chan->cdr) {
+ start = chan->cdr->answer.tv_sec;
+ if (start)
+ duration = time(NULL) - start;
+ else
+ duration = 0;
+ } else
+ ast_log(LOG_WARNING, "OSPFinish called on channel '%s' with no CDR!\n", chan->name);
+
+ cause = str2cause(args.status);
+ temp = pbx_builtin_getvar_helper(chan, "OSPHANDLE");
+ result.handle = -1;
+ if (!ast_strlen_zero(temp) && (sscanf(temp, "%d", &result.handle) == 1) && (result.handle > -1)) {
+ if (!ast_osp_terminate(result.handle, cause, start, duration)) {
+ pbx_builtin_setvar_helper(chan, "_OSPHANDLE", "");
+ pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", "SUCCESS");
+ res = 1;
+ }
+ } else {
+ if (!res) {
+ if (result.handle > -1)
+ ast_log(LOG_NOTICE, "OSP Finish failed for handle '%d'\n", result.handle);
+ else
+ ast_log(LOG_DEBUG, "No OSP handle specified\n");
+ pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", "FAILED");
+ } else
+ ast_log(LOG_DEBUG, "Got hangup on '%s' while doing OSP Terminate!\n", chan->name);
+ }
+ if (!res) {
+ /* Look for a "busy" place */
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ } else if (res > 0)
+ res = 0;
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app3);
+ res |= ast_unregister_application(app2);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app, osplookup_exec, synopsis, descrip);
+ res |= ast_register_application(app2, ospnext_exec, synopsis2, descrip2);
+ res |= ast_register_application(app3, ospfinished_exec, synopsis3, descrip3);
+
+ return res;
+}
+
+int reload(void)
+{
+ return 0;
+}
+
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_page.c b/1.2-netsec/apps/app_page.c
new file mode 100644
index 000000000..7826d285b
--- /dev/null
+++ b/1.2-netsec/apps/app_page.c
@@ -0,0 +1,234 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005 Digium, Inc. All rights reserved.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * This code is released under the GNU General Public License
+ * version 2.0. See LICENSE for more information.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ *
+ * \brief page() - Paging application
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/chanvars.h"
+
+
+static const char *tdesc = "Page Multiple Phones";
+
+static const char *app_page= "Page";
+
+static const char *page_synopsis = "Pages phones";
+
+static const char *page_descrip =
+"Page(Technology/Resource&Technology2/Resource2[|options])\n"
+" Places outbound calls to the given technology / resource and dumps\n"
+"them into a conference bridge as muted participants. The original\n"
+"caller is dumped into the conference as a speaker and the room is\n"
+"destroyed when the original caller leaves. Valid options are:\n"
+" d - full duplex audio\n"
+" q - quiet, do not play beep to caller\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+enum {
+ PAGE_DUPLEX = (1 << 0),
+ PAGE_QUIET = (1 << 1),
+} page_opt_flags;
+
+AST_APP_OPTIONS(page_opts, {
+ AST_APP_OPTION('d', PAGE_DUPLEX),
+ AST_APP_OPTION('q', PAGE_QUIET),
+});
+
+struct calloutdata {
+ char cidnum[64];
+ char cidname[64];
+ char tech[64];
+ char resource[256];
+ char meetmeopts[64];
+ struct ast_variable *variables;
+};
+
+static void *page_thread(void *data)
+{
+ struct calloutdata *cd = data;
+ ast_pbx_outgoing_app(cd->tech, AST_FORMAT_SLINEAR, cd->resource, 30000,
+ "MeetMe", cd->meetmeopts, NULL, 0, cd->cidnum, cd->cidname, cd->variables, NULL, NULL);
+ free(cd);
+ return NULL;
+}
+
+static void launch_page(struct ast_channel *chan, const char *meetmeopts, const char *tech, const char *resource)
+{
+ struct calloutdata *cd;
+ const char *varname;
+ struct ast_variable *lastvar = NULL;
+ struct ast_var_t *varptr;
+ pthread_t t;
+ pthread_attr_t attr;
+ cd = malloc(sizeof(struct calloutdata));
+ if (cd) {
+ memset(cd, 0, sizeof(struct calloutdata));
+ ast_copy_string(cd->cidnum, chan->cid.cid_num ? chan->cid.cid_num : "", sizeof(cd->cidnum));
+ ast_copy_string(cd->cidname, chan->cid.cid_name ? chan->cid.cid_name : "", sizeof(cd->cidname));
+ ast_copy_string(cd->tech, tech, sizeof(cd->tech));
+ ast_copy_string(cd->resource, resource, sizeof(cd->resource));
+ ast_copy_string(cd->meetmeopts, meetmeopts, sizeof(cd->meetmeopts));
+
+ AST_LIST_TRAVERSE(&chan->varshead, varptr, entries) {
+ if (!(varname = ast_var_full_name(varptr)))
+ continue;
+ if (varname[0] == '_') {
+ struct ast_variable *newvar = NULL;
+
+ if (varname[1] == '_') {
+ newvar = ast_variable_new(varname, ast_var_value(varptr));
+ } else {
+ newvar = ast_variable_new(&varname[1], ast_var_value(varptr));
+ }
+
+ if (newvar) {
+ if (lastvar)
+ lastvar->next = newvar;
+ else
+ cd->variables = newvar;
+ lastvar = newvar;
+ }
+ }
+ }
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&t, &attr, page_thread, cd)) {
+ ast_log(LOG_WARNING, "Unable to create paging thread: %s\n", strerror(errno));
+ free(cd);
+ }
+ }
+}
+
+static int page_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *options;
+ char *tech, *resource;
+ char meetmeopts[80];
+ struct ast_flags flags = { 0 };
+ unsigned int confid = rand();
+ struct ast_app *app;
+ char *tmp;
+ int res=0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(app = pbx_findapp("MeetMe"))) {
+ ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ };
+
+ options = ast_strdupa(data);
+ if (!options) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ tmp = strsep(&options, "|");
+ if (options)
+ ast_app_parse_options(page_opts, &flags, NULL, options);
+
+ snprintf(meetmeopts, sizeof(meetmeopts), "%ud|%sqxdw", confid, ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m");
+ while ((tech = strsep(&tmp, "&"))) {
+ if ((resource = strchr(tech, '/'))) {
+ *resource++ = '\0';
+ launch_page(chan, meetmeopts, tech, resource);
+ } else {
+ ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
+ }
+ }
+ if (!ast_test_flag(&flags, PAGE_QUIET)) {
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ }
+ if (!res) {
+ snprintf(meetmeopts, sizeof(meetmeopts), "%ud|A%sqxd", confid, ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "t");
+ pbx_exec(chan, app, meetmeopts, 1);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return -1;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_page);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app_page, page_exec, page_synopsis, page_descrip);
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_parkandannounce.c b/1.2-netsec/apps/app_parkandannounce.c
new file mode 100644
index 000000000..bb7aa38b0
--- /dev/null
+++ b/1.2-netsec/apps/app_parkandannounce.c
@@ -0,0 +1,281 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Author: Ben Miller <bgmiller@dccinc.com>
+ * With TONS of help from Mark!
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief ParkAndAnnounce application for Asterisk
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/features.h"
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+
+static char *tdesc = "Call Parking and Announce Application";
+
+static char *app = "ParkAndAnnounce";
+
+static char *synopsis = "Park and Announce";
+
+static char *descrip =
+" ParkAndAnnounce(announce:template|timeout|dial|[return_context]):\n"
+"Park a call into the parkinglot and announce the call over the console.\n"
+"announce template: colon separated list of files to announce, the word PARKED\n"
+" will be replaced by a say_digits of the ext the call is parked in\n"
+"timeout: time in seconds before the call returns into the return context.\n"
+"dial: The app_dial style resource to call to make the announcement. Console/dsp calls the console.\n"
+"return_context: the goto style label to jump the call back into after timeout. default=prio+1\n";
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int parkandannounce_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ char *return_context;
+ int l, lot, timeout = 0, dres;
+ char *working, *context, *exten, *priority, *dial, *dialtech, *dialstr;
+ char *template, *tpl_working, *tpl_current;
+ char *tmp[100];
+ int looptemp=0,i=0;
+ char *s,*orig_s;
+
+ struct ast_channel *dchan;
+ struct outgoing_helper oh;
+ int outstate;
+
+ struct localuser *u;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ l=strlen(data)+2;
+ orig_s=malloc(l);
+ if(!orig_s) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ s=orig_s;
+ strncpy(s,data,l);
+
+ template=strsep(&s,"|");
+ if(! template) {
+ ast_log(LOG_WARNING, "PARK: An announce template must be defined\n");
+ free(orig_s);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if(s) {
+ timeout = atoi(strsep(&s, "|"));
+ timeout *= 1000;
+ }
+ dial=strsep(&s, "|");
+ if(!dial) {
+ ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
+ free(orig_s);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ } else {
+ dialtech=strsep(&dial, "/");
+ dialstr=dial;
+ ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr);
+ }
+
+ return_context = s;
+
+ if(return_context != NULL) {
+ /* set the return context. Code borrowed from the Goto builtin */
+
+ working = return_context;
+ context = strsep(&working, "|");
+ exten = strsep(&working, "|");
+ if(!exten) {
+ /* Only a priority in this one */
+ priority = context;
+ exten = NULL;
+ context = NULL;
+ } else {
+ priority = strsep(&working, "|");
+ if(!priority) {
+ /* Only an extension and priority in this one */
+ priority = exten;
+ exten = context;
+ context = NULL;
+ }
+ }
+ if(atoi(priority) < 0) {
+ ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
+ free(orig_s);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ /* At this point we have a priority and maybe an extension and a context */
+ chan->priority = atoi(priority);
+ if(exten && strcasecmp(exten, "BYEXTENSION"))
+ strncpy(chan->exten, exten, sizeof(chan->exten)-1);
+ if(context)
+ strncpy(chan->context, context, sizeof(chan->context)-1);
+ } else { /* increment the priority by default*/
+ chan->priority++;
+ }
+
+ if(option_verbose > 2) {
+ ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num);
+ if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
+ ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
+ }
+ }
+
+ /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
+ before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
+
+ ast_masq_park_call(chan, NULL, timeout, &lot);
+
+ res=-1;
+
+ ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
+
+ /* Now place the call to the extention */
+
+ memset(&oh, 0, sizeof(oh));
+ oh.parent_channel = chan;
+ dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
+
+ if(dchan) {
+ if(dchan->_state == AST_STATE_UP) {
+ if(option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", dchan->name);
+ } else {
+ if(option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", dchan->name);
+ ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
+ ast_hangup(dchan);
+ free(orig_s);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
+ free(orig_s);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ ast_stopstream(dchan);
+
+ /* now we have the call placed and are ready to play stuff to it */
+
+ ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template);
+
+ tpl_working = template;
+ tpl_current=strsep(&tpl_working, ":");
+
+ while(tpl_current && looptemp < sizeof(tmp)) {
+ tmp[looptemp]=tpl_current;
+ looptemp++;
+ tpl_current=strsep(&tpl_working,":");
+ }
+
+ for(i=0; i<looptemp; i++) {
+ ast_verbose(VERBOSE_PREFIX_4 "Announce:%s\n", tmp[i]);
+ if(!strcmp(tmp[i], "PARKED")) {
+ ast_say_digits(dchan, lot, "", dchan->language);
+ } else {
+ dres = ast_streamfile(dchan, tmp[i], dchan->language);
+ if(!dres) {
+ dres = ast_waitstream(dchan, "");
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
+ dres = 0;
+ }
+ }
+ }
+
+ ast_stopstream(dchan);
+ ast_hangup(dchan);
+ free(orig_s);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ /* return ast_register_application(app, park_exec); */
+ return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_playback.c b/1.2-netsec/apps/app_playback.c
new file mode 100644
index 000000000..ed33097ab
--- /dev/null
+++ b/1.2-netsec/apps/app_playback.c
@@ -0,0 +1,180 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Trivial application to playback a sound file
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Sound File Playback Application";
+
+static char *app = "Playback";
+
+static char *synopsis = "Play a file";
+
+static char *descrip =
+" Playback(filename[&filename2...][|option]): Plays back given filenames (do not put\n"
+"extension). Options may also be included following a pipe symbol. The 'skip'\n"
+"option causes the playback of the message to be skipped if the channel\n"
+"is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
+"specified, the application will return immediately should the channel not be\n"
+"off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
+"be answered before the sound is played. Not all channels support playing\n"
+"messages while still on hook. If 'j' is specified, the application\n"
+"will jump to priority n+101 if present when a file specified to be played\n"
+"does not exist.\n"
+"This application sets the following channel variable upon completion:\n"
+" PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
+" SUCCESS | FAILED\n"
+;
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int playback_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0, mres = 0;
+ struct localuser *u;
+ char *tmp = NULL;
+ int option_skip=0;
+ int option_noanswer = 0;
+ char *front = NULL, *back = NULL;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filenames);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ tmp = ast_strdupa(data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (args.options) {
+ if (strcasestr(args.options, "skip"))
+ option_skip = 1;
+ if (strcasestr(args.options, "noanswer"))
+ option_noanswer = 1;
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ if (option_skip) {
+ /* At the user's option, skip if the line is not up */
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ } else if (!option_noanswer)
+ /* Otherwise answer unless we're supposed to send this while on-hook */
+ res = ast_answer(chan);
+ }
+ if (!res) {
+ ast_stopstream(chan);
+ front = tmp;
+ while (!res && front) {
+ if ((back = strchr(front, '&'))) {
+ *back = '\0';
+ back++;
+ }
+ res = ast_streamfile(chan, front, chan->language);
+ if (!res) {
+ res = ast_waitstream(chan, "");
+ ast_stopstream(chan);
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ res = 0;
+ mres = 1;
+ }
+ front = back;
+ }
+ if (mres)
+ pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", "FAILED");
+ else
+ pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", "SUCCESS");
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, playback_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_privacy.c b/1.2-netsec/apps/app_privacy.c
new file mode 100644
index 000000000..04ebf247b
--- /dev/null
+++ b/1.2-netsec/apps/app_privacy.c
@@ -0,0 +1,254 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Block all calls without Caller*ID, require phone # to be entered
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/utils.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+
+#define PRIV_CONFIG "privacy.conf"
+
+static char *tdesc = "Require phone number to be entered, if no CallerID sent";
+
+static char *app = "PrivacyManager";
+
+static char *synopsis = "Require phone number to be entered, if no CallerID sent";
+
+static char *descrip =
+ " PrivacyManager([maxretries[|minlength[|options]]]): If no Caller*ID \n"
+ "is sent, PrivacyManager answers the channel and asks the caller to\n"
+ "enter their phone number. The caller is given 3 attempts to do so.\n"
+ "The application does nothing if Caller*ID was received on the channel.\n"
+ " Configuration file privacy.conf contains two variables:\n"
+ " maxretries default 3 -maximum number of attempts the caller is allowed \n"
+ " to input a callerid.\n"
+ " minlength default 10 -minimum allowable digits in the input callerid number.\n"
+ "If you don't want to use the config file and have an i/o operation with\n"
+ "every call, you can also specify maxretries and minlength as application\n"
+ "parameters. Doing so supercedes any values set in privacy.conf.\n"
+ "The option string may contain the following character: \n"
+ " 'j' -- jump to n+101 priority after <maxretries> failed attempts to collect\n"
+ " the minlength number of digits.\n"
+ "The application sets the following channel variable upon completion: \n"
+ "PRIVACYMGRSTATUS The status of the privacy manager's attempt to collect \n"
+ " a phone number from the user. A text string that is either:\n"
+ " SUCCESS | FAILED \n"
+;
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+
+
+static int privacy_exec (struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int retries;
+ int maxretries = 3;
+ int minlength = 10;
+ int x = 0;
+ char *s;
+ char phone[30];
+ struct localuser *u;
+ struct ast_config *cfg = NULL;
+ char *parse = NULL;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(maxretries);
+ AST_APP_ARG(minlength);
+ AST_APP_ARG(options);
+ );
+
+ LOCAL_USER_ADD (u);
+ if (!ast_strlen_zero(chan->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "CallerID Present: Skipping\n");
+ } else {
+ /*Answer the channel if it is not already*/
+ if (chan->_state != AST_STATE_UP) {
+ res = ast_answer(chan);
+ if (res) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ if (!ast_strlen_zero((char *)data))
+ {
+ parse = ast_strdupa(data);
+ if (!parse) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.maxretries) {
+ if (sscanf(args.maxretries, "%d", &x) == 1)
+ maxretries = x;
+ else
+ ast_log(LOG_WARNING, "Invalid max retries argument\n");
+ }
+ if (args.minlength) {
+ if (sscanf(args.minlength, "%d", &x) == 1)
+ minlength = x;
+ else
+ ast_log(LOG_WARNING, "Invalid min length argument\n");
+ }
+ if (args.options)
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+
+ }
+
+ if (!x)
+ {
+ /*Read in the config file*/
+ cfg = ast_config_load(PRIV_CONFIG);
+
+ if (cfg && (s = ast_variable_retrieve(cfg, "general", "maxretries"))) {
+ if (sscanf(s, "%d", &x) == 1)
+ maxretries = x;
+ else
+ ast_log(LOG_WARNING, "Invalid max retries argument\n");
+ }
+
+ if (cfg && (s = ast_variable_retrieve(cfg, "general", "minlength"))) {
+ if (sscanf(s, "%d", &x) == 1)
+ minlength = x;
+ else
+ ast_log(LOG_WARNING, "Invalid min length argument\n");
+ }
+ }
+
+ /*Play unidentified call*/
+ res = ast_safe_sleep(chan, 1000);
+ if (!res)
+ res = ast_streamfile(chan, "privacy-unident", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+
+ /*Ask for 10 digit number, give 3 attempts*/
+ for (retries = 0; retries < maxretries; retries++) {
+ if (!res)
+ res = ast_streamfile(chan, "privacy-prompt", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+
+ if (!res )
+ res = ast_readstring(chan, phone, sizeof(phone) - 1, /* digit timeout ms */ 3200, /* first digit timeout */ 5000, "#");
+
+ if (res < 0)
+ break;
+
+ /*Make sure we get at least digits*/
+ if (strlen(phone) >= minlength )
+ break;
+ else {
+ res = ast_streamfile(chan, "privacy-incorrect", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ }
+ }
+
+ /*Got a number, play sounds and send them on their way*/
+ if ((retries < maxretries) && !res ) {
+ res = ast_streamfile(chan, "privacy-thankyou", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_set_callerid (chan, phone, "Privacy Manager", NULL);
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s\n",phone);
+ pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "SUCCESS");
+ } else {
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED");
+ }
+ if (cfg)
+ ast_config_destroy(cfg);
+ }
+
+ LOCAL_USER_REMOVE (u);
+ return 0;
+}
+
+int
+unload_module (void)
+{
+ int res;
+
+ res = ast_unregister_application (app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int
+load_module (void)
+{
+ return ast_register_application (app, privacy_exec, synopsis,
+ descrip);
+}
+
+char *
+description (void)
+{
+ return tdesc;
+}
+
+int
+usecount (void)
+{
+ int res;
+ STANDARD_USECOUNT (res);
+ return res;
+}
+
+char *
+key ()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_queue.c b/1.2-netsec/apps/app_queue.c
new file mode 100644
index 000000000..505ac2f85
--- /dev/null
+++ b/1.2-netsec/apps/app_queue.c
@@ -0,0 +1,3872 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief True call queues with optional send URL on answer
+ *
+ * \arg Config in \ref Config_qu queues.conf
+ *
+ * \par Development notes
+ * \note 2004-11-25: Persistent Dynamic Members added by:
+ * NetNation Communications (www.netnation.com)
+ * Kevin Lindsay <kevinl@netnation.com>
+ *
+ * Each dynamic agent in each queue is now stored in the astdb.
+ * When asterisk is restarted, each agent will be automatically
+ * readded into their recorded queues. This feature can be
+ * configured with the 'persistent_members=<1|0>' setting in the
+ * '[general]' category in queues.conf. The default is on.
+ *
+ * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
+ *
+ * \note These features added by David C. Troy <dave@toad.net>:
+ * - Per-queue holdtime calculation
+ * - Estimated holdtime announcement
+ * - Position announcement
+ * - Abandoned/completed call counters
+ * - Failout timer passed as optional app parameter
+ * - Optional monitoring of calls, started when call is answered
+ *
+ * Patch Version 1.07 2003-12-24 01
+ *
+ * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
+ * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
+ *
+ * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
+ * by Matthew Enger <m.enger@xi.com.au>
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/say.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/config.h"
+#include "asterisk/monitor.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astdb.h"
+#include "asterisk/devicestate.h"
+
+#define QUEUE_STRATEGY_RINGALL 0
+#define QUEUE_STRATEGY_ROUNDROBIN 1
+#define QUEUE_STRATEGY_LEASTRECENT 2
+#define QUEUE_STRATEGY_FEWESTCALLS 3
+#define QUEUE_STRATEGY_RANDOM 4
+#define QUEUE_STRATEGY_RRMEMORY 5
+
+static struct strategy {
+ int strategy;
+ char *name;
+} strategies[] = {
+ { QUEUE_STRATEGY_RINGALL, "ringall" },
+ { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
+ { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
+ { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
+ { QUEUE_STRATEGY_RANDOM, "random" },
+ { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
+};
+
+#define DEFAULT_RETRY 5
+#define DEFAULT_TIMEOUT 15
+#define RECHECK 1 /* Recheck every second to see we we're at the top yet */
+
+#define RES_OKAY 0 /* Action completed */
+#define RES_EXISTS (-1) /* Entry already exists */
+#define RES_OUTOFMEMORY (-2) /* Out of memory */
+#define RES_NOSUCHQUEUE (-3) /* No such queue */
+
+static char *tdesc = "True Call Queueing";
+
+static char *app = "Queue";
+
+static char *synopsis = "Queue a call for a call queue";
+
+static char *descrip =
+" Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
+"Queues an incoming call in a particular call queue as defined in queues.conf.\n"
+"This application will return to the dialplan if the queue does not exist, or\n"
+"any of the join options cause the caller to not enter the queue.\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'd' -- data-quality (modem) call (minimum delay).\n"
+" 'h' -- allow callee to hang up by hitting *.\n"
+" 'H' -- allow caller to hang up by hitting *.\n"
+" 'n' -- no retries on the timeout; will exit this application and \n"
+" go to the next step.\n"
+" 'r' -- ring instead of playing MOH\n"
+" 't' -- allow the called user transfer the calling user\n"
+" 'T' -- to allow the calling user to transfer the call.\n"
+" 'w' -- allow the called user to write the conversation to disk via Monitor\n"
+" 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
+" In addition to transferring the call, a call may be parked and then picked\n"
+"up by another user.\n"
+" The optional URL will be sent to the called party if the channel supports\n"
+"it.\n"
+" The timeout will cause the queue to fail out after a specified number of\n"
+"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
+" This application sets the following channel variable upon completion:\n"
+" QUEUESTATUS The status of the call as a text string, one of\n"
+" TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
+
+static char *app_aqm = "AddQueueMember" ;
+static char *app_aqm_synopsis = "Dynamically adds queue members" ;
+static char *app_aqm_descrip =
+" AddQueueMember(queuename[|interface[|penalty[|options]]]):\n"
+"Dynamically adds interface to an existing queue.\n"
+"If the interface is already in the queue and there exists an n+101 priority\n"
+"then it will then jump to this priority. Otherwise it will return an error\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'j' -- jump to +101 priority when appropriate.\n"
+" This application sets the following channel variable upon completion:\n"
+" AQMSTATUS The status of the attempt to add a queue member as a \n"
+" text string, one of\n"
+" ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
+"Example: AddQueueMember(techsupport|SIP/3000)\n"
+"";
+
+static char *app_rqm = "RemoveQueueMember" ;
+static char *app_rqm_synopsis = "Dynamically removes queue members" ;
+static char *app_rqm_descrip =
+" RemoveQueueMember(queuename[|interface[|options]]):\n"
+"Dynamically removes interface to an existing queue\n"
+"If the interface is NOT in the queue and there exists an n+101 priority\n"
+"then it will then jump to this priority. Otherwise it will return an error\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'j' -- jump to +101 priority when appropriate.\n"
+" This application sets the following channel variable upon completion:\n"
+" RQMSTATUS The status of the attempt to remove a queue member as a\n"
+" text string, one of\n"
+" REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
+"Example: RemoveQueueMember(techsupport|SIP/3000)\n"
+"";
+
+static char *app_pqm = "PauseQueueMember" ;
+static char *app_pqm_synopsis = "Pauses a queue member" ;
+static char *app_pqm_descrip =
+" PauseQueueMember([queuename]|interface[|options]):\n"
+"Pauses (blocks calls for) a queue member.\n"
+"The given interface will be paused in the given queue. This prevents\n"
+"any calls from being sent from the queue to the interface until it is\n"
+"unpaused with UnpauseQueueMember or the manager interface. If no\n"
+"queuename is given, the interface is paused in every queue it is a\n"
+"member of. If the interface is not in the named queue, or if no queue\n"
+"is given and the interface is not in any queue, it will jump to\n"
+"priority n+101, if it exists and the appropriate options are set.\n"
+"The application will fail if the interface is not found and no extension\n"
+"to jump to exists.\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'j' -- jump to +101 priority when appropriate.\n"
+" This application sets the following channel variable upon completion:\n"
+" PQMSTATUS The status of the attempt to pause a queue member as a\n"
+" text string, one of\n"
+" PAUSED | NOTFOUND\n"
+"Example: PauseQueueMember(|SIP/3000)\n";
+
+static char *app_upqm = "UnpauseQueueMember" ;
+static char *app_upqm_synopsis = "Unpauses a queue member" ;
+static char *app_upqm_descrip =
+" UnpauseQueueMember([queuename]|interface[|options]):\n"
+"Unpauses (resumes calls to) a queue member.\n"
+"This is the counterpart to PauseQueueMember and operates exactly the\n"
+"same way, except it unpauses instead of pausing the given interface.\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'j' -- jump to +101 priority when appropriate.\n"
+" This application sets the following channel variable upon completion:\n"
+" UPQMSTATUS The status of the attempt to unpause a queue \n"
+" member as a text string, one of\n"
+" UNPAUSED | NOTFOUND\n"
+"Example: UnpauseQueueMember(|SIP/3000)\n";
+
+/*! \brief Persistent Members astdb family */
+static const char *pm_family = "/Queue/PersistentMembers";
+/* The maximum lengh of each persistent member queue database entry */
+#define PM_MAX_LEN 2048
+
+/*! \brief queues.conf [general] option */
+static int queue_persistent_members = 0;
+
+/*! \brief queues.conf per-queue weight option */
+static int use_weight = 0;
+
+enum queue_result {
+ QUEUE_UNKNOWN = 0,
+ QUEUE_TIMEOUT = 1,
+ QUEUE_JOINEMPTY = 2,
+ QUEUE_LEAVEEMPTY = 3,
+ QUEUE_JOINUNAVAIL = 4,
+ QUEUE_LEAVEUNAVAIL = 5,
+ QUEUE_FULL = 6,
+};
+
+const struct {
+ enum queue_result id;
+ char *text;
+} queue_results[] = {
+ { QUEUE_UNKNOWN, "UNKNOWN" },
+ { QUEUE_TIMEOUT, "TIMEOUT" },
+ { QUEUE_JOINEMPTY,"JOINEMPTY" },
+ { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
+ { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
+ { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
+ { QUEUE_FULL, "FULL" },
+};
+
+/*! \brief We define a custom "local user" structure because we
+ use it not only for keeping track of what is in use but
+ also for keeping track of who we're dialing. */
+
+struct localuser {
+ struct ast_channel *chan;
+ char interface[256];
+ int stillgoing;
+ int metric;
+ int oldstatus;
+ time_t lastcall;
+ struct member *member;
+ struct localuser *next;
+};
+
+LOCAL_USER_DECL;
+
+
+struct queue_ent {
+ struct ast_call_queue *parent; /*!< What queue is our parent */
+ char moh[80]; /*!< Name of musiconhold to be used */
+ char announce[80]; /*!< Announcement to play for member when call is answered */
+ char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
+ char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
+ int pos; /*!< Where we are in the queue */
+ int prio; /*!< Our priority */
+ int last_pos_said; /*!< Last position we told the user */
+ time_t last_periodic_announce_time; /*!< The last time we played a periodic anouncement */
+ time_t last_pos; /*!< Last time we told the user their position */
+ int opos; /*!< Where we started in the queue */
+ int handled; /*!< Whether our call was handled */
+ time_t start; /*!< When we started holding */
+ time_t expire; /*!< When this entry should expire (time out of queue) */
+ struct ast_channel *chan; /*!< Our channel */
+ struct queue_ent *next; /*!< The next queue entry */
+};
+
+struct member {
+ char interface[80]; /*!< Technology/Location */
+ int penalty; /*!< Are we a last resort? */
+ int calls; /*!< Number of calls serviced by this member */
+ int dynamic; /*!< Are we dynamically added? */
+ int status; /*!< Status of queue member */
+ int paused; /*!< Are we paused (not accepting calls)? */
+ time_t lastcall; /*!< When last successful call was hungup */
+ int dead; /*!< Used to detect members deleted in realtime */
+ struct member *next; /*!< Next member */
+};
+
+/* values used in multi-bit flags in ast_call_queue */
+#define QUEUE_EMPTY_NORMAL 1
+#define QUEUE_EMPTY_STRICT 2
+#define ANNOUNCEHOLDTIME_ALWAYS 1
+#define ANNOUNCEHOLDTIME_ONCE 2
+
+struct ast_call_queue {
+ ast_mutex_t lock;
+ char name[80]; /*!< Name */
+ char moh[80]; /*!< Music On Hold class to be used */
+ char announce[80]; /*!< Announcement to play when call is answered */
+ char context[AST_MAX_CONTEXT]; /*!< Exit context */
+ unsigned int monjoin:1;
+ unsigned int dead:1;
+ unsigned int joinempty:2;
+ unsigned int eventwhencalled:1;
+ unsigned int leavewhenempty:2;
+ unsigned int reportholdtime:1;
+ unsigned int wrapped:1;
+ unsigned int timeoutrestart:1;
+ unsigned int announceholdtime:2;
+ unsigned int strategy:3;
+ unsigned int maskmemberstatus:1;
+ unsigned int realtime:1;
+ int announcefrequency; /*!< How often to announce their position */
+ int periodicannouncefrequency; /*!< How often to play periodic announcement */
+ int roundingseconds; /*!< How many seconds do we round to? */
+ int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
+ int callscompleted; /*!< Number of queue calls completed */
+ int callsabandoned; /*!< Number of queue calls abandoned */
+ int servicelevel; /*!< seconds setting for servicelevel*/
+ int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
+ char monfmt[8]; /*!< Format to use when recording calls */
+ char sound_next[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
+ char sound_thereare[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */
+ char sound_calls[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
+ char sound_holdtime[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
+ char sound_minutes[80]; /*!< Sound file: "minutes." (def. queue-minutes) */
+ char sound_lessthan[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */
+ char sound_seconds[80]; /*!< Sound file: "seconds." (def. queue-seconds) */
+ char sound_thanks[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
+ char sound_reporthold[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */
+ char sound_periodicannounce[80];/*!< Sound file: Custom announce, no default */
+
+ int count; /*!< How many entries */
+ int maxlen; /*!< Max number of entries */
+ int wrapuptime; /*!< Wrapup Time */
+
+ int retry; /*!< Retry calling everyone after this amount of time */
+ int timeout; /*!< How long to wait for an answer */
+ int weight; /*!< Respective weight */
+
+ /* Queue strategy things */
+ int rrpos; /*!< Round Robin - position */
+ int memberdelay; /*!< Seconds to delay connecting member to caller */
+
+ struct member *members; /*!< Head of the list of members */
+ struct queue_ent *head; /*!< Head of the list of callers */
+ struct ast_call_queue *next; /*!< Next call queue */
+};
+
+static struct ast_call_queue *queues = NULL;
+AST_MUTEX_DEFINE_STATIC(qlock);
+
+static void set_queue_result(struct ast_channel *chan, enum queue_result res)
+{
+ int i;
+
+ for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
+ if (queue_results[i].id == res) {
+ pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
+ return;
+ }
+ }
+}
+
+static char *int2strat(int strategy)
+{
+ int x;
+ for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
+ if (strategy == strategies[x].strategy)
+ return strategies[x].name;
+ }
+ return "<unknown>";
+}
+
+static int strat2int(const char *strategy)
+{
+ int x;
+ for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
+ if (!strcasecmp(strategy, strategies[x].name))
+ return strategies[x].strategy;
+ }
+ return -1;
+}
+
+/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
+static inline void insert_entry(struct ast_call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
+{
+ struct queue_ent *cur;
+
+ if (!q || !new)
+ return;
+ if (prev) {
+ cur = prev->next;
+ prev->next = new;
+ } else {
+ cur = q->head;
+ q->head = new;
+ }
+ new->next = cur;
+ new->parent = q;
+ new->pos = ++(*pos);
+ new->opos = *pos;
+}
+
+enum queue_member_status {
+ QUEUE_NO_MEMBERS,
+ QUEUE_NO_REACHABLE_MEMBERS,
+ QUEUE_NORMAL
+};
+
+static enum queue_member_status get_member_status(const struct ast_call_queue *q)
+{
+ struct member *member;
+ enum queue_member_status result = QUEUE_NO_MEMBERS;
+
+ for (member = q->members; member; member = member->next) {
+ switch (member->status) {
+ case AST_DEVICE_INVALID:
+ /* nothing to do */
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ result = QUEUE_NO_REACHABLE_MEMBERS;
+ break;
+ default:
+ return QUEUE_NORMAL;
+ }
+ }
+
+ return result;
+}
+
+struct statechange {
+ int state;
+ char dev[0];
+};
+
+static void *changethread(void *data)
+{
+ struct ast_call_queue *q;
+ struct statechange *sc = data;
+ struct member *cur;
+ char *loc;
+ char *technology;
+
+ technology = ast_strdupa(sc->dev);
+ loc = strchr(technology, '/');
+ if (loc) {
+ *loc = '\0';
+ loc++;
+ } else {
+ free(sc);
+ return NULL;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
+ ast_mutex_lock(&qlock);
+ for (q = queues; q; q = q->next) {
+ ast_mutex_lock(&q->lock);
+ cur = q->members;
+ while(cur) {
+ if (!strcasecmp(sc->dev, cur->interface)) {
+ if (cur->status != sc->state) {
+ cur->status = sc->state;
+ if (!q->maskmemberstatus) {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n",
+ q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
+ cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ ast_mutex_unlock(&qlock);
+ free(sc);
+ return NULL;
+}
+
+static int statechange_queue(const char *dev, int state, void *ign)
+{
+ /* Avoid potential for deadlocks by spawning a new thread to handle
+ the event */
+ struct statechange *sc;
+ pthread_t t;
+ pthread_attr_t attr;
+
+ sc = malloc(sizeof(struct statechange) + strlen(dev) + 1);
+ if (sc) {
+ sc->state = state;
+ strcpy(sc->dev, dev);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&t, &attr, changethread, sc)) {
+ ast_log(LOG_WARNING, "Failed to create update thread!\n");
+ free(sc);
+ }
+ }
+ return 0;
+}
+
+static struct member *create_queue_member(char *interface, int penalty, int paused)
+{
+ struct member *cur;
+
+ /* Add a new member */
+
+ cur = malloc(sizeof(struct member));
+
+ if (cur) {
+ memset(cur, 0, sizeof(struct member));
+ cur->penalty = penalty;
+ cur->paused = paused;
+ ast_copy_string(cur->interface, interface, sizeof(cur->interface));
+ if (!strchr(cur->interface, '/'))
+ ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
+ cur->status = ast_device_state(interface);
+ }
+
+ return cur;
+}
+
+static struct ast_call_queue *alloc_queue(const char *queuename)
+{
+ struct ast_call_queue *q;
+
+ q = malloc(sizeof(*q));
+ if (q) {
+ memset(q, 0, sizeof(*q));
+ ast_mutex_init(&q->lock);
+ ast_copy_string(q->name, queuename, sizeof(q->name));
+ }
+ return q;
+}
+
+static void init_queue(struct ast_call_queue *q)
+{
+ q->dead = 0;
+ q->retry = DEFAULT_RETRY;
+ q->timeout = -1;
+ q->maxlen = 0;
+ q->announcefrequency = 0;
+ q->announceholdtime = 0;
+ q->roundingseconds = 0; /* Default - don't announce seconds */
+ q->servicelevel = 0;
+ q->moh[0] = '\0';
+ q->announce[0] = '\0';
+ q->context[0] = '\0';
+ q->monfmt[0] = '\0';
+ q->periodicannouncefrequency = 0;
+ ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
+ ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
+ ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
+ ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
+ ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
+ ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
+ ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
+ ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
+ ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
+ ast_copy_string(q->sound_periodicannounce, "queue-periodic-announce", sizeof(q->sound_periodicannounce));
+}
+
+static void clear_queue(struct ast_call_queue *q)
+{
+ q->holdtime = 0;
+ q->callscompleted = 0;
+ q->callsabandoned = 0;
+ q->callscompletedinsl = 0;
+ q->wrapuptime = 0;
+}
+
+/*! \brief Configure a queue parameter.
+\par
+ For error reporting, line number is passed for .conf static configuration.
+ For Realtime queues, linenum is -1.
+ The failunknown flag is set for config files (and static realtime) to show
+ errors for unknown parameters. It is cleared for dynamic realtime to allow
+ extra fields in the tables. */
+static void queue_set_param(struct ast_call_queue *q, const char *param, const char *val, int linenum, int failunknown)
+{
+ if (!strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
+ ast_copy_string(q->moh, val, sizeof(q->moh));
+ } else if (!strcasecmp(param, "announce")) {
+ ast_copy_string(q->announce, val, sizeof(q->announce));
+ } else if (!strcasecmp(param, "context")) {
+ ast_copy_string(q->context, val, sizeof(q->context));
+ } else if (!strcasecmp(param, "timeout")) {
+ q->timeout = atoi(val);
+ if (q->timeout < 0)
+ q->timeout = DEFAULT_TIMEOUT;
+ } else if (!strcasecmp(param, "monitor-join")) {
+ q->monjoin = ast_true(val);
+ } else if (!strcasecmp(param, "monitor-format")) {
+ ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
+ } else if (!strcasecmp(param, "queue-youarenext")) {
+ ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
+ } else if (!strcasecmp(param, "queue-thereare")) {
+ ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
+ } else if (!strcasecmp(param, "queue-callswaiting")) {
+ ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
+ } else if (!strcasecmp(param, "queue-holdtime")) {
+ ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
+ } else if (!strcasecmp(param, "queue-minutes")) {
+ ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
+ } else if (!strcasecmp(param, "queue-seconds")) {
+ ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
+ } else if (!strcasecmp(param, "queue-lessthan")) {
+ ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
+ } else if (!strcasecmp(param, "queue-thankyou")) {
+ ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
+ } else if (!strcasecmp(param, "queue-reporthold")) {
+ ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
+ } else if (!strcasecmp(param, "announce-frequency")) {
+ q->announcefrequency = atoi(val);
+ } else if (!strcasecmp(param, "announce-round-seconds")) {
+ q->roundingseconds = atoi(val);
+ if (q->roundingseconds>60 || q->roundingseconds<0) {
+ if (linenum >= 0) {
+ ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
+ "using 0 instead for queue '%s' at line %d of queues.conf\n",
+ val, param, q->name, linenum);
+ } else {
+ ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
+ "using 0 instead for queue '%s'\n", val, param, q->name);
+ }
+ q->roundingseconds=0;
+ }
+ } else if (!strcasecmp(param, "announce-holdtime")) {
+ if (!strcasecmp(val, "once"))
+ q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
+ else if (ast_true(val))
+ q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
+ else
+ q->announceholdtime = 0;
+ } else if (!strcasecmp(param, "periodic-announce")) {
+ ast_copy_string(q->sound_periodicannounce, val, sizeof(q->sound_periodicannounce));
+ } else if (!strcasecmp(param, "periodic-announce-frequency")) {
+ q->periodicannouncefrequency = atoi(val);
+ } else if (!strcasecmp(param, "retry")) {
+ q->retry = atoi(val);
+ if (q->retry < 0)
+ q->retry = DEFAULT_RETRY;
+ } else if (!strcasecmp(param, "wrapuptime")) {
+ q->wrapuptime = atoi(val);
+ } else if (!strcasecmp(param, "maxlen")) {
+ q->maxlen = atoi(val);
+ if (q->maxlen < 0)
+ q->maxlen = 0;
+ } else if (!strcasecmp(param, "servicelevel")) {
+ q->servicelevel= atoi(val);
+ } else if (!strcasecmp(param, "strategy")) {
+ q->strategy = strat2int(val);
+ if (q->strategy < 0) {
+ ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
+ val, q->name);
+ q->strategy = 0;
+ }
+ } else if (!strcasecmp(param, "joinempty")) {
+ if (!strcasecmp(val, "strict"))
+ q->joinempty = QUEUE_EMPTY_STRICT;
+ else if (ast_true(val))
+ q->joinempty = QUEUE_EMPTY_NORMAL;
+ else
+ q->joinempty = 0;
+ } else if (!strcasecmp(param, "leavewhenempty")) {
+ if (!strcasecmp(val, "strict"))
+ q->leavewhenempty = QUEUE_EMPTY_STRICT;
+ else if (ast_true(val))
+ q->leavewhenempty = QUEUE_EMPTY_NORMAL;
+ else
+ q->leavewhenempty = 0;
+ } else if (!strcasecmp(param, "eventmemberstatus")) {
+ q->maskmemberstatus = !ast_true(val);
+ } else if (!strcasecmp(param, "eventwhencalled")) {
+ q->eventwhencalled = ast_true(val);
+ } else if (!strcasecmp(param, "reportholdtime")) {
+ q->reportholdtime = ast_true(val);
+ } else if (!strcasecmp(param, "memberdelay")) {
+ q->memberdelay = atoi(val);
+ } else if (!strcasecmp(param, "weight")) {
+ q->weight = atoi(val);
+ if (q->weight)
+ use_weight++;
+ /* With Realtime queues, if the last queue using weights is deleted in realtime,
+ we will not see any effect on use_weight until next reload. */
+ } else if (!strcasecmp(param, "timeoutrestart")) {
+ q->timeoutrestart = ast_true(val);
+ } else if(failunknown) {
+ if (linenum >= 0) {
+ ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
+ q->name, param, linenum);
+ } else {
+ ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
+ }
+ }
+}
+
+static void rt_handle_member_record(struct ast_call_queue *q, char *interface, const char *penalty_str)
+{
+ struct member *m, *prev_m;
+ int penalty = 0;
+
+ if(penalty_str) {
+ penalty = atoi(penalty_str);
+ if(penalty < 0)
+ penalty = 0;
+ }
+
+ /* Find the member, or the place to put a new one. */
+ prev_m = NULL;
+ m = q->members;
+ while (m && strcmp(m->interface, interface)) {
+ prev_m = m;
+ m = m->next;
+ }
+
+ /* Create a new one if not found, else update penalty */
+ if (!m) {
+ m = create_queue_member(interface, penalty, 0);
+ if (m) {
+ m->dead = 0;
+ if (prev_m) {
+ prev_m->next = m;
+ } else {
+ q->members = m;
+ }
+ }
+ } else {
+ m->dead = 0; /* Do not delete this one. */
+ m->penalty = penalty;
+ }
+}
+
+static void free_members(struct ast_call_queue *q, int all)
+{
+ /* Free non-dynamic members */
+ struct member *curm, *next, *prev = NULL;
+
+ for (curm = q->members; curm; curm = next) {
+ next = curm->next;
+ if (all || !curm->dynamic) {
+ if (prev)
+ prev->next = next;
+ else
+ q->members = next;
+ free(curm);
+ } else
+ prev = curm;
+ }
+}
+
+static void destroy_queue(struct ast_call_queue *q)
+{
+ free_members(q, 1);
+ ast_mutex_destroy(&q->lock);
+ free(q);
+}
+
+static void remove_queue(struct ast_call_queue *q)
+{
+ struct ast_call_queue *cur, *prev = NULL;
+
+ ast_mutex_lock(&qlock);
+ for (cur = queues; cur; cur = cur->next) {
+ if (cur == q) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ queues = cur->next;
+ } else {
+ prev = cur;
+ }
+ }
+ ast_mutex_unlock(&qlock);
+}
+
+/*!\brief Reload a single queue via realtime.
+ \return Return the queue, or NULL if it doesn't exist.
+ \note Should be called with the global qlock locked. */
+static struct ast_call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
+{
+ struct ast_variable *v;
+ struct ast_call_queue *q, *prev_q = NULL;
+ struct member *m, *prev_m, *next_m;
+ char *interface;
+ char *tmp, *tmp_name;
+ char tmpbuf[64]; /* Must be longer than the longest queue param name. */
+
+ /* Find the queue in the in-core list (we will create a new one if not found). */
+ for (q = queues; q; q = q->next) {
+ if (!strcasecmp(q->name, queuename)) {
+ break;
+ }
+ prev_q = q;
+ }
+
+ /* Static queues override realtime. */
+ if (q) {
+ ast_mutex_lock(&q->lock);
+ if (!q->realtime) {
+ if (q->dead) {
+ ast_mutex_unlock(&q->lock);
+ return NULL;
+ } else {
+ ast_mutex_unlock(&q->lock);
+ return q;
+ }
+ }
+ } else if (!member_config)
+ /* Not found in the list, and it's not realtime ... */
+ return NULL;
+
+ /* Check if queue is defined in realtime. */
+ if (!queue_vars) {
+ /* Delete queue from in-core list if it has been deleted in realtime. */
+ if (q) {
+ /*! \note Hmm, can't seem to distinguish a DB failure from a not
+ found condition... So we might delete an in-core queue
+ in case of DB failure. */
+ ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
+
+ q->dead = 1;
+ /* Delete if unused (else will be deleted when last caller leaves). */
+ if (!q->count) {
+ /* Delete. */
+ if (!prev_q) {
+ queues = q->next;
+ } else {
+ prev_q->next = q->next;
+ }
+ ast_mutex_unlock(&q->lock);
+ destroy_queue(q);
+ } else
+ ast_mutex_unlock(&q->lock);
+ }
+ return NULL;
+ }
+
+ /* Create a new queue if an in-core entry does not exist yet. */
+ if (!q) {
+ q = alloc_queue(queuename);
+ if (!q)
+ return NULL;
+ ast_mutex_lock(&q->lock);
+ clear_queue(q);
+ q->realtime = 1;
+ q->next = queues;
+ queues = q;
+ }
+ init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
+
+ v = queue_vars;
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ while(v) {
+ /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
+ if((tmp = strchr(v->name, '_')) != NULL) {
+ ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
+ tmp_name = tmpbuf;
+ tmp = tmp_name;
+ while((tmp = strchr(tmp, '_')) != NULL)
+ *tmp++ = '-';
+ } else
+ tmp_name = v->name;
+ queue_set_param(q, tmp_name, v->value, -1, 0);
+ v = v->next;
+ }
+
+ /* Temporarily set non-dynamic members dead so we can detect deleted ones. */
+ m = q->members;
+ while (m) {
+ if (!m->dynamic)
+ m->dead = 1;
+ m = m->next;
+ }
+
+ interface = ast_category_browse(member_config, NULL);
+ while (interface) {
+ rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty"));
+ interface = ast_category_browse(member_config, interface);
+ }
+
+ /* Delete all realtime members that have been deleted in DB. */
+ m = q->members;
+ prev_m = NULL;
+ while (m) {
+ next_m = m->next;
+ if (m->dead) {
+ if (prev_m) {
+ prev_m->next = next_m;
+ } else {
+ q->members = next_m;
+ }
+ free(m);
+ } else {
+ prev_m = m;
+ }
+ m = next_m;
+ }
+
+ ast_mutex_unlock(&q->lock);
+
+ return q;
+}
+
+static struct ast_call_queue *load_realtime_queue(char *queuename)
+{
+ struct ast_variable *queue_vars = NULL;
+ struct ast_config *member_config = NULL;
+ struct ast_call_queue *q;
+
+ /* Find the queue in the in-core list first. */
+ ast_mutex_lock(&qlock);
+ for (q = queues; q; q = q->next) {
+ if (!strcasecmp(q->name, queuename)) {
+ break;
+ }
+ }
+ ast_mutex_unlock(&qlock);
+
+ if (!q || q->realtime) {
+ /*! \note Load from realtime before taking the global qlock, to avoid blocking all
+ queue operations while waiting for the DB.
+
+ This will be two separate database transactions, so we might
+ see queue parameters as they were before another process
+ changed the queue and member list as it was after the change.
+ Thus we might see an empty member list when a queue is
+ deleted. In practise, this is unlikely to cause a problem. */
+
+ queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
+ if (queue_vars) {
+ member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
+ if (!member_config) {
+ ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
+ return NULL;
+ }
+ }
+
+ ast_mutex_lock(&qlock);
+
+ q = find_queue_by_name_rt(queuename, queue_vars, member_config);
+ if (member_config)
+ ast_config_destroy(member_config);
+ if (queue_vars)
+ ast_variables_destroy(queue_vars);
+
+ ast_mutex_unlock(&qlock);
+ }
+ return q;
+}
+
+static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
+{
+ struct ast_call_queue *q;
+ struct queue_ent *cur, *prev = NULL;
+ int res = -1;
+ int pos = 0;
+ int inserted = 0;
+ enum queue_member_status stat;
+
+ q = load_realtime_queue(queuename);
+ if (!q)
+ return res;
+
+ ast_mutex_lock(&qlock);
+ ast_mutex_lock(&q->lock);
+
+ /* This is our one */
+ stat = get_member_status(q);
+ if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
+ *reason = QUEUE_JOINEMPTY;
+ else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
+ *reason = QUEUE_JOINUNAVAIL;
+ else if (q->maxlen && (q->count >= q->maxlen))
+ *reason = QUEUE_FULL;
+ else {
+ /* There's space for us, put us at the right position inside
+ * the queue.
+ * Take into account the priority of the calling user */
+ inserted = 0;
+ prev = NULL;
+ cur = q->head;
+ while(cur) {
+ /* We have higher priority than the current user, enter
+ * before him, after all the other users with priority
+ * higher or equal to our priority. */
+ if ((!inserted) && (qe->prio > cur->prio)) {
+ insert_entry(q, prev, qe, &pos);
+ inserted = 1;
+ }
+ cur->pos = ++pos;
+ prev = cur;
+ cur = cur->next;
+ }
+ /* No luck, join at the end of the queue */
+ if (!inserted)
+ insert_entry(q, prev, qe, &pos);
+ ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
+ ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
+ ast_copy_string(qe->context, q->context, sizeof(qe->context));
+ q->count++;
+ res = 0;
+ manager_event(EVENT_FLAG_CALL, "Join",
+ "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
+ qe->chan->name,
+ qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
+ qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
+ q->name, qe->pos, q->count );
+#if 0
+ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
+#endif
+ }
+ ast_mutex_unlock(&q->lock);
+ ast_mutex_unlock(&qlock);
+ return res;
+}
+
+static int play_file(struct ast_channel *chan, char *filename)
+{
+ int res;
+
+ ast_stopstream(chan);
+ res = ast_streamfile(chan, filename, chan->language);
+
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ else
+ res = 0;
+
+ ast_stopstream(chan);
+
+ return res;
+}
+
+static int valid_exit(struct queue_ent *qe, char digit)
+{
+ int digitlen = strlen(qe->digits);
+
+ /* Prevent possible buffer overflow */
+ if (digitlen < sizeof(qe->digits) - 2) {
+ qe->digits[digitlen] = digit;
+ qe->digits[digitlen + 1] = '\0';
+ } else {
+ qe->digits[0] = '\0';
+ return 0;
+ }
+
+ /* If there's no context to goto, short-circuit */
+ if (ast_strlen_zero(qe->context))
+ return 0;
+
+ /* If the extension is bad, then reset the digits to blank */
+ if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
+ qe->digits[0] = '\0';
+ return 0;
+ }
+
+ /* We have an exact match */
+ if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
+ /* Return 1 on a successful goto */
+ return 1;
+ }
+ return 0;
+}
+
+static int say_position(struct queue_ent *qe)
+{
+ int res = 0, avgholdmins, avgholdsecs;
+ time_t now;
+
+ /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
+ time(&now);
+ if ( (now - qe->last_pos) < 15 )
+ return 0;
+
+ /* If either our position has changed, or we are over the freq timer, say position */
+ if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
+ return 0;
+
+ ast_moh_stop(qe->chan);
+ /* Say we're next, if we are */
+ if (qe->pos == 1) {
+ res = play_file(qe->chan, qe->parent->sound_next);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ else
+ goto posout;
+ } else {
+ res = play_file(qe->chan, qe->parent->sound_thereare);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
+ if (res && valid_exit(qe, res))
+ goto playout;
+ res = play_file(qe->chan, qe->parent->sound_calls);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ }
+ /* Round hold time to nearest minute */
+ avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
+
+ /* If they have specified a rounding then round the seconds as well */
+ if(qe->parent->roundingseconds) {
+ avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
+ avgholdsecs*= qe->parent->roundingseconds;
+ } else {
+ avgholdsecs=0;
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
+
+ /* If the hold time is >1 min, if it's enabled, and if it's not
+ supposed to be only once and we have already said it, say it */
+ if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
+ (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
+ res = play_file(qe->chan, qe->parent->sound_holdtime);
+ if (res && valid_exit(qe, res))
+ goto playout;
+
+ if (avgholdmins>0) {
+ if (avgholdmins < 2) {
+ res = play_file(qe->chan, qe->parent->sound_lessthan);
+ if (res && valid_exit(qe, res))
+ goto playout;
+
+ res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ } else {
+ res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ }
+
+ res = play_file(qe->chan, qe->parent->sound_minutes);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ }
+ if (avgholdsecs>0) {
+ res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
+ if (res && valid_exit(qe, res))
+ goto playout;
+
+ res = play_file(qe->chan, qe->parent->sound_seconds);
+ if (res && valid_exit(qe, res))
+ goto playout;
+ }
+
+ }
+
+ posout:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
+ qe->chan->name, qe->parent->name, qe->pos);
+ res = play_file(qe->chan, qe->parent->sound_thanks);
+
+ playout:
+ /* Set our last_pos indicators */
+ qe->last_pos = now;
+ qe->last_pos_said = qe->pos;
+ ast_moh_start(qe->chan, qe->moh);
+
+ return res;
+}
+
+static void recalc_holdtime(struct queue_ent *qe)
+{
+ int oldvalue, newvalue;
+
+ /* Calculate holdtime using a recursive boxcar filter */
+ /* Thanks to SRT for this contribution */
+ /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
+
+ newvalue = time(NULL) - qe->start;
+
+ ast_mutex_lock(&qe->parent->lock);
+ if (newvalue <= qe->parent->servicelevel)
+ qe->parent->callscompletedinsl++;
+ oldvalue = qe->parent->holdtime;
+ qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
+ ast_mutex_unlock(&qe->parent->lock);
+}
+
+
+static void leave_queue(struct queue_ent *qe)
+{
+ struct ast_call_queue *q;
+ struct queue_ent *cur, *prev = NULL;
+ int pos = 0;
+
+ q = qe->parent;
+ if (!q)
+ return;
+ ast_mutex_lock(&q->lock);
+
+ prev = NULL;
+ cur = q->head;
+ while(cur) {
+ if (cur == qe) {
+ q->count--;
+
+ /* Take us out of the queue */
+ manager_event(EVENT_FLAG_CALL, "Leave",
+ "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
+ qe->chan->name, q->name, q->count);
+#if 0
+ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
+#endif
+ /* Take us out of the queue */
+ if (prev)
+ prev->next = cur->next;
+ else
+ q->head = cur->next;
+ } else {
+ /* Renumber the people after us in the queue based on a new count */
+ cur->pos = ++pos;
+ prev = cur;
+ }
+ cur = cur->next;
+ }
+ ast_mutex_unlock(&q->lock);
+ if (q->dead && !q->count) {
+ /* It's dead and nobody is in it, so kill it */
+ remove_queue(q);
+ destroy_queue(q);
+ }
+}
+
+/* Hang up a list of outgoing calls */
+static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception)
+{
+ struct localuser *oo;
+
+ while(outgoing) {
+ /* Hangup any existing lines we have open */
+ if (outgoing->chan && (outgoing->chan != exception))
+ ast_hangup(outgoing->chan);
+ oo = outgoing;
+ outgoing=outgoing->next;
+ free(oo);
+ }
+}
+
+static int update_status(struct ast_call_queue *q, struct member *member, int status)
+{
+ struct member *cur;
+
+ /* Since a reload could have taken place, we have to traverse the list to
+ be sure it's still valid */
+ ast_mutex_lock(&q->lock);
+ cur = q->members;
+ while(cur) {
+ if (member == cur) {
+ cur->status = status;
+ if (!q->maskmemberstatus) {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n",
+ q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
+ cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
+ }
+ break;
+ }
+ cur = cur->next;
+ }
+ ast_mutex_unlock(&q->lock);
+ return 0;
+}
+
+static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
+{
+ if (status == AST_CAUSE_BUSY)
+ status = AST_DEVICE_BUSY;
+ else if (status == AST_CAUSE_UNREGISTERED)
+ status = AST_DEVICE_UNAVAILABLE;
+ else if (status == AST_CAUSE_NOSUCHDRIVER)
+ status = AST_DEVICE_INVALID;
+ else
+ status = AST_DEVICE_UNKNOWN;
+ return update_status(q, member, status);
+}
+
+/* traverse all defined queues which have calls waiting and contain this member
+ return 0 if no other queue has precedence (higher weight) or 1 if found */
+static int compare_weight(struct ast_call_queue *rq, struct member *member)
+{
+ struct ast_call_queue *q;
+ struct member *mem;
+ int found = 0;
+
+ /* &qlock and &rq->lock already set by try_calling()
+ * to solve deadlock */
+ for (q = queues; q; q = q->next) {
+ if (q == rq) /* don't check myself, could deadlock */
+ continue;
+ ast_mutex_lock(&q->lock);
+ if (q->count && q->members) {
+ for (mem = q->members; mem; mem = mem->next) {
+ if (!strcmp(mem->interface, member->interface)) {
+ ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
+ if (q->weight > rq->weight) {
+ ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
+ found = 1;
+ break;
+ }
+ }
+ }
+ }
+ ast_mutex_unlock(&q->lock);
+ if (found)
+ break;
+ }
+ ast_mutex_unlock(&qlock);
+ return found;
+}
+
+static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies)
+{
+ int res;
+ int status;
+ char tech[256];
+ char *location;
+
+ if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ (*busies)++;
+ return 0;
+ }
+
+ if (tmp->member->paused) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ return 0;
+ }
+ if (use_weight && compare_weight(qe->parent,tmp->member)) {
+ ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ (*busies)++;
+ return 0;
+ }
+
+ ast_copy_string(tech, tmp->interface, sizeof(tech));
+ if ((location = strchr(tech, '/')))
+ *location++ = '\0';
+ else
+ location = "";
+
+ /* Request the peer */
+ tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
+ if (!tmp->chan) { /* If we can't, just go on to the next call */
+#if 0
+ ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech);
+#endif
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ update_dial_status(qe->parent, tmp->member, status);
+ (*busies)++;
+ return 0;
+ } else if (status != tmp->oldstatus)
+ update_dial_status(qe->parent, tmp->member, status);
+
+ tmp->chan->appl = "AppQueue";
+ tmp->chan->data = "(Outgoing Line)";
+ tmp->chan->whentohangup = 0;
+ if (tmp->chan->cid.cid_num)
+ free(tmp->chan->cid.cid_num);
+ tmp->chan->cid.cid_num = NULL;
+ if (tmp->chan->cid.cid_name)
+ free(tmp->chan->cid.cid_name);
+ tmp->chan->cid.cid_name = NULL;
+ if (tmp->chan->cid.cid_ani)
+ free(tmp->chan->cid.cid_ani);
+ tmp->chan->cid.cid_ani = NULL;
+ if (qe->chan->cid.cid_num)
+ tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
+ if (qe->chan->cid.cid_name)
+ tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
+ if (qe->chan->cid.cid_ani)
+ tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
+
+ /* Inherit specially named variables from parent channel */
+ ast_channel_inherit_variables(qe->chan, tmp->chan);
+
+ /* Presense of ADSI CPE on outgoing channel follows ours */
+ tmp->chan->adsicpe = qe->chan->adsicpe;
+
+ /* Place the call, but don't wait on the answer */
+ res = ast_call(tmp->chan, location, 0);
+ if (res) {
+ /* Again, keep going even if there's an error */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
+ else if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
+ ast_hangup(tmp->chan);
+ tmp->chan = NULL;
+ tmp->stillgoing = 0;
+ (*busies)++;
+ return 0;
+ } else {
+ if (qe->parent->eventwhencalled) {
+ manager_event(EVENT_FLAG_AGENT, "AgentCalled",
+ "AgentCalled: %s\r\n"
+ "ChannelCalling: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n",
+ tmp->interface, qe->chan->name,
+ tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
+ tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
+ qe->chan->context, qe->chan->exten, qe->chan->priority);
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
+ }
+ return 1;
+}
+
+static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
+{
+ struct localuser *cur;
+ struct localuser *best;
+ int bestmetric=0;
+
+ do {
+ best = NULL;
+ cur = outgoing;
+ while(cur) {
+ if (cur->stillgoing && /* Not already done */
+ !cur->chan && /* Isn't already going */
+ (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
+ bestmetric = cur->metric;
+ best = cur;
+ }
+ cur = cur->next;
+ }
+ if (best) {
+ if (!qe->parent->strategy) {
+ /* Ring everyone who shares this best metric (for ringall) */
+ cur = outgoing;
+ while(cur) {
+ if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
+ ring_entry(qe, cur, busies);
+ }
+ cur = cur->next;
+ }
+ } else {
+ /* Ring just the best channel */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
+ ring_entry(qe, best, busies);
+ }
+ }
+ } while (best && !best->chan);
+ if (!best) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int store_next(struct queue_ent *qe, struct localuser *outgoing)
+{
+ struct localuser *cur;
+ struct localuser *best;
+ int bestmetric=0;
+
+ best = NULL;
+ cur = outgoing;
+ while(cur) {
+ if (cur->stillgoing && /* Not already done */
+ !cur->chan && /* Isn't already going */
+ (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
+ bestmetric = cur->metric;
+ best = cur;
+ }
+ cur = cur->next;
+ }
+ if (best) {
+ /* Ring just the best channel */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
+ qe->parent->rrpos = best->metric % 1000;
+ } else {
+ /* Just increment rrpos */
+ if (qe->parent->wrapped) {
+ /* No more channels, start over */
+ qe->parent->rrpos = 0;
+ } else {
+ /* Prioritize next entry */
+ qe->parent->rrpos++;
+ }
+ }
+ qe->parent->wrapped = 0;
+ return 0;
+}
+
+static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
+{
+ int res;
+
+ ast_stopstream(chan);
+ res = ast_streamfile(chan, filename, chan->language);
+
+ if (!res) {
+ /* Wait for a keypress */
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (res <= 0 || !valid_exit(qe, res))
+ res = 0;
+
+ /* Stop playback */
+ ast_stopstream(chan);
+ } else {
+ res = 0;
+ }
+
+ /*if (res) {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
+ res = 0;
+ }*/
+
+ return res;
+}
+
+static int say_periodic_announcement(struct queue_ent *qe)
+{
+ int res = 0;
+ time_t now;
+
+ /* Get the current time */
+ time(&now);
+
+ /* Check to see if it is time to announce */
+ if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
+ return 0;
+
+ /* Stop the music on hold so we can play our own file */
+ ast_moh_stop(qe->chan);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
+
+ /* play the announcement */
+ res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
+
+ /* Resume Music on Hold */
+ ast_moh_start(qe->chan, qe->moh);
+
+ /* update last_periodic_announce_time */
+ qe->last_periodic_announce_time = now;
+
+ return res;
+}
+
+static void record_abandoned(struct queue_ent *qe)
+{
+ ast_mutex_lock(&qe->parent->lock);
+ qe->parent->callsabandoned++;
+ ast_mutex_unlock(&qe->parent->lock);
+}
+
+
+#define AST_MAX_WATCHERS 256
+
+#define BUILD_WATCHERS do { \
+ o = outgoing; \
+ found = -1; \
+ pos = 1; \
+ numlines = 0; \
+ watchers[0] = in; \
+ while(o) { \
+ /* Keep track of important channels */ \
+ if (o->stillgoing) { \
+ stillgoing = 1; \
+ if (o->chan) { \
+ watchers[pos++] = o->chan; \
+ found = 1; \
+ } \
+ } \
+ o = o->next; \
+ numlines++; \
+ } \
+ } while(0)
+
+static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
+{
+ char *queue = qe->parent->name;
+ struct localuser *o;
+ int found;
+ int numlines;
+ int status;
+ int sentringing = 0;
+ int numbusies = prebusies;
+ int numnochan = 0;
+ int stillgoing = 0;
+ int orig = *to;
+ struct ast_frame *f;
+ struct localuser *peer = NULL;
+ struct ast_channel *watchers[AST_MAX_WATCHERS];
+ int pos;
+ struct ast_channel *winner;
+ struct ast_channel *in = qe->chan;
+
+ while(*to && !peer) {
+ BUILD_WATCHERS;
+ if ((found < 0) && stillgoing && !qe->parent->strategy) {
+ /* On "ringall" strategy we only move to the next penalty level
+ when *all* ringing phones are done in the current penalty level */
+ ring_one(qe, outgoing, &numbusies);
+ BUILD_WATCHERS;
+ }
+ if (found < 0) {
+ if (numlines == (numbusies + numnochan)) {
+ ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
+ } else {
+ ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
+ }
+ *to = 0;
+ return NULL;
+ }
+ winner = ast_waitfor_n(watchers, pos, to);
+ o = outgoing;
+ while(o) {
+ if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
+ if (!peer) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
+ peer = o;
+ }
+ } else if (o->chan && (o->chan == winner)) {
+ if (!ast_strlen_zero(o->chan->call_forward)) {
+ char tmpchan[256]="";
+ char *stuff;
+ char *tech;
+ ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
+ if ((stuff = strchr(tmpchan, '/'))) {
+ *stuff = '\0';
+ stuff++;
+ tech = tmpchan;
+ } else {
+ snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
+ stuff = tmpchan;
+ tech = "Local";
+ }
+ /* Before processing channel, go ahead and check for forwarding */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
+ /* Setup parameters */
+ o->chan = ast_request(tech, in->nativeformats, stuff, &status);
+ if (status != o->oldstatus)
+ update_dial_status(qe->parent, o->member, status);
+ if (!o->chan) {
+ ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
+ o->stillgoing = 0;
+ numnochan++;
+ } else {
+ if (o->chan->cid.cid_num)
+ free(o->chan->cid.cid_num);
+ o->chan->cid.cid_num = NULL;
+ if (o->chan->cid.cid_name)
+ free(o->chan->cid.cid_name);
+ o->chan->cid.cid_name = NULL;
+
+ if (in->cid.cid_num) {
+ o->chan->cid.cid_num = strdup(in->cid.cid_num);
+ if (!o->chan->cid.cid_num)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ if (in->cid.cid_name) {
+ o->chan->cid.cid_name = strdup(in->cid.cid_name);
+ if (!o->chan->cid.cid_name)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
+ o->chan->cdrflags = in->cdrflags;
+
+ if (in->cid.cid_ani) {
+ if (o->chan->cid.cid_ani)
+ free(o->chan->cid.cid_ani);
+ o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
+ if (o->chan->cid.cid_ani)
+ strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
+ else
+ ast_log(LOG_WARNING, "Out of memory\n");
+ }
+ if (o->chan->cid.cid_rdnis)
+ free(o->chan->cid.cid_rdnis);
+ if (!ast_strlen_zero(in->macroexten))
+ o->chan->cid.cid_rdnis = strdup(in->macroexten);
+ else
+ o->chan->cid.cid_rdnis = strdup(in->exten);
+ if (ast_call(o->chan, tmpchan, 0)) {
+ ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
+ o->stillgoing = 0;
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ numnochan++;
+ }
+ }
+ /* Hangup the original channel now, in case we needed it */
+ ast_hangup(winner);
+ continue;
+ }
+ f = ast_read(winner);
+ if (f) {
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch(f->subclass) {
+ case AST_CONTROL_ANSWER:
+ /* This is our guy if someone answered. */
+ if (!peer) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
+ peer = o;
+ }
+ break;
+ case AST_CONTROL_BUSY:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
+ o->stillgoing = 0;
+ if (in->cdr)
+ ast_cdr_busy(in->cdr);
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ if (qe->parent->strategy) {
+ if (qe->parent->timeoutrestart)
+ *to = orig;
+ ring_one(qe, outgoing, &numbusies);
+ }
+ numbusies++;
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
+ o->stillgoing = 0;
+ if (in->cdr)
+ ast_cdr_busy(in->cdr);
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ if (qe->parent->strategy) {
+ if (qe->parent->timeoutrestart)
+ *to = orig;
+ ring_one(qe, outgoing, &numbusies);
+ }
+ numbusies++;
+ break;
+ case AST_CONTROL_RINGING:
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
+ if (!sentringing) {
+#if 0
+ ast_indicate(in, AST_CONTROL_RINGING);
+#endif
+ sentringing++;
+ }
+ break;
+ case AST_CONTROL_OFFHOOK:
+ /* Ignore going off hook */
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else {
+ o->stillgoing = 0;
+ ast_hangup(o->chan);
+ o->chan = NULL;
+ if (qe->parent->strategy) {
+ if (qe->parent->timeoutrestart)
+ *to = orig;
+ ring_one(qe, outgoing, &numbusies);
+ }
+ }
+ }
+ o = o->next;
+ }
+ if (winner == in) {
+ f = ast_read(in);
+#if 0
+ if (f && (f->frametype != AST_FRAME_VOICE))
+ printf("Frame type: %d, %d\n", f->frametype, f->subclass);
+ else if (!f || (f->frametype != AST_FRAME_VOICE))
+ printf("Hangup received on %s\n", in->name);
+#endif
+ if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
+ /* Got hung up */
+ *to=-1;
+ if (f)
+ ast_frfree(f);
+ return NULL;
+ }
+ if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
+ *to=0;
+ ast_frfree(f);
+ return NULL;
+ }
+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
+ *to=0;
+ *digit=f->subclass;
+ ast_frfree(f);
+ return NULL;
+ }
+ ast_frfree(f);
+ }
+ if (!*to && (option_verbose > 2))
+ ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+ }
+
+ return peer;
+
+}
+
+static int is_our_turn(struct queue_ent *qe)
+{
+ struct queue_ent *ch;
+ int res;
+
+ /* Atomically read the parent head -- does not need a lock */
+ ch = qe->parent->head;
+ /* If we are now at the top of the head, break out */
+ if (ch == qe) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
+ res = 1;
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
+ res = 0;
+ }
+ return res;
+}
+
+static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
+{
+ int res = 0;
+
+ /* This is the holding pen for callers 2 through maxlen */
+ for (;;) {
+ enum queue_member_status stat;
+
+ if (is_our_turn(qe))
+ break;
+
+ /* If we have timed out, break out */
+ if (qe->expire && (time(NULL) > qe->expire)) {
+ *reason = QUEUE_TIMEOUT;
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe->pos);
+ break;
+ }
+
+ stat = get_member_status(qe->parent);
+
+ /* leave the queue if no agents, if enabled */
+ if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
+ *reason = QUEUE_LEAVEEMPTY;
+ leave_queue(qe);
+ break;
+ }
+
+ /* leave the queue if no reachable agents, if enabled */
+ if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
+ *reason = QUEUE_LEAVEUNAVAIL;
+ leave_queue(qe);
+ break;
+ }
+
+ /* Make a position announcement, if enabled */
+ if (qe->parent->announcefrequency && !ringing)
+ res = say_position(qe);
+ if (res)
+ break;
+
+ /* Make a periodic announcement, if enabled */
+ if (qe->parent->periodicannouncefrequency && !ringing)
+ res = say_periodic_announcement(qe);
+
+ /* Wait a second before checking again */
+ if (!res) res = ast_waitfordigit(qe->chan, RECHECK * 1000);
+ if (res)
+ break;
+ }
+ return res;
+}
+
+static int update_queue(struct ast_call_queue *q, struct member *member)
+{
+ struct member *cur;
+
+ /* Since a reload could have taken place, we have to traverse the list to
+ be sure it's still valid */
+ ast_mutex_lock(&q->lock);
+ cur = q->members;
+ while(cur) {
+ if (member == cur) {
+ time(&cur->lastcall);
+ cur->calls++;
+ break;
+ }
+ cur = cur->next;
+ }
+ q->callscompleted++;
+ ast_mutex_unlock(&q->lock);
+ return 0;
+}
+
+static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
+{
+ switch (q->strategy) {
+ case QUEUE_STRATEGY_RINGALL:
+ /* Everyone equal, except for penalty */
+ tmp->metric = mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_ROUNDROBIN:
+ if (!pos) {
+ if (!q->wrapped) {
+ /* No more channels, start over */
+ q->rrpos = 0;
+ } else {
+ /* Prioritize next entry */
+ q->rrpos++;
+ }
+ q->wrapped = 0;
+ }
+ /* Fall through */
+ case QUEUE_STRATEGY_RRMEMORY:
+ if (pos < q->rrpos) {
+ tmp->metric = 1000 + pos;
+ } else {
+ if (pos > q->rrpos)
+ /* Indicate there is another priority */
+ q->wrapped = 1;
+ tmp->metric = pos;
+ }
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_RANDOM:
+ tmp->metric = rand() % 1000;
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_FEWESTCALLS:
+ tmp->metric = mem->calls;
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_LEASTRECENT:
+ if (!mem->lastcall)
+ tmp->metric = 0;
+ else
+ tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
+ break;
+ }
+ return 0;
+}
+
+static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
+{
+ struct member *cur;
+ struct localuser *outgoing=NULL, *tmp = NULL;
+ int to;
+ char restofit[AST_MAX_EXTENSION];
+ char oldexten[AST_MAX_EXTENSION]="";
+ char oldcontext[AST_MAX_CONTEXT]="";
+ char queuename[256]="";
+ char *newnum;
+ char *monitorfilename;
+ struct ast_channel *peer;
+ struct ast_channel *which;
+ struct localuser *lpeer;
+ struct member *member;
+ int res = 0, bridge = 0;
+ int numbusies = 0;
+ int x=0;
+ char *announce = NULL;
+ char digit = 0;
+ time_t callstart;
+ time_t now = time(NULL);
+ struct ast_bridge_config bridge_config;
+ char nondataquality = 1;
+
+ memset(&bridge_config, 0, sizeof(bridge_config));
+ time(&now);
+
+ for (; options && *options; options++)
+ switch (*options) {
+ case 't':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
+ break;
+ case 'T':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
+ break;
+ case 'w':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
+ break;
+ case 'W':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
+ break;
+ case 'd':
+ nondataquality = 0;
+ break;
+ case 'h':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
+ break;
+ case 'H':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
+ break;
+ case 'n':
+ if ((now - qe->start >= qe->parent->timeout))
+ *go_on = 1;
+ break;
+ }
+
+ /* Hold the lock while we setup the outgoing calls */
+ if (use_weight)
+ ast_mutex_lock(&qlock);
+ ast_mutex_lock(&qe->parent->lock);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
+ qe->chan->name);
+ ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
+ cur = qe->parent->members;
+ if (!ast_strlen_zero(qe->announce))
+ announce = qe->announce;
+ if (!ast_strlen_zero(announceoverride))
+ announce = announceoverride;
+
+ while(cur) {
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp) {
+ ast_mutex_unlock(&qe->parent->lock);
+ if (use_weight)
+ ast_mutex_unlock(&qlock);
+ ast_log(LOG_WARNING, "Out of memory\n");
+ goto out;
+ }
+ memset(tmp, 0, sizeof(*tmp));
+ tmp->stillgoing = -1;
+ if (option_debug) {
+ if (url)
+ ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
+ else
+ ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
+ }
+
+ tmp->member = cur; /* Never directly dereference! Could change on reload */
+ tmp->oldstatus = cur->status;
+ tmp->lastcall = cur->lastcall;
+ ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
+ /* If we're dialing by extension, look at the extension to know what to dial */
+ if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
+ newnum++;
+ strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
+ snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
+ }
+ /* Special case: If we ring everyone, go ahead and ring them, otherwise
+ just calculate their metric for the appropriate strategy */
+ calc_metric(qe->parent, cur, x++, qe, tmp);
+ /* Put them in the list of outgoing thingies... We're ready now.
+ XXX If we're forcibly removed, these outgoing calls won't get
+ hung up XXX */
+ tmp->next = outgoing;
+ outgoing = tmp;
+ /* If this line is up, don't try anybody else */
+ if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
+ break;
+
+ cur = cur->next;
+ }
+ if (qe->parent->timeout)
+ to = qe->parent->timeout * 1000;
+ else
+ to = -1;
+ ring_one(qe, outgoing, &numbusies);
+ ast_mutex_unlock(&qe->parent->lock);
+ if (use_weight)
+ ast_mutex_unlock(&qlock);
+ lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT));
+ ast_mutex_lock(&qe->parent->lock);
+ if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
+ store_next(qe, outgoing);
+ }
+ ast_mutex_unlock(&qe->parent->lock);
+ if (lpeer)
+ peer = lpeer->chan;
+ else
+ peer = NULL;
+ if (!peer) {
+ if (to) {
+ /* Musta gotten hung up */
+ res = -1;
+ } else {
+ res = digit;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
+ goto out;
+ }
+ if (peer) {
+ /* Ah ha! Someone answered within the desired timeframe. Of course after this
+ we will always return with -1 so that it is hung up properly after the
+ conversation. */
+ qe->handled++;
+ if (!strcmp(qe->chan->type,"Zap"))
+ ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
+ if (!strcmp(peer->type,"Zap"))
+ ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
+ /* Update parameters for the queue */
+ recalc_holdtime(qe);
+ member = lpeer->member;
+ hangupcalls(outgoing, peer);
+ outgoing = NULL;
+ if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
+ int res2;
+ res2 = ast_autoservice_start(qe->chan);
+ if (!res2) {
+ if (qe->parent->memberdelay) {
+ ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
+ res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
+ }
+ if (!res2 && announce) {
+ if (play_file(peer, announce))
+ ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
+ }
+ if (!res2 && qe->parent->reportholdtime) {
+ if (!play_file(peer, qe->parent->sound_reporthold)) {
+ int holdtime;
+
+ time(&now);
+ holdtime = abs((now - qe->start) / 60);
+ if (holdtime < 2) {
+ play_file(peer, qe->parent->sound_lessthan);
+ ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
+ } else
+ ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
+ play_file(peer, qe->parent->sound_minutes);
+ }
+ }
+ }
+ res2 |= ast_autoservice_stop(qe->chan);
+ if (peer->_softhangup) {
+ /* Agent must have hung up */
+ ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
+ record_abandoned(qe);
+ if (qe->parent->eventwhencalled) {
+ manager_event(EVENT_FLAG_AGENT, "AgentDump",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "Member: %s\r\n",
+ queuename, qe->chan->uniqueid, peer->name, member->interface);
+ }
+ ast_hangup(peer);
+ goto out;
+ } else if (res2) {
+ /* Caller must have hung up just before being connected*/
+ ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
+ record_abandoned(qe);
+ ast_hangup(peer);
+ return -1;
+ }
+ }
+ /* Stop music on hold */
+ ast_moh_stop(qe->chan);
+ /* If appropriate, log that we have a destination channel */
+ if (qe->chan->cdr)
+ ast_cdr_setdestchan(qe->chan->cdr, peer->name);
+ /* Make sure channels are compatible */
+ res = ast_channel_make_compatible(qe->chan, peer);
+ if (res < 0) {
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
+ ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
+ record_abandoned(qe);
+ ast_hangup(peer);
+ return -1;
+ }
+ /* Begin Monitoring */
+ if (qe->parent->monfmt && *qe->parent->monfmt) {
+ monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
+ if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
+ which = qe->chan;
+ else
+ which = peer;
+ if (monitorfilename)
+ ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
+ else if (qe->chan->cdr)
+ ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
+ else {
+ /* Last ditch effort -- no CDR, make up something */
+ char tmpid[256];
+ snprintf(tmpid, sizeof(tmpid), "chan-%x", rand());
+ ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
+ }
+ if (qe->parent->monjoin)
+ ast_monitor_setjoinfiles(which, 1);
+ }
+ /* Drop out of the queue at this point, to prepare for next caller */
+ leave_queue(qe);
+ if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
+ ast_channel_sendurl(peer, url);
+ }
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
+ if (qe->parent->eventwhencalled)
+ manager_event(EVENT_FLAG_AGENT, "AgentConnect",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "Member: %s\r\n"
+ "Holdtime: %ld\r\n",
+ queuename, qe->chan->uniqueid, peer->name, member->interface,
+ (long)time(NULL) - qe->start);
+ ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
+ ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
+ time(&callstart);
+
+ bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
+
+ if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
+ } else if (qe->chan->_softhangup) {
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
+ (long)(callstart - qe->start), (long)(time(NULL) - callstart));
+ if (qe->parent->eventwhencalled)
+ manager_event(EVENT_FLAG_AGENT, "AgentComplete",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "Member: %s\r\n"
+ "HoldTime: %ld\r\n"
+ "TalkTime: %ld\r\n"
+ "Reason: caller\r\n",
+ queuename, qe->chan->uniqueid, peer->name, member->interface,
+ (long)(callstart - qe->start), (long)(time(NULL) - callstart));
+ } else {
+ ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
+ if (qe->parent->eventwhencalled)
+ manager_event(EVENT_FLAG_AGENT, "AgentComplete",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "HoldTime: %ld\r\n"
+ "TalkTime: %ld\r\n"
+ "Reason: agent\r\n",
+ queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start),
+ (long)(time(NULL) - callstart));
+ }
+
+ if(bridge != AST_PBX_NO_HANGUP_PEER)
+ ast_hangup(peer);
+ update_queue(qe->parent, member);
+ if (bridge == 0)
+ res = 1; /* JDG: bridge successfull, leave app_queue */
+ else
+ res = bridge; /* bridge error, stay in the queue */
+ }
+out:
+ hangupcalls(outgoing, NULL);
+ return res;
+}
+
+static int wait_a_bit(struct queue_ent *qe)
+{
+ /* Don't need to hold the lock while we setup the outgoing calls */
+ int retrywait = qe->parent->retry * 1000;
+
+ return ast_waitfordigit(qe->chan, retrywait);
+}
+
+static struct member * interface_exists(struct ast_call_queue *q, char *interface)
+{
+ struct member *mem;
+
+ if (q)
+ for (mem = q->members; mem; mem = mem->next)
+ if (!strcasecmp(interface, mem->interface))
+ return mem;
+
+ return NULL;
+}
+
+
+/* Dump all members in a specific queue to the databse
+ *
+ * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
+ *
+ */
+static void dump_queue_members(struct ast_call_queue *pm_queue)
+{
+ struct member *cur_member;
+ char value[PM_MAX_LEN];
+ int value_len = 0;
+ int res;
+
+ memset(value, 0, sizeof(value));
+
+ if (!pm_queue)
+ return;
+
+ for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
+ if (!cur_member->dynamic)
+ continue;
+
+ res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
+ cur_member->interface, cur_member->penalty, cur_member->paused,
+ cur_member->next ? "|" : "");
+ if (res != strlen(value + value_len)) {
+ ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
+ break;
+ }
+ value_len += res;
+ }
+
+ if (value_len && !cur_member) {
+ if (ast_db_put(pm_family, pm_queue->name, value))
+ ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
+ } else
+ /* Delete the entry if the queue is empty or there is an error */
+ ast_db_del(pm_family, pm_queue->name);
+}
+
+static int remove_from_queue(char *queuename, char *interface)
+{
+ struct ast_call_queue *q;
+ struct member *last_member, *look;
+ int res = RES_NOSUCHQUEUE;
+
+ ast_mutex_lock(&qlock);
+ for (q = queues ; q ; q = q->next) {
+ ast_mutex_lock(&q->lock);
+ if (!strcmp(q->name, queuename)) {
+ if ((last_member = interface_exists(q, interface))) {
+ if ((look = q->members) == last_member) {
+ q->members = last_member->next;
+ } else {
+ while (look != NULL) {
+ if (look->next == last_member) {
+ look->next = last_member->next;
+ break;
+ } else {
+ look = look->next;
+ }
+ }
+ }
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
+ "Queue: %s\r\n"
+ "Location: %s\r\n",
+ q->name, last_member->interface);
+ free(last_member);
+
+ if (queue_persistent_members)
+ dump_queue_members(q);
+
+ res = RES_OKAY;
+ } else {
+ res = RES_EXISTS;
+ }
+ ast_mutex_unlock(&q->lock);
+ break;
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ ast_mutex_unlock(&qlock);
+ return res;
+}
+
+static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump)
+{
+ struct ast_call_queue *q;
+ struct member *new_member;
+ int res = RES_NOSUCHQUEUE;
+
+ /* \note Ensure the appropriate realtime queue is loaded. Note that this
+ * short-circuits if the queue is already in memory. */
+ q = load_realtime_queue(queuename);
+
+ ast_mutex_lock(&qlock);
+
+ if (q) {
+ ast_mutex_lock(&q->lock);
+ if (interface_exists(q, interface) == NULL) {
+ new_member = create_queue_member(interface, penalty, paused);
+
+ if (new_member != NULL) {
+ new_member->dynamic = 1;
+ new_member->next = q->members;
+ q->members = new_member;
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n",
+ q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
+ new_member->penalty, new_member->calls, (int)new_member->lastcall, new_member->status, new_member->paused);
+
+ if (dump)
+ dump_queue_members(q);
+
+ res = RES_OKAY;
+ } else {
+ res = RES_OUTOFMEMORY;
+ }
+ } else {
+ res = RES_EXISTS;
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ ast_mutex_unlock(&qlock);
+ return res;
+}
+
+static int set_member_paused(char *queuename, char *interface, int paused)
+{
+ int found = 0;
+ struct ast_call_queue *q;
+ struct member *mem;
+
+ /* Special event for when all queues are paused - individual events still generated */
+
+ if (ast_strlen_zero(queuename))
+ ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
+
+ ast_mutex_lock(&qlock);
+ for (q = queues ; q ; q = q->next) {
+ ast_mutex_lock(&q->lock);
+ if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
+ if ((mem = interface_exists(q, interface))) {
+ found++;
+ if (mem->paused == paused)
+ ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
+ mem->paused = paused;
+
+ if (queue_persistent_members)
+ dump_queue_members(q);
+
+ ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
+
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Paused: %d\r\n",
+ q->name, mem->interface, paused);
+ }
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ ast_mutex_unlock(&qlock);
+
+ if (found)
+ return RESULT_SUCCESS;
+ else
+ return RESULT_FAILURE;
+}
+
+/* Reload dynamic queue members persisted into the astdb */
+static void reload_queue_members(void)
+{
+ char *cur_ptr;
+ char *queue_name;
+ char *member;
+ char *interface;
+ char *penalty_tok;
+ int penalty = 0;
+ char *paused_tok;
+ int paused = 0;
+ struct ast_db_entry *db_tree;
+ struct ast_db_entry *entry;
+ struct ast_call_queue *cur_queue;
+ char queue_data[PM_MAX_LEN];
+
+ ast_mutex_lock(&qlock);
+
+ /* Each key in 'pm_family' is the name of a queue */
+ db_tree = ast_db_gettree(pm_family, NULL);
+ for (entry = db_tree; entry; entry = entry->next) {
+
+ queue_name = entry->key + strlen(pm_family) + 2;
+
+ cur_queue = queues;
+ while (cur_queue) {
+ ast_mutex_lock(&cur_queue->lock);
+ if (!strcmp(queue_name, cur_queue->name))
+ break;
+ ast_mutex_unlock(&cur_queue->lock);
+ cur_queue = cur_queue->next;
+ }
+
+ if (!cur_queue) {
+ /* If the queue no longer exists, remove it from the
+ * database */
+ ast_db_del(pm_family, queue_name);
+ continue;
+ } else
+ ast_mutex_unlock(&cur_queue->lock);
+
+ if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
+ continue;
+
+ cur_ptr = queue_data;
+ while ((member = strsep(&cur_ptr, "|"))) {
+ if (ast_strlen_zero(member))
+ continue;
+
+ interface = strsep(&member, ";");
+ penalty_tok = strsep(&member, ";");
+ paused_tok = strsep(&member, ";");
+
+ if (!penalty_tok) {
+ ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name);
+ break;
+ }
+ penalty = strtol(penalty_tok, NULL, 10);
+ if (errno == ERANGE) {
+ ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
+ break;
+ }
+
+ if (!paused_tok) {
+ ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
+ break;
+ }
+ paused = strtol(paused_tok, NULL, 10);
+ if ((errno == ERANGE) || paused < 0 || paused > 1) {
+ ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
+ break;
+ }
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused);
+
+ if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
+ ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
+ break;
+ }
+ }
+ }
+
+ ast_mutex_unlock(&qlock);
+ if (db_tree) {
+ ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
+ ast_db_freetree(db_tree);
+ }
+}
+
+static int pqm_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *parse;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (ast_strlen_zero(args.interface)) {
+ ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (set_member_paused(args.queuename, args.interface, 1)) {
+ ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
+ return -1;
+ }
+
+ LOCAL_USER_REMOVE(u);
+ pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
+ return 0;
+}
+
+static int upqm_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *parse;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (ast_strlen_zero(args.interface)) {
+ ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (set_member_paused(args.queuename, args.interface, 0)) {
+ ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
+ if (priority_jump || option_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
+ return -1;
+ }
+
+ LOCAL_USER_REMOVE(u);
+ pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
+ return 0;
+}
+
+static int rqm_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ char *parse, *temppos = NULL;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(options);
+ );
+
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.interface)) {
+ args.interface = ast_strdupa(chan->name);
+ temppos = strrchr(args.interface, '-');
+ if (temppos)
+ *temppos = '\0';
+ }
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ switch (remove_from_queue(args.queuename, args.interface)) {
+ case RES_OKAY:
+ ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
+ res = 0;
+ break;
+ case RES_EXISTS:
+ ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
+ res = 0;
+ break;
+ case RES_NOSUCHQUEUE:
+ ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
+ res = 0;
+ break;
+ case RES_OUTOFMEMORY:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int aqm_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ char *parse, *temppos = NULL;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(penalty);
+ AST_APP_ARG(options);
+ );
+ int penalty = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options]])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (!(parse = ast_strdupa(data))) {
+ ast_log(LOG_WARNING, "Memory Error!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.interface)) {
+ args.interface = ast_strdupa(chan->name);
+ temppos = strrchr(args.interface, '-');
+ if (temppos)
+ *temppos = '\0';
+ }
+
+ if (!ast_strlen_zero(args.penalty)) {
+ if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
+ ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
+ penalty = 0;
+ }
+ }
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+
+ switch (add_to_queue(args.queuename, args.interface, penalty, 0, queue_persistent_members)) {
+ case RES_OKAY:
+ ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
+ pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
+ res = 0;
+ break;
+ case RES_EXISTS:
+ ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
+ res = 0;
+ break;
+ case RES_NOSUCHQUEUE:
+ ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
+ pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
+ res = 0;
+ break;
+ case RES_OUTOFMEMORY:
+ ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
+ break;
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int queue_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ int ringing=0;
+ struct localuser *u;
+ char *queuename;
+ char info[512];
+ char *info_ptr = info;
+ char *options = NULL;
+ char *url = NULL;
+ char *announceoverride = NULL;
+ char *user_priority;
+ int prio;
+ char *queuetimeoutstr = NULL;
+ enum queue_result reason = QUEUE_UNKNOWN;
+
+ /* whether to exit Queue application after the timeout hits */
+ int go_on = 0;
+
+ /* Our queue entry */
+ struct queue_ent qe;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Setup our queue entry */
+ memset(&qe, 0, sizeof(qe));
+ qe.start = time(NULL);
+
+ /* Parse our arguments XXX Check for failure XXX */
+ ast_copy_string(info, (char *) data, sizeof(info));
+ queuename = strsep(&info_ptr, "|");
+ options = strsep(&info_ptr, "|");
+ url = strsep(&info_ptr, "|");
+ announceoverride = strsep(&info_ptr, "|");
+ queuetimeoutstr = info_ptr;
+
+ /* set the expire time based on the supplied timeout; */
+ if (queuetimeoutstr)
+ qe.expire = qe.start + atoi(queuetimeoutstr);
+ else
+ qe.expire = 0;
+
+ /* Get the priority from the variable ${QUEUE_PRIO} */
+ user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
+ if (user_priority) {
+ if (sscanf(user_priority, "%d", &prio) == 1) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
+ chan->name, prio);
+ } else {
+ ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
+ user_priority, chan->name);
+ prio = 0;
+ }
+ } else {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
+ prio = 0;
+ }
+
+ if (options && (strchr(options, 'r')))
+ ringing = 1;
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
+ queuename, options, url, announceoverride, (long)qe.expire, (int)prio);
+
+ qe.chan = chan;
+ qe.prio = (int)prio;
+ qe.last_pos_said = 0;
+ qe.last_pos = 0;
+ qe.last_periodic_announce_time = time(NULL);
+ if (!join_queue(queuename, &qe, &reason)) {
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "",
+ chan->cid.cid_num ? chan->cid.cid_num : "");
+check_turns:
+ if (ringing) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ } else {
+ ast_moh_start(chan, qe.moh);
+ }
+ for (;;) {
+ /* This is the wait loop for callers 2 through maxlen */
+
+ res = wait_our_turn(&qe, ringing, &reason);
+ /* If they hungup, return immediately */
+ if (res < 0) {
+ /* Record this abandoned call */
+ record_abandoned(&qe);
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", queuename);
+ res = -1;
+ }
+ break;
+ }
+ if (!res)
+ break;
+ if (valid_exit(&qe, res)) {
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
+ break;
+ }
+ }
+ if (!res) {
+ int makeannouncement = 0;
+ for (;;) {
+ /* This is the wait loop for the head caller*/
+ /* To exit, they may get their call answered; */
+ /* they may dial a digit from the queue context; */
+ /* or, they may timeout. */
+
+ enum queue_member_status stat;
+
+ /* Leave if we have exceeded our queuetimeout */
+ if (qe.expire && (time(NULL) > qe.expire)) {
+ record_abandoned(&qe);
+ reason = QUEUE_TIMEOUT;
+ res = 0;
+ ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
+ break;
+ }
+
+ if (makeannouncement) {
+ /* Make a position announcement, if enabled */
+ if (qe.parent->announcefrequency && !ringing)
+ res = say_position(&qe);
+ if (res && valid_exit(&qe, res)) {
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
+ break;
+ }
+
+ }
+ makeannouncement = 1;
+
+ /* Make a periodic announcement, if enabled */
+ if (qe.parent->periodicannouncefrequency && !ringing)
+ res = say_periodic_announcement(&qe);
+
+ if (res && valid_exit(&qe, res)) {
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
+ break;
+ }
+
+ /* Try calling all queue members for 'timeout' seconds */
+ res = try_calling(&qe, options, announceoverride, url, &go_on);
+ if (res) {
+ if (res < 0) {
+ if (!qe.handled) {
+ record_abandoned(&qe);
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
+ }
+ } else if (res > 0)
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
+ break;
+ }
+
+ stat = get_member_status(qe.parent);
+
+ /* leave the queue if no agents, if enabled */
+ if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
+ record_abandoned(&qe);
+ reason = QUEUE_LEAVEEMPTY;
+ res = 0;
+ break;
+ }
+
+ /* leave the queue if no reachable agents, if enabled */
+ if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
+ record_abandoned(&qe);
+ reason = QUEUE_LEAVEUNAVAIL;
+ res = 0;
+ break;
+ }
+
+ /* Leave if we have exceeded our queuetimeout */
+ if (qe.expire && (time(NULL) > qe.expire)) {
+ record_abandoned(&qe);
+ reason = QUEUE_TIMEOUT;
+ res = 0;
+ ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
+ break;
+ }
+
+ /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
+ res = wait_a_bit(&qe);
+ if (res < 0) {
+ record_abandoned(&qe);
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", queuename);
+ res = -1;
+ }
+ break;
+ }
+ if (res && valid_exit(&qe, res)) {
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
+ break;
+ }
+ /* exit after 'timeout' cycle if 'n' option enabled */
+ if (go_on) {
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
+ res = -1;
+ }
+ ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
+ record_abandoned(&qe);
+ reason = QUEUE_TIMEOUT;
+ res = 0;
+ break;
+ }
+ /* Since this is a priority queue and
+ * it is not sure that we are still at the head
+ * of the queue, go and check for our turn again.
+ */
+ if (!is_our_turn(&qe)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
+ qe.chan->name);
+ goto check_turns;
+ }
+ }
+ }
+ /* Don't allow return code > 0 */
+ if (res >= 0 && res != AST_PBX_KEEPALIVE) {
+ res = 0;
+ if (ringing) {
+ ast_indicate(chan, -1);
+ } else {
+ ast_moh_stop(chan);
+ }
+ ast_stopstream(chan);
+ }
+ leave_queue(&qe);
+ if (reason != QUEUE_UNKNOWN)
+ set_queue_result(chan, reason);
+ } else {
+ ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
+ set_queue_result(chan, reason);
+ res = 0;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static char *queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+ int count = 0;
+ struct ast_call_queue *q;
+ struct localuser *u;
+ struct member *m;
+
+ LOCAL_USER_ACF_ADD(u);
+
+ ast_copy_string(buf, "0", len);
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "QUEUEAGENTCOUNT requires an argument: queuename\n");
+ LOCAL_USER_REMOVE(u);
+ return buf;
+ }
+
+ ast_mutex_lock(&qlock);
+
+ /* Find the right queue */
+ for (q = queues; q; q = q->next) {
+ if (!strcasecmp(q->name, data)) {
+ ast_mutex_lock(&q->lock);
+ break;
+ }
+ }
+
+ ast_mutex_unlock(&qlock);
+
+ if (q) {
+ for (m = q->members; m; m = m->next) {
+ /* Count the agents who are logged in and presently answering calls */
+ if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
+ count++;
+ }
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+
+ snprintf(buf, len, "%d", count);
+ LOCAL_USER_REMOVE(u);
+ return buf;
+}
+
+static struct ast_custom_function queueagentcount_function = {
+ .name = "QUEUEAGENTCOUNT",
+ .synopsis = "Count number of agents answering a queue",
+ .syntax = "QUEUEAGENTCOUNT(<queuename>)",
+ .read = queue_function_qac,
+};
+
+static void reload_queues(void)
+{
+ struct ast_call_queue *q, *ql, *qn;
+ struct ast_config *cfg;
+ char *cat, *tmp;
+ struct ast_variable *var;
+ struct member *prev, *cur;
+ int new;
+ char *general_val = NULL;
+ char interface[80];
+ int penalty;
+
+ cfg = ast_config_load("queues.conf");
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
+ return;
+ }
+ memset(interface, 0, sizeof(interface));
+ ast_mutex_lock(&qlock);
+ use_weight=0;
+ /* Mark all queues as dead for the moment */
+ q = queues;
+ while(q) {
+ q->dead = 1;
+ q = q->next;
+ }
+ /* Chug through config file */
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (!strcasecmp(cat, "general")) {
+ /* Initialize global settings */
+ queue_persistent_members = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
+ queue_persistent_members = ast_true(general_val);
+ } else { /* Define queue */
+ /* Look for an existing one */
+ q = queues;
+ while(q) {
+ if (!strcmp(q->name, cat))
+ break;
+ q = q->next;
+ }
+ if (!q) {
+ /* Make one then */
+ q = alloc_queue(cat);
+ new = 1;
+ } else
+ new = 0;
+ if (q) {
+ if (!new)
+ ast_mutex_lock(&q->lock);
+ /* Re-initialize the queue, and clear statistics */
+ init_queue(q);
+ clear_queue(q);
+ free_members(q, 0);
+ prev = q->members;
+ if (prev) {
+ /* find the end of any dynamic members */
+ while(prev->next)
+ prev = prev->next;
+ }
+ var = ast_variable_browse(cfg, cat);
+ while(var) {
+ if (!strcasecmp(var->name, "member")) {
+ /* Add a new member */
+ ast_copy_string(interface, var->value, sizeof(interface));
+ if ((tmp = strchr(interface, ','))) {
+ *tmp = '\0';
+ tmp++;
+ penalty = atoi(tmp);
+ if (penalty < 0) {
+ penalty = 0;
+ }
+ } else
+ penalty = 0;
+ cur = create_queue_member(interface, penalty, 0);
+ if (cur) {
+ if (prev)
+ prev->next = cur;
+ else
+ q->members = cur;
+ prev = cur;
+ }
+ } else {
+ queue_set_param(q, var->name, var->value, var->lineno, 1);
+ }
+ var = var->next;
+ }
+ if (!new)
+ ast_mutex_unlock(&q->lock);
+ if (new) {
+ q->next = queues;
+ queues = q;
+ }
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_config_destroy(cfg);
+ q = queues;
+ ql = NULL;
+ while(q) {
+ qn = q->next;
+ if (q->dead) {
+ if (ql)
+ ql->next = q->next;
+ else
+ queues = q->next;
+ if (!q->count) {
+ destroy_queue(q);
+ } else
+ ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
+ } else {
+ for (cur = q->members; cur; cur = cur->next)
+ cur->status = ast_device_state(cur->interface);
+ ql = q;
+ }
+ q = qn;
+ }
+ ast_mutex_unlock(&qlock);
+}
+
+static int __queues_show(int manager, int fd, int argc, char **argv, int queue_show)
+{
+ struct ast_call_queue *q;
+ struct queue_ent *qe;
+ struct member *mem;
+ int pos;
+ time_t now;
+ char max_buf[80];
+ char *max;
+ size_t max_left;
+ float sl = 0;
+ char *term = manager ? "\r\n" : "\n";
+
+ time(&now);
+ if ((!queue_show && argc != 2) || (queue_show && argc != 3))
+ return RESULT_SHOWUSAGE;
+
+ /* We only want to load realtime queues when a specific queue is asked for. */
+ if (queue_show)
+ load_realtime_queue(argv[2]);
+
+ ast_mutex_lock(&qlock);
+
+ q = queues;
+ if (!q) {
+ ast_mutex_unlock(&qlock);
+ if (queue_show)
+ ast_cli(fd, "No such queue: %s.%s",argv[2], term);
+ else
+ ast_cli(fd, "No queues.%s", term);
+ return RESULT_SUCCESS;
+ }
+ while (q) {
+ ast_mutex_lock(&q->lock);
+ if (queue_show) {
+ if (strcasecmp(q->name, argv[2]) != 0) {
+ ast_mutex_unlock(&q->lock);
+ q = q->next;
+ if (!q) {
+ ast_cli(fd, "No such queue: %s.%s",argv[2], term);
+ break;
+ }
+ continue;
+ }
+ }
+ max_buf[0] = '\0';
+ max = max_buf;
+ max_left = sizeof(max_buf);
+ if (q->maxlen)
+ ast_build_string(&max, &max_left, "%d", q->maxlen);
+ else
+ ast_build_string(&max, &max_left, "unlimited");
+ sl = 0;
+ if(q->callscompleted > 0)
+ sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
+ ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
+ q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
+ if (q->members) {
+ ast_cli(fd, " Members: %s", term);
+ for (mem = q->members; mem; mem = mem->next) {
+ max_buf[0] = '\0';
+ max = max_buf;
+ max_left = sizeof(max_buf);
+ if (mem->penalty)
+ ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
+ if (mem->dynamic)
+ ast_build_string(&max, &max_left, " (dynamic)");
+ if (mem->paused)
+ ast_build_string(&max, &max_left, " (paused)");
+ ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
+ if (mem->calls) {
+ ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
+ mem->calls, (long)(time(NULL) - mem->lastcall));
+ } else
+ ast_build_string(&max, &max_left, " has taken no calls yet");
+ ast_cli(fd, " %s%s%s", mem->interface, max_buf, term);
+ }
+ } else
+ ast_cli(fd, " No Members%s", term);
+ if (q->head) {
+ pos = 1;
+ ast_cli(fd, " Callers: %s", term);
+ for (qe = q->head; qe; qe = qe->next)
+ ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, qe->chan->name,
+ (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio, term);
+ } else
+ ast_cli(fd, " No Callers%s", term);
+ ast_cli(fd, "%s", term);
+ ast_mutex_unlock(&q->lock);
+ q = q->next;
+ if (queue_show)
+ break;
+ }
+ ast_mutex_unlock(&qlock);
+ return RESULT_SUCCESS;
+}
+
+static int queues_show(int fd, int argc, char **argv)
+{
+ return __queues_show(0, fd, argc, argv, 0);
+}
+
+static int queue_show(int fd, int argc, char **argv)
+{
+ return __queues_show(0, fd, argc, argv, 1);
+}
+
+static char *complete_queue(char *line, char *word, int pos, int state)
+{
+ struct ast_call_queue *q;
+ int which=0;
+
+ ast_mutex_lock(&qlock);
+ for (q = queues; q; q = q->next) {
+ if (!strncasecmp(word, q->name, strlen(word))) {
+ if (++which > state)
+ break;
+ }
+ }
+ ast_mutex_unlock(&qlock);
+ return q ? strdup(q->name) : NULL;
+}
+
+/*!\brief callback to display queues status in manager
+ \addtogroup Group_AMI
+ */
+static int manager_queues_show( struct mansession *s, struct message *m )
+{
+ char *a[] = { "show", "queues" };
+ __queues_show(1, s->fd, 2, a, 0);
+ ast_cli(s->fd, "\r\n\r\n"); /* Properly terminate Manager output */
+
+ return RESULT_SUCCESS;
+}
+
+/* Dump queue status */
+static int manager_queues_status( struct mansession *s, struct message *m )
+{
+ time_t now;
+ int pos;
+ char *id = astman_get_header(m,"ActionID");
+ char *queuefilter = astman_get_header(m,"Queue");
+ char *memberfilter = astman_get_header(m,"Member");
+ char idText[256] = "";
+ struct ast_call_queue *q;
+ struct queue_ent *qe;
+ float sl = 0;
+ struct member *mem;
+
+ astman_send_ack(s, m, "Queue status will follow");
+ time(&now);
+ ast_mutex_lock(&qlock);
+ if (!ast_strlen_zero(id)) {
+ snprintf(idText,256,"ActionID: %s\r\n",id);
+ }
+ for (q = queues; q; q = q->next) {
+ ast_mutex_lock(&q->lock);
+
+ /* List queue properties */
+ if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
+ if(q->callscompleted > 0)
+ sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
+ ast_cli(s->fd, "Event: QueueParams\r\n"
+ "Queue: %s\r\n"
+ "Max: %d\r\n"
+ "Calls: %d\r\n"
+ "Holdtime: %d\r\n"
+ "Completed: %d\r\n"
+ "Abandoned: %d\r\n"
+ "ServiceLevel: %d\r\n"
+ "ServicelevelPerf: %2.1f\r\n"
+ "Weight: %d\r\n"
+ "%s"
+ "\r\n",
+ q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
+ q->callsabandoned, q->servicelevel, sl, q->weight, idText);
+ /* List Queue Members */
+ for (mem = q->members; mem; mem = mem->next) {
+ if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
+ ast_cli(s->fd, "Event: QueueMember\r\n"
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n"
+ "%s"
+ "\r\n",
+ q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
+ mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
+ }
+ }
+ /* List Queue Entries */
+ pos = 1;
+ for (qe = q->head; qe; qe = qe->next) {
+ ast_cli(s->fd, "Event: QueueEntry\r\n"
+ "Queue: %s\r\n"
+ "Position: %d\r\n"
+ "Channel: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Wait: %ld\r\n"
+ "%s"
+ "\r\n",
+ q->name, pos++, qe->chan->name,
+ qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
+ qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
+ (long)(now - qe->start), idText);
+ }
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ ast_mutex_unlock(&qlock);
+
+ ast_cli(s->fd,
+ "Event: QueueStatusComplete\r\n"
+ "%s"
+ "\r\n",idText);
+
+
+ return RESULT_SUCCESS;
+}
+
+static int manager_add_queue_member(struct mansession *s, struct message *m)
+{
+ char *queuename, *interface, *penalty_s, *paused_s;
+ int paused, penalty = 0;
+
+ queuename = astman_get_header(m, "Queue");
+ interface = astman_get_header(m, "Interface");
+ penalty_s = astman_get_header(m, "Penalty");
+ paused_s = astman_get_header(m, "Paused");
+
+ if (ast_strlen_zero(queuename)) {
+ astman_send_error(s, m, "'Queue' not specified.");
+ return 0;
+ }
+
+ if (ast_strlen_zero(interface)) {
+ astman_send_error(s, m, "'Interface' not specified.");
+ return 0;
+ }
+
+ if (ast_strlen_zero(penalty_s))
+ penalty = 0;
+ else if (sscanf(penalty_s, "%d", &penalty) != 1) {
+ penalty = 0;
+ }
+
+ if (ast_strlen_zero(paused_s))
+ paused = 0;
+ else
+ paused = abs(ast_true(paused_s));
+
+ switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) {
+ case RES_OKAY:
+ astman_send_ack(s, m, "Added interface to queue");
+ break;
+ case RES_EXISTS:
+ astman_send_error(s, m, "Unable to add interface: Already there");
+ break;
+ case RES_NOSUCHQUEUE:
+ astman_send_error(s, m, "Unable to add interface to queue: No such queue");
+ break;
+ case RES_OUTOFMEMORY:
+ astman_send_error(s, m, "Out of memory");
+ break;
+ }
+ return 0;
+}
+
+static int manager_remove_queue_member(struct mansession *s, struct message *m)
+{
+ char *queuename, *interface;
+
+ queuename = astman_get_header(m, "Queue");
+ interface = astman_get_header(m, "Interface");
+
+ if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
+ astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
+ return 0;
+ }
+
+ switch (remove_from_queue(queuename, interface)) {
+ case RES_OKAY:
+ astman_send_ack(s, m, "Removed interface from queue");
+ break;
+ case RES_EXISTS:
+ astman_send_error(s, m, "Unable to remove interface: Not there");
+ break;
+ case RES_NOSUCHQUEUE:
+ astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
+ break;
+ case RES_OUTOFMEMORY:
+ astman_send_error(s, m, "Out of memory");
+ break;
+ }
+ return 0;
+}
+
+static int manager_pause_queue_member(struct mansession *s, struct message *m)
+{
+ char *queuename, *interface, *paused_s;
+ int paused;
+
+ interface = astman_get_header(m, "Interface");
+ paused_s = astman_get_header(m, "Paused");
+ queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
+
+ if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
+ astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
+ return 0;
+ }
+
+ paused = abs(ast_true(paused_s));
+
+ if (set_member_paused(queuename, interface, paused))
+ astman_send_error(s, m, "Interface not found");
+ else
+ if (paused)
+ astman_send_ack(s, m, "Interface paused successfully");
+ else
+ astman_send_ack(s, m, "Interface unpaused successfully");
+
+ return 0;
+}
+
+static int handle_add_queue_member(int fd, int argc, char *argv[])
+{
+ char *queuename, *interface;
+ int penalty;
+
+ if ((argc != 6) && (argc != 8)) {
+ return RESULT_SHOWUSAGE;
+ } else if (strcmp(argv[4], "to")) {
+ return RESULT_SHOWUSAGE;
+ } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
+ return RESULT_SHOWUSAGE;
+ }
+
+ queuename = argv[5];
+ interface = argv[3];
+ if (argc == 8) {
+ if (sscanf(argv[7], "%d", &penalty) == 1) {
+ if (penalty < 0) {
+ ast_cli(fd, "Penalty must be >= 0\n");
+ penalty = 0;
+ }
+ } else {
+ ast_cli(fd, "Penalty must be an integer >= 0\n");
+ penalty = 0;
+ }
+ } else {
+ penalty = 0;
+ }
+
+ switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) {
+ case RES_OKAY:
+ ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
+ return RESULT_SUCCESS;
+ case RES_EXISTS:
+ ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
+ return RESULT_FAILURE;
+ case RES_NOSUCHQUEUE:
+ ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
+ return RESULT_FAILURE;
+ case RES_OUTOFMEMORY:
+ ast_cli(fd, "Out of memory\n");
+ return RESULT_FAILURE;
+ default:
+ return RESULT_FAILURE;
+ }
+}
+
+static char *complete_add_queue_member(char *line, char *word, int pos, int state)
+{
+ /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty> */
+ switch (pos) {
+ case 3:
+ /* Don't attempt to complete name of member (infinite possibilities) */
+ return NULL;
+ case 4:
+ if (state == 0) {
+ return strdup("to");
+ } else {
+ return NULL;
+ }
+ case 5:
+ /* No need to duplicate code */
+ return complete_queue(line, word, pos, state);
+ case 6:
+ if (state == 0) {
+ return strdup("penalty");
+ } else {
+ return NULL;
+ }
+ case 7:
+ if (state < 100) { /* 0-99 */
+ char *num = malloc(3);
+ if (num) {
+ sprintf(num, "%d", state);
+ }
+ return num;
+ } else {
+ return NULL;
+ }
+ default:
+ return NULL;
+ }
+}
+
+static int handle_remove_queue_member(int fd, int argc, char *argv[])
+{
+ char *queuename, *interface;
+
+ if (argc != 6) {
+ return RESULT_SHOWUSAGE;
+ } else if (strcmp(argv[4], "from")) {
+ return RESULT_SHOWUSAGE;
+ }
+
+ queuename = argv[5];
+ interface = argv[3];
+
+ switch (remove_from_queue(queuename, interface)) {
+ case RES_OKAY:
+ ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
+ return RESULT_SUCCESS;
+ case RES_EXISTS:
+ ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
+ return RESULT_FAILURE;
+ case RES_NOSUCHQUEUE:
+ ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
+ return RESULT_FAILURE;
+ case RES_OUTOFMEMORY:
+ ast_cli(fd, "Out of memory\n");
+ return RESULT_FAILURE;
+ default:
+ return RESULT_FAILURE;
+ }
+}
+
+static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
+{
+ int which = 0;
+ struct ast_call_queue *q;
+ struct member *m;
+
+ /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue> */
+ if ((pos > 5) || (pos < 3)) {
+ return NULL;
+ }
+ if (pos == 4) {
+ if (state == 0) {
+ return strdup("from");
+ } else {
+ return NULL;
+ }
+ }
+
+ if (pos == 5) {
+ /* No need to duplicate code */
+ return complete_queue(line, word, pos, state);
+ }
+
+ if (queues != NULL) {
+ for (q = queues ; q ; q = q->next) {
+ ast_mutex_lock(&q->lock);
+ for (m = q->members ; m ; m = m->next) {
+ if (++which > state) {
+ ast_mutex_unlock(&q->lock);
+ return strdup(m->interface);
+ }
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ }
+ return NULL;
+}
+
+static char show_queues_usage[] =
+"Usage: show queues\n"
+" Provides summary information on call queues.\n";
+
+static struct ast_cli_entry cli_show_queues = {
+ { "show", "queues", NULL }, queues_show,
+ "Show status of queues", show_queues_usage, NULL };
+
+static char show_queue_usage[] =
+"Usage: show queue\n"
+" Provides summary information on a specified queue.\n";
+
+static struct ast_cli_entry cli_show_queue = {
+ { "show", "queue", NULL }, queue_show,
+ "Show status of a specified queue", show_queue_usage, complete_queue };
+
+static char aqm_cmd_usage[] =
+"Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
+
+static struct ast_cli_entry cli_add_queue_member = {
+ { "add", "queue", "member", NULL }, handle_add_queue_member,
+ "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };
+
+static char rqm_cmd_usage[] =
+"Usage: remove queue member <channel> from <queue>\n";
+
+static struct ast_cli_entry cli_remove_queue_member = {
+ { "remove", "queue", "member", NULL }, handle_remove_queue_member,
+ "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member };
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_cli_unregister(&cli_show_queue);
+ res |= ast_cli_unregister(&cli_show_queues);
+ res |= ast_cli_unregister(&cli_add_queue_member);
+ res |= ast_cli_unregister(&cli_remove_queue_member);
+ res |= ast_manager_unregister("Queues");
+ res |= ast_manager_unregister("QueueStatus");
+ res |= ast_manager_unregister("QueueAdd");
+ res |= ast_manager_unregister("QueueRemove");
+ res |= ast_manager_unregister("QueuePause");
+ ast_devstate_del(statechange_queue, NULL);
+ res |= ast_unregister_application(app_aqm);
+ res |= ast_unregister_application(app_rqm);
+ res |= ast_unregister_application(app_pqm);
+ res |= ast_unregister_application(app_upqm);
+ res |= ast_custom_function_unregister(&queueagentcount_function);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app, queue_exec, synopsis, descrip);
+ res |= ast_cli_register(&cli_show_queue);
+ res |= ast_cli_register(&cli_show_queues);
+ res |= ast_cli_register(&cli_add_queue_member);
+ res |= ast_cli_register(&cli_remove_queue_member);
+ res |= ast_devstate_add(statechange_queue, NULL);
+ res |= ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
+ res |= ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
+ res |= ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
+ res |= ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
+ res |= ast_manager_register( "QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable" );
+ res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
+ res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
+ res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ;
+ res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ;
+ res |= ast_custom_function_register(&queueagentcount_function);
+
+ if (!res) {
+ reload_queues();
+ if (queue_persistent_members)
+ reload_queue_members();
+ }
+
+ return res;
+}
+
+
+int reload(void)
+{
+ reload_queues();
+ return 0;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_random.c b/1.2-netsec/apps/app_random.c
new file mode 100644
index 000000000..57ef4dd32
--- /dev/null
+++ b/1.2-netsec/apps/app_random.c
@@ -0,0 +1,127 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2003 - 2005 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <asterisk__app_random__200508@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage or distribution.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Random application
+ *
+ * \author Tilghman Lesher <asterisk__app_random__200508@the-tilghman.com>
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+static char *tdesc = "Random goto";
+
+static char *app_random = "Random";
+
+static char *random_synopsis = "Conditionally branches, based upon a probability";
+
+static char *random_descrip =
+"Random([probability]:[[context|]extension|]priority)\n"
+" probability := INTEGER in the range 1 to 100\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char random_state[256];
+
+static int random_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+
+ char *s;
+ char *prob;
+ int probint;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Random requires an argument ([probability]:[[context|]extension|]priority)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ s = ast_strdupa(data);
+ if (!s) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ prob = strsep(&s,":");
+ if ((!prob) || (sscanf(prob, "%d", &probint) != 1))
+ probint = 0;
+
+ if ((random() % 100) + probint > 100) {
+ res = ast_parseable_goto(chan, s);
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Random branches to (%s,%s,%d)\n",
+ chan->context,chan->exten, chan->priority+1);
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_random);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ initstate((getppid() * 65535 + getpid()) % RAND_MAX, random_state, 256);
+ return ast_register_application(app_random, random_exec, random_synopsis, random_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_read.c b/1.2-netsec/apps/app_read.c
new file mode 100644
index 000000000..90e180fcf
--- /dev/null
+++ b/1.2-netsec/apps/app_read.c
@@ -0,0 +1,235 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Trivial application to read a variable
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Read Variable Application";
+
+static char *app = "Read";
+
+static char *synopsis = "Read a variable";
+
+static char *descrip =
+" Read(variable[|filename][|maxdigits][|option][|attempts][|timeout])\n\n"
+"Reads a #-terminated string of digits a certain number of times from the\n"
+"user in to the given variable.\n"
+" filename -- file to play before reading digits.\n"
+" maxdigits -- maximum acceptable number of digits. Stops reading after\n"
+" maxdigits have been entered (without requiring the user to\n"
+" press the '#' key).\n"
+" Defaults to 0 - no limit - wait for the user press the '#' key.\n"
+" Any value below 0 means the same. Max accepted value is 255.\n"
+" option -- may be 'skip' to return immediately if the line is not up,\n"
+" or 'noanswer' to read digits even if the line is not up.\n"
+" attempts -- if greater than 1, that many attempts will be made in the \n"
+" event no data is entered.\n"
+" timeout -- if greater than 0, that value will override the default timeout.\n\n"
+"Read should disconnect if the function fails or errors out.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+#define ast_next_data(instr,ptr,delim) if((ptr=strchr(instr,delim))) { *(ptr) = '\0' ; ptr++;}
+
+static int read_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char tmp[256];
+ char *timeout = NULL;
+ char *varname = NULL;
+ char *filename = NULL;
+ char *loops;
+ char *maxdigitstr=NULL;
+ char *options=NULL;
+ int option_skip = 0;
+ int option_noanswer = 0;
+ int maxdigits=255;
+ int tries = 1;
+ int to = 0;
+ int x = 0;
+ char *argcopy = NULL;
+ char *args[8];
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ argcopy = ast_strdupa(data);
+ if (!argcopy) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (ast_app_separate_args(argcopy, '|', args, sizeof(args) / sizeof(args[0])) < 1) {
+ ast_log(LOG_WARNING, "Cannot Parse Arguments.\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ varname = args[x++];
+ filename = args[x++];
+ maxdigitstr = args[x++];
+ options = args[x++];
+ loops = args[x++];
+ timeout = args[x++];
+
+ if (options) {
+ if (!strcasecmp(options, "skip"))
+ option_skip = 1;
+ else if (!strcasecmp(options, "noanswer"))
+ option_noanswer = 1;
+ else {
+ if (strchr(options, 's'))
+ option_skip = 1;
+ if (strchr(options, 'n'))
+ option_noanswer = 1;
+ }
+ }
+
+ if(loops) {
+ tries = atoi(loops);
+ if(tries <= 0)
+ tries = 1;
+ }
+
+ if(timeout) {
+ to = atoi(timeout);
+ if (to <= 0)
+ to = 0;
+ else
+ to *= 1000;
+ }
+
+ if (ast_strlen_zero(filename))
+ filename = NULL;
+ if (maxdigitstr) {
+ maxdigits = atoi(maxdigitstr);
+ if ((maxdigits<1) || (maxdigits>255)) {
+ maxdigits = 255;
+ } else if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Accepting a maximum of %d digits.\n", maxdigits);
+ }
+ if (ast_strlen_zero(varname)) {
+ ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[|filename][|maxdigits][|option][|attempts][|timeout])\n\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ if (option_skip) {
+ /* At the user's option, skip if the line is not up */
+ pbx_builtin_setvar_helper(chan, varname, "\0");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ } else if (!option_noanswer) {
+ /* Otherwise answer unless we're supposed to read while on-hook */
+ res = ast_answer(chan);
+ }
+ }
+ if (!res) {
+ while(tries && !res) {
+ ast_stopstream(chan);
+ res = ast_app_getdata(chan, filename, tmp, maxdigits, to);
+ if (res > -1) {
+ pbx_builtin_setvar_helper(chan, varname, tmp);
+ if (!ast_strlen_zero(tmp)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User entered '%s'\n", tmp);
+ tries = 0;
+ } else {
+ tries--;
+ if (option_verbose > 2) {
+ if (tries)
+ ast_verbose(VERBOSE_PREFIX_3 "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
+ else
+ ast_verbose(VERBOSE_PREFIX_3 "User entered nothing.\n");
+ }
+ }
+ res = 0;
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User disconnected\n");
+ }
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, read_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_readfile.c b/1.2-netsec/apps/app_readfile.c
new file mode 100644
index 000000000..bc418aaa1
--- /dev/null
+++ b/1.2-netsec/apps/app_readfile.c
@@ -0,0 +1,144 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief ReadFile application -- Reads in a File for you.
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+
+static char *tdesc = "Stores output of file into a variable";
+
+static char *app_readfile = "ReadFile";
+
+static char *readfile_synopsis = "ReadFile(varname=file,length)";
+
+static char *readfile_descrip =
+"ReadFile(varname=file,length)\n"
+" Varname - Result stored here.\n"
+" File - The name of the file to read.\n"
+" Length - Maximum number of characters to capture.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+
+static int readfile_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *s, *varname=NULL, *file=NULL, *length=NULL, *returnvar=NULL;
+ int len=0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ReadFile require an argument!\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ s = ast_strdupa(data);
+ if (!s) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ varname = strsep(&s, "=");
+ file = strsep(&s, "|");
+ length = s;
+
+ if (!varname || !file) {
+ ast_log(LOG_ERROR, "No file or variable specified!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (length) {
+ if ((sscanf(length, "%d", &len) != 1) || (len < 0)) {
+ ast_log(LOG_WARNING, "%s is not a positive number, defaulting length to max\n", length);
+ len = 0;
+ }
+ }
+
+ if ((returnvar = ast_read_textfile(file))) {
+ if (len > 0) {
+ if (len < strlen(returnvar))
+ returnvar[len]='\0';
+ else
+ ast_log(LOG_WARNING, "%s is longer than %d, and %d \n", file, len, (int)strlen(returnvar));
+ }
+ pbx_builtin_setvar_helper(chan, varname, returnvar);
+ free(returnvar);
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_readfile);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app_readfile, readfile_exec, readfile_synopsis, readfile_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_realtime.c b/1.2-netsec/apps/app_realtime.c
new file mode 100644
index 000000000..a731fd7c9
--- /dev/null
+++ b/1.2-netsec/apps/app_realtime.c
@@ -0,0 +1,262 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Anthony Minessale <anthmct@yahoo.com>
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief RealTime App
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+
+#define next_one(var) var = var->next
+#define crop_data(str) { *(str) = '\0' ; (str)++; }
+
+static char *tdesc = "Realtime Data Lookup/Rewrite";
+static char *app = "RealTime";
+static char *uapp = "RealTimeUpdate";
+static char *synopsis = "Realtime Data Lookup";
+static char *usynopsis = "Realtime Data Rewrite";
+static char *USAGE = "RealTime(<family>|<colmatch>|<value>[|<prefix>])";
+static char *UUSAGE = "RealTimeUpdate(<family>|<colmatch>|<value>|<newcol>|<newval>)";
+static char *desc = "Use the RealTime config handler system to read data into channel variables.\n"
+"RealTime(<family>|<colmatch>|<value>[|<prefix>])\n\n"
+"All unique column names will be set as channel variables with optional prefix to the name.\n"
+"e.g. prefix of 'var_' would make the column 'name' become the variable ${var_name}\n\n";
+static char *udesc = "Use the RealTime config handler system to update a value\n"
+"RealTimeUpdate(<family>|<colmatch>|<value>|<newcol>|<newval>)\n\n"
+"The column <newcol> in 'family' matching column <colmatch>=<value> will be updated to <newval>\n";
+
+STANDARD_LOCAL_USER;
+LOCAL_USER_DECL;
+
+static int cli_load_realtime(int fd, int argc, char **argv)
+{
+ char *header_format = "%30s %-30s\n";
+ struct ast_variable *var=NULL;
+
+ if(argc<5) {
+ ast_cli(fd, "You must supply a family name, a column to match on, and a value to match to.\n");
+ return RESULT_FAILURE;
+ }
+
+ var = ast_load_realtime(argv[2], argv[3], argv[4], NULL);
+
+ if(var) {
+ ast_cli(fd, header_format, "Column Name", "Column Value");
+ ast_cli(fd, header_format, "--------------------", "--------------------");
+ while(var) {
+ ast_cli(fd, header_format, var->name, var->value);
+ var = var->next;
+ }
+ } else {
+ ast_cli(fd, "No rows found matching search criteria.\n");
+ }
+ return RESULT_SUCCESS;
+}
+
+static int cli_update_realtime(int fd, int argc, char **argv) {
+ int res = 0;
+
+ if(argc<7) {
+ ast_cli(fd, "You must supply a family name, a column to update on, a new value, column to match, and value to to match.\n");
+ ast_cli(fd, "Ex: realtime update sipfriends name bobsphone port 4343\n will execute SQL as UPDATE sipfriends SET port = 4343 WHERE name = bobsphone\n");
+ return RESULT_FAILURE;
+ }
+
+ res = ast_update_realtime(argv[2], argv[3], argv[4], argv[5], argv[6], NULL);
+
+ if(res < 0) {
+ ast_cli(fd, "Failed to update. Check the debug log for possible SQL related entries.\n");
+ return RESULT_SUCCESS;
+ }
+
+ ast_cli(fd, "Updated %d RealTime record%s.\n", res, (res != 1) ? "s" : "");
+
+ return RESULT_SUCCESS;
+}
+
+static char cli_load_realtime_usage[] =
+"Usage: realtime load <family> <colmatch> <value>\n"
+" Prints out a list of variables using the RealTime driver.\n";
+
+static struct ast_cli_entry cli_load_realtime_cmd = {
+ { "realtime", "load", NULL, NULL }, cli_load_realtime,
+ "Used to print out RealTime variables.", cli_load_realtime_usage, NULL };
+
+static char cli_update_realtime_usage[] =
+"Usage: realtime update <family> <colmatch> <value>\n"
+" Update a single variable using the RealTime driver.\n";
+
+static struct ast_cli_entry cli_update_realtime_cmd = {
+ { "realtime", "update", NULL, NULL }, cli_update_realtime,
+ "Used to update RealTime variables.", cli_update_realtime_usage, NULL };
+
+static int realtime_update_exec(struct ast_channel *chan, void *data)
+{
+ char *family=NULL, *colmatch=NULL, *value=NULL, *newcol=NULL, *newval=NULL;
+ struct localuser *u;
+ int res = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR,"Invalid input: usage %s\n",UUSAGE);
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if ((family = ast_strdupa(data))) {
+ if ((colmatch = strchr(family,'|'))) {
+ crop_data(colmatch);
+ if ((value = strchr(colmatch,'|'))) {
+ crop_data(value);
+ if ((newcol = strchr(value,'|'))) {
+ crop_data(newcol);
+ if ((newval = strchr(newcol,'|')))
+ crop_data(newval);
+ }
+ }
+ }
+ }
+ if (! (family && value && colmatch && newcol && newval) ) {
+ ast_log(LOG_ERROR,"Invalid input: usage %s\n",UUSAGE);
+ res = -1;
+ } else {
+ ast_update_realtime(family,colmatch,value,newcol,newval,NULL);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+
+static int realtime_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ struct ast_variable *var, *itt;
+ char *family=NULL, *colmatch=NULL, *value=NULL, *prefix=NULL, *vname=NULL;
+ size_t len;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR,"Invalid input: usage %s\n",USAGE);
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if ((family = ast_strdupa(data))) {
+ if ((colmatch = strchr(family,'|'))) {
+ crop_data(colmatch);
+ if ((value = strchr(colmatch,'|'))) {
+ crop_data(value);
+ if ((prefix = strchr(value,'|')))
+ crop_data(prefix);
+ }
+ }
+ }
+ if (! (family && value && colmatch) ) {
+ ast_log(LOG_ERROR,"Invalid input: usage %s\n",USAGE);
+ res = -1;
+ } else {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4"Realtime Lookup: family:'%s' colmatch:'%s' value:'%s'\n",family,colmatch,value);
+ if ((var = ast_load_realtime(family, colmatch, value, NULL))) {
+ for (itt = var; itt; itt = itt->next) {
+ if(prefix) {
+ len = strlen(prefix) + strlen(itt->name) + 2;
+ vname = alloca(len);
+ snprintf(vname,len,"%s%s",prefix,itt->name);
+
+ } else
+ vname = itt->name;
+
+ pbx_builtin_setvar_helper(chan, vname, itt->value);
+ }
+ ast_variables_destroy(var);
+ } else if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4"No Realtime Matches Found.\n");
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_cli_unregister(&cli_load_realtime_cmd);
+ res |= ast_cli_unregister(&cli_update_realtime_cmd);
+ res |= ast_unregister_application(uapp);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_cli_register(&cli_load_realtime_cmd);
+ res |= ast_cli_register(&cli_update_realtime_cmd);
+ res |= ast_register_application(uapp, realtime_update_exec, usynopsis, udesc);
+ res |= ast_register_application(app, realtime_exec, synopsis, desc);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_record.c b/1.2-netsec/apps/app_record.c
new file mode 100644
index 000000000..85310ea70
--- /dev/null
+++ b/1.2-netsec/apps/app_record.c
@@ -0,0 +1,377 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matthew Fredrickson <creslin@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Trivial application to record a sound file
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/dsp.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Trivial Record Application";
+
+static char *app = "Record";
+
+static char *synopsis = "Record to a file";
+
+static char *descrip =
+" Record(filename.format|silence[|maxduration][|options])\n\n"
+"Records from the channel into a given filename. If the file exists it will\n"
+"be overwritten.\n"
+"- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
+"- 'silence' is the number of seconds of silence to allow before returning.\n"
+"- 'maxduration' is the maximum recording duration in seconds. If missing\n"
+"or 0 there is no maximum.\n"
+"- 'options' may contain any of the following letters:\n"
+" 'a' : append to existing recording rather than replacing\n"
+" 'n' : do not answer, but record anyway if line not yet answered\n"
+" 'q' : quiet (do not play a beep tone)\n"
+" 's' : skip recording if the line is not yet answered\n"
+" 't' : use alternate '*' terminator key instead of default '#'\n"
+"\n"
+"If filename contains '%d', these characters will be replaced with a number\n"
+"incremented by one each time the file is recorded. \n\n"
+"Use 'show file formats' to see the available formats on your system\n\n"
+"User can press '#' to terminate the recording and continue to the next priority.\n\n"
+"If the user should hangup during a recording, all data will be lost and the\n"
+"application will teminate. \n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int record_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int count = 0;
+ int percentflag = 0;
+ char *filename, *ext = NULL, *silstr, *maxstr, *options;
+ char *vdata, *p;
+ int i = 0;
+ char tmp[256];
+
+ struct ast_filestream *s = '\0';
+ struct localuser *u;
+ struct ast_frame *f = NULL;
+
+ struct ast_dsp *sildet = NULL; /* silence detector dsp */
+ int totalsilence = 0;
+ int dspsilence = 0;
+ int silence = 0; /* amount of silence to allow */
+ int gotsilence = 0; /* did we timeout for silence? */
+ int maxduration = 0; /* max duration of recording in milliseconds */
+ int gottimeout = 0; /* did we timeout for maxduration exceeded? */
+ int option_skip = 0;
+ int option_noanswer = 0;
+ int option_append = 0;
+ int terminator = '#';
+ int option_quiet = 0;
+ int rfmt = 0;
+ int flags;
+ int waitres;
+ struct ast_silence_generator *silgen = NULL;
+
+ /* The next few lines of code parse out the filename and header from the input string */
+ if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
+ ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Yay for strsep being easy */
+ vdata = ast_strdupa(data);
+ if (!vdata) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ p = vdata;
+ filename = strsep(&p, "|");
+ silstr = strsep(&p, "|");
+ maxstr = strsep(&p, "|");
+ options = strsep(&p, "|");
+
+ if (filename) {
+ if (strstr(filename, "%d"))
+ percentflag = 1;
+ ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */
+ if (!ext)
+ ext = strchr(filename, ':');
+ if (ext) {
+ *ext = '\0';
+ ext++;
+ }
+ }
+ if (!ext) {
+ ast_log(LOG_WARNING, "No extension specified to filename!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if (silstr) {
+ if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) {
+ silence = i * 1000;
+ } else if (!ast_strlen_zero(silstr)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
+ }
+ }
+
+ if (maxstr) {
+ if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1))
+ /* Convert duration to milliseconds */
+ maxduration = i * 1000;
+ else if (!ast_strlen_zero(maxstr))
+ ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
+ }
+ if (options) {
+ /* Retain backwards compatibility with old style options */
+ if (!strcasecmp(options, "skip"))
+ option_skip = 1;
+ else if (!strcasecmp(options, "noanswer"))
+ option_noanswer = 1;
+ else {
+ if (strchr(options, 's'))
+ option_skip = 1;
+ if (strchr(options, 'n'))
+ option_noanswer = 1;
+ if (strchr(options, 'a'))
+ option_append = 1;
+ if (strchr(options, 't'))
+ terminator = '*';
+ if (strchr(options, 'q'))
+ option_quiet = 1;
+ }
+ }
+
+ /* done parsing */
+
+ /* these are to allow the use of the %d in the config file for a wild card of sort to
+ create a new file with the inputed name scheme */
+ if (percentflag) {
+ do {
+ snprintf(tmp, sizeof(tmp), filename, count);
+ count++;
+ } while ( ast_fileexists(tmp, ext, chan->language) != -1 );
+ pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
+ } else
+ strncpy(tmp, filename, sizeof(tmp)-1);
+ /* end of routine mentioned */
+
+
+
+ if (chan->_state != AST_STATE_UP) {
+ if (option_skip) {
+ /* At the user's option, skip if the line is not up */
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ } else if (!option_noanswer) {
+ /* Otherwise answer unless we're supposed to record while on-hook */
+ res = ast_answer(chan);
+ }
+ }
+
+ if (res) {
+ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
+ goto out;
+ }
+
+ if (!option_quiet) {
+ /* Some code to play a nice little beep to signify the start of the record operation */
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res) {
+ res = ast_waitstream(chan, "");
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
+ }
+ ast_stopstream(chan);
+ }
+
+ /* The end of beep code. Now the recording starts */
+
+ if (silence > 0) {
+ rfmt = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ sildet = ast_dsp_new();
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, 256);
+ }
+
+
+ flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
+ s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
+
+ if (!s) {
+ ast_log(LOG_WARNING, "Could not create file %s\n", filename);
+ goto out;
+ }
+
+ if (option_transmit_silence_during_record)
+ silgen = ast_channel_start_silence_generator(chan);
+
+ /* Request a video update */
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+
+ if (maxduration <= 0)
+ maxduration = -1;
+
+ while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
+ if (maxduration > 0) {
+ if (waitres == 0) {
+ gottimeout = 1;
+ break;
+ }
+ maxduration = waitres;
+ }
+
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ res = ast_writestream(s, f);
+
+ if (res) {
+ ast_log(LOG_WARNING, "Problem writing frame\n");
+ ast_frfree(f);
+ break;
+ }
+
+ if (silence > 0) {
+ dspsilence = 0;
+ ast_dsp_silence(sildet, f, &dspsilence);
+ if (dspsilence) {
+ totalsilence = dspsilence;
+ } else {
+ totalsilence = 0;
+ }
+ if (totalsilence > silence) {
+ /* Ended happily with silence */
+ ast_frfree(f);
+ gotsilence = 1;
+ break;
+ }
+ }
+ } else if (f->frametype == AST_FRAME_VIDEO) {
+ res = ast_writestream(s, f);
+
+ if (res) {
+ ast_log(LOG_WARNING, "Problem writing frame\n");
+ ast_frfree(f);
+ break;
+ }
+ } else if ((f->frametype == AST_FRAME_DTMF) &&
+ (f->subclass == terminator)) {
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ }
+ if (!f) {
+ ast_log(LOG_DEBUG, "Got hangup\n");
+ res = -1;
+ }
+
+ if (gotsilence) {
+ ast_stream_rewind(s, silence-1000);
+ ast_truncstream(s);
+ } else if (!gottimeout) {
+ /* Strip off the last 1/4 second of it */
+ ast_stream_rewind(s, 250);
+ ast_truncstream(s);
+ }
+ ast_closestream(s);
+
+ if (silgen)
+ ast_channel_stop_silence_generator(chan, silgen);
+
+ out:
+ if ((silence > 0) && rfmt) {
+ res = ast_set_read_format(chan, rfmt);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
+ if (sildet)
+ ast_dsp_free(sildet);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, record_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_rpt.c b/1.2-netsec/apps/app_rpt.c
new file mode 100644
index 000000000..c8b10e44d
--- /dev/null
+++ b/1.2-netsec/apps/app_rpt.c
@@ -0,0 +1,6560 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002-2005, Jim Dixon, WB6NIL
+ *
+ * Jim Dixon, WB6NIL <jim@lambdatel.com>
+ * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*
+ *
+ * Radio Repeater / Remote Base program
+ * version 0.37 11/3/05
+ *
+ * See http://www.zapatatelephony.org/app_rpt.html
+ *
+ *
+ * Repeater / Remote Functions:
+ * "Simple" Mode: * - autopatch access, # - autopatch hangup
+ * Normal mode:
+ * See the function list in rpt.conf
+ *
+ * To send an asterisk (*) while dialing or talking on phone,
+ * use the autopatch acess code.
+ *
+ *
+ * status cmds:
+ *
+ * 1 - Force ID
+ * 2 - Give Time of Day
+ * 3 - Give software Version
+ *
+ * cop (control operator) cmds:
+ *
+ * 1 - System warm boot
+ * 2 - System enable
+ * 3 - System disable
+ * 4 - Test Tone On
+ * 5 - Dump System Variables on Console (debug)
+ * 6 - PTT (phone mode only)
+ *
+ * ilink cmds:
+ *
+ * 1 - Disconnect specified link
+ * 2 - Connect specified link -- monitor only
+ * 3 - Connect specified link -- tranceive
+ * 4 - Enter command mode on specified link
+ * 5 - System status
+ * 6 - Disconnect all links
+ *
+ * remote cmds:
+ *
+ * 1 - Recall Memory MM (*000-*099) (Gets memory from rpt.conf)
+ * 2 - Set VFO MMMMM*KKK*O (Mhz digits, Khz digits, Offset)
+ * 3 - Set Rx PL Tone HHH*D*
+ * 4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
+ * 5 - Link Status (long)
+ * 6 - Set operating mode M (FM, USB, LSB, AM, etc)
+ * 100 - RX PL off (Default)
+ * 101 - RX PL On
+ * 102 - TX PL Off (Default)
+ * 103 - TX PL On
+ * 104 - Low Power
+ * 105 - Med Power
+ * 106 - Hi Power
+ * 107 - Bump Down 20 Hz
+ * 108 - Bump Down 100 Hz
+ * 109 - Bump Down 500 Hz
+ * 110 - Bump Up 20 Hz
+ * 111 - Bump Up 100 Hz
+ * 112 - Bump Up 500 Hz
+ * 113 - Scan Down Slow
+ * 114 - Scan Down Medium
+ * 115 - Scan Down Fast
+ * 116 - Scan Up Slow
+ * 117 - Scan Up Medium
+ * 118 - Scan Up Fast
+ * 119 - Transmit allowing auto-tune
+ * 140 - Link Status (brief)
+ *
+ *
+*/
+
+/* The following is JUST GROSS!! There is some soft of underlying problem,
+ probably in channel_iax2.c, that causes an IAX2 connection to sometimes
+ stop transmitting randomly. We have been working for weeks to try to
+ locate it and fix it, but to no avail We finally decided to put our
+ tail between our legs, and just make the radio system re-connect upon
+ network failure. This just shouldnt have to be done. For normal operation,
+ comment-out the following line */
+#define RECONNECT_KLUDGE
+
+/* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
+
+#define MAXDTMF 32
+#define DTMF_TIMEOUT 3
+
+#define DISC_TIME 10000 /* report disc after 10 seconds of no connect */
+#define MAX_RETRIES 5
+
+#define REDUNDANT_TX_TIME 2000
+
+#define RETRY_TIMER_MS 5000
+
+#define MAXREMSTR 15
+
+#define NODES "nodes"
+#define MEMORY "memory"
+#define FUNCTIONS "functions"
+#define TELEMETRY "telemetry"
+#define MORSE "morse"
+#define FUNCCHAR '*'
+#define ENDCHAR '#'
+
+#define DEFAULT_IOBASE 0x378
+
+#define MAXCONNECTTIME 5000
+
+#define MAXNODESTR 300
+
+#define ACTIONSIZE 32
+
+#define TELEPARAMSIZE 256
+
+#define REM_SCANTIME 100
+
+
+enum {REM_OFF,REM_MONITOR,REM_TX};
+
+enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
+ CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME,
+ STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH};
+
+enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
+
+enum {REM_LOWPWR,REM_MEDPWR,REM_HIPWR};
+
+enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
+
+enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
+
+enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
+
+enum {REM_MODE_FM,REM_MODE_USB,REM_MODE_LSB,REM_MODE_AM};
+
+enum {HF_SCAN_OFF,HF_SCAN_DOWN_SLOW,HF_SCAN_DOWN_QUICK,HF_SCAN_DOWN_FAST,HF_SCAN_UP_SLOW,HF_SCAN_UP_QUICK,HF_SCAN_UP_FAST};
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <search.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/io.h>
+#include <math.h>
+#include <tonezone.h>
+#include <linux/zaptel.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/callerid.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/features.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/localtime.h"
+
+static char *tdesc = "Radio Repeater / Remote Base version 0.37 11/03/2005";
+
+static char *app = "Rpt";
+
+static char *synopsis = "Radio Repeater/Remote Base Control System";
+
+static char *descrip =
+" Rpt(nodename[|options]): Radio Remote Link or Remote Base Link Endpoint Process.\n"
+"\n"
+" Not specifying an option puts it in normal endpoint mode (where source\n"
+" IP and nodename are verified).\n"
+"\n"
+" Options are as follows:\n"
+"\n"
+" X - Normal endpoint mode WITHOUT security check. Only specify\n"
+" this if you have checked security already (like with an IAX2\n"
+" user/password or something).\n"
+"\n"
+" Rannounce-string[|timeout[|timeout-destination]] - Amateur Radio\n"
+" Reverse Autopatch. Caller is put on hold, and announcement (as\n"
+" specified by the 'announce-string') is played on radio system.\n"
+" Users of radio system can access autopatch, dial specified\n"
+" code, and pick up call. Announce-string is list of names of\n"
+" recordings, or \"PARKED\" to substitute code for un-parking,\n"
+" or \"NODE\" to substitute node number.\n"
+"\n"
+" P - Phone Control mode. This allows a regular phone user to have\n"
+" full control and audio access to the radio system. For the\n"
+" user to have DTMF control, the 'phone_functions' parameter\n"
+" must be specified for the node in 'rpt.conf'. An additional\n"
+" function (cop,6) must be listed so that PTT control is available.\n"
+"\n"
+" D - Dumb Phone Control mode. This allows a regular phone user to\n"
+" have full control and audio access to the radio system. In this\n"
+" mode, the PTT is activated for the entire length of the call.\n"
+" For the user to have DTMF control (not generally recomended in\n"
+" this mode), the 'dphone_functions' parameter must be specified\n"
+" for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
+" available to the phone user.\n"
+"\n";
+
+static int debug = 0; /* Set this >0 for extra debug output */
+static int nrpts = 0;
+
+char *discstr = "!!DISCONNECT!!";
+static char *remote_rig_ft897="ft897";
+static char *remote_rig_rbi="rbi";
+
+struct ast_config *cfg;
+
+STANDARD_LOCAL_USER;
+LOCAL_USER_DECL;
+
+#define MSWAIT 200
+#define HANGTIME 5000
+#define TOTIME 180000
+#define IDTIME 300000
+#define MAXRPTS 20
+#define POLITEID 30000
+#define FUNCTDELAY 1500
+
+static pthread_t rpt_master_thread;
+
+struct rpt;
+
+struct rpt_link
+{
+ struct rpt_link *next;
+ struct rpt_link *prev;
+ char mode; /* 1 if in tx mode */
+ char isremote;
+ char phonemode;
+ char name[MAXNODESTR]; /* identifier (routing) string */
+ char lasttx;
+ char lastrx;
+ char connected;
+ char hasconnected;
+ char outbound;
+ char disced;
+ char killme;
+ long elaptime;
+ long disctime;
+ long retrytimer;
+ long retxtimer;
+ int retries;
+ struct ast_channel *chan;
+ struct ast_channel *pchan;
+} ;
+
+struct rpt_tele
+{
+ struct rpt_tele *next;
+ struct rpt_tele *prev;
+ struct rpt *rpt;
+ struct ast_channel *chan;
+ int mode;
+ struct rpt_link mylink;
+ char param[TELEPARAMSIZE];
+ pthread_t threadid;
+} ;
+
+struct function_table_tag
+{
+ char action[ACTIONSIZE];
+ int (*function)(struct rpt *myrpt, char *param, char *digitbuf,
+ int command_source, struct rpt_link *mylink);
+} ;
+
+/* Used to store the morse code patterns */
+
+struct morse_bits
+{
+ int len;
+ int ddcomb;
+} ;
+
+struct telem_defaults
+{
+ char name[20];
+ char value[80];
+} ;
+
+
+static struct rpt
+{
+ char *name;
+ ast_mutex_t lock;
+ char *rxchanname;
+ char *txchanname;
+ char *ourcontext;
+ char *ourcallerid;
+ char *acctcode;
+ char *ident;
+ char *tonezone;
+ char *functions;
+ char *link_functions;
+ char *phone_functions;
+ char *dphone_functions;
+ char *nodes;
+ struct rpt_link links;
+ int hangtime;
+ int totime;
+ int idtime;
+ int unkeytocttimer;
+ char keyed;
+ char exttx;
+ char localtx;
+ char remoterx;
+ char remotetx;
+ char remoteon;
+ char simple;
+ char *remote;
+ char tounkeyed;
+ char tonotify;
+ char enable;
+ char dtmfbuf[MAXDTMF];
+ char rem_dtmfbuf[MAXDTMF];
+ char cmdnode[50];
+ struct ast_channel *rxchannel,*txchannel;
+ struct ast_channel *pchannel,*txpchannel, *remchannel;
+ struct rpt_tele tele;
+ pthread_t rpt_call_thread,rpt_thread;
+ time_t rem_dtmf_time,dtmf_time_rem;
+ int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer;
+ int mustid;
+ int politeid;
+ int dtmfidx,rem_dtmfidx;
+ long retxtimer;
+ char mydtmf;
+ int iobase;
+ char exten[AST_MAX_EXTENSION];
+ char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR];
+ char offset;
+ char powerlevel;
+ char txplon;
+ char rxplon;
+ char remmode;
+ char tunerequest;
+ char hfscanmode;
+ int hfscanstatus;
+ char lastlinknode[MAXNODESTR];
+ char funcchar;
+ char endchar;
+ char stopgen;
+ int phone_longestfunc;
+ int dphone_longestfunc;
+ int link_longestfunc;
+ int longestfunc;
+ int longestnode;
+ int threadrestarts;
+ time_t disgorgetime;
+ time_t lastthreadrestarttime;
+ char nobusyout;
+} rpt_vars[MAXRPTS];
+
+/*
+* CLI extensions
+*/
+
+/* Debug mode */
+static int rpt_do_debug(int fd, int argc, char *argv[]);
+
+static char debug_usage[] =
+"Usage: rpt debug level {0-7}\n"
+" Enables debug messages in app_rpt\n";
+
+static struct ast_cli_entry cli_debug =
+ { { "rpt", "debug", "level" }, rpt_do_debug, "Enable app_rpt debugging", debug_usage };
+
+
+
+/*
+* Telemetry defaults
+*/
+
+
+static struct telem_defaults tele_defs[] = {
+ {"ct1","|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
+ {"ct2","|t(660,880,150,3072)"},
+ {"ct3","|t(440,0,150,3072)"},
+ {"ct4","|t(550,0,150,3072)"},
+ {"ct5","|t(660,0,150,3072)"},
+ {"ct6","|t(880,0,150,3072)"},
+ {"ct7","|t(660,440,150,3072)"},
+ {"ct8","|t(700,1100,150,3072)"},
+ {"remotemon","|t(1600,0,75,2048)"},
+ {"remotetx","|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
+ {"cmdmode","|t(900,904,200,2048)"},
+ {"functcomplete","|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
+} ;
+
+/*
+* Forward decl's - these suppress compiler warnings when funcs coded further down the file than thier invokation
+*/
+
+static int setrbi(struct rpt *myrpt);
+
+
+
+/*
+* Define function protos for function table here
+*/
+
+static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+/*
+* Function table
+*/
+
+static struct function_table_tag function_table[] = {
+ {"cop", function_cop},
+ {"autopatchup", function_autopatchup},
+ {"autopatchdn", function_autopatchdn},
+ {"ilink", function_ilink},
+ {"status", function_status},
+ {"remote", function_remote}
+} ;
+
+static int myatoi(char *str)
+{
+int ret;
+
+ if (str == NULL) return -1;
+ /* leave this %i alone, non-base-10 input is useful here */
+ if (sscanf(str,"%i",&ret) != 1) return -1;
+ return ret;
+}
+
+/*
+* Enable or disable debug output at a given level at the console
+*/
+
+static int rpt_do_debug(int fd, int argc, char *argv[])
+{
+ int newlevel;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ newlevel = myatoi(argv[3]);
+ if((newlevel < 0) || (newlevel > 7))
+ return RESULT_SHOWUSAGE;
+ if(newlevel)
+ ast_cli(fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
+ else
+ ast_cli(fd, "app_rpt Debugging disabled\n");
+
+ debug = newlevel;
+ return RESULT_SUCCESS;
+}
+
+
+
+static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
+{
+ int res;
+
+ if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
+ return res;
+
+ while(chan->generatordata) {
+ if (ast_safe_sleep(chan,1)) return -1;
+ }
+
+ return 0;
+}
+
+static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
+{
+ return play_tone_pair(chan, freq, 0, duration, amplitude);
+}
+
+static int play_silence(struct ast_channel *chan, int duration)
+{
+ return play_tone_pair(chan, 0, 0, duration, 0);
+}
+
+
+static int send_morse(struct ast_channel *chan, char *string, int speed, int freq, int amplitude)
+{
+
+static struct morse_bits mbits[] = {
+ {0, 0}, /* SPACE */
+ {0, 0},
+ {6, 18},/* " */
+ {0, 0},
+ {7, 72},/* $ */
+ {0, 0},
+ {0, 0},
+ {6, 30},/* ' */
+ {5, 13},/* ( */
+ {6, 29},/* ) */
+ {0, 0},
+ {5, 10},/* + */
+ {6, 51},/* , */
+ {6, 33},/* - */
+ {6, 42},/* . */
+ {5, 9}, /* / */
+ {5, 31},/* 0 */
+ {5, 30},/* 1 */
+ {5, 28},/* 2 */
+ {5, 24},/* 3 */
+ {5, 16},/* 4 */
+ {5, 0}, /* 5 */
+ {5, 1}, /* 6 */
+ {5, 3}, /* 7 */
+ {5, 7}, /* 8 */
+ {5, 15},/* 9 */
+ {6, 7}, /* : */
+ {6, 21},/* ; */
+ {0, 0},
+ {5, 33},/* = */
+ {0, 0},
+ {6, 12},/* ? */
+ {0, 0},
+ {2, 2}, /* A */
+ {4, 1}, /* B */
+ {4, 5}, /* C */
+ {3, 1}, /* D */
+ {1, 0}, /* E */
+ {4, 4}, /* F */
+ {3, 3}, /* G */
+ {4, 0}, /* H */
+ {2, 0}, /* I */
+ {4, 14},/* J */
+ {3, 5}, /* K */
+ {4, 2}, /* L */
+ {2, 3}, /* M */
+ {2, 1}, /* N */
+ {3, 7}, /* O */
+ {4, 6}, /* P */
+ {4, 11},/* Q */
+ {3, 2}, /* R */
+ {3, 0}, /* S */
+ {1, 1}, /* T */
+ {3, 4}, /* U */
+ {4, 8}, /* V */
+ {3, 6}, /* W */
+ {4, 9}, /* X */
+ {4, 13},/* Y */
+ {4, 3} /* Z */
+ };
+
+
+ int dottime;
+ int dashtime;
+ int intralettertime;
+ int interlettertime;
+ int interwordtime;
+ int len, ddcomb;
+ int res;
+ int c;
+ int i;
+ int flags;
+
+ res = 0;
+
+ /* Approximate the dot time from the speed arg. */
+
+ dottime = 900/speed;
+
+ /* Establish timing releationships */
+
+ dashtime = 3 * dottime;
+ intralettertime = dottime;
+ interlettertime = dottime * 4 ;
+ interwordtime = dottime * 7;
+
+ for(;(*string) && (!res); string++){
+
+ c = *string;
+
+ /* Convert lower case to upper case */
+
+ if((c >= 'a') && (c <= 'z'))
+ c -= 0x20;
+
+ /* Can't deal with any char code greater than Z, skip it */
+
+ if(c > 'Z')
+ continue;
+
+ /* If space char, wait the inter word time */
+
+ if(c == ' '){
+ if(!res)
+ res = play_silence(chan, interwordtime);
+ continue;
+ }
+
+ /* Subtract out control char offset to match our table */
+
+ c -= 0x20;
+
+ /* Get the character data */
+
+ len = mbits[c].len;
+ ddcomb = mbits[c].ddcomb;
+
+ /* Send the character */
+
+ for(; len ; len--){
+ if(!res)
+ res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
+ if(!res)
+ res = play_silence(chan, intralettertime);
+ ddcomb >>= 1;
+ }
+
+ /* Wait the interletter time */
+
+ if(!res)
+ res = play_silence(chan, interlettertime - intralettertime);
+ }
+
+ /* Wait for all the frames to be sent */
+
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_stopstream(chan);
+
+ /*
+ * Wait for the zaptel driver to physically write the tone blocks to the hardware
+ */
+
+ for(i = 0; i < 20 ; i++){
+ flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
+ res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
+ if(flags & ZT_IOMUX_WRITEEMPTY)
+ break;
+ if( ast_safe_sleep(chan, 50)){
+ res = -1;
+ break;
+ }
+ }
+
+
+ return res;
+}
+
+static int send_tone_telemetry(struct ast_channel *chan, char *tonestring)
+{
+ char *stringp;
+ char *tonesubset;
+ int f1,f2;
+ int duration;
+ int amplitude;
+ int res;
+ int i;
+ int flags;
+
+ res = 0;
+
+ stringp = ast_strdupa(tonestring);
+
+ for(;tonestring;){
+ tonesubset = strsep(&stringp,")");
+ if(!tonesubset)
+ break;
+ if(sscanf(tonesubset,"(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
+ break;
+ res = play_tone_pair(chan, f1, f2, duration, amplitude);
+ if(res)
+ break;
+ }
+ if(!res)
+ res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
+
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_stopstream(chan);
+
+ /*
+ * Wait for the zaptel driver to physically write the tone blocks to the hardware
+ */
+
+ for(i = 0; i < 20 ; i++){
+ flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
+ res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
+ if(flags & ZT_IOMUX_WRITEEMPTY)
+ break;
+ if( ast_safe_sleep(chan, 50)){
+ res = -1;
+ break;
+ }
+ }
+
+ return res;
+
+}
+
+
+static int sayfile(struct ast_channel *mychannel,char *fname)
+{
+int res;
+
+ res = ast_streamfile(mychannel, fname, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+static int saycharstr(struct ast_channel *mychannel,char *str)
+{
+int res;
+
+ res = ast_say_character_str(mychannel,str,NULL,mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+static int saynum(struct ast_channel *mychannel, int num)
+{
+ int res;
+ res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
+ if(!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+
+/* Retrieve an int from a config file */
+
+static int retrieve_astcfgint(char *category, char *name, int min, int max, int defl)
+{
+ char *var;
+ int ret;
+
+ var = ast_variable_retrieve(cfg, category, name);
+ if(var){
+ ret = myatoi(var);
+ if(ret < min)
+ ret = min;
+ if(ret > max)
+ ret = max;
+ }
+ else
+ ret = defl;
+ return ret;
+}
+
+static int telem_any(struct ast_channel *chan, char *entry)
+{
+ int res;
+ char c;
+
+ static int morsespeed;
+ static int morsefreq;
+ static int morseampl;
+ static int morseidfreq = 0;
+ static int morseidampl;
+ static char mcat[] = MORSE;
+
+ res = 0;
+
+ if(!morseidfreq){ /* Get the morse parameters if not already loaded */
+ morsespeed = retrieve_astcfgint( mcat, "speed", 5, 20, 20);
+ morsefreq = retrieve_astcfgint( mcat, "frequency", 300, 3000, 800);
+ morseampl = retrieve_astcfgint( mcat, "amplitude", 200, 8192, 4096);
+ morseidampl = retrieve_astcfgint( mcat, "idamplitude", 200, 8192, 2048);
+ morseidfreq = retrieve_astcfgint( mcat, "idfrequency", 300, 3000, 330);
+ }
+
+ /* Is it a file, or a tone sequence? */
+
+ if(entry[0] == '|'){
+ c = entry[1];
+ if((c >= 'a')&&(c <= 'z'))
+ c -= 0x20;
+
+ switch(c){
+ case 'I': /* Morse ID */
+ res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
+ break;
+
+ case 'M': /* Morse Message */
+ res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
+ break;
+
+ case 'T': /* Tone sequence */
+ res = send_tone_telemetry(chan, entry + 2);
+ break;
+ default:
+ res = -1;
+ }
+ }
+ else
+ res = sayfile(chan, entry); /* File */
+ return res;
+}
+
+/*
+* This function looks up a telemetry name in the config file, and does a telemetry response as configured.
+*
+* 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
+*/
+
+static int telem_lookup(struct ast_channel *chan, char *node, char *name)
+{
+
+ int res;
+ int i;
+ char *entry;
+ char *telemetry;
+ char *telemetry_save;
+
+ res = 0;
+ telemetry_save = NULL;
+ entry = NULL;
+
+
+ /* Retrieve the section name for telemetry from the node section */
+
+ telemetry = ast_variable_retrieve(cfg, node, TELEMETRY);
+ if(telemetry){
+ telemetry_save = ast_strdupa(telemetry);
+ if(!telemetry_save){
+ ast_log(LOG_WARNING,"ast_strdupa() failed in telem_lookup()\n");
+ return res;
+ }
+ entry = ast_variable_retrieve(cfg, telemetry_save, name);
+ }
+
+ /* Try to look up the telemetry name */
+
+ if(!entry){
+ /* Telemetry name wasn't found in the config file, use the default */
+ for(i = 0; i < sizeof(tele_defs)/sizeof(struct telem_defaults) ; i++){
+ if(!strcasecmp(tele_defs[i].name, name))
+ entry = tele_defs[i].value;
+ }
+ }
+ if(entry)
+ telem_any(chan, entry);
+ else{
+ ast_log(LOG_WARNING, "Telemetry name not found: %s\n", name);
+ res = -1;
+ }
+ return res;
+}
+
+/*
+* Retrieve a wait interval
+*/
+
+static int get_wait_interval(struct rpt *myrpt, int type)
+{
+ int interval;
+ char *wait_times;
+ char *wait_times_save;
+
+ wait_times_save = NULL;
+ wait_times = ast_variable_retrieve(cfg, myrpt->name, "wait_times");
+
+ if(wait_times){
+ wait_times_save = ast_strdupa(wait_times);
+ if(!wait_times_save){
+ ast_log(LOG_WARNING, "Out of memory in wait_interval()\n");
+ wait_times = NULL;
+ }
+ }
+
+ switch(type){
+ case DLY_TELEM:
+ if(wait_times)
+ interval = retrieve_astcfgint(wait_times_save, "telemwait", 500, 5000, 1000);
+ else
+ interval = 1000;
+ break;
+
+ case DLY_ID:
+ if(wait_times)
+ interval = retrieve_astcfgint(wait_times_save, "idwait",250,5000,500);
+ else
+ interval = 500;
+ break;
+
+ case DLY_UNKEY:
+ if(wait_times)
+ interval = retrieve_astcfgint(wait_times_save, "unkeywait",500,5000,1000);
+ else
+ interval = 1000;
+ break;
+
+ case DLY_CALLTERM:
+ if(wait_times)
+ interval = retrieve_astcfgint(wait_times_save, "calltermwait",500,5000,1500);
+ else
+ interval = 1500;
+ break;
+
+ default:
+ return 0;
+ }
+ return interval;
+}
+
+
+/*
+* Wait a configurable interval of time
+*/
+
+
+static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
+{
+ int interval;
+ if((interval = get_wait_interval(myrpt, type)))
+ ast_safe_sleep(chan,interval);
+ return;
+}
+
+
+static void *rpt_tele_thread(void *this)
+{
+ZT_CONFINFO ci; /* conference info */
+int res = 0,haslink,hastx,hasremote,imdone = 0, unkeys_queued, x;
+struct rpt_tele *mytele = (struct rpt_tele *)this;
+struct rpt_tele *tlist;
+struct rpt *myrpt;
+struct rpt_link *l,*m,linkbase;
+struct ast_channel *mychannel;
+int vmajor, vminor;
+char *p,*ct,*ct_copy,*ident, *nodename;
+time_t t;
+struct tm localtm;
+
+
+ /* get a pointer to myrpt */
+ myrpt = mytele->rpt;
+
+ /* Snag copies of a few key myrpt variables */
+ ast_mutex_lock(&myrpt->lock);
+ nodename = ast_strdupa(myrpt->name);
+ ident = ast_strdupa(myrpt->ident);
+ ast_mutex_unlock(&myrpt->lock);
+
+
+ /* allocate a pseudo-channel thru asterisk */
+ mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!mychannel)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ ast_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ ast_mutex_unlock(&myrpt->lock);
+ free(mytele);
+ pthread_exit(NULL);
+ }
+ ast_mutex_lock(&myrpt->lock);
+ mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
+ ast_mutex_unlock(&myrpt->lock);
+
+ /* make a conference for the tx */
+ ci.chan = 0;
+ /* If there's an ID queued, only connect the ID audio to the local tx conference so
+ linked systems can't hear it */
+ ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY)) ?
+ myrpt->txconf : myrpt->conf);
+ ci.confmode = ZT_CONF_CONFANN;
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ ast_mutex_unlock(&myrpt->lock);
+ free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ ast_stopstream(mychannel);
+ switch(mytele->mode)
+ {
+ case ID:
+ case ID1:
+ /* wait a bit */
+ wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM,mychannel);
+ res = telem_any(mychannel, ident);
+ imdone=1;
+
+ break;
+
+
+ case IDTALKOVER:
+ p = ast_variable_retrieve(cfg, nodename, "idtalkover");
+ if(p)
+ res = telem_any(mychannel, p);
+ imdone=1;
+ break;
+
+ case PROC:
+ /* wait a little bit longer */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
+ break;
+ case TERM:
+ /* wait a little bit longer */
+ wait_interval(myrpt, DLY_CALLTERM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
+ break;
+ case COMPLETE:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = telem_lookup(mychannel, myrpt->name, "functcomplete");
+ break;
+ case UNKEY:
+
+ /*
+ * Reset the Unkey to CT timer
+ */
+
+ x = get_wait_interval(myrpt, DLY_UNKEY);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */
+ ast_mutex_unlock(&myrpt->lock);
+
+ /*
+ * If there's one already queued, don't do another
+ */
+
+ tlist = myrpt->tele.next;
+ unkeys_queued = 0;
+ if (tlist != &myrpt->tele)
+ {
+ ast_mutex_lock(&myrpt->lock);
+ while(tlist != &myrpt->tele){
+ if (tlist->mode == UNKEY) unkeys_queued++;
+ tlist = tlist->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ }
+ if( unkeys_queued > 1){
+ imdone = 1;
+ break;
+ }
+
+ /* Wait for the telemetry timer to expire */
+ /* Periodically check the timer since it can be re-initialized above */
+
+ while(myrpt->unkeytocttimer)
+ {
+ int ctint;
+ if(myrpt->unkeytocttimer > 100)
+ ctint = 100;
+ else
+ ctint = myrpt->unkeytocttimer;
+ ast_safe_sleep(mychannel, ctint);
+ ast_mutex_lock(&myrpt->lock);
+ if(myrpt->unkeytocttimer < ctint)
+ myrpt->unkeytocttimer = 0;
+ else
+ myrpt->unkeytocttimer -= ctint;
+ ast_mutex_unlock(&myrpt->lock);
+ }
+
+
+ /*
+ * Now, the carrier on the rptr rx should be gone.
+ * If it re-appeared, then forget about sending the CT
+ */
+ if(myrpt->keyed){
+ imdone = 1;
+ break;
+ }
+
+ haslink = 0;
+ hastx = 0;
+ hasremote = 0;
+ l = myrpt->links.next;
+ if (l != &myrpt->links)
+ {
+ ast_mutex_lock(&myrpt->lock);
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ haslink = 1;
+ if (l->mode) {
+ hastx++;
+ if (l->isremote) hasremote++;
+ }
+ l = l->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ }
+ if (haslink)
+ {
+
+ res = telem_lookup(mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
+ if(res)
+ ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
+
+
+ /* if in remote cmd mode, indicate it */
+ if (myrpt->cmdnode[0])
+ {
+ ast_safe_sleep(mychannel,200);
+ res = telem_lookup(mychannel, myrpt->name, "cmdmode");
+ if(res)
+ ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ }
+ }
+ else if((ct = ast_variable_retrieve(cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */
+ ct_copy = ast_strdupa(ct);
+ res = telem_lookup(mychannel, myrpt->name, ct_copy);
+ if(res)
+ ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
+ }
+
+ if (hasremote && (!myrpt->cmdnode[0]))
+ {
+ /* set for all to hear */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONFANN;
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ ast_mutex_unlock(&myrpt->lock);
+ free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ if((ct = ast_variable_retrieve(cfg, nodename, "remotect"))){ /* Unlinked Courtesy Tone */
+ ast_safe_sleep(mychannel,200);
+ ct_copy = ast_strdupa(ct);
+ res = telem_lookup(mychannel, myrpt->name, ct_copy);
+ if(res)
+ ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
+ }
+ }
+ imdone = 1;
+ break;
+ case REMDISC:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language);
+ res = ast_streamfile(mychannel, ((mytele->mylink.connected) ?
+ "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language);
+ break;
+ case REMALREADY:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language);
+ break;
+ case REMNOTFOUND:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language);
+ break;
+ case REMGO:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language);
+ break;
+ case CONNECTED:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language);
+ res = ast_streamfile(mychannel, "rpt/connected", mychannel->language);
+ break;
+ case CONNFAIL:
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language);
+ res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language);
+ break;
+ case STATUS:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ hastx = 0;
+ linkbase.next = &linkbase;
+ linkbase.prev = &linkbase;
+ ast_mutex_lock(&myrpt->lock);
+ /* make our own list of links */
+ l = myrpt->links.next;
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ m = malloc(sizeof(struct rpt_link));
+ if (!m)
+ {
+ ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name);
+ ast_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ ast_mutex_unlock(&myrpt->lock);
+ free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ memcpy(m,l,sizeof(struct rpt_link));
+ m->next = m->prev = NULL;
+ insque((struct qelem *)m,(struct qelem *)linkbase.next);
+ l = l->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel,myrpt->name,NULL,mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ if (myrpt->callmode)
+ {
+ hastx = 1;
+ res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ }
+ l = linkbase.next;
+ while(l != &linkbase)
+ {
+ hastx = 1;
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel,l->name,NULL,mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ res = ast_streamfile(mychannel, ((l->mode) ?
+ "rpt/tranceive" : "rpt/monitor"), mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ l = l->next;
+ }
+ if (!hastx)
+ {
+ res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ }
+ /* destroy our local link queue */
+ l = linkbase.next;
+ while(l != &linkbase)
+ {
+ m = l;
+ l = l->next;
+ remque((struct qelem *)m);
+ free(m);
+ }
+ imdone = 1;
+ break;
+ case TIMEOUT:
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel,myrpt->name,NULL,mychannel->language);
+ res = ast_streamfile(mychannel, "rpt/timeout", mychannel->language);
+ break;
+
+ case STATS_TIME:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ t = time(NULL);
+ localtime_r(&t, &localtm);
+ /* Say the phase of the day is before the time */
+ if((localtm.tm_hour >= 0) && (localtm.tm_hour < 12))
+ p = "rpt/goodmorning";
+ else if((localtm.tm_hour >= 12) && (localtm.tm_hour < 18))
+ p = "rpt/goodafternoon";
+ else
+ p = "rpt/goodevening";
+ if (sayfile(mychannel,p) == -1)
+ {
+ imdone = 1;
+ break;
+ }
+ /* Say the time is ... */
+ if (sayfile(mychannel,"rpt/thetimeis") == -1)
+ {
+ imdone = 1;
+ break;
+ }
+ /* Say the time */
+ res = ast_say_time(mychannel, t, "", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ ast_stopstream(mychannel);
+ imdone = 1;
+ break;
+ case STATS_VERSION:
+ p = strstr(tdesc, "version");
+ if(!p)
+ break;
+ if(sscanf(p, "version %d.%d", &vmajor, &vminor) != 2)
+ break;
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ /* Say "version" */
+ if (sayfile(mychannel,"rpt/version") == -1)
+ {
+ imdone = 1;
+ break;
+ }
+ if(!res) /* Say "X" */
+ ast_say_number(mychannel, vmajor, "", mychannel->language, (char *) NULL);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ ast_stopstream(mychannel);
+ if (saycharstr(mychannel,".") == -1)
+ {
+ imdone = 1;
+ break;
+ }
+ if(!res) /* Say "Y" */
+ ast_say_number(mychannel, vminor, "", mychannel->language, (char *) NULL);
+ if (!res){
+ res = ast_waitstream(mychannel, "");
+ ast_stopstream(mychannel);
+ }
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ imdone = 1;
+ break;
+ case ARB_ALPHA:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ if(mytele->param)
+ saycharstr(mychannel, mytele->param);
+ imdone = 1;
+ break;
+ case REV_PATCH:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ if(mytele->param) {
+
+ /* Parts of this section taken from app_parkandannounce */
+ char *tpl_working, *tpl_current;
+ char *tmp[100], *myparm;
+ int looptemp=0,i=0, dres = 0;
+
+
+ tpl_working = strdupa(mytele->param);
+ myparm = strsep(&tpl_working,",");
+ tpl_current=strsep(&tpl_working, ":");
+
+ while(tpl_current && looptemp < sizeof(tmp)) {
+ tmp[looptemp]=tpl_current;
+ looptemp++;
+ tpl_current=strsep(&tpl_working,":");
+ }
+
+ for(i=0; i<looptemp; i++) {
+ if(!strcmp(tmp[i], "PARKED")) {
+ ast_say_digits(mychannel, atoi(myparm), "", mychannel->language);
+ } else if(!strcmp(tmp[i], "NODE")) {
+ ast_say_digits(mychannel, atoi(myrpt->name), "", mychannel->language);
+ } else {
+ dres = ast_streamfile(mychannel, tmp[i], mychannel->language);
+ if(!dres) {
+ dres = ast_waitstream(mychannel, "");
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], mychannel->name);
+ dres = 0;
+ }
+ }
+ }
+ }
+ imdone = 1;
+ break;
+ case TEST_TONE:
+ imdone = 1;
+ myrpt->stopgen = 0;
+ if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0)))
+ break;
+ while(mychannel->generatordata && (!myrpt->stopgen)) {
+ if (ast_safe_sleep(mychannel,1)) break;
+ imdone = 1;
+ }
+ break;
+ default:
+ break;
+ }
+ myrpt->stopgen = 0;
+ if (!imdone)
+ {
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ res = 0;
+ }
+ }
+ ast_stopstream(mychannel);
+ ast_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ ast_mutex_unlock(&myrpt->lock);
+ free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+}
+
+static void rpt_telemetry(struct rpt *myrpt,int mode, void *data)
+{
+struct rpt_tele *tele;
+struct rpt_link *mylink = (struct rpt_link *) data;
+pthread_attr_t attr;
+
+ tele = malloc(sizeof(struct rpt_tele));
+ if (!tele)
+ {
+ ast_log(LOG_WARNING, "Unable to allocate memory\n");
+ pthread_exit(NULL);
+ return;
+ }
+ /* zero it out */
+ memset((char *)tele,0,sizeof(struct rpt_tele));
+ tele->rpt = myrpt;
+ tele->mode = mode;
+ ast_mutex_lock(&myrpt->lock);
+ if((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED)){
+ memset(&tele->mylink,0,sizeof(struct rpt_link));
+ if (mylink){
+ memcpy(&tele->mylink,mylink,sizeof(struct rpt_link));
+ }
+ }
+ else if ((mode == ARB_ALPHA) || (mode == REV_PATCH)) {
+ strncpy(tele->param, (char *) data, TELEPARAMSIZE - 1);
+ tele->param[TELEPARAMSIZE - 1] = 0;
+ }
+ insque((struct qelem *)tele,(struct qelem *)myrpt->tele.next);
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&tele->threadid,&attr,rpt_tele_thread,(void *) tele);
+ return;
+}
+
+static void *rpt_call(void *this)
+{
+ZT_CONFINFO ci; /* conference info */
+struct rpt *myrpt = (struct rpt *)this;
+int res;
+struct ast_frame wf;
+int stopped,congstarted;
+struct ast_channel *mychannel,*genchannel;
+
+ myrpt->mydtmf = 0;
+ /* allocate a pseudo-channel thru asterisk */
+ mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!mychannel)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ pthread_exit(NULL);
+ }
+ ci.chan = 0;
+ ci.confno = myrpt->conf; /* use the pseudo conference */
+ ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
+ | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
+ /* first put the channel on the conference */
+ if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(mychannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ /* allocate a pseudo-channel thru asterisk */
+ genchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!genchannel)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
+ | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
+ /* first put the channel on the conference */
+ if (ioctl(genchannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ if (myrpt->tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->tonezone) == -1))
+ {
+ ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone);
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ if (myrpt->tonezone && (tone_zone_set_zone(genchannel->fds[0],myrpt->tonezone) == -1))
+ {
+ ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone);
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ /* start dialtone */
+ if (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0)
+ {
+ ast_log(LOG_WARNING, "Cannot start dialtone\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ stopped = 0;
+ congstarted = 0;
+ while ((myrpt->callmode == 1) || (myrpt->callmode == 4))
+ {
+
+ if ((myrpt->callmode == 1) && (myrpt->cidx > 0) && (!stopped))
+ {
+ stopped = 1;
+ /* stop dial tone */
+ tone_zone_play_tone(mychannel->fds[0],-1);
+ }
+ if ((myrpt->callmode == 4) && (!congstarted))
+ {
+ congstarted = 1;
+ /* start congestion tone */
+ tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION);
+ }
+ res = ast_safe_sleep(mychannel, MSWAIT);
+ if (res < 0)
+ {
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ }
+ /* stop any tone generation */
+ tone_zone_play_tone(mychannel->fds[0],-1);
+ /* end if done */
+ if (!myrpt->callmode)
+ {
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+
+ if (myrpt->ourcallerid && *myrpt->ourcallerid){
+ char *name, *loc, *instr;
+ instr = strdup(myrpt->ourcallerid);
+ if(instr){
+ ast_callerid_parse(instr, &name, &loc);
+ if(loc){
+ if(mychannel->cid.cid_num)
+ free(mychannel->cid.cid_num);
+ mychannel->cid.cid_num = strdup(loc);
+ }
+ if(name){
+ if(mychannel->cid.cid_name)
+ free(mychannel->cid.cid_name);
+ mychannel->cid.cid_name = strdup(name);
+ }
+ free(instr);
+ }
+ }
+
+ strncpy(mychannel->exten, myrpt->exten, sizeof(mychannel->exten) - 1);
+ strncpy(mychannel->context, myrpt->ourcontext, sizeof(mychannel->context) - 1);
+ if (myrpt->acctcode)
+ strncpy(mychannel->accountcode, myrpt->acctcode, sizeof(mychannel->accountcode) - 1);
+ mychannel->priority = 1;
+ ast_channel_undefer_dtmf(mychannel);
+ if (ast_pbx_start(mychannel) < 0)
+ {
+ ast_log(LOG_WARNING, "Unable to start PBX!!\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ usleep(10000);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 3;
+ while(myrpt->callmode)
+ {
+ if ((!mychannel->pbx) && (myrpt->callmode != 4))
+ {
+ myrpt->callmode = 4;
+ ast_mutex_unlock(&myrpt->lock);
+ /* start congestion tone */
+ tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ if (myrpt->mydtmf)
+ {
+ wf.frametype = AST_FRAME_DTMF;
+ wf.subclass = myrpt->mydtmf;
+ wf.offset = 0;
+ wf.mallocd = 0;
+ wf.data = NULL;
+ wf.datalen = 0;
+ wf.samples = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ ast_write(genchannel,&wf);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->mydtmf = 0;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ usleep(MSWAIT * 1000);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ tone_zone_play_tone(genchannel->fds[0],-1);
+ if (mychannel->pbx) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV);
+ ast_hangup(genchannel);
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+}
+
+static void send_link_dtmf(struct rpt *myrpt,char c)
+{
+char str[300];
+struct ast_frame wf;
+struct rpt_link *l;
+
+ snprintf(str, sizeof(str), "D %s %s %d %c", myrpt->cmdnode, myrpt->name, ++(myrpt->dtmfidx), c);
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 1;
+ wf.datalen = strlen(str) + 1;
+ wf.samples = 0;
+ l = myrpt->links.next;
+ /* first, see if our dude is there */
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* if we found it, write it and were done */
+ if (!strcmp(l->name,myrpt->cmdnode))
+ {
+ wf.data = strdup(str);
+ if (l->chan) ast_write(l->chan,&wf);
+ return;
+ }
+ l = l->next;
+ }
+ l = myrpt->links.next;
+ /* if not, give it to everyone */
+ while(l != &myrpt->links)
+ {
+ wf.data = strdup(str);
+ if (l->chan) ast_write(l->chan,&wf);
+ l = l->next;
+ }
+ return;
+}
+
+/*
+* Internet linking function
+*/
+
+static int function_ilink(struct rpt *myrpt, char *param, char *digits, int command_source, struct rpt_link *mylink)
+{
+
+ char *val, *s, *s1, *s2, *tele;
+ char tmp[300], deststr[300] = "",modechange = 0;
+ char digitbuf[MAXNODESTR];
+ struct rpt_link *l;
+ ZT_CONFINFO ci; /* conference info */
+
+ if(!param)
+ return DC_ERROR;
+
+
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ strncpy(digitbuf,digits,MAXNODESTR - 1);
+
+ if(debug)
+ printf("@@@@ ilink param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
+
+ switch(myatoi(param)){
+ case 1: /* Link off */
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf,myrpt->lastlinknode);
+ val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+ if (!val){
+ if(strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ strncpy(tmp,val,sizeof(tmp) - 1);
+ s = tmp;
+ s1 = strsep(&s,",");
+ s2 = strsep(&s,",");
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while(l != &myrpt->links){
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, digitbuf))
+ break;
+ l = l->next;
+ }
+ if (l != &myrpt->links){ /* if found */
+ struct ast_frame wf;
+
+ strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1);
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 1;
+ ast_mutex_unlock(&myrpt->lock);
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 1;
+ wf.datalen = strlen(discstr) + 1;
+ wf.samples = 0;
+ wf.data = strdup(discstr);
+ if (l->chan)
+ {
+ ast_write(l->chan,&wf);
+ if (ast_safe_sleep(l->chan,250) == -1) return DC_ERROR;
+ ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
+ }
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ return DC_COMPLETE;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+ case 2: /* Link Monitor */
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf,myrpt->lastlinknode);
+ val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+ if (!val){
+ if(strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ strncpy(tmp,val,sizeof(tmp) - 1);
+ s = tmp;
+ s1 = strsep(&s,",");
+ s2 = strsep(&s,",");
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while(l != &myrpt->links){
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, digitbuf))
+ break;
+ l = l->next;
+ }
+ /* if found */
+ if (l != &myrpt->links)
+ {
+ /* if already in this mode, just ignore */
+ if ((!l->mode) || (!l->chan)) {
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,REMALREADY,NULL);
+ return DC_COMPLETE;
+
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 2;
+ modechange = 1;
+ } else
+ ast_mutex_unlock(&myrpt->lock);
+ strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1);
+ /* establish call in monitor mode */
+ l = malloc(sizeof(struct rpt_link));
+ if (!l){
+ ast_log(LOG_WARNING, "Unable to malloc\n");
+ return DC_ERROR;
+ }
+ /* zero the silly thing */
+ memset((char *)l,0,sizeof(struct rpt_link));
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
+ tele = strchr(deststr,'/');
+ if (!tele){
+ fprintf(stderr,"link2:Dial number (%s) must be in format tech/number\n",deststr);
+ return DC_ERROR;
+ }
+ *tele++ = 0;
+ l->isremote = (s && ast_true(s));
+ strncpy(l->name, digitbuf, MAXNODESTR - 1);
+ l->chan = ast_request(deststr,AST_FORMAT_SLINEAR,tele,NULL);
+ if (modechange) l->connected = 1;
+ if (l->chan){
+ ast_set_read_format(l->chan,AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan,AST_FORMAT_SLINEAR);
+ l->chan->whentohangup = 0;
+ l->chan->appl = "Apprpt";
+ l->chan->data = "(Remote Rx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n",
+ deststr,tele,l->chan->name);
+ if(l->chan->cid.cid_num)
+ free(l->chan->cid.cid_num);
+ l->chan->cid.cid_num = strdup(myrpt->name);
+ ast_call(l->chan,tele,0);
+ }
+ else
+ {
+ rpt_telemetry(myrpt,CONNFAIL,l);
+ free(l);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n",
+ deststr,tele,l->chan->name);
+ return DC_ERROR;
+ }
+ /* allocate a pseudo-channel thru asterisk */
+ l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!l->pchan){
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ ast_hangup(l->chan);
+ free(l);
+ return DC_ERROR;
+ }
+ ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR);
+ /* make a conference for the pseudo-one */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ free(l);
+ return DC_ERROR;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ /* insert at end of queue */
+ insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,COMPLETE,NULL);
+ return DC_COMPLETE;
+ case 3: /* Link transceive */
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf,myrpt->lastlinknode);
+ val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+ if (!val){
+ if(strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ strncpy(tmp,val,sizeof(tmp) - 1);
+ s = tmp;
+ s1 = strsep(&s,",");
+ s2 = strsep(&s,",");
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while(l != &myrpt->links){
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, digitbuf))
+ break;
+ l = l->next;
+ }
+ /* if found */
+ if (l != &myrpt->links){
+ /* if already in this mode, just ignore */
+ if ((l->mode) || (!l->chan)) {
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, REMALREADY, NULL);
+ return DC_COMPLETE;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 2;
+ modechange = 1;
+ } else
+ ast_mutex_unlock(&myrpt->lock);
+ strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1);
+ /* establish call in tranceive mode */
+ l = malloc(sizeof(struct rpt_link));
+ if (!l){
+ ast_log(LOG_WARNING, "Unable to malloc\n");
+ return(DC_ERROR);
+ }
+ /* zero the silly thing */
+ memset((char *)l,0,sizeof(struct rpt_link));
+ l->mode = 1;
+ l->outbound = 1;
+ strncpy(l->name, digitbuf, MAXNODESTR - 1);
+ l->isremote = (s && ast_true(s));
+ if (modechange) l->connected = 1;
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
+ tele = strchr(deststr, '/');
+ if (!tele){
+ fprintf(stderr,"link3:Dial number (%s) must be in format tech/number\n",deststr);
+ free(l);
+ return DC_ERROR;
+ }
+ *tele++ = 0;
+ l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele,NULL);
+ if (l->chan){
+ ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
+ l->chan->whentohangup = 0;
+ l->chan->appl = "Apprpt";
+ l->chan->data = "(Remote Rx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ if(l->chan->cid.cid_num)
+ free(l->chan->cid.cid_num);
+ l->chan->cid.cid_num = strdup(myrpt->name);
+ ast_call(l->chan,tele,999);
+ }
+ else{
+ rpt_telemetry(myrpt,CONNFAIL,l);
+ free(l);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n",
+ deststr,tele,l->chan->name);
+ return DC_ERROR;
+ }
+ /* allocate a pseudo-channel thru asterisk */
+ l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!l->pchan){
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ ast_hangup(l->chan);
+ free(l);
+ return DC_ERROR;
+ }
+ ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ free(l);
+ return DC_ERROR;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ /* insert at end of queue */
+ insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,COMPLETE,NULL);
+ return DC_COMPLETE;
+ case 4: /* Enter Command Mode */
+
+ /* if doesnt allow link cmd, or no links active, return */
+ if (((command_source != SOURCE_RPT) && (command_source != SOURCE_PHONE) && (command_source != SOURCE_DPHONE)) || (myrpt->links.next == &myrpt->links))
+ return DC_COMPLETE;
+
+ /* if already in cmd mode, or selected self, fughetabahtit */
+ if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name, digitbuf))){
+
+ rpt_telemetry(myrpt, REMALREADY, NULL);
+ return DC_COMPLETE;
+ }
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf,myrpt->lastlinknode);
+ /* node must at least exist in list */
+ val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+ if (!val){
+ if(strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+
+ }
+ ast_mutex_lock(&myrpt->lock);
+ strcpy(myrpt->lastlinknode,digitbuf);
+ strncpy(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode) - 1);
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, REMGO, NULL);
+ return DC_COMPLETE;
+
+ case 5: /* Status */
+ rpt_telemetry(myrpt, STATUS, NULL);
+ return DC_COMPLETE;
+
+
+ case 6: /* All Links Off */
+ l = myrpt->links.next;
+
+ while(l != &myrpt->links){
+ if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); /* Hang 'em up */
+ l = l->next;
+ }
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ break;
+
+ default:
+ return DC_ERROR;
+
+ }
+
+ return DC_INDETERMINATE;
+}
+
+/*
+* Autopatch up
+*/
+
+static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ pthread_attr_t attr;
+
+
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ if(debug)
+ printf("@@@@ Autopatch up\n");
+
+ ast_mutex_lock(&myrpt->lock);
+
+ /* if on call, force * into current audio stream */
+
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3)){
+ myrpt->mydtmf = myrpt->funcchar;
+ }
+ if (myrpt->callmode){
+ ast_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+ }
+ myrpt->callmode = 1;
+ myrpt->cidx = 0;
+ myrpt->exten[myrpt->cidx] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *) myrpt);
+ return DC_COMPLETE;
+}
+
+/*
+* Autopatch down
+*/
+
+static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ if(debug)
+ printf("@@@@ Autopatch down\n");
+
+ ast_mutex_lock(&myrpt->lock);
+
+ if (!myrpt->callmode){
+ ast_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+ }
+
+ myrpt->callmode = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, TERM, NULL);
+ return DC_COMPLETE;
+}
+
+/*
+* Status
+*/
+
+static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+
+ if(!param)
+ return DC_ERROR;
+
+
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ if(debug)
+ printf("@@@@ status param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
+
+ switch(myatoi(param)){
+ case 1: /* System ID */
+ rpt_telemetry(myrpt, ID1, NULL);
+ return DC_COMPLETE;
+ case 2: /* System Time */
+ rpt_telemetry(myrpt, STATS_TIME, NULL);
+ return DC_COMPLETE;
+ case 3: /* app_rpt.c version */
+ rpt_telemetry(myrpt, STATS_VERSION, NULL);
+ default:
+ return DC_ERROR;
+ }
+ return DC_INDETERMINATE;
+}
+
+/*
+* COP - Control operator
+*/
+
+static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ if(!param)
+ return DC_ERROR;
+
+ switch(myatoi(param)){
+ case 1: /* System reset */
+ system("killall -9 asterisk"); /* FIXME to drastic? */
+ return DC_COMPLETE;
+
+ case 2:
+ myrpt->enable = 1;
+ rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA");
+ return DC_COMPLETE;
+
+ case 3:
+ myrpt->enable = 0;
+ return DC_COMPLETE;
+
+ case 4: /* test tone on */
+ rpt_telemetry(myrpt, TEST_TONE, NULL);
+ return DC_COMPLETE;
+
+ case 5: /* Disgorge variables to log for debug purposes */
+ myrpt->disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
+ return DC_COMPLETE;
+
+ case 6: /* Simulate COR being activated (phone only) */
+ if (command_source != SOURCE_PHONE) return DC_INDETERMINATE;
+ return DC_DOKEY;
+
+ }
+ return DC_INDETERMINATE;
+}
+
+/*
+* Collect digits one by one until something matches
+*/
+
+static int collect_function_digits(struct rpt *myrpt, char *digits,
+ int command_source, struct rpt_link *mylink)
+{
+ int i;
+ char *stringp,*action,*param,*functiondigits;
+ char function_table_name[30] = "";
+ char workstring[80];
+
+ struct ast_variable *vp;
+
+ if(debug)
+ printf("@@@@ Digits collected: %s, source: %d\n", digits, command_source);
+
+ if (command_source == SOURCE_DPHONE) {
+ if (!myrpt->dphone_functions) return DC_INDETERMINATE;
+ strncpy(function_table_name, myrpt->dphone_functions, sizeof(function_table_name) - 1);
+ }
+ else if (command_source == SOURCE_PHONE) {
+ if (!myrpt->phone_functions) return DC_INDETERMINATE;
+ strncpy(function_table_name, myrpt->phone_functions, sizeof(function_table_name) - 1);
+ }
+ else if (command_source == SOURCE_LNK)
+ strncpy(function_table_name, myrpt->link_functions, sizeof(function_table_name) - 1);
+ else
+ strncpy(function_table_name, myrpt->functions, sizeof(function_table_name) - 1);
+ vp = ast_variable_browse(cfg, function_table_name);
+ while(vp) {
+ if(!strncasecmp(vp->name, digits, strlen(vp->name)))
+ break;
+ vp = vp->next;
+ }
+ if(!vp) {
+ int n;
+
+ n = myrpt->longestfunc;
+ if (command_source == SOURCE_LNK) n = myrpt->link_longestfunc;
+ else
+ if (command_source == SOURCE_PHONE) n = myrpt->phone_longestfunc;
+ else
+ if (command_source == SOURCE_DPHONE) n = myrpt->dphone_longestfunc;
+
+ if(strlen(digits) >= n)
+ return DC_ERROR;
+ else
+ return DC_INDETERMINATE;
+ }
+ /* Found a match, retrieve value part and parse */
+ strncpy(workstring, vp->value, sizeof(workstring) - 1 );
+ stringp = workstring;
+ action = strsep(&stringp, ",");
+ param = stringp;
+ if(debug)
+ printf("@@@@ action: %s, param = %s\n",action, (param) ? param : "(null)");
+ /* Look up the action */
+ for(i = 0 ; i < (sizeof(function_table)/sizeof(struct function_table_tag)); i++){
+ if(!strncasecmp(action, function_table[i].action, strlen(action)))
+ break;
+ }
+ if(debug)
+ printf("@@@@ table index i = %d\n",i);
+ if(i == (sizeof(function_table)/sizeof(struct function_table_tag))){
+ /* Error, action not in table */
+ return DC_ERROR;
+ }
+ if(function_table[i].function == NULL){
+ /* Error, function undefined */
+ if(debug)
+ printf("@@@@ NULL for action: %s\n",action);
+ return DC_ERROR;
+ }
+ functiondigits = digits + strlen(vp->name);
+ return (*function_table[i].function)(myrpt, param, functiondigits, command_source, mylink);
+}
+
+
+static void handle_link_data(struct rpt *myrpt, struct rpt_link *mylink,
+ char *str)
+{
+char tmp[300],cmd[300] = "",dest[300],src[300],c;
+int seq, res;
+struct rpt_link *l;
+struct ast_frame wf;
+
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 1;
+ wf.datalen = strlen(str) + 1;
+ wf.samples = 0;
+ /* put string in our buffer */
+ strncpy(tmp,str,sizeof(tmp) - 1);
+
+ if (!strcmp(tmp,discstr))
+ {
+ mylink->disced = 1;
+ mylink->retries = MAX_RETRIES + 1;
+ ast_softhangup(mylink->chan,AST_SOFTHANGUP_DEV);
+ return;
+ }
+ if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5)
+ {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
+ return;
+ }
+ if (strcmp(cmd,"D"))
+ {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
+ return;
+ }
+
+ if (dest[0] == '0')
+ {
+ strcpy(dest,myrpt->name);
+ }
+
+ /* if not for me, redistribute to all links */
+ if (strcmp(dest,myrpt->name))
+ {
+ l = myrpt->links.next;
+ /* see if this is one in list */
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* dont send back from where it came */
+ if ((l == mylink) || (!strcmp(l->name,mylink->name)))
+ {
+ l = l->next;
+ continue;
+ }
+ /* if it is, send it and we're done */
+ if (!strcmp(l->name,dest))
+ {
+ /* send, but not to src */
+ if (strcmp(l->name,src)) {
+ wf.data = strdup(str);
+ if (l->chan) ast_write(l->chan,&wf);
+ }
+ return;
+ }
+ l = l->next;
+ }
+ l = myrpt->links.next;
+ /* otherwise, send it to all of em */
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* dont send back from where it came */
+ if ((l == mylink) || (!strcmp(l->name,mylink->name)))
+ {
+ l = l->next;
+ continue;
+ }
+ /* send, but not to src */
+ if (strcmp(l->name,src)) {
+ wf.data = strdup(str);
+ if (l->chan) ast_write(l->chan,&wf);
+ }
+ l = l->next;
+ }
+ return;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ if (c == myrpt->endchar) myrpt->stopgen = 1;
+ if (myrpt->callmode == 1)
+ {
+ myrpt->exten[myrpt->cidx++] = c;
+ myrpt->exten[myrpt->cidx] = 0;
+ /* if this exists */
+ if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+ {
+ myrpt->callmode = 2;
+ rpt_telemetry(myrpt,PROC,NULL);
+ }
+ /* if can continue, do so */
+ if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+ {
+ /* call has failed, inform user */
+ myrpt->callmode = 4;
+ }
+ }
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
+ {
+ myrpt->mydtmf = c;
+ }
+ if (c == myrpt->funcchar)
+ {
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+ time(&myrpt->rem_dtmf_time);
+ ast_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ else if ((c != myrpt->endchar) && (myrpt->rem_dtmfidx >= 0))
+ {
+ time(&myrpt->rem_dtmf_time);
+ if (myrpt->rem_dtmfidx < MAXDTMF)
+ {
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+
+ ast_mutex_unlock(&myrpt->lock);
+ strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1);
+ res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink);
+ ast_mutex_lock(&myrpt->lock);
+
+ switch(res){
+
+ case DC_INDETERMINATE:
+ break;
+
+ case DC_REQ_FLUSH:
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[0] = 0;
+ break;
+
+
+ case DC_COMPLETE:
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+
+ case DC_ERROR:
+ default:
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+ }
+ }
+
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ return;
+}
+
+static void handle_link_phone_dtmf(struct rpt *myrpt, struct rpt_link *mylink,
+ char c)
+{
+
+char cmd[300];
+int res;
+
+ ast_mutex_lock(&myrpt->lock);
+ if (c == myrpt->endchar)
+ {
+ if (mylink->lastrx)
+ {
+ mylink->lastrx = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ myrpt->stopgen = 1;
+ if (myrpt->cmdnode[0])
+ {
+ myrpt->cmdnode[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,COMPLETE,NULL);
+ ast_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ }
+ if (myrpt->cmdnode[0])
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ send_link_dtmf(myrpt,c);
+ return;
+ }
+ if (myrpt->callmode == 1)
+ {
+ myrpt->exten[myrpt->cidx++] = c;
+ myrpt->exten[myrpt->cidx] = 0;
+ /* if this exists */
+ if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+ {
+ myrpt->callmode = 2;
+ rpt_telemetry(myrpt,PROC,NULL);
+ }
+ /* if can continue, do so */
+ if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+ {
+ /* call has failed, inform user */
+ myrpt->callmode = 4;
+ }
+ }
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
+ {
+ myrpt->mydtmf = c;
+ }
+ if (c == myrpt->funcchar)
+ {
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+ time(&myrpt->rem_dtmf_time);
+ ast_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ else if ((c != myrpt->endchar) && (myrpt->rem_dtmfidx >= 0))
+ {
+ time(&myrpt->rem_dtmf_time);
+ if (myrpt->rem_dtmfidx < MAXDTMF)
+ {
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+
+ ast_mutex_unlock(&myrpt->lock);
+ strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1);
+ res = collect_function_digits(myrpt, cmd,
+ ((mylink->phonemode == 2) ? SOURCE_DPHONE : SOURCE_PHONE), mylink);
+ ast_mutex_lock(&myrpt->lock);
+
+ switch(res){
+
+ case DC_INDETERMINATE:
+ break;
+
+ case DC_DOKEY:
+ mylink->lastrx = 1;
+ break;
+
+ case DC_REQ_FLUSH:
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[0] = 0;
+ break;
+
+
+ case DC_COMPLETE:
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+
+ case DC_ERROR:
+ default:
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+ }
+ }
+
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ return;
+}
+
+/* Doug Hall RBI-1 serial data definitions:
+ *
+ * Byte 0: Expansion external outputs
+ * Byte 1:
+ * Bits 0-3 are BAND as follows:
+ * Bits 4-5 are POWER bits as follows:
+ * 00 - Low Power
+ * 01 - Hi Power
+ * 02 - Med Power
+ * Bits 6-7 are always set
+ * Byte 2:
+ * Bits 0-3 MHZ in BCD format
+ * Bits 4-5 are offset as follows:
+ * 00 - minus
+ * 01 - plus
+ * 02 - simplex
+ * 03 - minus minus (whatever that is)
+ * Bit 6 is the 0/5 KHZ bit
+ * Bit 7 is always set
+ * Byte 3:
+ * Bits 0-3 are 10 KHZ in BCD format
+ * Bits 4-7 are 100 KHZ in BCD format
+ * Byte 4: PL Tone code and encode/decode enable bits
+ * Bits 0-5 are PL tone code (comspec binary codes)
+ * Bit 6 is encode enable/disable
+ * Bit 7 is decode enable/disable
+ */
+
+/* take the frequency from the 10 mhz digits (and up) and convert it
+ to a band number */
+
+static int rbi_mhztoband(char *str)
+{
+int i;
+
+ i = atoi(str) / 10; /* get the 10's of mhz */
+ switch(i)
+ {
+ case 2:
+ return 10;
+ case 5:
+ return 11;
+ case 14:
+ return 2;
+ case 22:
+ return 3;
+ case 44:
+ return 4;
+ case 124:
+ return 0;
+ case 125:
+ return 1;
+ case 126:
+ return 8;
+ case 127:
+ return 5;
+ case 128:
+ return 6;
+ case 129:
+ return 7;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* take a PL frequency and turn it into a code */
+static int rbi_pltocode(char *str)
+{
+int i;
+char *s;
+
+ s = strchr(str,'.');
+ i = 0;
+ if (s) i = atoi(s + 1);
+ i += atoi(str) * 10;
+ switch(i)
+ {
+ case 670:
+ return 0;
+ case 719:
+ return 1;
+ case 744:
+ return 2;
+ case 770:
+ return 3;
+ case 797:
+ return 4;
+ case 825:
+ return 5;
+ case 854:
+ return 6;
+ case 885:
+ return 7;
+ case 915:
+ return 8;
+ case 948:
+ return 9;
+ case 974:
+ return 10;
+ case 1000:
+ return 11;
+ case 1035:
+ return 12;
+ case 1072:
+ return 13;
+ case 1109:
+ return 14;
+ case 1148:
+ return 15;
+ case 1188:
+ return 16;
+ case 1230:
+ return 17;
+ case 1273:
+ return 18;
+ case 1318:
+ return 19;
+ case 1365:
+ return 20;
+ case 1413:
+ return 21;
+ case 1462:
+ return 22;
+ case 1514:
+ return 23;
+ case 1567:
+ return 24;
+ case 1622:
+ return 25;
+ case 1679:
+ return 26;
+ case 1738:
+ return 27;
+ case 1799:
+ return 28;
+ case 1862:
+ return 29;
+ case 1928:
+ return 30;
+ case 2035:
+ return 31;
+ case 2107:
+ return 32;
+ case 2181:
+ return 33;
+ case 2257:
+ return 34;
+ case 2336:
+ return 35;
+ case 2418:
+ return 36;
+ case 2503:
+ return 37;
+ }
+ return -1;
+}
+
+/*
+* Shift out a formatted serial bit stream
+*/
+
+static void rbi_out_parallel(struct rpt *myrpt,unsigned char *data)
+ {
+ int i,j;
+ unsigned char od,d;
+ static volatile long long delayvar;
+
+ for(i = 0 ; i < 5 ; i++){
+ od = *data++;
+ for(j = 0 ; j < 8 ; j++){
+ d = od & 1;
+ outb(d,myrpt->iobase);
+ /* >= 15 us */
+ for(delayvar = 1; delayvar < 15000; delayvar++);
+ od >>= 1;
+ outb(d | 2,myrpt->iobase);
+ /* >= 30 us */
+ for(delayvar = 1; delayvar < 30000; delayvar++);
+ outb(d,myrpt->iobase);
+ /* >= 10 us */
+ for(delayvar = 1; delayvar < 10000; delayvar++);
+ }
+ }
+ /* >= 50 us */
+ for(delayvar = 1; delayvar < 50000; delayvar++);
+ }
+
+static void rbi_out(struct rpt *myrpt,unsigned char *data)
+{
+struct zt_radio_param r;
+
+ memset(&r,0,sizeof(struct zt_radio_param));
+ r.radpar = ZT_RADPAR_REMMODE;
+ r.data = ZT_RADPAR_REM_RBI1;
+ /* if setparam ioctl fails, its probably not a pciradio card */
+ if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1)
+ {
+ rbi_out_parallel(myrpt,data);
+ return;
+ }
+ r.radpar = ZT_RADPAR_REMCOMMAND;
+ memcpy(&r.data,data,5);
+ if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1)
+ {
+ ast_log(LOG_WARNING,"Cannot send RBI command for channel %s\n",myrpt->rxchannel->name);
+ return;
+ }
+}
+
+static int serial_remote_io(struct rpt *myrpt, char *txbuf, int txbytes, char *rxbuf,
+ int rxmaxbytes, int asciiflag)
+{
+ int i;
+ struct zt_radio_param prm;
+
+ if(debug){
+ printf("String output was: ");
+ for(i = 0; i < txbytes; i++)
+ printf("%02X ", (unsigned char ) txbuf[i]);
+ printf("\n");
+ }
+
+ prm.radpar = ZT_RADPAR_REMMODE;
+ if (asciiflag) prm.data = ZT_RADPAR_REM_SERIAL_ASCII;
+ else prm.data = ZT_RADPAR_REM_SERIAL;
+ if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ prm.radpar = ZT_RADPAR_REMCOMMAND;
+ prm.data = rxmaxbytes;
+ memcpy(prm.buf,txbuf,txbytes);
+ prm.index = txbytes;
+ if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ if (rxbuf)
+ {
+ *rxbuf = 0;
+ memcpy(rxbuf,prm.buf,prm.index);
+ }
+ return(prm.index);
+}
+
+static int setrbi(struct rpt *myrpt)
+{
+char tmp[MAXREMSTR] = "",rbicmd[5],*s;
+int band,txoffset = 0,txpower = 0,txpl;
+
+ /* must be a remote system */
+ if (!myrpt->remote) return(0);
+ /* must have rbi hardware */
+ if (strncmp(myrpt->remote,remote_rig_rbi,3)) return(0);
+ strncpy(tmp, myrpt->freq, sizeof(tmp) - 1);
+ s = strchr(tmp,'.');
+ /* if no decimal, is invalid */
+
+ if (s == NULL){
+ if(debug)
+ printf("@@@@ Frequency needs a decimal\n");
+ return -1;
+ }
+
+ *s++ = 0;
+ if (strlen(tmp) < 2){
+ if(debug)
+ printf("@@@@ Bad MHz digits: %s\n", tmp);
+ return -1;
+ }
+
+ if (strlen(s) < 3){
+ if(debug)
+ printf("@@@@ Bad KHz digits: %s\n", s);
+ return -1;
+ }
+
+ if ((s[2] != '0') && (s[2] != '5')){
+ if(debug)
+ printf("@@@@ KHz must end in 0 or 5: %c\n", s[2]);
+ return -1;
+ }
+
+ band = rbi_mhztoband(tmp);
+ if (band == -1){
+ if(debug)
+ printf("@@@@ Bad Band: %s\n", tmp);
+ return -1;
+ }
+
+ txpl = rbi_pltocode(myrpt->txpl);
+
+ if (txpl == -1){
+ if(debug)
+ printf("@@@@ Bad TX PL: %s\n", myrpt->txpl);
+ return -1;
+ }
+
+
+ switch(myrpt->offset)
+ {
+ case REM_MINUS:
+ txoffset = 0;
+ break;
+ case REM_PLUS:
+ txoffset = 0x10;
+ break;
+ case REM_SIMPLEX:
+ txoffset = 0x20;
+ break;
+ }
+ switch(myrpt->powerlevel)
+ {
+ case REM_LOWPWR:
+ txpower = 0;
+ break;
+ case REM_MEDPWR:
+ txpower = 0x20;
+ break;
+ case REM_HIPWR:
+ txpower = 0x10;
+ break;
+ }
+ rbicmd[0] = 0;
+ rbicmd[1] = band | txpower | 0xc0;
+ rbicmd[2] = (*(s - 2) - '0') | txoffset | 0x80;
+ if (s[2] == '5') rbicmd[2] |= 0x40;
+ rbicmd[3] = ((*s - '0') << 4) + (s[1] - '0');
+ rbicmd[4] = txpl;
+ if (myrpt->txplon) rbicmd[4] |= 0x40;
+ if (myrpt->rxplon) rbicmd[4] |= 0x80;
+ rbi_out(myrpt,rbicmd);
+ return 0;
+}
+
+
+/* Check for valid rbi frequency */
+/* Hard coded limits now, configurable later, maybe? */
+
+static int check_freq_rbi(int m, int d, int *defmode)
+{
+ int dflmd = REM_MODE_FM;
+
+ if(m == 50){ /* 6 meters */
+ if(d < 10100)
+ return -1;
+ }
+ else if((m >= 51) && ( m < 54)){
+ ;
+ }
+ else if(m == 144){ /* 2 meters */
+ if(d < 10100)
+ return -1;
+ }
+ else if((m >= 145) && (m < 148)){
+ ;
+ }
+ else if((m >= 222) && (m < 225)){ /* 1.25 meters */
+ ;
+ }
+ else if((m >= 430) && (m < 450)){ /* 70 centimeters */
+ ;
+ }
+ else if((m >= 1240) && (m < 1300)){ /* 23 centimeters */
+ ;
+ }
+ else
+ return -1;
+
+ if(defmode)
+ *defmode = dflmd;
+
+
+ return 0;
+}
+
+/*
+* Split frequency into mhz and decimals
+*/
+
+static int split_freq(char *mhz, char *decimals, char *freq)
+{
+ char freq_copy[MAXREMSTR];
+ char *decp;
+
+ decp = strchr(strncpy(freq_copy, freq, MAXREMSTR),'.');
+ if(decp){
+ *decp++ = 0;
+ strncpy(mhz, freq_copy, MAXREMSTR);
+ strcpy(decimals, "00000");
+ strncpy(decimals, decp, strlen(decp));
+ decimals[5] = 0;
+ return 0;
+ }
+ else
+ return -1;
+
+}
+
+/*
+* Split ctcss frequency into hertz and decimal
+*/
+
+static int split_ctcss_freq(char *hertz, char *decimal, char *freq)
+{
+ char freq_copy[MAXREMSTR];
+ char *decp;
+
+ decp = strchr(strncpy(freq_copy, freq, MAXREMSTR),'.');
+ if(decp){
+ *decp++ = 0;
+ strncpy(hertz, freq_copy, MAXREMSTR);
+ strncpy(decimal, decp, strlen(decp));
+ decimal[strlen(decp)] = '\0';
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+
+/*
+* FT-897 I/O handlers
+*/
+
+/* Check to see that the frequency is valid */
+/* Hard coded limits now, configurable later, maybe? */
+
+
+static int check_freq_ft897(int m, int d, int *defmode)
+{
+ int dflmd = REM_MODE_FM;
+
+ if(m == 1){ /* 160 meters */
+ dflmd = REM_MODE_LSB;
+ if(d < 80001)
+ return -1;
+ }
+ else if(m == 3){ /* 80 meters */
+ dflmd = REM_MODE_LSB;
+ if(d < 75001)
+ return -1;
+ }
+ else if(m == 7){ /* 40 meters */
+ dflmd = REM_MODE_LSB;
+ if((d < 15001) || (d > 29999))
+ return -1;
+ }
+ else if(m == 14){ /* 20 meters */
+ dflmd = REM_MODE_USB;
+ if((d < 15001) || (d > 34999))
+ return -1;
+ }
+ else if(m == 18){ /* 17 meters */
+ dflmd = REM_MODE_USB;
+ if((d < 11001) || (d > 16797))
+ return -1;
+ }
+ else if(m == 21){ /* 15 meters */
+ dflmd = REM_MODE_USB;
+ if((d < 20001) || (d > 44999))
+ return -1;
+ }
+ else if(m == 24){ /* 12 meters */
+ dflmd = REM_MODE_USB;
+ if((d < 93001) || (d > 98999))
+ return -1;
+ }
+ else if(m == 28){ /* 10 meters */
+ dflmd = REM_MODE_USB;
+ if(d < 30001)
+ return -1;
+ }
+ else if(m == 29){
+ if(d >= 51000)
+ dflmd = REM_MODE_FM;
+ else
+ dflmd = REM_MODE_USB;
+ if(d > 69999)
+ return -1;
+ }
+ else if(m == 50){ /* 6 meters */
+ if(d < 10100)
+ return -1;
+ if(d >= 30000)
+ dflmd = REM_MODE_FM;
+ else
+ dflmd = REM_MODE_USB;
+
+ }
+ else if((m >= 51) && ( m < 54)){
+ dflmd = REM_MODE_FM;
+ }
+ else if(m == 144){ /* 2 meters */
+ if(d < 10100)
+ return -1;
+ if(d >= 30000)
+ dflmd = REM_MODE_FM;
+ else
+ dflmd = REM_MODE_USB;
+ }
+ else if((m >= 145) && (m < 148)){
+ dflmd = REM_MODE_FM;
+ }
+ else if((m >= 430) && (m < 450)){ /* 70 centimeters */
+ if(m < 438)
+ dflmd = REM_MODE_USB;
+ else
+ dflmd = REM_MODE_FM;
+ ;
+ }
+ else
+ return -1;
+
+ if(defmode)
+ *defmode = dflmd;
+
+ return 0;
+}
+
+/*
+* Set a new frequency for the FT897
+*/
+
+static int set_freq_ft897(struct rpt *myrpt, char *newfreq)
+{
+ char mhz[MAXREMSTR];
+ char decimals[MAXREMSTR];
+ unsigned char cmdstr[5];
+ int fd,m,d;
+
+ fd = 0;
+ if(debug)
+ printf("New frequency: %s\n",newfreq);
+
+ if(split_freq(mhz, decimals, newfreq))
+ return -1;
+
+ m = atoi(mhz);
+ d = atoi(decimals);
+
+ /* The FT-897 likes packed BCD frequencies */
+
+ cmdstr[0] = ((m / 100) << 4) + ((m % 100)/10); /* 100MHz 10Mhz */
+ cmdstr[1] = ((m % 10) << 4) + (d / 10000); /* 1MHz 100KHz */
+ cmdstr[2] = (((d % 10000)/1000) << 4) + ((d % 1000)/ 100); /* 10KHz 1KHz */
+ cmdstr[3] = (((d % 100)/10) << 4) + (d % 10); /* 100Hz 10Hz */
+ cmdstr[4] = 0x01; /* command */
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+
+}
+
+/* ft-897 simple commands */
+
+static int simple_command_ft897(struct rpt *myrpt, char command)
+{
+ unsigned char cmdstr[5];
+
+ memset(cmdstr, 0, 5);
+
+ cmdstr[4] = command;
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+
+}
+
+/* ft-897 offset */
+
+static int set_offset_ft897(struct rpt *myrpt, char offset)
+{
+ unsigned char cmdstr[5];
+
+ memset(cmdstr, 0, 5);
+
+ switch(offset){
+ case REM_SIMPLEX:
+ cmdstr[0] = 0x89;
+ break;
+
+ case REM_MINUS:
+ cmdstr[0] = 0x09;
+ break;
+
+ case REM_PLUS:
+ cmdstr[0] = 0x49;
+ break;
+
+ default:
+ return -1;
+ }
+
+ cmdstr[4] = 0x09;
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+/* ft-897 mode */
+
+static int set_mode_ft897(struct rpt *myrpt, char newmode)
+{
+ unsigned char cmdstr[5];
+
+ memset(cmdstr, 0, 5);
+
+ switch(newmode){
+ case REM_MODE_FM:
+ cmdstr[0] = 0x08;
+ break;
+
+ case REM_MODE_USB:
+ cmdstr[0] = 0x01;
+ break;
+
+ case REM_MODE_LSB:
+ cmdstr[0] = 0x00;
+ break;
+
+ case REM_MODE_AM:
+ cmdstr[0] = 0x04;
+ break;
+
+ default:
+ return -1;
+ }
+ cmdstr[4] = 0x07;
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+/* Set tone encode and decode modes */
+
+static int set_ctcss_mode_ft897(struct rpt *myrpt, char txplon, char rxplon)
+{
+ unsigned char cmdstr[5];
+
+ memset(cmdstr, 0, 5);
+
+ if(rxplon && txplon)
+ cmdstr[0] = 0x2A; /* Encode and Decode */
+ else if (!rxplon && txplon)
+ cmdstr[0] = 0x4A; /* Encode only */
+ else if (rxplon && !txplon)
+ cmdstr[0] = 0x3A; /* Encode only */
+ else
+ cmdstr[0] = 0x8A; /* OFF */
+
+ cmdstr[4] = 0x0A;
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+
+/* Set transmit and receive ctcss tone frequencies */
+
+static int set_ctcss_freq_ft897(struct rpt *myrpt, char *txtone, char *rxtone)
+{
+ unsigned char cmdstr[5];
+ char hertz[MAXREMSTR],decimal[MAXREMSTR];
+ int h,d;
+
+ memset(cmdstr, 0, 5);
+
+ if(split_ctcss_freq(hertz, decimal, txtone))
+ return -1;
+
+ h = atoi(hertz);
+ d = atoi(decimal);
+
+ cmdstr[0] = ((h / 100) << 4) + (h % 100)/ 10;
+ cmdstr[1] = ((h % 10) << 4) + (d % 10);
+
+ if(rxtone){
+
+ if(split_ctcss_freq(hertz, decimal, rxtone))
+ return -1;
+
+ h = atoi(hertz);
+ d = atoi(decimal);
+
+ cmdstr[2] = ((h / 100) << 4) + (h % 100)/ 10;
+ cmdstr[3] = ((h % 10) << 4) + (d % 10);
+ }
+ cmdstr[4] = 0x0B;
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+
+
+static int set_ft897(struct rpt *myrpt)
+{
+ int res;
+
+ if(debug)
+ printf("@@@@ lock on\n");
+
+ res = simple_command_ft897(myrpt, 0x00); /* LOCK on */
+
+ if(debug)
+ printf("@@@@ ptt off\n");
+
+ if(!res)
+ res = simple_command_ft897(myrpt, 0x88); /* PTT off */
+
+ if(debug)
+ printf("Modulation mode\n");
+
+ if(!res)
+ res = set_mode_ft897(myrpt, myrpt->remmode); /* Modulation mode */
+
+ if(debug)
+ printf("Split off\n");
+
+ if(!res)
+ simple_command_ft897(myrpt, 0x82); /* Split off */
+
+ if(debug)
+ printf("Frequency\n");
+
+ if(!res)
+ res = set_freq_ft897(myrpt, myrpt->freq); /* Frequency */
+ if((myrpt->remmode == REM_MODE_FM)){
+ if(debug)
+ printf("Offset\n");
+ if(!res)
+ res = set_offset_ft897(myrpt, myrpt->offset); /* Offset if FM */
+ if((!res)&&(myrpt->rxplon || myrpt->txplon)){
+ if(debug)
+ printf("CTCSS tone freqs.\n");
+ res = set_ctcss_freq_ft897(myrpt, myrpt->txpl, myrpt->rxpl); /* CTCSS freqs if CTCSS is enabled */
+ }
+ if(!res){
+ if(debug)
+ printf("CTCSS mode\n");
+ res = set_ctcss_mode_ft897(myrpt, myrpt->txplon, myrpt->rxplon); /* CTCSS mode */
+ }
+ }
+ if((myrpt->remmode == REM_MODE_USB)||(myrpt->remmode == REM_MODE_LSB)){
+ if(debug)
+ printf("Clarifier off\n");
+ simple_command_ft897(myrpt, 0x85); /* Clarifier off if LSB or USB */
+ }
+ return res;
+}
+
+static int closerem_ft897(struct rpt *myrpt)
+{
+ simple_command_ft897(myrpt, 0x88); /* PTT off */
+ return 0;
+}
+
+/*
+* Bump frequency up or down by a small amount
+* Return 0 if the new frequnecy is valid, or -1 if invalid
+* Interval is in Hz, resolution is 10Hz
+*/
+
+static int multimode_bump_freq_ft897(struct rpt *myrpt, int interval)
+{
+ int m,d;
+ char mhz[MAXREMSTR], decimals[MAXREMSTR];
+
+ if(debug)
+ printf("Before bump: %s\n", myrpt->freq);
+
+ if(split_freq(mhz, decimals, myrpt->freq))
+ return -1;
+
+ m = atoi(mhz);
+ d = atoi(decimals);
+
+ d += (interval / 10); /* 10Hz resolution */
+ if(d < 0){
+ m--;
+ d += 100000;
+ }
+ else if(d >= 100000){
+ m++;
+ d -= 100000;
+ }
+
+ if(check_freq_ft897(m, d, NULL)){
+ if(debug)
+ printf("Bump freq invalid\n");
+ return -1;
+ }
+
+ snprintf(myrpt->freq, MAXREMSTR, "%d.%05d", m, d);
+
+ if(debug)
+ printf("After bump: %s\n", myrpt->freq);
+
+ return set_freq_ft897(myrpt, myrpt->freq);
+}
+
+
+
+/*
+* Dispatch to correct I/O handler
+*/
+
+static int setrem(struct rpt *myrpt)
+{
+ if(!strcmp(myrpt->remote, remote_rig_ft897))
+ return set_ft897(myrpt);
+ else if(!strcmp(myrpt->remote, remote_rig_rbi))
+ return setrbi(myrpt);
+ else
+ return -1;
+}
+
+static int closerem(struct rpt *myrpt)
+{
+ if(!strcmp(myrpt->remote, remote_rig_ft897))
+ return closerem_ft897(myrpt);
+ else
+ return 0;
+}
+
+/*
+* Dispatch to correct frequency checker
+*/
+
+static int check_freq(struct rpt *myrpt, int m, int d, int *defmode)
+{
+ if(!strcmp(myrpt->remote, remote_rig_ft897))
+ return check_freq_ft897(m, d, defmode);
+ else if(!strcmp(myrpt->remote, remote_rig_rbi))
+ return check_freq_rbi(m, d, defmode);
+ else
+ return -1;
+}
+
+/*
+* Return 1 if rig is multimode capable
+*/
+
+static int multimode_capable(struct rpt *myrpt)
+{
+ if(!strcmp(myrpt->remote, remote_rig_ft897))
+ return 1;
+ return 0;
+}
+
+/*
+* Dispatch to correct frequency bumping function
+*/
+
+static int multimode_bump_freq(struct rpt *myrpt, int interval)
+{
+ if(!strcmp(myrpt->remote, remote_rig_ft897))
+ return multimode_bump_freq_ft897(myrpt, interval);
+ else
+ return -1;
+}
+
+
+/*
+* Queue announcment that scan has been stopped
+*/
+
+static void stop_scan(struct rpt *myrpt, int flag)
+{
+ myrpt->hfscanmode = 0;
+ myrpt->hfscanstatus = ((flag) ? -2 : -1);
+}
+
+/*
+* This is called periodically when in scan mode
+*/
+
+
+static int service_scan(struct rpt *myrpt)
+{
+ int res, interval;
+ char mhz[MAXREMSTR], decimals[MAXREMSTR], k10=0i, k100=0;
+
+ switch(myrpt->hfscanmode){
+
+ case HF_SCAN_DOWN_SLOW:
+ interval = -10; /* 100Hz /sec */
+ break;
+
+ case HF_SCAN_DOWN_QUICK:
+ interval = -50; /* 500Hz /sec */
+ break;
+
+ case HF_SCAN_DOWN_FAST:
+ interval = -200; /* 2KHz /sec */
+ break;
+
+ case HF_SCAN_UP_SLOW:
+ interval = 10; /* 100Hz /sec */
+ break;
+
+ case HF_SCAN_UP_QUICK:
+ interval = 50; /* 500 Hz/sec */
+ break;
+
+ case HF_SCAN_UP_FAST:
+ interval = 200; /* 2KHz /sec */
+ break;
+
+ default:
+ myrpt->hfscanmode = 0; /* Huh? */
+ return -1;
+ }
+
+ res = split_freq(mhz, decimals, myrpt->freq);
+
+ if(!res){
+ k100 =decimals[0];
+ k10 = decimals[1];
+ res = multimode_bump_freq(myrpt, interval);
+ }
+
+ if(!res)
+ res = split_freq(mhz, decimals, myrpt->freq);
+
+
+ if(res){
+ stop_scan(myrpt,1);
+ return -1;
+ }
+
+ /* Announce 10KHz boundaries */
+ if(k10 != decimals[1]){
+ int myhund = (interval < 0) ? k100 : decimals[0];
+ int myten = (interval < 0) ? k10 : decimals[1];
+ myrpt->hfscanstatus = (myten == '0') ? (myhund - '0') * 100 : (myten - '0') * 10;
+ }
+ return res;
+
+}
+
+
+static int rmt_telem_start(struct rpt *myrpt, struct ast_channel *chan, int delay)
+{
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ ast_indicate(chan,AST_CONTROL_RADIO_KEY);
+ if (ast_safe_sleep(chan, delay) == -1)
+ return -1;
+ return 0;
+}
+
+
+static int rmt_telem_finish(struct rpt *myrpt, struct ast_channel *chan)
+{
+
+struct zt_params par;
+
+ if (ioctl(myrpt->txchannel->fds[0],ZT_GET_PARAMS,&par) == -1)
+ {
+ return -1;
+
+ }
+ if (!par.rxisoffhook)
+ {
+ ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_UNKEY);
+ myrpt->remoterx = 0;
+ }
+ else
+ {
+ myrpt->remoterx = 1;
+ }
+ return 0;
+}
+
+
+static int rmt_sayfile(struct rpt *myrpt, struct ast_channel *chan, int delay, char *filename)
+{
+ int res;
+
+ res = rmt_telem_start(myrpt, chan, delay);
+
+ if(!res)
+ res = sayfile(chan, filename);
+
+ if(!res)
+ res = rmt_telem_finish(myrpt, chan);
+ return res;
+}
+
+static int rmt_saycharstr(struct rpt *myrpt, struct ast_channel *chan, int delay, char *charstr)
+{
+ int res;
+
+ res = rmt_telem_start(myrpt, chan, delay);
+
+ if(!res)
+ res = saycharstr(chan, charstr);
+
+ if(!res)
+ res = rmt_telem_finish(myrpt, chan);
+ return res;
+}
+
+
+
+/*
+* Remote base function
+*/
+
+static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ char *s,*s1,*s2,*val;
+ int i,j,ht,k,l,ls2,m,d,res,offset,offsave, modesave, defmode;
+ char multimode = 0;
+ char oc;
+ char tmp[20], freq[20] = "", savestr[20] = "";
+ char mhz[MAXREMSTR], decimals[MAXREMSTR];
+ struct ast_channel *mychannel;
+
+ if((!param) || (command_source == SOURCE_RPT) || (command_source == SOURCE_LNK))
+ return DC_ERROR;
+
+ multimode = multimode_capable(myrpt);
+
+ mychannel = myrpt->remchannel;
+
+
+ switch(myatoi(param)){
+
+ case 1: /* retrieve memory */
+ if(strlen(digitbuf) < 2) /* needs 2 digits */
+ break;
+
+ for(i = 0 ; i < 2 ; i++){
+ if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ }
+
+ val = ast_variable_retrieve(cfg, MEMORY, digitbuf);
+ if (!val){
+ if (ast_safe_sleep(mychannel,1000) == -1)
+ return DC_ERROR;
+ sayfile(mychannel,"rpt/memory_notfound");
+ return DC_COMPLETE;
+ }
+ strncpy(tmp,val,sizeof(tmp) - 1);
+ s = strchr(tmp,',');
+ if (!s)
+ return DC_ERROR;
+ *s++ = 0;
+ s1 = strchr(s,',');
+ if (!s1)
+ return DC_ERROR;
+ *s1++ = 0;
+ strncpy(myrpt->freq, tmp, sizeof(myrpt->freq) - 1);
+ strncpy(myrpt->rxpl, s, sizeof(myrpt->rxpl) - 1);
+ strncpy(myrpt->txpl, s, sizeof(myrpt->rxpl) - 1);
+ myrpt->remmode = REM_MODE_FM;
+ myrpt->offset = REM_SIMPLEX;
+ myrpt->powerlevel = REM_MEDPWR;
+ myrpt->txplon = myrpt->rxplon = 0;
+ while(*s1)
+ {
+ switch(*s1++){
+ case 'A':
+ case 'a':
+ strcpy(myrpt->rxpl, "100.0");
+ strcpy(myrpt->txpl, "100.0");
+ myrpt->remmode = REM_MODE_AM;
+ break;
+
+ case 'B':
+ case 'b':
+ strcpy(myrpt->rxpl, "100.0");
+ strcpy(myrpt->txpl, "100.0");
+ myrpt->remmode = REM_MODE_LSB;
+ break;
+
+ case 'F':
+ myrpt->remmode = REM_MODE_FM;
+ break;
+
+ case 'L':
+ case 'l':
+ myrpt->powerlevel = REM_LOWPWR;
+ break;
+ case 'H':
+ case 'h':
+ myrpt->powerlevel = REM_HIPWR;
+ break;
+
+ case 'M':
+ case 'm':
+ myrpt->powerlevel = REM_MEDPWR;
+ break;
+
+ case '-':
+ myrpt->offset = REM_MINUS;
+ break;
+
+ case '+':
+ myrpt->offset = REM_PLUS;
+ break;
+
+ case 'S':
+ case 's':
+ myrpt->offset = REM_SIMPLEX;
+ break;
+
+ case 'T':
+ case 't':
+ myrpt->txplon = 1;
+ break;
+
+ case 'R':
+ case 'r':
+ myrpt->rxplon = 1;
+ break;
+
+ case 'U':
+ case 'u':
+ strcpy(myrpt->rxpl, "100.0");
+ strcpy(myrpt->txpl, "100.0");
+ myrpt->remmode = REM_MODE_USB;
+ break;
+ }
+ }
+
+
+ if (setrem(myrpt) == -1)
+ return DC_ERROR;
+
+
+ return DC_COMPLETE;
+
+ case 2: /* set freq and offset */
+
+
+ for(i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++){ /* look for M+*K+*O or M+*H+* depending on mode */
+ if(digitbuf[i] == '*'){
+ j++;
+ continue;
+ }
+ if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ goto invalid_freq;
+ else{
+ if(j == 0)
+ l++; /* # of digits before first * */
+ if(j == 1)
+ k++; /* # of digits after first * */
+ }
+ }
+
+ i = strlen(digitbuf) - 1;
+ if(multimode){
+ if((j > 2) || (l > 3) || (k > 6))
+ goto invalid_freq; /* &^@#! */
+ }
+ else{
+ if((j > 2) || (l > 4) || (k > 3))
+ goto invalid_freq; /* &^@#! */
+ }
+
+ /* Wait for M+*K+* */
+
+ if(j < 2)
+ break; /* Not yet */
+
+ /* We have a frequency */
+
+ strncpy(tmp, digitbuf ,sizeof(tmp) - 1);
+
+ s = tmp;
+ s1 = strsep(&s, "*"); /* Pick off MHz */
+ s2 = strsep(&s,"*"); /* Pick off KHz and Hz */
+ ls2 = strlen(s2);
+
+ switch(ls2){ /* Allow partial entry of khz and hz digits for laziness support */
+ case 1:
+ ht = 0;
+ k = 100 * atoi(s2);
+ break;
+
+ case 2:
+ ht = 0;
+ k = 10 * atoi(s2);
+ break;
+
+ case 3:
+ if(!multimode){
+ if((s2[2] != '0')&&(s2[2] != '5'))
+ goto invalid_freq;
+ }
+ ht = 0;
+ k = atoi(s2);
+ break;
+ case 4:
+ k = atoi(s2)/10;
+ ht = 10 * (atoi(s2+(ls2-1)));
+ break;
+
+ case 5:
+ k = atoi(s2)/100;
+ ht = (atoi(s2+(ls2-2)));
+ break;
+
+ default:
+ goto invalid_freq;
+ }
+
+ /* Check frequency for validity and establish a default mode */
+
+ snprintf(freq, sizeof(freq), "%s.%03d%02d",s1, k, ht);
+
+ if(debug)
+ printf("New frequency: %s\n", freq);
+
+ split_freq(mhz, decimals, freq);
+ m = atoi(mhz);
+ d = atoi(decimals);
+
+ if(check_freq(myrpt, m, d, &defmode)) /* Check to see if frequency entered is legit */
+ goto invalid_freq;
+
+
+ if((defmode == REM_MODE_FM) && (digitbuf[i] == '*')) /* If FM, user must enter and additional offset digit */
+ break; /* Not yet */
+
+
+ offset = REM_SIMPLEX; /* Assume simplex */
+
+ if(defmode == REM_MODE_FM){
+ oc = *s; /* Pick off offset */
+
+ if (oc){
+ switch(oc){
+ case '1':
+ offset = REM_MINUS;
+ break;
+
+ case '2':
+ offset = REM_SIMPLEX;
+ break;
+
+ case '3':
+ offset = REM_PLUS;
+ break;
+
+ default:
+ goto invalid_freq;
+ }
+ }
+ }
+ offsave = myrpt->offset;
+ modesave = myrpt->remmode;
+ strncpy(savestr, myrpt->freq, sizeof(savestr) - 1);
+ strncpy(myrpt->freq, freq, sizeof(myrpt->freq) - 1);
+ myrpt->offset = offset;
+ myrpt->remmode = defmode;
+
+ if (setrem(myrpt) == -1){
+ myrpt->offset = offsave;
+ myrpt->remmode = modesave;
+ strncpy(myrpt->freq, savestr, sizeof(myrpt->freq) - 1);
+ goto invalid_freq;
+ }
+
+ return DC_COMPLETE;
+
+
+ invalid_freq:
+
+ rmt_sayfile(myrpt, mychannel, 1000, "rpt/invalid-freq");
+
+ return DC_ERROR;
+
+ case 3: /* set rx PL tone */
+
+ for(i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++){ /* look for N+*N */
+ if(digitbuf[i] == '*'){
+ j++;
+ continue;
+ }
+ if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ else{
+ if(j)
+ l++;
+ else
+ k++;
+ }
+ }
+ if((j > 1) || (k > 3) || (l > 1))
+ return DC_ERROR; /* &$@^! */
+ i = strlen(digitbuf) - 1;
+ if((j != 1) || (k < 2)|| (l != 1))
+ break; /* Not yet */
+ if(debug)
+ printf("PL digits entered %s\n", digitbuf);
+
+ strncpy(tmp, digitbuf, sizeof(tmp) - 1);
+ /* see if we have at least 1 */
+ s = strchr(tmp,'*');
+ if(s)
+ *s = '.';
+ strncpy(savestr, myrpt->rxpl, sizeof(savestr) - 1);
+ strncpy(myrpt->rxpl, tmp, sizeof(myrpt->rxpl) - 1);
+
+ if (setrem(myrpt) == -1){
+ strncpy(myrpt->rxpl, savestr, sizeof(myrpt->rxpl) - 1);
+ return DC_ERROR;
+ }
+
+
+ return DC_COMPLETE;
+
+ case 4: /* set tx PL tone */
+
+ for(i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++){ /* look for N+*N */
+ if(digitbuf[i] == '*'){
+ j++;
+ continue;
+ }
+ if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ else{
+ if(j)
+ l++;
+ else
+ k++;
+ }
+ }
+ if((j > 1) || (k > 3) || (l > 1))
+ return DC_ERROR; /* &$@^! */
+ i = strlen(digitbuf) - 1;
+ if((j != 1) || (k < 2)|| (l != 1))
+ break; /* Not yet */
+ if(debug)
+ printf("PL digits entered %s\n", digitbuf);
+
+ strncpy(tmp, digitbuf, sizeof(tmp) - 1);
+ /* see if we have at least 1 */
+ s = strchr(tmp,'*');
+ if(s)
+ *s = '.';
+ strncpy(savestr, myrpt->txpl, sizeof(savestr) - 1);
+ strncpy(myrpt->txpl, tmp, sizeof(myrpt->txpl) - 1);
+
+ if (setrem(myrpt) == -1){
+ strncpy(myrpt->txpl, savestr, sizeof(myrpt->txpl) - 1);
+ return DC_ERROR;
+ }
+
+
+ return DC_COMPLETE;
+
+
+ case 6: /* MODE (FM,USB,LSB,AM) */
+ if(strlen(digitbuf) < 1)
+ break;
+
+ if(!multimode)
+ return DC_ERROR; /* Multimode radios only */
+
+ switch(*digitbuf){
+ case '1':
+ split_freq(mhz, decimals, myrpt->freq);
+ m=atoi(mhz);
+ if(m < 29) /* No FM allowed below 29MHz! */
+ return DC_ERROR;
+ myrpt->remmode = REM_MODE_FM;
+ res = rmt_saycharstr(myrpt, mychannel, 1000,"FM");
+ break;
+
+ case '2':
+ myrpt->remmode = REM_MODE_USB;
+ res = rmt_saycharstr(myrpt, mychannel, 1000,"USB");
+ break;
+
+ case '3':
+ myrpt->remmode = REM_MODE_LSB;
+ res = rmt_saycharstr(myrpt, mychannel, 1000,"LSB");
+ break;
+
+ case '4':
+ myrpt->remmode = REM_MODE_AM;
+ res = rmt_saycharstr(myrpt, mychannel, 1000,"AM");
+ break;
+
+ default:
+ return DC_ERROR;
+ }
+ if(res)
+ return DC_ERROR;
+
+ if(setrem(myrpt))
+ return DC_ERROR;
+ return DC_COMPLETE;
+
+ case 100: /* other stuff */
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ res = rmt_telem_start(myrpt, mychannel, 1000);
+ switch(myatoi(param)){ /* Quick commands requiring a setrem call */
+ case 100: /* RX PL Off */
+ myrpt->rxplon = 0;
+ if(!res)
+ res = sayfile(mychannel, "rpt/rxpl");
+ if(!res)
+ sayfile(mychannel, "rpt/off");
+ break;
+
+ case 101: /* RX PL On */
+ myrpt->rxplon = 1;
+ if(!res)
+ res = sayfile(mychannel, "rpt/rxpl");
+ if(!res)
+ sayfile(mychannel, "rpt/on");
+ break;
+
+
+ case 102: /* TX PL Off */
+ myrpt->txplon = 0;
+ if(!res)
+ res = sayfile(mychannel, "rpt/txpl");
+ if(!res)
+ sayfile(mychannel, "rpt/off");
+ break;
+
+ case 103: /* TX PL On */
+ myrpt->txplon = 1;
+ if(!res)
+ res = sayfile(mychannel, "rpt/txpl");
+ if(!res)
+ sayfile(mychannel, "rpt/on");
+ break;
+
+ case 104: /* Low Power */
+ myrpt->powerlevel = REM_LOWPWR;
+ if(!res)
+ res = sayfile(mychannel, "rpt/lopwr");
+ break;
+
+ case 105: /* Medium Power */
+ myrpt->powerlevel = REM_MEDPWR;
+ if(!res)
+ res = sayfile(mychannel, "rpt/medpwr");
+ break;
+
+ case 106: /* Hi Power */
+ myrpt->powerlevel = REM_HIPWR;
+ if(!res)
+ res = sayfile(mychannel, "rpt/hipwr");
+ break;
+
+ default:
+ if(!res)
+ rmt_telem_finish(myrpt, mychannel);
+ return DC_ERROR;
+ }
+ if(!res)
+ res = rmt_telem_finish(myrpt, mychannel);
+ if(res)
+ return DC_ERROR;
+
+ if (setrem(myrpt) == -1)
+ return DC_ERROR;
+ return DC_COMPLETE;
+
+ case 107: /* Bump down 20Hz */
+ multimode_bump_freq(myrpt, -20);
+ return DC_COMPLETE;
+
+ case 108: /* Bump down 100Hz */
+ multimode_bump_freq(myrpt, -100);
+ return DC_COMPLETE;
+
+ case 109: /* Bump down 500Hz */
+ multimode_bump_freq(myrpt, -500);
+ return DC_COMPLETE;
+
+ case 110: /* Bump up 20Hz */
+ multimode_bump_freq(myrpt, 20);
+ return DC_COMPLETE;
+
+ case 111: /* Bump up 100Hz */
+ multimode_bump_freq(myrpt, 100);
+ return DC_COMPLETE;
+
+ case 112: /* Bump up 500Hz */
+ multimode_bump_freq(myrpt, 500);
+ return DC_COMPLETE;
+
+
+ case 113:
+ case 114:
+ case 115:
+ case 116:
+ case 117:
+ case 118:
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ ast_indicate(mychannel,AST_CONTROL_RADIO_KEY);
+ if (ast_safe_sleep(mychannel,1000) == -1)
+ return DC_ERROR;
+
+ switch(myatoi(param)){
+
+ case 113: /* Scan down slow */
+ res = sayfile(mychannel,"rpt/down");
+ if(!res)
+ res = sayfile(mychannel, "rpt/slow");
+ if(!res){
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_DOWN_SLOW;
+ }
+ break;
+
+ case 114: /* Scan down quick */
+ res = sayfile(mychannel,"rpt/down");
+ if(!res)
+ res = sayfile(mychannel, "rpt/quick");
+ if(!res){
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_DOWN_QUICK;
+ }
+ break;
+
+ case 115: /* Scan down fast */
+ res = sayfile(mychannel,"rpt/down");
+ if(!res)
+ res = sayfile(mychannel, "rpt/fast");
+ if(!res){
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_DOWN_FAST;
+ }
+ break;
+
+ case 116: /* Scan up slow */
+ res = sayfile(mychannel,"rpt/up");
+ if(!res)
+ res = sayfile(mychannel, "rpt/slow");
+ if(!res){
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_UP_SLOW;
+ }
+ break;
+
+ case 117: /* Scan up quick */
+ res = sayfile(mychannel,"rpt/up");
+ if(!res)
+ res = sayfile(mychannel, "rpt/quick");
+ if(!res){
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_UP_QUICK;
+ }
+ break;
+
+ case 118: /* Scan up fast */
+ res = sayfile(mychannel,"rpt/up");
+ if(!res)
+ res = sayfile(mychannel, "rpt/fast");
+ if(!res){
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_UP_FAST;
+ }
+ break;
+ }
+ rmt_telem_finish(myrpt,mychannel);
+ return DC_COMPLETE;
+
+
+ case 119: /* Tune Request */
+ myrpt->tunerequest = 1;
+ return DC_COMPLETE;
+
+ case 5: /* Long Status */
+ case 140: /* Short Status */
+ res = rmt_telem_start(myrpt, mychannel, 1000);
+
+ res = sayfile(mychannel,"rpt/node");
+ if(!res)
+ res = saycharstr(mychannel, myrpt->name);
+ if(!res)
+ res = sayfile(mychannel,"rpt/frequency");
+ if(!res)
+ res = split_freq(mhz, decimals, myrpt->freq);
+ if(!res){
+ m = atoi(mhz);
+ if(m < 100)
+ res = saynum(mychannel, m);
+ else
+ res = saycharstr(mychannel, mhz);
+ }
+ if(!res)
+ res = sayfile(mychannel, "letters/dot");
+ if(!res)
+ res = saycharstr(mychannel, decimals);
+
+ if(res){
+ rmt_telem_finish(myrpt,mychannel);
+ return DC_ERROR;
+ }
+ if(myrpt->remmode == REM_MODE_FM){ /* Mode FM? */
+ switch(myrpt->offset){
+
+ case REM_MINUS:
+ res = sayfile(mychannel,"rpt/minus");
+ break;
+
+ case REM_SIMPLEX:
+ res = sayfile(mychannel,"rpt/simplex");
+ break;
+
+ case REM_PLUS:
+ res = sayfile(mychannel,"rpt/plus");
+ break;
+
+ default:
+ return DC_ERROR;
+
+ }
+ }
+ else{ /* Must be USB, LSB, or AM */
+ switch(myrpt->remmode){
+
+ case REM_MODE_USB:
+ res = saycharstr(mychannel, "USB");
+ break;
+
+ case REM_MODE_LSB:
+ res = saycharstr(mychannel, "LSB");
+ break;
+
+ case REM_MODE_AM:
+ res = saycharstr(mychannel, "AM");
+ break;
+
+
+ default:
+ return DC_ERROR;
+ }
+ }
+
+ if (res == -1){
+ rmt_telem_finish(myrpt,mychannel);
+ return DC_ERROR;
+ }
+
+ if(myatoi(param) == 140){ /* Short status? */
+ if(!res)
+ res = rmt_telem_finish(myrpt, mychannel);
+ if(res)
+ return DC_ERROR;
+ return DC_COMPLETE;
+ }
+
+ switch(myrpt->powerlevel){
+
+ case REM_LOWPWR:
+ res = sayfile(mychannel,"rpt/lopwr") ;
+ break;
+
+ case REM_MEDPWR:
+ res = sayfile(mychannel,"rpt/medpwr");
+ break;
+ case REM_HIPWR:
+ res = sayfile(mychannel,"rpt/hipwr");
+ break;
+ }
+ if (res || (sayfile(mychannel,"rpt/rxpl") == -1) ||
+ (sayfile(mychannel,"rpt/frequency") == -1) ||
+ (saycharstr(mychannel,myrpt->rxpl) == -1) ||
+ (sayfile(mychannel,"rpt/txpl") == -1) ||
+ (sayfile(mychannel,"rpt/frequency") == -1) ||
+ (saycharstr(mychannel,myrpt->txpl) == -1) ||
+ (sayfile(mychannel,"rpt/txpl") == -1) ||
+ (sayfile(mychannel,((myrpt->txplon) ? "rpt/on" : "rpt/off")) == -1) ||
+ (sayfile(mychannel,"rpt/rxpl") == -1) ||
+ (sayfile(mychannel,((myrpt->rxplon) ? "rpt/on" : "rpt/off")) == -1))
+ {
+ rmt_telem_finish(myrpt,mychannel);
+ return DC_ERROR;
+ }
+ if(!res)
+ res = rmt_telem_finish(myrpt,mychannel);
+ if(res)
+ return DC_ERROR;
+
+ return DC_COMPLETE;
+ default:
+ return DC_ERROR;
+ }
+
+ return DC_INDETERMINATE;
+}
+
+static int handle_remote_dtmf_digit(struct rpt *myrpt,char c, char *keyed, int phonemode)
+{
+time_t now;
+int ret,res = 0,src;
+
+ /* Stop scan mode if in scan mode */
+ if(myrpt->hfscanmode){
+ stop_scan(myrpt,0);
+ return 0;
+ }
+
+ time(&now);
+ /* if timed-out */
+ if ((myrpt->dtmf_time_rem + DTMF_TIMEOUT) < now)
+ {
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = 0;
+ }
+ /* if decode not active */
+ if (myrpt->dtmfidx == -1)
+ {
+ /* if not lead-in digit, dont worry */
+ if (c != myrpt->funcchar) return 0;
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = now;
+ return 0;
+ }
+ /* if too many in buffer, start over */
+ if (myrpt->dtmfidx >= MAXDTMF)
+ {
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = now;
+ }
+ if (c == myrpt->funcchar)
+ {
+ /* if star at beginning, or 2 together, erase buffer */
+ if ((myrpt->dtmfidx < 1) ||
+ (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->funcchar))
+ {
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = now;
+ return 0;
+ }
+ }
+ myrpt->dtmfbuf[myrpt->dtmfidx++] = c;
+ myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
+ myrpt->dtmf_time_rem = now;
+
+
+ src = SOURCE_RMT;
+ if (phonemode > 1) src = SOURCE_DPHONE;
+ else if (phonemode) src = SOURCE_PHONE;
+ ret = collect_function_digits(myrpt, myrpt->dtmfbuf, src, NULL);
+
+ switch(ret){
+
+ case DC_INDETERMINATE:
+ res = 0;
+ break;
+
+ case DC_DOKEY:
+ if (keyed) *keyed = 1;
+ res = 0;
+ break;
+
+ case DC_REQ_FLUSH:
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ res = 0;
+ break;
+
+
+ case DC_COMPLETE:
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmf_time_rem = 0;
+ res = 1;
+ break;
+
+ case DC_ERROR:
+ default:
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmf_time_rem = 0;
+ res = 0;
+ break;
+ }
+
+ return res;
+}
+
+static int handle_remote_data(struct rpt *myrpt, char *str)
+{
+char tmp[300],cmd[300],dest[300],src[300],c;
+int seq,res;
+
+ /* put string in our buffer */
+ strncpy(tmp,str,sizeof(tmp) - 1);
+ if (!strcmp(tmp,discstr)) return 0;
+ if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5)
+ {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
+ return 0;
+ }
+ if (strcmp(cmd,"D"))
+ {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
+ return 0;
+ }
+ /* if not for me, ignore */
+ if (strcmp(dest,myrpt->name)) return 0;
+ res = handle_remote_dtmf_digit(myrpt,c, NULL, 0);
+ if (res != 1)
+ return res;
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ {
+ ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY);
+ }
+ if (ast_safe_sleep(myrpt->remchannel,1000) == -1) return -1;
+ res = telem_lookup(myrpt->remchannel, myrpt->name, "functcomplete");
+ rmt_telem_finish(myrpt,myrpt->remchannel);
+ return res;
+}
+
+static int handle_remote_phone_dtmf(struct rpt *myrpt, char c, char *keyed, int phonemode)
+{
+int res;
+
+
+ if (keyed && *keyed && (c == myrpt->endchar))
+ {
+ *keyed = 0;
+ return DC_INDETERMINATE;
+ }
+
+ res = handle_remote_dtmf_digit(myrpt,c,keyed, phonemode);
+ if (res != 1)
+ return res;
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ {
+ ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY);
+ }
+ if (ast_safe_sleep(myrpt->remchannel,1000) == -1) return -1;
+ res = telem_lookup(myrpt->remchannel, myrpt->name, "functcomplete");
+ rmt_telem_finish(myrpt,myrpt->remchannel);
+ return res;
+}
+
+static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l)
+{
+ char *val, *s, *s1, *s2, *tele;
+ char tmp[300], deststr[300] = "";
+
+ val = ast_variable_retrieve(cfg, myrpt->nodes, l->name);
+ if (!val)
+ {
+ fprintf(stderr,"attempt_reconnect: cannot find node %s\n",l->name);
+ return -1;
+ }
+
+ ast_mutex_lock(&myrpt->lock);
+ /* remove from queue */
+ remque((struct qelem *) l);
+ ast_mutex_unlock(&myrpt->lock);
+ strncpy(tmp,val,sizeof(tmp) - 1);
+ s = tmp;
+ s1 = strsep(&s,",");
+ s2 = strsep(&s,",");
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
+ tele = strchr(deststr, '/');
+ if (!tele) {
+ fprintf(stderr,"attempt_reconnect:Dial number (%s) must be in format tech/number\n",deststr);
+ return -1;
+ }
+ *tele++ = 0;
+ l->elaptime = 0;
+ l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele,NULL);
+ if (l->chan){
+ ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
+ l->chan->whentohangup = 0;
+ l->chan->appl = "Apprpt";
+ l->chan->data = "(Remote Rx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (attempt_reconnect) initiating call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ if(l->chan->cid.cid_num)
+ free(l->chan->cid.cid_num);
+ l->chan->cid.cid_num = strdup(myrpt->name);
+ ast_call(l->chan,tele,999);
+
+ }
+ else
+ {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n",
+ deststr,tele,l->chan->name);
+ return -1;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ /* put back in queue queue */
+ insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
+ ast_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_NOTICE,"Reconnect Attempt to %s in process\n",l->name);
+ return 0;
+}
+
+/* single thread with one file (request) to dial */
+static void *rpt(void *this)
+{
+struct rpt *myrpt = (struct rpt *)this;
+char *tele,*idtalkover;
+int ms = MSWAIT,lasttx=0,val,remrx=0,identqueued,nonidentqueued,res;
+struct ast_channel *who;
+ZT_CONFINFO ci; /* conference info */
+time_t dtmf_time,t;
+struct rpt_link *l,*m;
+struct rpt_tele *telem;
+pthread_attr_t attr;
+char tmpstr[300];
+char cmd[MAXDTMF+1] = "";
+
+
+ ast_mutex_lock(&myrpt->lock);
+ strncpy(tmpstr,myrpt->rxchanname,sizeof(tmpstr) - 1);
+ tele = strchr(tmpstr,'/');
+ if (!tele)
+ {
+ fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",myrpt->rxchanname);
+ ast_mutex_unlock(&myrpt->lock);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->rxchannel = ast_request(tmpstr,AST_FORMAT_SLINEAR,tele,NULL);
+ if (myrpt->rxchannel)
+ {
+ if (myrpt->rxchannel->_state == AST_STATE_BUSY)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
+ myrpt->rxchannel->whentohangup = 0;
+ myrpt->rxchannel->appl = "Apprpt";
+ myrpt->rxchannel->data = "(Repeater Rx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (Rx) initiating call to %s/%s on %s\n",
+ tmpstr,tele,myrpt->rxchannel->name);
+ ast_call(myrpt->rxchannel,tele,999);
+ if (myrpt->rxchannel->_state != AST_STATE_UP)
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ }
+ else
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ if (myrpt->txchanname)
+ {
+ strncpy(tmpstr,myrpt->txchanname,sizeof(tmpstr) - 1);
+ tele = strchr(tmpstr,'/');
+ if (!tele)
+ {
+ fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",myrpt->txchanname);
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->txchannel = ast_request(tmpstr,AST_FORMAT_SLINEAR,tele,NULL);
+ if (myrpt->txchannel)
+ {
+ if (myrpt->txchannel->_state == AST_STATE_BUSY)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
+ myrpt->txchannel->whentohangup = 0;
+ myrpt->txchannel->appl = "Apprpt";
+ myrpt->txchannel->data = "(Repeater Tx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (Tx) initiating call to %s/%s on %s\n",
+ tmpstr,tele,myrpt->txchannel->name);
+ ast_call(myrpt->txchannel,tele,999);
+ if (myrpt->rxchannel->_state != AST_STATE_UP)
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ ast_hangup(myrpt->txchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ }
+ else
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ }
+ else
+ {
+ myrpt->txchannel = myrpt->rxchannel;
+ }
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY);
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ /* allocate a pseudo-channel thru asterisk */
+ myrpt->pchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!myrpt->pchannel)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = -1; /* make a new conf */
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(myrpt->txchannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* save tx conference number */
+ myrpt->txconf = ci.confno;
+ /* make a conference for the pseudo */
+ ci.chan = 0;
+ ci.confno = -1; /* make a new conf */
+ ci.confmode = ZT_CONF_CONFANNMON;
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* save pseudo channel conference number */
+ myrpt->conf = ci.confno;
+ /* allocate a pseudo-channel thru asterisk */
+ myrpt->txpchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!myrpt->txpchannel)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = myrpt->txconf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER ;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(myrpt->txpchannel->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->txpchannel);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* Now, the idea here is to copy from the physical rx channel buffer
+ into the pseudo tx buffer, and from the pseudo rx buffer into the
+ tx channel buffer */
+ myrpt->links.next = &myrpt->links;
+ myrpt->links.prev = &myrpt->links;
+ myrpt->tailtimer = 0;
+ myrpt->totimer = 0;
+ myrpt->idtimer = myrpt->politeid;
+ myrpt->mustid = 0;
+ myrpt->callmode = 0;
+ myrpt->tounkeyed = 0;
+ myrpt->tonotify = 0;
+ myrpt->retxtimer = 0;
+ lasttx = 0;
+ myrpt->keyed = 0;
+ idtalkover = ast_variable_retrieve(cfg, myrpt->name, "idtalkover");
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmfbuf[0] = 0;
+ dtmf_time = 0;
+ myrpt->rem_dtmf_time = 0;
+ myrpt->enable = 1;
+ myrpt->disgorgetime = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ val = 0;
+ ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0);
+ val = 1;
+ ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0);
+ while (ms >= 0)
+ {
+ struct ast_frame *f;
+ struct ast_channel *cs[300];
+ int totx=0,elap=0,n,toexit=0;
+
+ /* DEBUG Dump */
+ if((myrpt->disgorgetime) && (time(NULL) >= myrpt->disgorgetime)){
+ struct rpt_link *zl;
+ struct rpt_tele *zt;
+
+ myrpt->disgorgetime = 0;
+ ast_log(LOG_NOTICE,"********** Variable Dump Start (app_rpt) **********\n");
+ ast_log(LOG_NOTICE,"totx = %d\n",totx);
+ ast_log(LOG_NOTICE,"remrx = %d\n",remrx);
+ ast_log(LOG_NOTICE,"lasttx = %d\n",lasttx);
+ ast_log(LOG_NOTICE,"elap = %d\n",elap);
+ ast_log(LOG_NOTICE,"toexit = %d\n",toexit);
+
+ ast_log(LOG_NOTICE,"myrpt->keyed = %d\n",myrpt->keyed);
+ ast_log(LOG_NOTICE,"myrpt->localtx = %d\n",myrpt->localtx);
+ ast_log(LOG_NOTICE,"myrpt->callmode = %d\n",myrpt->callmode);
+ ast_log(LOG_NOTICE,"myrpt->enable = %d\n",myrpt->enable);
+ ast_log(LOG_NOTICE,"myrpt->mustid = %d\n",myrpt->mustid);
+ ast_log(LOG_NOTICE,"myrpt->tounkeyed = %d\n",myrpt->tounkeyed);
+ ast_log(LOG_NOTICE,"myrpt->tonotify = %d\n",myrpt->tonotify);
+ ast_log(LOG_NOTICE,"myrpt->retxtimer = %ld\n",myrpt->retxtimer);
+ ast_log(LOG_NOTICE,"myrpt->totimer = %d\n",myrpt->totimer);
+ ast_log(LOG_NOTICE,"myrpt->tailtimer = %d\n",myrpt->tailtimer);
+
+ zl = myrpt->links.next;
+ while(zl != &myrpt->links){
+ ast_log(LOG_NOTICE,"*** Link Name: %s ***\n",zl->name);
+ ast_log(LOG_NOTICE," link->lasttx %d\n",zl->lasttx);
+ ast_log(LOG_NOTICE," link->lastrx %d\n",zl->lastrx);
+ ast_log(LOG_NOTICE," link->connected %d\n",zl->connected);
+ ast_log(LOG_NOTICE," link->hasconnected %d\n",zl->hasconnected);
+ ast_log(LOG_NOTICE," link->outbound %d\n",zl->outbound);
+ ast_log(LOG_NOTICE," link->disced %d\n",zl->disced);
+ ast_log(LOG_NOTICE," link->killme %d\n",zl->killme);
+ ast_log(LOG_NOTICE," link->disctime %ld\n",zl->disctime);
+ ast_log(LOG_NOTICE," link->retrytimer %ld\n",zl->retrytimer);
+ ast_log(LOG_NOTICE," link->retries = %d\n",zl->retries);
+
+ zl = zl->next;
+ }
+
+ zt = myrpt->tele.next;
+ if(zt != &myrpt->tele)
+ ast_log(LOG_NOTICE,"*** Telemetry Queue ***\n");
+ while(zt != &myrpt->tele){
+ ast_log(LOG_NOTICE," Telemetry mode: %d\n",zt->mode);
+ zt = zt->next;
+ }
+ ast_log(LOG_NOTICE,"******* Variable Dump End (app_rpt) *******\n");
+
+ }
+
+
+
+
+
+ ast_mutex_lock(&myrpt->lock);
+ if (ast_check_hangup(myrpt->rxchannel)) break;
+ if (ast_check_hangup(myrpt->txchannel)) break;
+ if (ast_check_hangup(myrpt->pchannel)) break;
+ if (ast_check_hangup(myrpt->txpchannel)) break;
+ myrpt->localtx = myrpt->keyed && (myrpt->dtmfidx == -1) && (!myrpt->cmdnode[0]);
+
+ /* If someone's connected, and they're transmitting from their end to us, set remrx true */
+
+ l = myrpt->links.next;
+ remrx = 0;
+ while(l != &myrpt->links)
+ {
+ if (l->lastrx) remrx = 1;
+ l = l->next;
+ }
+
+ /* Create a "must_id" flag for the cleanup ID */
+
+ myrpt->mustid |= (myrpt->idtimer) && (myrpt->keyed || remrx) ;
+
+ /* Build a fresh totx from myrpt->keyed and autopatch activated */
+
+ totx = myrpt->localtx || myrpt->callmode;
+
+ /* Traverse the telemetry list to see if there's an ID queued and if there is not an ID queued */
+
+ identqueued = 0;
+ nonidentqueued = 0;
+
+ telem = myrpt->tele.next;
+ while(telem != &myrpt->tele)
+ {
+ if((telem->mode == ID) || (telem->mode == IDTALKOVER)){
+ identqueued = 1;
+ }
+ else
+ nonidentqueued = 1;
+ telem = telem->next;
+ }
+
+ /* Add in any non-id telemetry */
+
+ totx = totx || nonidentqueued;
+
+ /* Update external transmitter PTT state with everything but ID telemetry */
+
+ myrpt->exttx = totx;
+
+ /* Add in ID telemetry to local transmitter */
+
+ totx = totx || remrx || identqueued;
+
+ if (!totx)
+ {
+ myrpt->totimer = myrpt->totime;
+ myrpt->tounkeyed = 0;
+ myrpt->tonotify = 0;
+ }
+ else myrpt->tailtimer = myrpt->hangtime;
+ totx = totx && myrpt->totimer;
+ /* if timed-out and not said already, say it */
+ if ((!myrpt->totimer) && (!myrpt->tonotify))
+ {
+ myrpt->tonotify = 1;
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,TIMEOUT,NULL);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ /* if wants to transmit and in phone call, but timed out,
+ reset time-out timer if keyed */
+ if ((!totx) && (!myrpt->totimer) && (!myrpt->tounkeyed) && (!myrpt->keyed))
+ {
+ myrpt->tounkeyed = 1;
+ }
+ if ((!totx) && (!myrpt->totimer) && myrpt->tounkeyed && myrpt->keyed)
+ {
+ myrpt->totimer = myrpt->totime;
+ myrpt->tounkeyed = 0;
+ myrpt->tonotify = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ /* if timed-out and in circuit busy after call */
+ if ((!totx) && (!myrpt->totimer) && (myrpt->callmode == 4))
+ {
+ myrpt->callmode = 0;
+ }
+ /* get rid of tail if timed out */
+ if (!myrpt->totimer) myrpt->tailtimer = 0;
+ /* if not timed-out, add in tail */
+ if (myrpt->totimer) totx = totx || myrpt->tailtimer;
+ /* If user or links key up or are keyed up over standard ID, switch to talkover ID, if one is defined */
+ if (identqueued && (myrpt->keyed || remrx) && idtalkover) {
+ int hasid = 0,hastalkover = 0;
+
+ telem = myrpt->tele.next;
+ while(telem != &myrpt->tele){
+ if(telem->mode == ID){
+ if (telem->chan) ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */
+ hasid = 1;
+ }
+ if (telem->mode == IDTALKOVER) hastalkover = 1;
+ telem = telem->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ if (hasid && (!hastalkover)) rpt_telemetry(myrpt, IDTALKOVER, NULL); /* Start Talkover ID */
+ ast_mutex_lock(&myrpt->lock);
+ }
+ /* Try to be polite */
+ /* If the repeater has been inactive for longer than the ID time, do an initial ID in the tail*/
+ /* If within 30 seconds of the time to ID, try do it in the tail */
+ /* else if at ID time limit, do it right over the top of them */
+ /* Lastly, if the repeater has been keyed, and the ID timer is expired, do a clean up ID */
+ if (((totx && (!myrpt->exttx) && (myrpt->idtimer <= myrpt->politeid) && myrpt->tailtimer)) ||
+ (myrpt->mustid && (!myrpt->idtimer)))
+ {
+ myrpt->mustid = 0;
+ myrpt->idtimer = myrpt->idtime; /* Reset our ID timer */
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,ID,NULL);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ /* let telemetry transmit anyway (regardless of timeout) */
+ totx = totx || (myrpt->tele.next != &myrpt->tele);
+ if (totx && (!lasttx))
+ {
+ lasttx = 1;
+ ast_mutex_unlock(&myrpt->lock);
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ totx = totx && myrpt->enable;
+ if ((!totx) && lasttx)
+ {
+ lasttx = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ time(&t);
+ /* if DTMF timeout */
+ if ((!myrpt->cmdnode[0]) && (myrpt->dtmfidx >= 0) && ((dtmf_time + DTMF_TIMEOUT) < t))
+ {
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ }
+ /* if remote DTMF timeout */
+ if ((myrpt->rem_dtmfidx >= 0) && ((myrpt->rem_dtmf_time + DTMF_TIMEOUT) < t))
+ {
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmfbuf[0] = 0;
+ }
+
+ /* Reconnect kludge */
+ l = myrpt->links.next;
+ while(l != &myrpt->links)
+ {
+ if (l->killme)
+ {
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode,l->name))
+ myrpt->cmdnode[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ /* hang-up on call to device */
+ if (l->chan) ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ free(l);
+ ast_mutex_lock(&myrpt->lock);
+ /* re-start link traversal */
+ l = myrpt->links.next;
+ continue;
+ }
+ l = l->next;
+ }
+ n = 0;
+ cs[n++] = myrpt->rxchannel;
+ cs[n++] = myrpt->pchannel;
+ cs[n++] = myrpt->txpchannel;
+ if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel;
+ l = myrpt->links.next;
+ while(l != &myrpt->links)
+ {
+ if ((!l->killme) && (!l->disctime) && l->chan)
+ {
+ cs[n++] = l->chan;
+ cs[n++] = l->pchan;
+ }
+ l = l->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ ms = MSWAIT;
+ who = ast_waitfor_n(cs,n,&ms);
+ if (who == NULL) ms = 0;
+ elap = MSWAIT - ms;
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ while(l != &myrpt->links)
+ {
+ if (!l->lasttx)
+ {
+ if ((l->retxtimer += elap) >= REDUNDANT_TX_TIME)
+ {
+ l->retxtimer = 0;
+ if (l->chan) ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY);
+ }
+ } else l->retxtimer = 0;
+#ifdef RECONNECT_KLUDGE
+ if (l->disctime) /* Disconnect timer active on a channel ? */
+ {
+ l->disctime -= elap;
+ if (l->disctime <= 0) /* Disconnect timer expired on inbound channel ? */
+ l->disctime = 0; /* Yep */
+ }
+
+ if (l->retrytimer)
+ {
+ l->retrytimer -= elap;
+ if (l->retrytimer < 0) l->retrytimer = 0;
+ }
+#endif
+ /* ignore non-timing channels */
+ if (l->elaptime < 0)
+ {
+ l = l->next;
+ continue;
+ }
+ l->elaptime += elap;
+ /* if connection has taken too long */
+ if ((l->elaptime > MAXCONNECTTIME) &&
+ ((!l->chan) || (l->chan->_state != AST_STATE_UP)))
+ {
+ l->elaptime = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
+#ifndef RECONNECT_KLUDGE
+ rpt_telemetry(myrpt,CONNFAIL,l);
+#endif
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+#ifdef RECONNECT_KLUDGE
+ if ((!l->chan) && (!l->retrytimer) && l->outbound &&
+ (l->retries++ < MAX_RETRIES) && (l->hasconnected))
+ {
+ if (l->chan) ast_hangup(l->chan);
+ ast_mutex_unlock(&myrpt->lock);
+ if ((l->name[0] != '0') && (!l->isremote))
+ {
+ l->retrytimer = MAX_RETRIES + 1;
+ }
+ else
+ {
+ if (attempt_reconnect(myrpt,l) == -1)
+ {
+ l->retrytimer = RETRY_TIMER_MS;
+ }
+ }
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if ((!l->chan) && (!l->retrytimer) && l->outbound &&
+ (l->retries >= MAX_RETRIES))
+ {
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode,l->name))
+ myrpt->cmdnode[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (l->name[0] != '0')
+ {
+ if (!l->hasconnected)
+ rpt_telemetry(myrpt,CONNFAIL,l);
+ else rpt_telemetry(myrpt,REMDISC,l);
+ }
+ /* hang-up on call to device */
+ ast_hangup(l->pchan);
+ free(l);
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if ((!l->chan) && (!l->disctime) && (!l->outbound))
+ {
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode,l->name))
+ myrpt->cmdnode[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (l->name[0] != '0')
+ {
+ rpt_telemetry(myrpt,REMDISC,l);
+ }
+ /* hang-up on call to device */
+ ast_hangup(l->pchan);
+ free(l);
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+#endif
+ l = l->next;
+ }
+ if (myrpt->tailtimer) myrpt->tailtimer -= elap;
+ if (myrpt->tailtimer < 0) myrpt->tailtimer = 0;
+ if (myrpt->totimer) myrpt->totimer -= elap;
+ if (myrpt->totimer < 0) myrpt->totimer = 0;
+ if (myrpt->idtimer) myrpt->idtimer -= elap;
+ if (myrpt->idtimer < 0) myrpt->idtimer = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (!ms) continue;
+ if (who == myrpt->rxchannel) /* if it was a read from rx */
+ {
+ f = ast_read(myrpt->rxchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ if (!myrpt->localtx)
+ memset(f->data,0,f->datalen);
+ ast_write(myrpt->pchannel,f);
+ }
+ else if (f->frametype == AST_FRAME_DTMF)
+ {
+ char c;
+
+ c = (char) f->subclass; /* get DTMF char */
+ ast_frfree(f);
+ if (!myrpt->keyed) continue;
+ if (c == myrpt->endchar)
+ {
+ /* if in simple mode, kill autopatch */
+ if (myrpt->simple && myrpt->callmode)
+ {
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,TERM,NULL);
+ continue;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ myrpt->stopgen = 1;
+ if (myrpt->cmdnode[0])
+ {
+ myrpt->cmdnode[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,COMPLETE,NULL);
+ } else ast_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ if (myrpt->cmdnode[0])
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ send_link_dtmf(myrpt,c);
+ continue;
+ }
+ if (!myrpt->simple)
+ {
+ if (c == myrpt->funcchar)
+ {
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ time(&dtmf_time);
+ continue;
+ }
+ else if ((c != myrpt->endchar) && (myrpt->dtmfidx >= 0))
+ {
+ time(&dtmf_time);
+
+ if (myrpt->dtmfidx < MAXDTMF)
+ {
+ myrpt->dtmfbuf[myrpt->dtmfidx++] = c;
+ myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
+
+ strncpy(cmd, myrpt->dtmfbuf, sizeof(cmd) - 1);
+
+ ast_mutex_unlock(&myrpt->lock);
+ res = collect_function_digits(myrpt, cmd, SOURCE_RPT, NULL);
+ ast_mutex_lock(&myrpt->lock);
+
+ switch(res){
+
+ case DC_INDETERMINATE:
+ break;
+
+ case DC_REQ_FLUSH:
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ break;
+
+
+ case DC_COMPLETE:
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ dtmf_time = 0;
+ break;
+
+ case DC_ERROR:
+ default:
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ dtmf_time = 0;
+ break;
+ }
+ if(res != DC_INDETERMINATE) {
+ ast_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ }
+ }
+ }
+ else /* if simple */
+ {
+ if ((!myrpt->callmode) && (c == myrpt->funcchar))
+ {
+ myrpt->callmode = 1;
+ myrpt->cidx = 0;
+ myrpt->exten[myrpt->cidx] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt);
+ continue;
+ }
+ }
+ if (myrpt->callmode == 1)
+ {
+ myrpt->exten[myrpt->cidx++] = c;
+ myrpt->exten[myrpt->cidx] = 0;
+ /* if this exists */
+ if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+ {
+ myrpt->callmode = 2;
+ ast_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt,PROC,NULL);
+ continue;
+ }
+ /* if can continue, do so */
+ if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+ {
+ /* call has failed, inform user */
+ myrpt->callmode = 4;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
+ {
+ myrpt->mydtmf = c;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ else if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY)
+ {
+ if (debug) printf("@@@@ rx key\n");
+ myrpt->keyed = 1;
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY)
+ {
+ if (debug) printf("@@@@ rx un-key\n");
+ if(myrpt->keyed) {
+ rpt_telemetry(myrpt,UNKEY,NULL);
+ }
+ myrpt->keyed = 0;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if (who == myrpt->pchannel) /* if it was a read from pseudo */
+ {
+ f = ast_read(myrpt->pchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ ast_write(myrpt->txpchannel,f);
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if (who == myrpt->txchannel) /* if it was a read from tx */
+ {
+ f = ast_read(myrpt->txchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ toexit = 0;
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ while(l != &myrpt->links)
+ {
+ if (l->disctime)
+ {
+ l = l->next;
+ continue;
+ }
+ if (who == l->chan) /* if it was a read from rx */
+ {
+ remrx = 0;
+ /* see if any other links are receiving */
+ m = myrpt->links.next;
+ while(m != &myrpt->links)
+ {
+ /* if not us, count it */
+ if ((m != l) && (m->lastrx)) remrx = 1;
+ m = m->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ totx = (((l->isremote) ? myrpt->localtx :
+ myrpt->exttx) || remrx) && l->mode;
+ if (l->chan && (l->lasttx != totx))
+ {
+ if (totx)
+ {
+ ast_indicate(l->chan,AST_CONTROL_RADIO_KEY);
+ }
+ else
+ {
+ ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY);
+ }
+ }
+ l->lasttx = totx;
+ f = ast_read(l->chan);
+ if (!f)
+ {
+#ifdef RECONNECT_KLUDGE
+ if ((!l->disced) && (!l->outbound))
+ {
+ if ((l->name[0] == '0') || l->isremote)
+ l->disctime = 1;
+ else
+ l->disctime = DISC_TIME;
+ ast_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ break;
+ }
+
+ if (l->retrytimer)
+ {
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected))
+ {
+ ast_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (attempt_reconnect(myrpt,l) == -1)
+ {
+ l->retrytimer = RETRY_TIMER_MS;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+#endif
+ ast_mutex_lock(&myrpt->lock);
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode,l->name))
+ myrpt->cmdnode[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (!l->hasconnected)
+ rpt_telemetry(myrpt,CONNFAIL,l);
+ else if (l->disced != 2) rpt_telemetry(myrpt,REMDISC,l);
+ /* hang-up on call to device */
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ free(l);
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ if (l->phonemode && (!l->lastrx))
+ {
+ memset(f->data,0,f->datalen);
+ }
+ ast_write(l->pchan,f);
+ }
+ if (f->frametype == AST_FRAME_TEXT)
+ {
+ handle_link_data(myrpt,l,f->data);
+ }
+ if (f->frametype == AST_FRAME_DTMF)
+ {
+ handle_link_phone_dtmf(myrpt,l,f->subclass);
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_ANSWER)
+ {
+ char lconnected = l->connected;
+ l->connected = 1;
+ l->hasconnected = 1;
+ l->elaptime = -1;
+ l->retries = 0;
+ if (!lconnected) rpt_telemetry(myrpt,CONNECTED,l);
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY)
+ {
+ if (debug) printf("@@@@ rx key\n");
+ l->lastrx = 1;
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY)
+ {
+ if (debug) printf("@@@@ rx un-key\n");
+ l->lastrx = 0;
+ }
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ ast_frfree(f);
+#ifdef RECONNECT_KLUDGE
+ if ((!l->outbound) && (!l->disced))
+ {
+ if ((l->name[0] == '0') || l->isremote)
+ l->disctime = 1;
+ else
+ l->disctime = DISC_TIME;
+ ast_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ break;
+ }
+ if (l->retrytimer)
+ {
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected))
+ {
+ ast_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (attempt_reconnect(myrpt,l) == -1)
+ {
+ l->retrytimer = RETRY_TIMER_MS;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+#endif
+ ast_mutex_lock(&myrpt->lock);
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode,l->name))
+ myrpt->cmdnode[0] = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ if (!l->hasconnected)
+ rpt_telemetry(myrpt,CONNFAIL,l);
+ else if (l->disced != 2) rpt_telemetry(myrpt,REMDISC,l);
+ /* hang-up on call to device */
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ free(l);
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ }
+ ast_frfree(f);
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (who == l->pchan)
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ f = ast_read(l->pchan);
+ if (!f)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ toexit = 1;
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ if (l->chan) ast_write(l->chan,f);
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ toexit = 1;
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ }
+ ast_frfree(f);
+ ast_mutex_lock(&myrpt->lock);
+ break;
+ }
+ l = l->next;
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ if (toexit) break;
+ if (who == myrpt->txpchannel) /* if it was a read from remote tx */
+ {
+ f = ast_read(myrpt->txpchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ }
+ usleep(100000);
+ ast_hangup(myrpt->pchannel);
+ ast_hangup(myrpt->txpchannel);
+ if (myrpt->txchannel != myrpt->rxchannel) ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ while(l != &myrpt->links)
+ {
+ struct rpt_link *ll = l;
+ /* remove from queue */
+ remque((struct qelem *) l);
+ /* hang-up on call to device */
+ if (l->chan) ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ l = l->next;
+ free(ll);
+ }
+ ast_mutex_unlock(&myrpt->lock);
+ if (debug) printf("@@@@ rpt:Hung up channel\n");
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ return NULL;
+}
+
+
+static void *rpt_master(void *ignore)
+{
+char *this,*val;
+struct ast_variable *vp;
+int i,j,n,longestnode;
+pthread_attr_t attr;
+
+ /* start with blank config */
+ memset(&rpt_vars,0,sizeof(rpt_vars));
+
+ cfg = ast_config_load("rpt.conf");
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
+ pthread_exit(NULL);
+ }
+
+ /* go thru all the specified repeaters */
+ this = NULL;
+ n = 0;
+ while((this = ast_category_browse(cfg,this)) != NULL)
+ {
+
+ for(i = 0 ; i < strlen(this) ; i++){
+ if((this[i] < '0') || (this[i] > '9'))
+ break;
+ }
+ if(i != strlen(this))
+ continue; /* Not a node defn */
+
+ ast_log(LOG_DEBUG,"Loading config for repeater %s\n",this);
+ ast_mutex_init(&rpt_vars[n].lock);
+ rpt_vars[n].tele.next = &rpt_vars[n].tele;
+ rpt_vars[n].tele.prev = &rpt_vars[n].tele;
+ rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
+ rpt_vars[n].name = this;
+ rpt_vars[n].rxchanname = ast_variable_retrieve(cfg,this,"rxchannel");
+ rpt_vars[n].txchanname = ast_variable_retrieve(cfg,this,"txchannel");
+ rpt_vars[n].ourcontext = ast_variable_retrieve(cfg,this,"context");
+ if (!rpt_vars[n].ourcontext) rpt_vars[n].ourcontext = this;
+ rpt_vars[n].ourcallerid = ast_variable_retrieve(cfg,this,"callerid");
+ rpt_vars[n].acctcode = ast_variable_retrieve(cfg,this,"accountcode");
+ rpt_vars[n].ident = ast_variable_retrieve(cfg,this,"idrecording");
+ val = ast_variable_retrieve(cfg,this,"hangtime");
+ if (val) rpt_vars[n].hangtime = atoi(val);
+ else rpt_vars[n].hangtime = HANGTIME;
+ val = ast_variable_retrieve(cfg,this,"totime");
+ if (val) rpt_vars[n].totime = atoi(val);
+ else rpt_vars[n].totime = TOTIME;
+
+ rpt_vars[n].idtime = retrieve_astcfgint( this, "idtime", 60000, 2400000, IDTIME); /* Enforce a min max */
+ rpt_vars[n].politeid = retrieve_astcfgint( this, "politeid", 30000, 300000, POLITEID); /* Enforce a min max */
+ rpt_vars[n].remote = ast_variable_retrieve(cfg,this,"remote");
+ rpt_vars[n].tonezone = ast_variable_retrieve(cfg,this,"tonezone");
+ val = ast_variable_retrieve(cfg,this,"iobase");
+ /* do not use atoi() here, we need to be able to have
+ the input specified in hex or decimal so we use
+ sscanf with a %i */
+ if ((!val) || (sscanf(val,"%i",&rpt_vars[n].iobase) != 1))
+ rpt_vars[n].iobase = DEFAULT_IOBASE;
+ rpt_vars[n].simple = 0;
+ rpt_vars[n].functions = ast_variable_retrieve(cfg,this,"functions");
+ if (!rpt_vars[n].functions)
+ {
+ rpt_vars[n].functions = FUNCTIONS;
+ rpt_vars[n].simple = 1;
+ }
+ rpt_vars[n].link_functions = ast_variable_retrieve(cfg,this,"link_functions");
+ if (!rpt_vars[n].link_functions)
+ rpt_vars[n].link_functions = rpt_vars[n].functions;
+ rpt_vars[n].phone_functions = ast_variable_retrieve(cfg,this,"phone_functions");
+ rpt_vars[n].dphone_functions = ast_variable_retrieve(cfg,this,"dphone_functions");
+ val = ast_variable_retrieve(cfg,this,"funcchar");
+ if (!val) rpt_vars[n].funcchar = FUNCCHAR; else
+ rpt_vars[n].funcchar = *val;
+ val = ast_variable_retrieve(cfg,this,"endchar");
+ if (!val) rpt_vars[n].endchar = ENDCHAR; else
+ rpt_vars[n].endchar = *val;
+ val = ast_variable_retrieve(cfg,this,"nobusyout");
+ if (val) rpt_vars[n].nobusyout = ast_true(val);
+ rpt_vars[n].nodes = ast_variable_retrieve(cfg,this,"nodes");
+ if (!rpt_vars[n].nodes)
+ rpt_vars[n].nodes = NODES;
+ n++;
+ }
+ nrpts = n;
+ ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n);
+ /* start em all */
+ for(i = 0; i < n; i++)
+ {
+
+ /*
+ * Go through the node list to determine the longest node
+ */
+ longestnode = 0;
+
+ vp = ast_variable_browse(cfg, rpt_vars[i].nodes);
+
+ while(vp){
+ j = strlen(vp->name);
+ if (j > longestnode)
+ longestnode = j;
+ vp = vp->next;
+ }
+
+
+ rpt_vars[i].longestnode = longestnode;
+
+ /*
+ * For this repeater, Determine the length of the longest function
+ */
+ rpt_vars[i].longestfunc = 0;
+ vp = ast_variable_browse(cfg, rpt_vars[i].functions);
+ while(vp){
+ j = strlen(vp->name);
+ if (j > rpt_vars[i].longestfunc)
+ rpt_vars[i].longestfunc = j;
+ vp = vp->next;
+ }
+ /*
+ * For this repeater, Determine the length of the longest function
+ */
+ rpt_vars[i].link_longestfunc = 0;
+ vp = ast_variable_browse(cfg, rpt_vars[i].link_functions);
+ while(vp){
+ j = strlen(vp->name);
+ if (j > rpt_vars[i].link_longestfunc)
+ rpt_vars[i].link_longestfunc = j;
+ vp = vp->next;
+ }
+ rpt_vars[i].phone_longestfunc = 0;
+ if (rpt_vars[i].phone_functions)
+ {
+ vp = ast_variable_browse(cfg, rpt_vars[i].phone_functions);
+ while(vp){
+ j = strlen(vp->name);
+ if (j > rpt_vars[i].phone_longestfunc)
+ rpt_vars[i].phone_longestfunc = j;
+ vp = vp->next;
+ }
+ }
+ rpt_vars[i].dphone_longestfunc = 0;
+ if (rpt_vars[i].dphone_functions)
+ {
+ vp = ast_variable_browse(cfg, rpt_vars[i].dphone_functions);
+ while(vp){
+ j = strlen(vp->name);
+ if (j > rpt_vars[i].dphone_longestfunc)
+ rpt_vars[i].dphone_longestfunc = j;
+ vp = vp->next;
+ }
+ }
+ if (!rpt_vars[i].rxchanname)
+ {
+ ast_log(LOG_WARNING,"Did not specify rxchanname for node %s\n",rpt_vars[i].name);
+ ast_config_destroy(cfg);
+ pthread_exit(NULL);
+ }
+ /* if is a remote, dont start one for it */
+ if (rpt_vars[i].remote)
+ {
+ strncpy(rpt_vars[i].freq, "146.580", sizeof(rpt_vars[i].freq) - 1);
+ strncpy(rpt_vars[i].rxpl, "100.0", sizeof(rpt_vars[i].rxpl) - 1);
+
+ strncpy(rpt_vars[i].txpl, "100.0", sizeof(rpt_vars[i].txpl) - 1);
+ rpt_vars[i].remmode = REM_MODE_FM;
+ rpt_vars[i].offset = REM_SIMPLEX;
+ rpt_vars[i].powerlevel = REM_MEDPWR;
+ continue;
+ }
+ if (!rpt_vars[i].ident)
+ {
+ ast_log(LOG_WARNING,"Did not specify ident for node %s\n",rpt_vars[i].name);
+ ast_config_destroy(cfg);
+ pthread_exit(NULL);
+ }
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&rpt_vars[i].rpt_thread,&attr,rpt,(void *) &rpt_vars[i]);
+ }
+ usleep(500000);
+ for(;;)
+ {
+ /* Now monitor each thread, and restart it if necessary */
+ for(i = 0; i < n; i++)
+ {
+ int rv;
+ if (rpt_vars[i].remote) continue;
+ if (rpt_vars[i].rpt_thread == AST_PTHREADT_STOP)
+ rv = -1;
+ else
+ rv = pthread_kill(rpt_vars[i].rpt_thread,0);
+ if (rv)
+ {
+ if(time(NULL) - rpt_vars[i].lastthreadrestarttime <= 15)
+ {
+ if(rpt_vars[i].threadrestarts >= 5)
+ {
+ ast_log(LOG_ERROR,"Continual RPT thread restarts, killing Asterisk\n");
+ exit(1); /* Stuck in a restart loop, kill Asterisk and start over */
+ }
+ else
+ {
+ ast_log(LOG_NOTICE,"RPT thread restarted on %s\n",rpt_vars[i].name);
+ rpt_vars[i].threadrestarts++;
+ }
+ }
+ else
+ rpt_vars[i].threadrestarts = 0;
+
+ rpt_vars[i].lastthreadrestarttime = time(NULL);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&rpt_vars[i].rpt_thread,&attr,rpt,(void *) &rpt_vars[i]);
+ ast_log(LOG_WARNING, "rpt_thread restarted on node %s\n", rpt_vars[i].name);
+ }
+
+ }
+ usleep(2000000);
+ }
+ ast_config_destroy(cfg);
+ pthread_exit(NULL);
+}
+
+static int rpt_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1,i,rem_totx,n,phone_mode = 0;
+ struct localuser *u;
+ char tmp[256], keyed = 0;
+ char *options,*stringp,*tele;
+ struct rpt *myrpt;
+ struct ast_frame *f;
+ struct ast_channel *who;
+ struct ast_channel *cs[20];
+ struct rpt_link *l;
+ ZT_CONFINFO ci; /* conference info */
+ ZT_PARAMS par;
+ int ms,elap;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Rpt requires an argument (system node)\n");
+ return -1;
+ }
+ strncpy(tmp, (char *)data, sizeof(tmp)-1);
+ stringp=tmp;
+ strsep(&stringp, "|");
+ options = stringp;
+ myrpt = NULL;
+ /* see if we can find our specified one */
+ for(i = 0; i < nrpts; i++)
+ {
+ /* if name matches, assign it and exit loop */
+ if (!strcmp(tmp,rpt_vars[i].name))
+ {
+ myrpt = &rpt_vars[i];
+ break;
+ }
+ }
+ if (myrpt == NULL)
+ {
+ ast_log(LOG_WARNING, "Cannot find specified system node %s\n",tmp);
+ return -1;
+ }
+
+ /* if not phone access, must be an IAX connection */
+ if (options && ((*options == 'P') || (*options == 'D') || (*options == 'R')))
+ {
+ phone_mode = 1;
+ if (*options == 'D') phone_mode = 2;
+ ast_set_callerid(chan,"0","app_rpt user","0");
+ }
+ else
+ {
+ if (strncmp(chan->name,"IAX2",4))
+ {
+ ast_log(LOG_WARNING, "We only accept links via IAX2!!\n");
+ return -1;
+ }
+ }
+ if (options && (*options == 'R'))
+ {
+
+ /* Parts of this section taken from app_parkandannounce */
+ char *return_context;
+ int l, m, lot, timeout = 0;
+ char tmp[256],*template;
+ char *working, *context, *exten, *priority;
+ char *s,*orig_s;
+
+
+ ast_mutex_lock(&myrpt->lock);
+ m = myrpt->callmode;
+ ast_mutex_unlock(&myrpt->lock);
+
+ if ((!myrpt->nobusyout) && m)
+ {
+ if (chan->_state != AST_STATE_UP)
+ {
+ ast_indicate(chan,AST_CONTROL_BUSY);
+ }
+ while(ast_safe_sleep(chan,10000) != -1);
+ return -1;
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ {
+ ast_answer(chan);
+ }
+
+ l=strlen(options)+2;
+ orig_s=malloc(l);
+ if(!orig_s) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return -1;
+ }
+ s=orig_s;
+ strncpy(s,options,l);
+
+ template=strsep(&s,"|");
+ if(!template) {
+ ast_log(LOG_WARNING, "An announce template must be defined\n");
+ free(orig_s);
+ return -1;
+ }
+
+ if(s) {
+ timeout = atoi(strsep(&s, "|"));
+ timeout *= 1000;
+ }
+
+ return_context = s;
+
+ if(return_context != NULL) {
+ /* set the return context. Code borrowed from the Goto builtin */
+
+ working = return_context;
+ context = strsep(&working, "|");
+ exten = strsep(&working, "|");
+ if(!exten) {
+ /* Only a priority in this one */
+ priority = context;
+ exten = NULL;
+ context = NULL;
+ } else {
+ priority = strsep(&working, "|");
+ if(!priority) {
+ /* Only an extension and priority in this one */
+ priority = exten;
+ exten = context;
+ context = NULL;
+ }
+ }
+ if(atoi(priority) < 0) {
+ ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
+ free(orig_s);
+ return -1;
+ }
+ /* At this point we have a priority and maybe an extension and a context */
+ chan->priority = atoi(priority);
+ if(exten && strcasecmp(exten, "BYEXTENSION"))
+ strncpy(chan->exten, exten, sizeof(chan->exten)-1);
+ if(context)
+ strncpy(chan->context, context, sizeof(chan->context)-1);
+ } else { /* increment the priority by default*/
+ chan->priority++;
+ }
+
+ if(option_verbose > 2) {
+ ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num);
+ if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
+ ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
+ }
+ }
+
+ /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
+ before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
+
+ ast_masq_park_call(chan, NULL, timeout, &lot);
+
+ if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
+
+ snprintf(tmp,sizeof(tmp) - 1,"%d,%s",lot,template + 1);
+
+ rpt_telemetry(myrpt,REV_PATCH,tmp);
+
+ free(orig_s);
+
+ return 0;
+
+ }
+
+ if (!options)
+ {
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ struct in_addr ia;
+ char hisip[100],nodeip[100],*val, *s, *s1, *s2, *b,*b1;
+
+ /* look at callerid to see what node this comes from */
+ if (!chan->cid.cid_num) /* if doesn't have caller id */
+ {
+ ast_log(LOG_WARNING, "Doesnt have callerid on %s\n",tmp);
+ return -1;
+ }
+
+ /* get his IP from IAX2 module */
+ memset(hisip,0,sizeof(hisip));
+ pbx_substitute_variables_helper(chan,"${IAXPEER(CURRENTCHANNEL)}",hisip,sizeof(hisip) - 1);
+ if (!hisip[0])
+ {
+ ast_log(LOG_WARNING, "Link IP address cannot be determined!!\n");
+ return -1;
+ }
+
+ ast_callerid_parse(chan->cid.cid_num,&b,&b1);
+ ast_shrink_phone_number(b1);
+ if (!strcmp(myrpt->name,b1))
+ {
+ ast_log(LOG_WARNING, "Trying to link to self!!\n");
+ return -1;
+ }
+
+ if (*b1 < '1')
+ {
+ ast_log(LOG_WARNING, "Node %s Invalid for connection here!!\n",b1);
+ return -1;
+ }
+
+
+ /* look for his reported node string */
+ val = ast_variable_retrieve(cfg, myrpt->nodes, b1);
+ if (!val)
+ {
+ ast_log(LOG_WARNING, "Reported node %s cannot be found!!\n",b1);
+ return -1;
+ }
+ strncpy(tmp,val,sizeof(tmp) - 1);
+ s = tmp;
+ s1 = strsep(&s,",");
+ s2 = strsep(&s,",");
+ if (!s2)
+ {
+ ast_log(LOG_WARNING, "Reported node %s not in correct format!!\n",b1);
+ return -1;
+ }
+ if (strcmp(s2,"NONE")) {
+ hp = ast_gethostbyname(s2, &ahp);
+ if (!hp)
+ {
+ ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n",b1,s2);
+ return -1;
+ }
+ memcpy(&ia,hp->h_addr,sizeof(in_addr_t));
+ ast_inet_ntoa(nodeip,sizeof(nodeip) - 1,ia);
+ if (strcmp(hisip,nodeip))
+ {
+ char *s3 = strchr(s1,'@');
+ if (s3) s1 = s3 + 1;
+ s3 = strchr(s1,'/');
+ if (s3) *s3 = 0;
+ hp = ast_gethostbyname(s1, &ahp);
+ if (!hp)
+ {
+ ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n",b1,s1);
+ return -1;
+ }
+ memcpy(&ia,hp->h_addr,sizeof(in_addr_t));
+ ast_inet_ntoa(nodeip,sizeof(nodeip) - 1,ia);
+ if (strcmp(hisip,nodeip))
+ {
+ ast_log(LOG_WARNING, "Node %s IP %s does not match link IP %s!!\n",b1,nodeip,hisip);
+ return -1;
+ }
+ }
+ }
+ }
+
+ /* if is not a remote */
+ if (!myrpt->remote)
+ {
+
+ char *b,*b1;
+
+ /* look at callerid to see what node this comes from */
+ if (!chan->cid.cid_num) /* if doesn't have caller id */
+ {
+ ast_log(LOG_WARNING, "Doesnt have callerid on %s\n",tmp);
+ return -1;
+ }
+
+ ast_callerid_parse(chan->cid.cid_num,&b,&b1);
+ ast_shrink_phone_number(b1);
+ if (!strcmp(myrpt->name,b1))
+ {
+ ast_log(LOG_WARNING, "Trying to link to self!!\n");
+ return -1;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name,b1)) break;
+ l = l->next;
+ }
+ /* if found */
+ if (l != &myrpt->links)
+ {
+ l->killme = 1;
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 2;
+ ast_mutex_unlock(&myrpt->lock);
+ usleep(500000);
+ } else
+ ast_mutex_unlock(&myrpt->lock);
+ /* establish call in tranceive mode */
+ l = malloc(sizeof(struct rpt_link));
+ if (!l)
+ {
+ ast_log(LOG_WARNING, "Unable to malloc\n");
+ pthread_exit(NULL);
+ }
+ /* zero the silly thing */
+ memset((char *)l,0,sizeof(struct rpt_link));
+ l->mode = 1;
+ strncpy(l->name,b1,MAXNODESTR - 1);
+ l->isremote = 0;
+ l->chan = chan;
+ l->connected = 1;
+ l->hasconnected = 1;
+ l->phonemode = phone_mode;
+ ast_set_read_format(l->chan,AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan,AST_FORMAT_SLINEAR);
+ /* allocate a pseudo-channel thru asterisk */
+ l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!l->pchan)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR);
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1)
+ {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ pthread_exit(NULL);
+ }
+ ast_mutex_lock(&myrpt->lock);
+ if (phone_mode > 1) l->lastrx = 1;
+ /* insert at end of queue */
+ insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
+ ast_mutex_unlock(&myrpt->lock);
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+ return AST_PBX_KEEPALIVE;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ /* if remote, error if anyone else already linked */
+ if (myrpt->remoteon)
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ usleep(500000);
+ if (myrpt->remoteon)
+ {
+ ast_log(LOG_WARNING, "Trying to use busy link on %s\n",tmp);
+ return -1;
+ }
+ ast_mutex_lock(&myrpt->lock);
+ }
+ myrpt->remoteon = 1;
+ if (ioperm(myrpt->iobase,1,1) == -1)
+ {
+ ast_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->iobase);
+ return -1;
+ }
+ LOCAL_USER_ADD(u);
+ tele = strchr(myrpt->rxchanname,'/');
+ if (!tele)
+ {
+ fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele,NULL);
+ if (myrpt->rxchannel)
+ {
+ ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
+ myrpt->rxchannel->whentohangup = 0;
+ myrpt->rxchannel->appl = "Apprpt";
+ myrpt->rxchannel->data = "(Link Rx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (Rx) initiating call to %s/%s on %s\n",
+ myrpt->rxchanname,tele,myrpt->rxchannel->name);
+ ast_mutex_unlock(&myrpt->lock);
+ ast_call(myrpt->rxchannel,tele,999);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ else
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ *--tele = '/';
+ if (myrpt->txchanname)
+ {
+ tele = strchr(myrpt->txchanname,'/');
+ if (!tele)
+ {
+ fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->txchannel = ast_request(myrpt->txchanname,AST_FORMAT_SLINEAR,tele,NULL);
+ if (myrpt->txchannel)
+ {
+ ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
+ myrpt->txchannel->whentohangup = 0;
+ myrpt->txchannel->appl = "Apprpt";
+ myrpt->txchannel->data = "(Link Tx)";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "rpt (Tx) initiating call to %s/%s on %s\n",
+ myrpt->txchanname,tele,myrpt->txchannel->name);
+ ast_mutex_unlock(&myrpt->lock);
+ ast_call(myrpt->txchannel,tele,999);
+ ast_mutex_lock(&myrpt->lock);
+ }
+ else
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n");
+ ast_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ pthread_exit(NULL);
+ }
+ *--tele = '/';
+ }
+ else
+ {
+ myrpt->txchannel = myrpt->rxchannel;
+ }
+ myrpt->remoterx = 0;
+ myrpt->remotetx = 0;
+ myrpt->retxtimer = 0;
+ myrpt->remoteon = 1;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = 0;
+ myrpt->hfscanmode = 0;
+ myrpt->hfscanstatus = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ setrem(myrpt);
+ ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ /* if we are on 2w loop and are a remote, turn EC on */
+ if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel))
+ {
+ i = 128;
+ ioctl(myrpt->rxchannel->fds[0],ZT_ECHOCANCEL,&i);
+ }
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+
+ if (ioctl(myrpt->txchannel->fds[0],ZT_GET_PARAMS,&par) != -1)
+ {
+ if (par.rxisoffhook)
+ {
+ ast_indicate(chan,AST_CONTROL_RADIO_KEY);
+ myrpt->remoterx = 1;
+ }
+ }
+ n = 0;
+ cs[n++] = chan;
+ cs[n++] = myrpt->rxchannel;
+ if (myrpt->rxchannel != myrpt->txchannel)
+ cs[n++] = myrpt->txchannel;
+ for(;;)
+ {
+ if (ast_check_hangup(chan)) break;
+ if (ast_check_hangup(myrpt->rxchannel)) break;
+ ms = MSWAIT;
+ who = ast_waitfor_n(cs,n,&ms);
+ if (who == NULL) ms = 0;
+ elap = MSWAIT - ms;
+ if (!ms) continue;
+ rem_totx = keyed;
+
+
+ if ((!myrpt->remoterx) && (!myrpt->remotetx))
+ {
+ if ((myrpt->retxtimer += elap) >= REDUNDANT_TX_TIME)
+ {
+ myrpt->retxtimer = 0;
+ ast_indicate(chan,AST_CONTROL_RADIO_UNKEY);
+ }
+ } else myrpt->retxtimer = 0;
+ if (rem_totx && (!myrpt->remotetx)) /* Remote base radio TX key */
+ {
+ myrpt->remotetx = 1;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY);
+ }
+ if ((!rem_totx) && myrpt->remotetx) /* Remote base radio TX unkey */
+ {
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ }
+
+ if(myrpt->tunerequest && (!strcmp(myrpt->remote, remote_rig_ft897))){ /* ft-897 specific for now... */
+ myrpt->tunerequest = 0;
+ set_mode_ft897(myrpt, REM_MODE_AM);
+ simple_command_ft897(myrpt, 8);
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ ast_indicate(chan, AST_CONTROL_RADIO_KEY);
+ if(play_tone(chan, 800, 6000, 8192) == -1)
+ break;
+
+ rmt_telem_finish(myrpt,chan);
+ set_mode_ft897(myrpt, 0x88);
+ setrem(myrpt);
+ }
+
+ if (myrpt->hfscanmode){
+ myrpt->scantimer -= elap;
+ if(myrpt->scantimer <= 0){
+ myrpt->scantimer = REM_SCANTIME;
+ service_scan(myrpt);
+ }
+ }
+
+
+ if (who == chan) /* if it was a read from incomming */
+ {
+ f = ast_read(chan);
+ if (!f)
+ {
+ if (debug) printf("@@@@ link:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ /* if not transmitting, zero-out audio */
+ if (!myrpt->remotetx)
+ memset(f->data,0,f->datalen);
+ ast_write(myrpt->txchannel,f);
+ }
+ if (f->frametype == AST_FRAME_DTMF)
+ {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ if (handle_remote_phone_dtmf(myrpt,f->subclass,&keyed,phone_mode) == -1)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ if (f->frametype == AST_FRAME_TEXT)
+ {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ if (handle_remote_data(myrpt,f->data) == -1)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY)
+ {
+ if (debug) printf("@@@@ rx key\n");
+ keyed = 1;
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY)
+ {
+ if (debug) printf("@@@@ rx un-key\n");
+ keyed = 0;
+ }
+ }
+ if (myrpt->hfscanstatus){
+ myrpt->remchannel = chan; /* Save copy of channel */
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ {
+ ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY);
+ }
+ if(myrpt->hfscanstatus < 0) {
+ if (myrpt->hfscanstatus == -1) {
+ if (ast_safe_sleep(myrpt->remchannel,1000) == -1) break;
+ }
+ sayfile(myrpt->remchannel, "rpt/stop");
+ }
+ else
+ {
+ saynum(myrpt->remchannel, myrpt->hfscanstatus );
+ }
+ rmt_telem_finish(myrpt,myrpt->remchannel);
+ myrpt->hfscanstatus = 0;
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if (who == myrpt->rxchannel) /* if it was a read from radio */
+ {
+ f = ast_read(myrpt->rxchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ link:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ if ((myrpt->remote) && (myrpt->remotetx))
+ memset(f->data,0,f->datalen);
+ ast_write(chan,f);
+ }
+ else if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY)
+ {
+ if (debug) printf("@@@@ remote rx key\n");
+ if (!myrpt->remotetx)
+ {
+ ast_indicate(chan,AST_CONTROL_RADIO_KEY);
+ myrpt->remoterx = 1;
+ }
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY)
+ {
+ if (debug) printf("@@@@ remote rx un-key\n");
+ if (!myrpt->remotetx)
+ {
+ ast_indicate(chan,AST_CONTROL_RADIO_UNKEY);
+ myrpt->remoterx = 0;
+ }
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if ((myrpt->rxchannel != myrpt->txchannel) &&
+ (who == myrpt->txchannel)) /* do this cuz you have to */
+ {
+ f = ast_read(myrpt->txchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ link:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+
+ }
+ ast_mutex_lock(&myrpt->lock);
+ if (myrpt->rxchannel != myrpt->txchannel) ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->hfscanmode = 0;
+ myrpt->hfscanstatus = 0;
+ myrpt->remoteon = 0;
+ ast_mutex_unlock(&myrpt->lock);
+ closerem(myrpt);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int i;
+
+ STANDARD_HANGUP_LOCALUSERS;
+ for(i = 0; i < nrpts; i++) {
+ if (!strcmp(rpt_vars[i].name,rpt_vars[i].nodes)) continue;
+ ast_mutex_destroy(&rpt_vars[i].lock);
+ }
+ i = ast_unregister_application(app);
+
+ /* Unregister cli extensions */
+ ast_cli_unregister(&cli_debug);
+
+ return i;
+}
+
+int load_module(void)
+{
+ ast_pthread_create(&rpt_master_thread,NULL,rpt_master,NULL);
+
+ /* Register cli extensions */
+ ast_cli_register(&cli_debug);
+
+ return ast_register_application(app, rpt_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_sayunixtime.c b/1.2-netsec/apps/app_sayunixtime.c
new file mode 100644
index 000000000..fc9cd25bf
--- /dev/null
+++ b/1.2-netsec/apps/app_sayunixtime.c
@@ -0,0 +1,163 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2003 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ *
+ * \brief SayUnixTime application
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/say.h"
+
+
+static char *tdesc = "Say time";
+
+static char *app_sayunixtime = "SayUnixTime";
+static char *app_datetime = "DateTime";
+
+static char *sayunixtime_synopsis = "Says a specified time in a custom format";
+
+static char *sayunixtime_descrip =
+"SayUnixTime([unixtime][|[timezone][|format]])\n"
+" unixtime: time, in seconds since Jan 1, 1970. May be negative.\n"
+" defaults to now.\n"
+" timezone: timezone, see /usr/share/zoneinfo for a list.\n"
+" defaults to machine default.\n"
+" format: a format the time is to be said in. See voicemail.conf.\n"
+" defaults to \"ABdY 'digits/at' IMp\"\n";
+static char *datetime_descrip =
+"DateTime([unixtime][|[timezone][|format]])\n"
+" unixtime: time, in seconds since Jan 1, 1970. May be negative.\n"
+" defaults to now.\n"
+" timezone: timezone, see /usr/share/zoneinfo for a list.\n"
+" defaults to machine default.\n"
+" format: a format the time is to be said in. See voicemail.conf.\n"
+" defaults to \"ABdY 'digits/at' IMp\"\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int sayunixtime_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ char *s,*zone=NULL,*timec,*format;
+ time_t unixtime;
+ struct timeval tv;
+
+ LOCAL_USER_ADD(u);
+
+ tv = ast_tvnow();
+ unixtime = (time_t)tv.tv_sec;
+
+ if( !strcasecmp(chan->language, "da" ) ) {
+ format = "A dBY HMS";
+ } else if ( !strcasecmp(chan->language, "de" ) ) {
+ format = "A dBY HMS";
+ } else {
+ format = "ABdY 'digits/at' IMp";
+ }
+
+ if (data) {
+ s = data;
+ s = ast_strdupa(s);
+ if (s) {
+ timec = strsep(&s,"|");
+ if ((timec) && (*timec != '\0')) {
+ long timein;
+ if (sscanf(timec,"%ld",&timein) == 1) {
+ unixtime = (time_t)timein;
+ }
+ }
+ if (s) {
+ zone = strsep(&s,"|");
+ if (zone && (*zone == '\0'))
+ zone = NULL;
+ if (s) {
+ format = s;
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory error\n");
+ }
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ res = ast_answer(chan);
+ }
+ if (!res)
+ res = ast_say_date_with_format(chan, unixtime, AST_DIGIT_ANY, chan->language, format, zone);
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_sayunixtime);
+ res |= ast_unregister_application(app_datetime);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app_sayunixtime, sayunixtime_exec, sayunixtime_synopsis, sayunixtime_descrip);
+ res |= ast_register_application(app_datetime, sayunixtime_exec, sayunixtime_synopsis, datetime_descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_senddtmf.c b/1.2-netsec/apps/app_senddtmf.c
new file mode 100644
index 000000000..c2c3c765d
--- /dev/null
+++ b/1.2-netsec/apps/app_senddtmf.c
@@ -0,0 +1,129 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to send DTMF digits
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Send DTMF digits Application";
+
+static char *app = "SendDTMF";
+
+static char *synopsis = "Sends arbitrary DTMF digits";
+
+static char *descrip =
+" SendDTMF(digits[|timeout_ms]): Sends DTMF digits on a channel. \n"
+" Accepted digits: 0-9, *#abcd\n"
+" The application will either pass the assigned digits or terminate if it\n"
+" encounters an error.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int senddtmf_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *digits = NULL, *to = NULL;
+ int timeout = 250;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SendDTMF requires an argument (digits or *#aAbBcCdD)\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ digits = ast_strdupa(data);
+ if (!digits) {
+ ast_log(LOG_ERROR, "Out of Memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if ((to = strchr(digits,'|'))) {
+ *to = '\0';
+ to++;
+ timeout = atoi(to);
+ }
+
+ if(timeout <= 0)
+ timeout = 250;
+
+ res = ast_dtmf_stream(chan,NULL,digits,timeout);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, senddtmf_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_sendtext.c b/1.2-netsec/apps/app_sendtext.c
new file mode 100644
index 000000000..7830ee860
--- /dev/null
+++ b/1.2-netsec/apps/app_sendtext.c
@@ -0,0 +1,156 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to transmit a text message
+ *
+ * Requires support of sending text messages from channel driver
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+
+static const char *tdesc = "Send Text Applications";
+
+static const char *app = "SendText";
+
+static const char *synopsis = "Send a Text Message";
+
+static const char *descrip =
+" SendText(text[|options]): Sends text to current channel (callee).\n"
+"Result of transmission will be stored in the SENDTEXTSTATUS\n"
+"channel variable:\n"
+" SUCCESS Transmission succeeded\n"
+" FAILURE Transmission failed\n"
+" UNSUPPORTED Text transmission not supported by channel\n"
+"\n"
+"At this moment, text is supposed to be 7 bit ASCII in most channels.\n"
+"The option string many contain the following character:\n"
+"'j' -- jump to n+101 priority if the channel doesn't support\n"
+" text transport\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int sendtext_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *status = "UNSUPPORTED";
+ char *parse = NULL;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(text);
+ AST_APP_ARG(options);
+ );
+
+ LOCAL_USER_ADD(u);
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SendText requires an argument (text[|options])\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ } else {
+ parse = ast_strdupa(data);
+ if (!parse) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ ast_mutex_lock(&chan->lock);
+ if (!chan->tech->send_text) {
+ ast_mutex_unlock(&chan->lock);
+ /* Does not support transport */
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ status = "FAILURE";
+ ast_mutex_unlock(&chan->lock);
+ res = ast_sendtext(chan, args.text);
+ if (!res)
+ status = "SUCCESS";
+ pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, sendtext_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_setcallerid.c b/1.2-netsec/apps/app_setcallerid.c
new file mode 100644
index 000000000..877efb533
--- /dev/null
+++ b/1.2-netsec/apps/app_setcallerid.c
@@ -0,0 +1,178 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to set callerid
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+
+static char *app2 = "SetCallerPres";
+
+static char *synopsis2 = "Set CallerID Presentation";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char *descrip2 =
+" SetCallerPres(presentation): Set Caller*ID presentation on a call.\n"
+" Valid presentations are:\n"
+"\n"
+" allowed_not_screened : Presentation Allowed, Not Screened\n"
+" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
+" allowed_failed_screen : Presentation Allowed, Failed Screen\n"
+" allowed : Presentation Allowed, Network Number\n"
+" prohib_not_screened : Presentation Prohibited, Not Screened\n"
+" prohib_passed_screen : Presentation Prohibited, Passed Screen\n"
+" prohib_failed_screen : Presentation Prohibited, Failed Screen\n"
+" prohib : Presentation Prohibited, Network Number\n"
+" unavailable : Number Unavailable\n"
+"\n"
+;
+
+static int setcallerid_pres_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int pres = -1;
+
+ LOCAL_USER_ADD(u);
+
+ pres = ast_parse_caller_presentation(data);
+
+ if (pres < 0) {
+ ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show application SetCallerPres')\n",
+ (char *) data);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ chan->cid.cid_pres = pres;
+ LOCAL_USER_REMOVE(u);
+ return 0;
+}
+
+
+
+static char *tdesc = "Set CallerID Application";
+
+static char *app = "SetCallerID";
+
+static char *synopsis = "Set CallerID";
+
+static char *descrip =
+" SetCallerID(clid[|a]): Set Caller*ID on a call to a new\n"
+"value. Sets ANI as well if a flag is used. \n";
+
+static int setcallerid_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *tmp = NULL;
+ char name[256];
+ char num[256];
+ struct localuser *u;
+ char *opt;
+ int anitoo = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SetCallerID requires an argument!\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ tmp = ast_strdupa(data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ opt = strchr(tmp, '|');
+ if (opt) {
+ *opt = '\0';
+ opt++;
+ if (*opt == 'a')
+ anitoo = 1;
+ }
+
+ ast_callerid_split(tmp, name, sizeof(name), num, sizeof(num));
+ ast_set_callerid(chan, num, name, anitoo ? num : NULL);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app2);
+ res |= ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app2, setcallerid_pres_exec, synopsis2, descrip2);
+ res |= ast_register_application(app, setcallerid_exec, synopsis, descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_setcdruserfield.c b/1.2-netsec/apps/app_setcdruserfield.c
new file mode 100644
index 000000000..c477663dc
--- /dev/null
+++ b/1.2-netsec/apps/app_setcdruserfield.c
@@ -0,0 +1,179 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Justin Huff <jjhuff@mspin.net>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Applictions connected with CDR engine
+ *
+ * \ingroup applications
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/logger.h"
+#include "asterisk/config.h"
+#include "asterisk/manager.h"
+#include "asterisk/utils.h"
+
+
+static char *tdesc = "CDR user field apps";
+
+static char *setcdruserfield_descrip =
+ "[Synopsis]\n"
+ "SetCDRUserField(value)\n\n"
+ "[Description]\n"
+ "SetCDRUserField(value): Set the CDR 'user field' to value\n"
+ " The Call Data Record (CDR) user field is an extra field you\n"
+ " can use for data not stored anywhere else in the record.\n"
+ " CDR records can be used for billing or storing other arbitrary data\n"
+ " (I.E. telephone survey responses)\n"
+ " Also see AppendCDRUserField().\n";
+
+
+static char *setcdruserfield_app = "SetCDRUserField";
+static char *setcdruserfield_synopsis = "Set the CDR user field";
+
+static char *appendcdruserfield_descrip =
+ "[Synopsis]\n"
+ "AppendCDRUserField(value)\n\n"
+ "[Description]\n"
+ "AppendCDRUserField(value): Append value to the CDR user field\n"
+ " The Call Data Record (CDR) user field is an extra field you\n"
+ " can use for data not stored anywhere else in the record.\n"
+ " CDR records can be used for billing or storing other arbitrary data\n"
+ " (I.E. telephone survey responses)\n"
+ " Also see SetCDRUserField().\n";
+
+static char *appendcdruserfield_app = "AppendCDRUserField";
+static char *appendcdruserfield_synopsis = "Append to the CDR user field";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int action_setcdruserfield(struct mansession *s, struct message *m)
+{
+ struct ast_channel *c = NULL;
+ char *userfield = astman_get_header(m, "UserField");
+ char *channel = astman_get_header(m, "Channel");
+ char *append = astman_get_header(m, "Append");
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No Channel specified");
+ return 0;
+ }
+ if (ast_strlen_zero(userfield)) {
+ astman_send_error(s, m, "No UserField specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(channel);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ if (ast_true(append))
+ ast_cdr_appenduserfield(c, userfield);
+ else
+ ast_cdr_setuserfield(c, userfield);
+ ast_mutex_unlock(&c->lock);
+ astman_send_ack(s, m, "CDR Userfield Set");
+ return 0;
+}
+
+static int setcdruserfield_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int res = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->cdr && data) {
+ ast_cdr_setuserfield(chan, (char*)data);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int appendcdruserfield_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int res = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->cdr && data) {
+ ast_cdr_appenduserfield(chan, (char*)data);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(setcdruserfield_app);
+ res |= ast_unregister_application(appendcdruserfield_app);
+ res |= ast_manager_unregister("SetCDRUserField");
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(setcdruserfield_app, setcdruserfield_exec, setcdruserfield_synopsis, setcdruserfield_descrip);
+ res |= ast_register_application(appendcdruserfield_app, appendcdruserfield_exec, appendcdruserfield_synopsis, appendcdruserfield_descrip);
+ res |= ast_manager_register("SetCDRUserField", EVENT_FLAG_CALL, action_setcdruserfield, "Set the CDR UserField");
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_setcidname.c b/1.2-netsec/apps/app_setcidname.c
new file mode 100644
index 000000000..6906c132a
--- /dev/null
+++ b/1.2-netsec/apps/app_setcidname.c
@@ -0,0 +1,132 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to set callerid
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Set CallerID Name";
+
+static char *app = "SetCIDName";
+
+static char *synopsis = "Set CallerID Name";
+
+static char *descrip =
+" SetCIDName(cname[|a]): Set Caller*ID Name on a call to a new\n"
+"value, while preserving the original Caller*ID number. This is\n"
+"useful for providing additional information to the called\n"
+"party. \n"
+"SetCIDName has been deprecated in favor of the function\n"
+"CALLERID(name)\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int setcallerid_exec(struct ast_channel *chan, void *data)
+{
+ char *tmp = NULL;
+ struct localuser *u;
+ char *opt;
+ static int deprecation_warning = 0;
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "SetCIDName is deprecated, please use Set(CALLERID(name)=value) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "SetCIDName requires an argument!\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ tmp = ast_strdupa(data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ opt = strchr(tmp, '|');
+ if (opt) {
+ *opt = '\0';
+ }
+
+ ast_set_callerid(chan, NULL, tmp, NULL);
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, setcallerid_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_setcidnum.c b/1.2-netsec/apps/app_setcidnum.c
new file mode 100644
index 000000000..a8a7e0706
--- /dev/null
+++ b/1.2-netsec/apps/app_setcidnum.c
@@ -0,0 +1,131 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Oliver Daudey <traveler@xs4all.nl>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to set callerid number
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Set CallerID Number";
+
+static char *app = "SetCIDNum";
+
+static char *synopsis = "Set CallerID Number";
+
+static char *descrip =
+" SetCIDNum(cnum[|a]): Set Caller*ID Number on a call to a new\n"
+"value, while preserving the original Caller*ID name. This is\n"
+"useful for providing additional information to the called\n"
+"party. Sets ANI as well if a flag is used.\n"
+"SetCIDNum has been deprecated in favor of the function\n"
+"CALLERID(number)\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int setcallerid_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *opt;
+ int anitoo = 0;
+ char *tmp = NULL;
+ static int deprecation_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "SetCIDNum is deprecated, please use Set(CALLERID(number)=value) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ if (data)
+ tmp = ast_strdupa(data);
+ else
+ tmp = "";
+
+ opt = strchr(tmp, '|');
+ if (opt) {
+ *opt = '\0';
+ opt++;
+ if (*opt == 'a')
+ anitoo = 1;
+ }
+
+ ast_set_callerid(chan, tmp, NULL, anitoo ? tmp : NULL);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, setcallerid_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_setrdnis.c b/1.2-netsec/apps/app_setrdnis.c
new file mode 100644
index 000000000..69c3ed568
--- /dev/null
+++ b/1.2-netsec/apps/app_setrdnis.c
@@ -0,0 +1,132 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Oliver Daudey <traveler@xs4all.nl>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to set rdnis
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Set RDNIS Number";
+
+static char *app = "SetRDNIS";
+
+static char *synopsis = "Set RDNIS Number";
+
+static char *descrip =
+" SetRDNIS(cnum): Set RDNIS Number on a call to a new\n"
+"value.\n"
+"SetRDNIS has been deprecated in favor of the function\n"
+"CALLERID(rdnis)\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int setrdnis_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *opt, *n, *l;
+ char *tmp = NULL;
+ static int deprecation_warning = 0;
+
+ LOCAL_USER_ADD(u);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "SetRDNIS is deprecated, please use Set(CALLERID(rdnis)=value) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ if (data)
+ tmp = ast_strdupa(data);
+ else
+ tmp = "";
+
+ opt = strchr(tmp, '|');
+ if (opt)
+ *opt = '\0';
+
+ n = l = NULL;
+ ast_callerid_parse(tmp, &n, &l);
+ if (l) {
+ ast_shrink_phone_number(l);
+ ast_mutex_lock(&chan->lock);
+ if (chan->cid.cid_rdnis)
+ free(chan->cid.cid_rdnis);
+ chan->cid.cid_rdnis = (l[0]) ? strdup(l) : NULL;
+ ast_mutex_unlock(&chan->lock);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, setrdnis_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_settransfercapability.c b/1.2-netsec/apps/app_settransfercapability.c
new file mode 100644
index 000000000..5a9614afa
--- /dev/null
+++ b/1.2-netsec/apps/app_settransfercapability.c
@@ -0,0 +1,145 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Frank Sautter, levigo holding gmbh, www.levigo.de
+ *
+ * Frank Sautter - asterisk+at+sautter+dot+com
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief App to set the ISDN Transfer Capability
+ *
+ * \ingroup applications
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/transcap.h"
+
+
+static char *app = "SetTransferCapability";
+
+static char *synopsis = "Set ISDN Transfer Capability";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static struct { int val; char *name; } transcaps[] = {
+ { AST_TRANS_CAP_SPEECH, "SPEECH" },
+ { AST_TRANS_CAP_DIGITAL, "DIGITAL" },
+ { AST_TRANS_CAP_RESTRICTED_DIGITAL, "RESTRICTED_DIGITAL" },
+ { AST_TRANS_CAP_3_1K_AUDIO, "3K1AUDIO" },
+ { AST_TRANS_CAP_DIGITAL_W_TONES, "DIGITAL_W_TONES" },
+ { AST_TRANS_CAP_VIDEO, "VIDEO" },
+};
+
+static char *descrip =
+" SetTransferCapability(transfercapability): Set the ISDN Transfer \n"
+"Capability of a call to a new value.\n"
+"Valid Transfer Capabilities are:\n"
+"\n"
+" SPEECH : 0x00 - Speech (default, voice calls)\n"
+" DIGITAL : 0x08 - Unrestricted digital information (data calls)\n"
+" RESTRICTED_DIGITAL : 0x09 - Restricted digital information\n"
+" 3K1AUDIO : 0x10 - 3.1kHz Audio (fax calls)\n"
+" DIGITAL_W_TONES : 0x11 - Unrestricted digital information with tones/announcements\n"
+" VIDEO : 0x18 - Video:\n"
+"\n"
+;
+
+static int settransfercapability_exec(struct ast_channel *chan, void *data)
+{
+ char *tmp = NULL;
+ struct localuser *u;
+ int x;
+ char *opts;
+ int transfercapability = -1;
+
+ LOCAL_USER_ADD(u);
+
+ if (data)
+ tmp = ast_strdupa(data);
+ else
+ tmp = "";
+
+ opts = strchr(tmp, '|');
+ if (opts)
+ *opts = '\0';
+
+ for (x = 0; x < (sizeof(transcaps) / sizeof(transcaps[0])); x++) {
+ if (!strcasecmp(transcaps[x].name, tmp)) {
+ transfercapability = transcaps[x].val;
+ break;
+ }
+ }
+ if (transfercapability < 0) {
+ ast_log(LOG_WARNING, "'%s' is not a valid transfer capability (see 'show application SetTransferCapability')\n", tmp);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ chan->transfercapability = (unsigned short)transfercapability;
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Setting transfer capability to: 0x%.2x - %s.\n", transfercapability, tmp);
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, settransfercapability_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return synopsis;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_skel.c b/1.2-netsec/apps/app_skel.c
new file mode 100644
index 000000000..9fb2e757a
--- /dev/null
+++ b/1.2-netsec/apps/app_skel.c
@@ -0,0 +1,149 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Skeleton application
+ *
+ * This is a skeleton for development of an Asterisk application
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Trivial skeleton Application";
+static char *app = "Skel";
+static char *synopsis =
+"Skeleton application.";
+static char *descrip = "This application is a template to build other applications from.\n"
+ " It shows you the basic structure to create your own Asterisk applications.\n";
+
+#define OPTION_A (1 << 0) /* Option A */
+#define OPTION_B (1 << 1) /* Option B(n) */
+#define OPTION_C (1 << 2) /* Option C(str) */
+#define OPTION_NULL (1 << 3) /* Dummy Termination */
+
+AST_DECLARE_OPTIONS(app_opts,{
+ ['a'] = { OPTION_A },
+ ['b'] = { OPTION_B, 1 },
+ ['c'] = { OPTION_C, 2 }
+});
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int app_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_flags flags;
+ struct localuser *u;
+ char *options=NULL;
+ char *dummy = NULL;
+ char *args;
+ int argc = 0;
+ char *opts[2];
+ char *argv[2];
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s requires an argument (dummy|[options])\n",app);
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Do our thing here */
+
+ /* We need to make a copy of the input string if we are going to modify it! */
+ args = ast_strdupa(data);
+ if (!args) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+ dummy = argv[0];
+ options = argv[1];
+ ast_parseoptions(app_opts, &flags, opts, options);
+ }
+
+ if (!ast_strlen_zero(dummy))
+ ast_log(LOG_NOTICE, "Dummy value is : %s\n", dummy);
+
+ if (ast_test_flag(&flags, OPTION_A))
+ ast_log(LOG_NOTICE, "Option A is set\n");
+
+ if (ast_test_flag(&flags, OPTION_B))
+ ast_log(LOG_NOTICE,"Option B is set with : %s\n", opts[0] ? opts[0] : "<unspecified>");
+
+ if (ast_test_flag(&flags, OPTION_C))
+ ast_log(LOG_NOTICE,"Option C is set with : %s\n", opts[1] ? opts[1] : "<unspecified>");
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, app_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_sms.c b/1.2-netsec/apps/app_sms.c
new file mode 100644
index 000000000..4c15cc185
--- /dev/null
+++ b/1.2-netsec/apps/app_sms.c
@@ -0,0 +1,1555 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Adrian Kennard, rights assigned to Digium
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief SMS application - ETSI ES 201 912 protocol 1 implimentation
+ * \ingroup applications
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/alaw.h"
+#include "asterisk/callerid.h"
+
+/* output using Alaw rather than linear */
+/* #define OUTALAW */
+
+/* ToDo */
+/* Add full VP support */
+/* Handle status report messages (generation and reception) */
+/* Time zones on time stamps */
+/* user ref field */
+
+static volatile unsigned char message_ref; /* arbitary message ref */
+static volatile unsigned int seq; /* arbitrary message sequence number for unqiue files */
+
+static char log_file[255];
+static char spool_dir[255];
+
+static char *tdesc = "SMS/PSTN handler";
+
+static char *app = "SMS";
+
+static char *synopsis = "Communicates with SMS service centres and SMS capable analogue phones";
+
+static char *descrip =
+ " SMS(name|[a][s]): SMS handles exchange of SMS data with a call to/from SMS capabale\n"
+ "phone or SMS PSTN service center. Can send and/or receive SMS messages.\n"
+ "Works to ETSI ES 201 912 compatible with BT SMS PSTN service in UK\n"
+ "Typical usage is to use to handle called from the SMS service centre CLI,\n"
+ "or to set up a call using 'outgoing' or manager interface to connect\n"
+ "service centre to SMS()\n"
+ "name is the name of the queue used in /var/spool/asterisk/sms\n"
+ "Arguments:\n"
+ " a: answer, i.e. send initial FSK packet.\n"
+ " s: act as service centre talking to a phone.\n"
+ "Messages are processed as per text file message queues.\n"
+ "smsq (a separate software) is a command to generate message\n"
+ "queues and send messages.\n";
+
+static signed short wave[] = {
+ 0, 392, 782, 1167, 1545, 1913, 2270, 2612, 2939, 3247, 3536, 3802, 4045, 4263, 4455, 4619, 4755, 4862, 4938, 4985,
+ 5000, 4985, 4938, 4862, 4755, 4619, 4455, 4263, 4045, 3802, 3536, 3247, 2939, 2612, 2270, 1913, 1545, 1167, 782, 392,
+ 0, -392, -782, -1167,
+ -1545, -1913, -2270, -2612, -2939, -3247, -3536, -3802, -4045, -4263, -4455, -4619, -4755, -4862, -4938, -4985, -5000,
+ -4985, -4938, -4862,
+ -4755, -4619, -4455, -4263, -4045, -3802, -3536, -3247, -2939, -2612, -2270, -1913, -1545, -1167, -782, -392
+};
+
+#ifdef OUTALAW
+static unsigned char wavea[80];
+#endif
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+/* SMS 7 bit character mapping to UCS-2 */
+static const unsigned short defaultalphabet[] = {
+ 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
+ 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
+ 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
+ 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
+ ' ', '!', '"', '#', 164, '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
+ 161, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 196, 214, 209, 220, 167,
+ 191, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 228, 246, 241, 252, 224,
+};
+
+static const unsigned short escapes[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x000C, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x005E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0x007B, 0x007D, 0, 0, 0, 0, 0, 0x005C,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x005B, 0x007E, 0x005D, 0,
+ 0x007C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x20AC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+#define SMSLEN 160 /* max SMS length */
+
+typedef struct sms_s
+{
+ unsigned char hangup; /* we are done... */
+ unsigned char err; /* set for any errors */
+ unsigned char smsc:1; /* we are SMSC */
+ unsigned char rx:1; /* this is a received message */
+ char queue[30]; /* queue name */
+ char oa[20]; /* originating address */
+ char da[20]; /* destination address */
+ time_t scts; /* time stamp, UTC */
+ unsigned char pid; /* protocol ID */
+ unsigned char dcs; /* data coding scheme */
+ short mr; /* message reference - actually a byte, but usde -1 for not set */
+ int udl; /* user data length */
+ int udhl; /* user data header length */
+ unsigned char srr:1; /* Status Report request */
+ unsigned char udhi:1; /* User Data Header required, even if length 0 */
+ unsigned char rp:1; /* Reply Path */
+ unsigned int vp; /* validity period in minutes, 0 for not set */
+ unsigned short ud[SMSLEN]; /* user data (message), UCS-2 coded */
+ unsigned char udh[SMSLEN]; /* user data header */
+ char cli[20]; /* caller ID */
+ unsigned char ophase; /* phase (0-79) for 0 and 1 frequencies (1300Hz and 2100Hz) */
+ unsigned char ophasep; /* phase (0-79) for 1200 bps */
+ unsigned char obyte; /* byte being sent */
+ unsigned int opause; /* silent pause before sending (in sample periods) */
+ unsigned char obitp; /* bit in byte */
+ unsigned char osync; /* sync bits to send */
+ unsigned char obytep; /* byte in data */
+ unsigned char obyten; /* bytes in data */
+ unsigned char omsg[256]; /* data buffer (out) */
+ unsigned char imsg[200]; /* data buffer (in) */
+ signed long long ims0,
+ imc0,
+ ims1,
+ imc1; /* magnitude averages sin/cos 0/1 */
+ unsigned int idle;
+ unsigned short imag; /* signal level */
+ unsigned char ips0,
+ ips1,
+ ipc0,
+ ipc1; /* phase sin/cos 0/1 */
+ unsigned char ibitl; /* last bit */
+ unsigned char ibitc; /* bit run length count */
+ unsigned char iphasep; /* bit phase (0-79) for 1200 bps */
+ unsigned char ibitn; /* bit number in byte being received */
+ unsigned char ibytev; /* byte value being received */
+ unsigned char ibytep; /* byte pointer in messafe */
+ unsigned char ibytec; /* byte checksum for message */
+ unsigned char ierr; /* error flag */
+ unsigned char ibith; /* history of last bits */
+ unsigned char ibitt; /* total of 1's in last 3 bites */
+ /* more to go here */
+} sms_t;
+
+/* different types of encoding */
+#define is7bit(dcs) (((dcs)&0xC0)?(!((dcs)&4)):(!((dcs)&12)))
+#define is8bit(dcs) (((dcs)&0xC0)?(((dcs)&4)):(((dcs)&12)==4))
+#define is16bit(dcs) (((dcs)&0xC0)?0:(((dcs)&12)==8))
+
+static void *sms_alloc (struct ast_channel *chan, void *params)
+{
+ return params;
+}
+
+static void sms_release (struct ast_channel *chan, void *data)
+{
+ return;
+}
+
+static void sms_messagetx (sms_t * h);
+
+/*--- numcpy: copy number, skipping non digits apart from leading + */
+static void numcpy (char *d, char *s)
+{
+ if (*s == '+')
+ *d++ = *s++;
+ while (*s) {
+ if (isdigit (*s))
+ *d++ = *s;
+ s++;
+ }
+ *d = 0;
+}
+
+/*--- isodate: static, return a date/time in ISO format */
+static char * isodate (time_t t)
+{
+ static char date[20];
+ strftime (date, sizeof (date), "%Y-%m-%dT%H:%M:%S", localtime (&t));
+ return date;
+}
+
+/*--- utf8decode: reads next UCS character from null terminated UTF-8 string and advanced pointer */
+/* for non valid UTF-8 sequences, returns character as is */
+/* Does not advance pointer for null termination */
+static long utf8decode (unsigned char **pp)
+{
+ unsigned char *p = *pp;
+ if (!*p)
+ return 0; /* null termination of string */
+ (*pp)++;
+ if (*p < 0xC0)
+ return *p; /* ascii or continuation character */
+ if (*p < 0xE0) {
+ if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp)++;
+ return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
+ }
+ if (*p < 0xF0) {
+ if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 2;
+ return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
+ }
+ if (*p < 0xF8) {
+ if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 3;
+ return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
+ }
+ if (*p < 0xFC) {
+ if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
+ || (p[4] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 4;
+ return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
+ }
+ if (*p < 0xFE) {
+ if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
+ || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 5;
+ return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + (p[5] & 0x3F);
+ }
+ return *p; /* not sensible */
+}
+
+/*--- packsms7: takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using SMS 7 bit character codes */
+/* The return value is the number of septets packed in to o, which is internally limited to SMSLEN */
+/* o can be null, in which case this is used to validate or count only */
+/* if the input contains invalid characters then the return value is -1 */
+static int packsms7 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char p = 0, b = 0, n = 0;
+
+ if (udhl) { /* header */
+ if (o)
+ o[p++] = udhl;
+ b = 1;
+ n = 1;
+ while (udhl--) {
+ if (o)
+ o[p++] = *udh++;
+ b += 8;
+ while (b >= 7) {
+ b -= 7;
+ n++;
+ }
+ if (n >= SMSLEN)
+ return n;
+ }
+ if (b) {
+ b = 7 - b;
+ if (++n >= SMSLEN)
+ return n;
+ }; /* filling to septet boundary */
+ }
+ if (o)
+ o[p] = 0;
+ /* message */
+ while (udl--) {
+ long u;
+ unsigned char v;
+ u = *ud++;
+ for (v = 0; v < 128 && defaultalphabet[v] != u; v++);
+ if (v == 128 && u && n + 1 < SMSLEN) {
+ for (v = 0; v < 128 && escapes[v] != u; v++);
+ if (v < 128) { /* escaped sequence */
+ if (o)
+ o[p] |= (27 << b);
+ b += 7;
+ if (b >= 8) {
+ b -= 8;
+ p++;
+ if (o)
+ o[p] = (27 >> (7 - b));
+ }
+ n++;
+ }
+ }
+ if (v == 128)
+ return -1; /* invalid character */
+ if (o)
+ o[p] |= (v << b);
+ b += 7;
+ if (b >= 8) {
+ b -= 8;
+ p++;
+ if (o)
+ o[p] = (v >> (7 - b));
+ }
+ if (++n >= SMSLEN)
+ return n;
+ }
+ return n;
+}
+
+/*--- packsms8: takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using 8 bit character codes */
+/* The return value is the number of bytes packed in to o, which is internally limited to 140 */
+/* o can be null, in which case this is used to validate or count only */
+/* if the input contains invalid characters then the return value is -1 */
+static int packsms8 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char p = 0;
+
+ /* header - no encoding */
+ if (udhl) {
+ if (o)
+ o[p++] = udhl;
+ while (udhl--) {
+ if (o)
+ o[p++] = *udh++;
+ if (p >= 140)
+ return p;
+ }
+ }
+ while (udl--) {
+ long u;
+ u = *ud++;
+ if (u < 0 || u > 0xFF)
+ return -1; /* not valid */
+ if (o)
+ o[p++] = u;
+ if (p >= 140)
+ return p;
+ }
+ return p;
+}
+
+/*--- packsms16: takes a binary header (udhl bytes at udh) and UCS-2
+ message (udl characters at ud) and packs in to o using 16 bit
+ UCS-2 character codes
+ The return value is the number of bytes packed in to o, which is
+ internally limited to 140
+ o can be null, in which case this is used to validate or count
+ only if the input contains invalid characters then
+ the return value is -1 */
+static int packsms16 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char p = 0;
+ /* header - no encoding */
+ if (udhl) {
+ if (o)
+ o[p++] = udhl;
+ while (udhl--) {
+ if (o)
+ o[p++] = *udh++;
+ if (p >= 140)
+ return p;
+ }
+ }
+ while (udl--) {
+ long u;
+ u = *ud++;
+ if (o)
+ o[p++] = (u >> 8);
+ if (p >= 140)
+ return p - 1; /* could not fit last character */
+ if (o)
+ o[p++] = u;
+ if (p >= 140)
+ return p;
+ }
+ return p;
+}
+
+/*--- packsms: general pack, with length and data,
+ returns number of bytes of target used */
+static int packsms (unsigned char dcs, unsigned char *base, unsigned int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char *p = base;
+ if (udl) {
+ int l = 0;
+ if (is7bit (dcs)) { /* 7 bit */
+ l = packsms7 (p + 1, udhl, udh, udl, ud);
+ if (l < 0)
+ l = 0;
+ *p++ = l;
+ p += (l * 7 + 7) / 8;
+ } else if (is8bit (dcs)) { /* 8 bit */
+ l = packsms8 (p + 1, udhl, udh, udl, ud);
+ if (l < 0)
+ l = 0;
+ *p++ = l;
+ p += l;
+ } else { /* UCS-2 */
+ l = packsms16 (p + 1, udhl, udh, udl, ud);
+ if (l < 0)
+ l = 0;
+ *p++ = l;
+ p += l;
+ }
+ } else
+ *p++ = 0; /* no user data */
+ return p - base;
+}
+
+
+/*--- packdate: pack a date and return */
+static void packdate (unsigned char *o, time_t w)
+{
+ struct tm *t = localtime (&w);
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__)
+ int z = -t->tm_gmtoff / 60 / 15;
+#else
+ int z = timezone / 60 / 15;
+#endif
+ *o++ = ((t->tm_year % 10) << 4) + (t->tm_year % 100) / 10;
+ *o++ = (((t->tm_mon + 1) % 10) << 4) + (t->tm_mon + 1) / 10;
+ *o++ = ((t->tm_mday % 10) << 4) + t->tm_mday / 10;
+ *o++ = ((t->tm_hour % 10) << 4) + t->tm_hour / 10;
+ *o++ = ((t->tm_min % 10) << 4) + t->tm_min / 10;
+ *o++ = ((t->tm_sec % 10) << 4) + t->tm_sec / 10;
+ if (z < 0)
+ *o++ = (((-z) % 10) << 4) + (-z) / 10 + 0x08;
+ else
+ *o++ = ((z % 10) << 4) + z / 10;
+}
+
+/*--- unpackdate: unpack a date and return */
+static time_t unpackdate (unsigned char *i)
+{
+ struct tm t;
+ t.tm_year = 100 + (i[0] & 0xF) * 10 + (i[0] >> 4);
+ t.tm_mon = (i[1] & 0xF) * 10 + (i[1] >> 4) - 1;
+ t.tm_mday = (i[2] & 0xF) * 10 + (i[2] >> 4);
+ t.tm_hour = (i[3] & 0xF) * 10 + (i[3] >> 4);
+ t.tm_min = (i[4] & 0xF) * 10 + (i[4] >> 4);
+ t.tm_sec = (i[5] & 0xF) * 10 + (i[5] >> 4);
+ t.tm_isdst = 0;
+ if (i[6] & 0x08)
+ t.tm_min += 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
+ else
+ t.tm_min -= 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
+ return mktime (&t);
+}
+
+/*--- unpacksms7: unpacks bytes (7 bit encoding) at i, len l septets,
+ and places in udh and ud setting udhl and udl. udh not used
+ if udhi not set */
+static void unpacksms7 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ unsigned char b = 0, p = 0;
+ unsigned short *o = ud;
+ *udhl = 0;
+ if (udhi && l) { /* header */
+ int h = i[p];
+ *udhl = h;
+ if (h) {
+ b = 1;
+ p++;
+ l--;
+ while (h-- && l) {
+ *udh++ = i[p++];
+ b += 8;
+ while (b >= 7) {
+ b -= 7;
+ l--;
+ if (!l)
+ break;
+ }
+ }
+ /* adjust for fill, septets */
+ if (b) {
+ b = 7 - b;
+ l--;
+ }
+ }
+ }
+ while (l--) {
+ unsigned char v;
+ if (b < 2)
+ v = ((i[p] >> b) & 0x7F);
+ else
+ v = ((((i[p] >> b) + (i[p + 1] << (8 - b)))) & 0x7F);
+ b += 7;
+ if (b >= 8) {
+ b -= 8;
+ p++;
+ }
+ if (o > ud && o[-1] == 0x00A0 && escapes[v])
+ o[-1] = escapes[v];
+ else
+ *o++ = defaultalphabet[v];
+ }
+ *udl = (o - ud);
+}
+
+/*--- unpacksms8: unpacks bytes (8 bit encoding) at i, len l septets,
+ and places in udh and ud setting udhl and udl. udh not used
+ if udhi not set */
+static void unpacksms8 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ unsigned short *o = ud;
+ *udhl = 0;
+ if (udhi) {
+ int n = *i;
+ *udhl = n;
+ if (n) {
+ i++;
+ l--;
+ while (l && n) {
+ l--;
+ n--;
+ *udh++ = *i++;
+ }
+ }
+ }
+ while (l--)
+ *o++ = *i++; /* not to UTF-8 as explicitely 8 bit coding in DCS */
+ *udl = (o - ud);
+}
+
+/*--- unpacksms16: unpacks bytes (16 bit encoding) at i, len l septets,
+ and places in udh and ud setting udhl and udl.
+ udh not used if udhi not set */
+static void unpacksms16 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ unsigned short *o = ud;
+ *udhl = 0;
+ if (udhi) {
+ int n = *i;
+ *udhl = n;
+ if (n) {
+ i++;
+ l--;
+ while (l && n) {
+ l--;
+ n--;
+ *udh++ = *i++;
+ }
+ }
+ }
+ while (l--) {
+ int v = *i++;
+ if (l--)
+ v = (v << 8) + *i++;
+ *o++ = v;
+ }
+ *udl = (o - ud);
+}
+
+/*--- unpacksms: general unpack - starts with length byte (octet or septet) and returns number of bytes used, inc length */
+static int unpacksms (unsigned char dcs, unsigned char *i, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ int l = *i++;
+ if (is7bit (dcs)) {
+ unpacksms7 (i, l, udh, udhl, ud, udl, udhi);
+ l = (l * 7 + 7) / 8; /* adjust length to return */
+ } else if (is8bit (dcs))
+ unpacksms8 (i, l, udh, udhl, ud, udl, udhi);
+ else
+ unpacksms16 (i, l, udh, udhl, ud, udl, udhi);
+ return l + 1;
+}
+
+/*--- unpackaddress: unpack an address from i, return byte length, unpack to o */
+static unsigned char unpackaddress (char *o, unsigned char *i)
+{
+ unsigned char l = i[0],
+ p;
+ if (i[1] == 0x91)
+ *o++ = '+';
+ for (p = 0; p < l; p++) {
+ if (p & 1)
+ *o++ = (i[2 + p / 2] >> 4) + '0';
+ else
+ *o++ = (i[2 + p / 2] & 0xF) + '0';
+ }
+ *o = 0;
+ return (l + 5) / 2;
+}
+
+/*--- packaddress: store an address at o, and return number of bytes used */
+static unsigned char packaddress (unsigned char *o, char *i)
+{
+ unsigned char p = 2;
+ o[0] = 0;
+ if (*i == '+') {
+ i++;
+ o[1] = 0x91;
+ } else
+ o[1] = 0x81;
+ while (*i)
+ if (isdigit (*i)) {
+ if (o[0] & 1)
+ o[p++] |= ((*i & 0xF) << 4);
+ else
+ o[p] = (*i & 0xF);
+ o[0]++;
+ i++;
+ } else
+ i++;
+ if (o[0] & 1)
+ o[p++] |= 0xF0; /* pad */
+ return p;
+}
+
+/*--- sms_log: Log the output, and remove file */
+static void sms_log (sms_t * h, char status)
+{
+ if (*h->oa || *h->da) {
+ int o = open (log_file, O_CREAT | O_APPEND | O_WRONLY, 0666);
+ if (o >= 0) {
+ char line[1000], mrs[3] = "", *p;
+ unsigned char n;
+
+ if (h->mr >= 0)
+ snprintf (mrs, sizeof (mrs), "%02X", h->mr);
+ snprintf (line, sizeof (line), "%s %c%c%c%s %s %s %s ",
+ isodate (time (0)), status, h->rx ? 'I' : 'O', h->smsc ? 'S' : 'M', mrs, h->queue, *h->oa ? h->oa : "-",
+ *h->da ? h->da : "-");
+ p = line + strlen (line);
+ for (n = 0; n < h->udl; n++)
+ if (h->ud[n] == '\\') {
+ *p++ = '\\';
+ *p++ = '\\';
+ } else if (h->ud[n] == '\n') {
+ *p++ = '\\';
+ *p++ = 'n';
+ } else if (h->ud[n] == '\r') {
+ *p++ = '\\';
+ *p++ = 'r';
+ } else if (h->ud[n] < 32 || h->ud[n] == 127)
+ *p++ = 191;
+ else
+ *p++ = h->ud[n];
+ *p++ = '\n';
+ *p = 0;
+ write (o, line, strlen (line));
+ close (o);
+ }
+ *h->oa = *h->da = h->udl = 0;
+ }
+}
+
+/*--- sms_readfile: parse and delete a file */
+static void sms_readfile (sms_t * h, char *fn)
+{
+ char line[1000];
+ FILE *s;
+ char dcsset = 0; /* if DSC set */
+ ast_log (LOG_EVENT, "Sending %s\n", fn);
+ h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0;
+ h->mr = -1;
+ h->dcs = 0xF1; /* normal messages class 1 */
+ h->scts = time (0);
+ s = fopen (fn, "r");
+ if (s)
+ {
+ if (unlink (fn))
+ { /* concurrent access, we lost */
+ fclose (s);
+ return;
+ }
+ while (fgets (line, sizeof (line), s))
+ { /* process line in file */
+ unsigned char *p;
+ for (p = line; *p && *p != '\n' && *p != '\r'; p++);
+ *p = 0; /* strip eoln */
+ p = line;
+ if (!*p || *p == ';')
+ continue; /* blank line or comment, ignore */
+ while (isalnum (*p))
+ {
+ *p = tolower (*p);
+ p++;
+ }
+ while (isspace (*p))
+ *p++ = 0;
+ if (*p == '=')
+ {
+ *p++ = 0;
+ if (!strcmp (line, "ud"))
+ { /* parse message (UTF-8) */
+ unsigned char o = 0;
+ while (*p && o < SMSLEN)
+ h->ud[o++] = utf8decode((unsigned char **)&p);
+ h->udl = o;
+ if (*p)
+ ast_log (LOG_WARNING, "UD too long in %s\n", fn);
+ } else
+ {
+ while (isspace (*p))
+ p++;
+ if (!strcmp (line, "oa") && strlen (p) < sizeof (h->oa))
+ numcpy (h->oa, p);
+ else if (!strcmp (line, "da") && strlen (p) < sizeof (h->oa))
+ numcpy (h->da, p);
+ else if (!strcmp (line, "pid"))
+ h->pid = atoi (p);
+ else if (!strcmp (line, "dcs"))
+ {
+ h->dcs = atoi (p);
+ dcsset = 1;
+ } else if (!strcmp (line, "mr"))
+ h->mr = atoi (p);
+ else if (!strcmp (line, "srr"))
+ h->srr = (atoi (p) ? 1 : 0);
+ else if (!strcmp (line, "vp"))
+ h->vp = atoi (p);
+ else if (!strcmp (line, "rp"))
+ h->rp = (atoi (p) ? 1 : 0);
+ else if (!strcmp (line, "scts"))
+ { /* get date/time */
+ int Y,
+ m,
+ d,
+ H,
+ M,
+ S;
+ if (sscanf (p, "%d-%d-%dT%d:%d:%d", &Y, &m, &d, &H, &M, &S) == 6)
+ {
+ struct tm t;
+ t.tm_year = Y - 1900;
+ t.tm_mon = m - 1;
+ t.tm_mday = d;
+ t.tm_hour = H;
+ t.tm_min = M;
+ t.tm_sec = S;
+ t.tm_isdst = -1;
+ h->scts = mktime (&t);
+ if (h->scts == (time_t) - 1)
+ ast_log (LOG_WARNING, "Bad date/timein %s: %s", fn, p);
+ }
+ } else
+ ast_log (LOG_WARNING, "Cannot parse in %s: %s=%si\n", fn, line, p);
+ }
+ } else if (*p == '#')
+ { /* raw hex format */
+ *p++ = 0;
+ if (*p == '#')
+ {
+ p++;
+ if (!strcmp (line, "ud"))
+ { /* user data */
+ int o = 0;
+ while (*p && o < SMSLEN)
+ {
+ if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3]))
+ {
+ h->ud[o++] =
+ (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) +
+ (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
+ (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF));
+ p += 4;
+ } else
+ break;
+ }
+ h->udl = o;
+ if (*p)
+ ast_log (LOG_WARNING, "UD too long / invalid UCS-2 hex in %s\n", fn);
+ } else
+ ast_log (LOG_WARNING, "Only ud can use ## format, %s\n", fn);
+ } else if (!strcmp (line, "ud"))
+ { /* user data */
+ int o = 0;
+ while (*p && o < SMSLEN)
+ {
+ if (isxdigit (*p) && isxdigit (p[1]))
+ {
+ h->ud[o++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
+ p += 2;
+ } else
+ break;
+ }
+ h->udl = o;
+ if (*p)
+ ast_log (LOG_WARNING, "UD too long / invalid UCS-1 hex in %s\n", fn);
+ } else if (!strcmp (line, "udh"))
+ { /* user data header */
+ unsigned char o = 0;
+ h->udhi = 1;
+ while (*p && o < SMSLEN)
+ {
+ if (isxdigit (*p) && isxdigit (p[1]))
+ {
+ h->udh[o] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
+ o++;
+ p += 2;
+ } else
+ break;
+ }
+ h->udhl = o;
+ if (*p)
+ ast_log (LOG_WARNING, "UDH too long / invalid hex in %s\n", fn);
+ } else
+ ast_log (LOG_WARNING, "Only ud and udh can use # format, %s\n", fn);
+ } else
+ ast_log (LOG_WARNING, "Cannot parse in %s: %s\n", fn, line);
+ }
+ fclose (s);
+ if (!dcsset && packsms7 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ {
+ if (packsms8 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ {
+ if (packsms16 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log (LOG_WARNING, "Invalid UTF-8 message even for UCS-2 (%s)\n", fn);
+ else
+ {
+ h->dcs = 0x08; /* default to 16 bit */
+ ast_log (LOG_WARNING, "Sending in 16 bit format (%s)\n", fn);
+ }
+ } else
+ {
+ h->dcs = 0xF5; /* default to 8 bit */
+ ast_log (LOG_WARNING, "Sending in 8 bit format (%s)\n", fn);
+ }
+ }
+ if (is7bit (h->dcs) && packsms7 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log (LOG_WARNING, "Invalid 7 bit GSM data %s\n", fn);
+ if (is8bit (h->dcs) && packsms8 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log (LOG_WARNING, "Invalid 8 bit data %s\n", fn);
+ if (is16bit (h->dcs) && packsms16 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log (LOG_WARNING, "Invalid 16 bit data %s\n", fn);
+ }
+}
+
+/*--- sms_writefile: white a received text message to a file */
+static void sms_writefile (sms_t * h)
+{
+ char fn[200] = "", fn2[200] = "";
+ FILE *o;
+ ast_copy_string (fn, spool_dir, sizeof (fn));
+ mkdir (fn, 0777); /* ensure it exists */
+ snprintf (fn + strlen (fn), sizeof (fn) - strlen (fn), "/%s", h->smsc ? h->rx ? "morx" : "mttx" : h->rx ? "mtrx" : "motx");
+ mkdir (fn, 0777); /* ensure it exists */
+ ast_copy_string (fn2, fn, sizeof (fn2));
+ snprintf (fn2 + strlen (fn2), sizeof (fn2) - strlen (fn2), "/%s.%s-%d", h->queue, isodate (h->scts), seq++);
+ snprintf (fn + strlen (fn), sizeof (fn) - strlen (fn), "/.%s", fn2 + strlen (fn) + 1);
+ o = fopen (fn, "w");
+ if (o) {
+ if (*h->oa)
+ fprintf (o, "oa=%s\n", h->oa);
+ if (*h->da)
+ fprintf (o, "da=%s\n", h->da);
+ if (h->udhi) {
+ unsigned int p;
+ fprintf (o, "udh#");
+ for (p = 0; p < h->udhl; p++)
+ fprintf (o, "%02X", h->udh[p]);
+ fprintf (o, "\n");
+ }
+ if (h->udl) {
+ unsigned int p;
+ for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
+ if (p < h->udl)
+ fputc (';', o); /* cannot use ud=, but include as a comment for human readable */
+ fprintf (o, "ud=");
+ for (p = 0; p < h->udl; p++) {
+ unsigned short v = h->ud[p];
+ if (v < 32)
+ fputc (191, o);
+ else if (v < 0x80)
+ fputc (v, o);
+ else if (v < 0x800)
+ {
+ fputc (0xC0 + (v >> 6), o);
+ fputc (0x80 + (v & 0x3F), o);
+ } else
+ {
+ fputc (0xE0 + (v >> 12), o);
+ fputc (0x80 + ((v >> 6) & 0x3F), o);
+ fputc (0x80 + (v & 0x3F), o);
+ }
+ }
+ fprintf (o, "\n");
+ for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
+ if (p < h->udl) {
+ for (p = 0; p < h->udl && h->ud[p] < 0x100; p++);
+ if (p == h->udl) { /* can write in ucs-1 hex */
+ fprintf (o, "ud#");
+ for (p = 0; p < h->udl; p++)
+ fprintf (o, "%02X", h->ud[p]);
+ fprintf (o, "\n");
+ } else { /* write in UCS-2 */
+ fprintf (o, "ud##");
+ for (p = 0; p < h->udl; p++)
+ fprintf (o, "%04X", h->ud[p]);
+ fprintf (o, "\n");
+ }
+ }
+ }
+ if (h->scts)
+ fprintf (o, "scts=%s\n", isodate (h->scts));
+ if (h->pid)
+ fprintf (o, "pid=%d\n", h->pid);
+ if (h->dcs != 0xF1)
+ fprintf (o, "dcs=%d\n", h->dcs);
+ if (h->vp)
+ fprintf (o, "vp=%d\n", h->vp);
+ if (h->srr)
+ fprintf (o, "srr=1\n");
+ if (h->mr >= 0)
+ fprintf (o, "mr=%d\n", h->mr);
+ if (h->rp)
+ fprintf (o, "rp=1\n");
+ fclose (o);
+ if (rename (fn, fn2))
+ unlink (fn);
+ else
+ ast_log (LOG_EVENT, "Received to %s\n", fn2);
+ }
+}
+
+/*--- readdirqueue: read dir skipping dot files... */
+static struct dirent *readdirqueue (DIR * d, char *queue)
+{
+ struct dirent *f;
+ do {
+ f = readdir (d);
+ } while (f && (*f->d_name == '.' || strncmp (f->d_name, queue, strlen (queue)) || f->d_name[strlen (queue)] != '.'));
+ return f;
+}
+
+/*--- sms_handleincoming: handle the incoming message */
+static unsigned char sms_handleincoming (sms_t * h)
+{
+ unsigned char p = 3;
+ if (h->smsc) { /* SMSC */
+ if ((h->imsg[2] & 3) == 1) { /* SMS-SUBMIT */
+ h->udhl = h->udl = 0;
+ h->vp = 0;
+ h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
+ h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
+ h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
+ ast_copy_string (h->oa, h->cli, sizeof (h->oa));
+ h->scts = time (0);
+ h->mr = h->imsg[p++];
+ p += unpackaddress (h->da, h->imsg + p);
+ h->pid = h->imsg[p++];
+ h->dcs = h->imsg[p++];
+ if ((h->imsg[2] & 0x18) == 0x10) { /* relative VP */
+ if (h->imsg[p] < 144)
+ h->vp = (h->imsg[p] + 1) * 5;
+ else if (h->imsg[p] < 168)
+ h->vp = 720 + (h->imsg[p] - 143) * 30;
+ else if (h->imsg[p] < 197)
+ h->vp = (h->imsg[p] - 166) * 1440;
+ else
+ h->vp = (h->imsg[p] - 192) * 10080;
+ p++;
+ } else if (h->imsg[2] & 0x18)
+ p += 7; /* ignore enhanced / absolute VP */
+ p += unpacksms (h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
+ h->rx = 1; /* received message */
+ sms_writefile (h); /* write the file */
+ if (p != h->imsg[1] + 2) {
+ ast_log (LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
+ return 0xFF; /* duh! */
+ }
+ } else {
+ ast_log (LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
+ return 0xFF;
+ }
+ } else { /* client */
+ if (!(h->imsg[2] & 3)) { /* SMS-DELIVER */
+ *h->da = h->srr = h->rp = h->vp = h->udhi = h->udhl = h->udl = 0;
+ h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
+ h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
+ h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
+ h->mr = -1;
+ p += unpackaddress (h->oa, h->imsg + p);
+ h->pid = h->imsg[p++];
+ h->dcs = h->imsg[p++];
+ h->scts = unpackdate (h->imsg + p);
+ p += 7;
+ p += unpacksms (h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
+ h->rx = 1; /* received message */
+ sms_writefile (h); /* write the file */
+ if (p != h->imsg[1] + 2) {
+ ast_log (LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
+ return 0xFF; /* duh! */
+ }
+ } else {
+ ast_log (LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
+ return 0xFF;
+ }
+ }
+ return 0; /* no error */
+}
+
+#ifdef SOLARIS
+#define NAME_MAX 1024
+#endif
+
+/*--- sms_nextoutgoing: find and fill in next message,
+ or send a REL if none waiting */
+static void sms_nextoutgoing (sms_t * h)
+{
+ char fn[100 + NAME_MAX] = "";
+ DIR *d;
+ char more = 0;
+ ast_copy_string (fn, spool_dir, sizeof (fn));
+ mkdir (fn, 0777); /* ensure it exists */
+ h->rx = 0; /* outgoing message */
+ snprintf (fn + strlen (fn), sizeof (fn) - strlen (fn), "/%s", h->smsc ? "mttx" : "motx");
+ mkdir (fn, 0777); /* ensure it exists */
+ d = opendir (fn);
+ if (d) {
+ struct dirent *f = readdirqueue (d, h->queue);
+ if (f) {
+ snprintf (fn + strlen (fn), sizeof (fn) - strlen (fn), "/%s", f->d_name);
+ sms_readfile (h, fn);
+ if (readdirqueue (d, h->queue))
+ more = 1; /* more to send */
+ }
+ closedir (d);
+ }
+ if (*h->da || *h->oa) { /* message to send */
+ unsigned char p = 2;
+ h->omsg[0] = 0x91; /* SMS_DATA */
+ if (h->smsc) { /* deliver */
+ h->omsg[p++] = (more ? 4 : 0);
+ p += packaddress (h->omsg + p, h->oa);
+ h->omsg[p++] = h->pid;
+ h->omsg[p++] = h->dcs;
+ packdate (h->omsg + p, h->scts);
+ p += 7;
+ p += packsms (h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
+ } else { /* submit */
+ h->omsg[p++] =
+ 0x01 + (more ? 4 : 0) + (h->srr ? 0x20 : 0) + (h->rp ? 0x80 : 0) + (h->vp ? 0x10 : 0) + (h->udhi ? 0x40 : 0);
+ if (h->mr < 0)
+ h->mr = message_ref++;
+ h->omsg[p++] = h->mr;
+ p += packaddress (h->omsg + p, h->da);
+ h->omsg[p++] = h->pid;
+ h->omsg[p++] = h->dcs;
+ if (h->vp) { /* relative VP */
+ if (h->vp < 720)
+ h->omsg[p++] = (h->vp + 4) / 5 - 1;
+ else if (h->vp < 1440)
+ h->omsg[p++] = (h->vp - 720 + 29) / 30 + 143;
+ else if (h->vp < 43200)
+ h->omsg[p++] = (h->vp + 1439) / 1440 + 166;
+ else if (h->vp < 635040)
+ h->omsg[p++] = (h->vp + 10079) / 10080 + 192;
+ else
+ h->omsg[p++] = 255; /* max */
+ }
+ p += packsms (h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
+ }
+ h->omsg[1] = p - 2;
+ sms_messagetx (h);
+ } else { /* no message */
+ h->omsg[0] = 0x94; /* SMS_REL */
+ h->omsg[1] = 0;
+ sms_messagetx (h);
+ }
+}
+
+static void sms_debug (char *dir, unsigned char *msg)
+{
+ char txt[259 * 3 + 1],
+ *p = txt; /* always long enough */
+ int n = msg[1] + 3,
+ q = 0;
+ while (q < n && q < 30) {
+ sprintf (p, " %02X", msg[q++]);
+ p += 3;
+ }
+ if (q < n)
+ sprintf (p, "...");
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "SMS %s%s\n", dir, txt);
+}
+
+static void sms_messagerx(sms_t * h)
+{
+ sms_debug ("RX", h->imsg);
+ /* testing */
+ switch (h->imsg[0]) {
+ case 0x91: /* SMS_DATA */
+ {
+ unsigned char cause = sms_handleincoming (h);
+ if (!cause) {
+ sms_log (h, 'Y');
+ h->omsg[0] = 0x95; /* SMS_ACK */
+ h->omsg[1] = 0x02;
+ h->omsg[2] = 0x00; /* deliver report */
+ h->omsg[3] = 0x00; /* no parameters */
+ } else { /* NACK */
+ sms_log (h, 'N');
+ h->omsg[0] = 0x96; /* SMS_NACK */
+ h->omsg[1] = 3;
+ h->omsg[2] = 0; /* delivery report */
+ h->omsg[3] = cause; /* cause */
+ h->omsg[4] = 0; /* no parameters */
+ }
+ sms_messagetx (h);
+ }
+ break;
+ case 0x92: /* SMS_ERROR */
+ h->err = 1;
+ sms_messagetx (h); /* send whatever we sent again */
+ break;
+ case 0x93: /* SMS_EST */
+ sms_nextoutgoing (h);
+ break;
+ case 0x94: /* SMS_REL */
+ h->hangup = 1; /* hangup */
+ break;
+ case 0x95: /* SMS_ACK */
+ sms_log (h, 'Y');
+ sms_nextoutgoing (h);
+ break;
+ case 0x96: /* SMS_NACK */
+ h->err = 1;
+ sms_log (h, 'N');
+ sms_nextoutgoing (h);
+ break;
+ default: /* Unknown */
+ h->omsg[0] = 0x92; /* SMS_ERROR */
+ h->omsg[1] = 1;
+ h->omsg[2] = 3; /* unknown message type; */
+ sms_messagetx (h);
+ break;
+ }
+}
+
+static void sms_messagetx(sms_t * h)
+{
+ unsigned char c = 0, p;
+ for (p = 0; p < h->omsg[1] + 2; p++)
+ c += h->omsg[p];
+ h->omsg[h->omsg[1] + 2] = 0 - c;
+ sms_debug ("TX", h->omsg);
+ h->obyte = 1;
+ h->opause = 200;
+ if (h->omsg[0] == 0x93)
+ h->opause = 2400; /* initial message delay 300ms (for BT) */
+ h->obytep = 0;
+ h->obitp = 0;
+ h->osync = 80;
+ h->obyten = h->omsg[1] + 3;
+}
+
+static int sms_generate (struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct ast_frame f = { 0 };
+ unsigned char waste[AST_FRIENDLY_OFFSET];
+#ifdef OUTALAW
+ unsigned char buf[800];
+#else
+ signed short buf[800];
+#endif
+ sms_t *h = data;
+ int i;
+
+ if (len > sizeof (buf)) {
+ ast_log (LOG_WARNING, "Only doing %d bytes (%d bytes requested)\n", (int)(sizeof (buf) / sizeof (signed short)), len);
+ len = sizeof (buf);
+#ifdef OUTALAW
+ samples = len;
+#else
+ samples = len / 2;
+#endif
+ }
+ waste[0] = 0; /* make compiler happy */
+ f.frametype = AST_FRAME_VOICE;
+#ifdef OUTALAW
+ f.subclass = AST_FORMAT_ALAW;
+ f.datalen = samples;
+#else
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = samples * 2;
+#endif
+ f.offset = AST_FRIENDLY_OFFSET;
+ f.mallocd = 0;
+ f.data = buf;
+ f.samples = samples;
+ f.src = "app_sms";
+ /* create a buffer containing the digital sms pattern */
+ for (i = 0; i < samples; i++) {
+#ifdef OUTALAW
+ buf[i] = wavea[0];
+#else
+ buf[i] = wave[0];
+#endif
+ if (h->opause)
+ h->opause--;
+ else if (h->obyten || h->osync) { /* sending data */
+#ifdef OUTALAW
+ buf[i] = wavea[h->ophase];
+#else
+ buf[i] = wave[h->ophase];
+#endif
+ if ((h->ophase += ((h->obyte & 1) ? 13 : 21)) >= 80)
+ h->ophase -= 80;
+ if ((h->ophasep += 12) >= 80) { /* next bit */
+ h->ophasep -= 80;
+ if (h->osync)
+ h->osync--; /* sending sync bits */
+ else {
+ h->obyte >>= 1;
+ h->obitp++;
+ if (h->obitp == 1)
+ h->obyte = 0; /* start bit; */
+ else if (h->obitp == 2)
+ h->obyte = h->omsg[h->obytep];
+ else if (h->obitp == 10) {
+ h->obyte = 1; /* stop bit */
+ h->obitp = 0;
+ h->obytep++;
+ if (h->obytep == h->obyten) {
+ h->obytep = h->obyten = 0; /* sent */
+ h->osync = 10; /* trailing marks */
+ }
+ }
+ }
+ }
+ }
+ }
+ if (ast_write (chan, &f) < 0) {
+ ast_log (LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror (errno));
+ return -1;
+ }
+ return 0;
+}
+
+static void sms_process (sms_t * h, int samples, signed short *data)
+{
+ if (h->obyten || h->osync)
+ return; /* sending */
+ while (samples--) {
+ unsigned long long m0, m1;
+ if (abs (*data) > h->imag)
+ h->imag = abs (*data);
+ else
+ h->imag = h->imag * 7 / 8;
+ if (h->imag > 500) {
+ h->idle = 0;
+ h->ims0 = (h->ims0 * 6 + *data * wave[h->ips0]) / 7;
+ h->imc0 = (h->imc0 * 6 + *data * wave[h->ipc0]) / 7;
+ h->ims1 = (h->ims1 * 6 + *data * wave[h->ips1]) / 7;
+ h->imc1 = (h->imc1 * 6 + *data * wave[h->ipc1]) / 7;
+ m0 = h->ims0 * h->ims0 + h->imc0 * h->imc0;
+ m1 = h->ims1 * h->ims1 + h->imc1 * h->imc1;
+ if ((h->ips0 += 21) >= 80)
+ h->ips0 -= 80;
+ if ((h->ipc0 += 21) >= 80)
+ h->ipc0 -= 80;
+ if ((h->ips1 += 13) >= 80)
+ h->ips1 -= 80;
+ if ((h->ipc1 += 13) >= 80)
+ h->ipc1 -= 80;
+ {
+ char bit;
+ h->ibith <<= 1;
+ if (m1 > m0)
+ h->ibith |= 1;
+ if (h->ibith & 8)
+ h->ibitt--;
+ if (h->ibith & 1)
+ h->ibitt++;
+ bit = ((h->ibitt > 1) ? 1 : 0);
+ if (bit != h->ibitl)
+ h->ibitc = 1;
+ else
+ h->ibitc++;
+ h->ibitl = bit;
+ if (!h->ibitn && h->ibitc == 4 && !bit) {
+ h->ibitn = 1;
+ h->iphasep = 0;
+ }
+ if (bit && h->ibitc == 200) { /* sync, restart message */
+ h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
+ }
+ if (h->ibitn) {
+ h->iphasep += 12;
+ if (h->iphasep >= 80) { /* next bit */
+ h->iphasep -= 80;
+ if (h->ibitn++ == 9) { /* end of byte */
+ if (!bit) /* bad stop bit */
+ h->ierr = 0xFF; /* unknown error */
+ else {
+ if (h->ibytep < sizeof (h->imsg)) {
+ h->imsg[h->ibytep] = h->ibytev;
+ h->ibytec += h->ibytev;
+ h->ibytep++;
+ } else if (h->ibytep == sizeof (h->imsg))
+ h->ierr = 2; /* bad message length */
+ if (h->ibytep > 1 && h->ibytep == 3 + h->imsg[1] && !h->ierr) {
+ if (!h->ibytec)
+ sms_messagerx (h);
+ else
+ h->ierr = 1; /* bad checksum */
+ }
+ }
+ h->ibitn = 0;
+ }
+ h->ibytev = (h->ibytev >> 1) + (bit ? 0x80 : 0);
+ }
+ }
+ }
+ } else { /* lost carrier */
+ if (h->idle++ == 80000) { /* nothing happening */
+ ast_log (LOG_EVENT, "No data, hanging up\n");
+ h->hangup = 1;
+ h->err = 1;
+ }
+ if (h->ierr) { /* error */
+ h->err = 1;
+ h->omsg[0] = 0x92; /* error */
+ h->omsg[1] = 1;
+ h->omsg[2] = h->ierr;
+ sms_messagetx (h); /* send error */
+ }
+ h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
+ }
+ data++;
+ }
+}
+
+static struct ast_generator smsgen = {
+ alloc:sms_alloc,
+ release:sms_release,
+ generate:sms_generate,
+};
+
+static int sms_exec (struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ struct localuser *u;
+ struct ast_frame *f;
+ sms_t h = { 0 };
+
+ LOCAL_USER_ADD(u);
+
+ h.ipc0 = h.ipc1 = 20; /* phase for cosine */
+ h.dcs = 0xF1; /* default */
+ if (!data) {
+ ast_log (LOG_ERROR, "Requires queue name at least\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if (chan->cid.cid_num)
+ ast_copy_string (h.cli, chan->cid.cid_num, sizeof (h.cli));
+
+ {
+ unsigned char *p;
+ unsigned char *d = data,
+ answer = 0;
+ if (!*d || *d == '|') {
+ ast_log (LOG_ERROR, "Requires queue name\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ for (p = d; *p && *p != '|'; p++);
+ if (p - d >= sizeof (h.queue)) {
+ ast_log (LOG_ERROR, "Queue name too long\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ strncpy (h.queue, d, p - d);
+ if (*p == '|')
+ p++;
+ d = p;
+ for (p = h.queue; *p; p++)
+ if (!isalnum (*p))
+ *p = '-'; /* make very safe for filenames */
+ while (*d && *d != '|') {
+ switch (*d) {
+ case 'a': /* we have to send the initial FSK sequence */
+ answer = 1;
+ break;
+ case 's': /* we are acting as a service centre talking to a phone */
+ h.smsc = 1;
+ break;
+ /* the following apply if there is an arg3/4 and apply to the created message file */
+ case 'r':
+ h.srr = 1;
+ break;
+ case 'o':
+ h.dcs |= 4; /* octets */
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': /* set the pid for saved local message */
+ h.pid = 0x40 + (*d & 0xF);
+ break;
+ }
+ d++;
+ }
+ if (*d == '|') {
+ /* submitting a message, not taking call. */
+ /* depricated, use smsq instead */
+ d++;
+ h.scts = time (0);
+ for (p = d; *p && *p != '|'; p++);
+ if (*p)
+ *p++ = 0;
+ if (strlen (d) >= sizeof (h.oa)) {
+ ast_log (LOG_ERROR, "Address too long %s\n", d);
+ return 0;
+ }
+ if (h.smsc) {
+ ast_copy_string (h.oa, d, sizeof (h.oa));
+ } else {
+ ast_copy_string (h.da, d, sizeof (h.da));
+ }
+ if (!h.smsc)
+ ast_copy_string (h.oa, h.cli, sizeof (h.oa));
+ d = p;
+ h.udl = 0;
+ while (*p && h.udl < SMSLEN)
+ h.ud[h.udl++] = utf8decode(&p);
+ if (is7bit (h.dcs) && packsms7 (0, h.udhl, h.udh, h.udl, h.ud) < 0)
+ ast_log (LOG_WARNING, "Invalid 7 bit GSM data\n");
+ if (is8bit (h.dcs) && packsms8 (0, h.udhl, h.udh, h.udl, h.ud) < 0)
+ ast_log (LOG_WARNING, "Invalid 8 bit data\n");
+ if (is16bit (h.dcs) && packsms16 (0, h.udhl, h.udh, h.udl, h.ud) < 0)
+ ast_log (LOG_WARNING, "Invalid 16 bit data\n");
+ h.rx = 0; /* sent message */
+ h.mr = -1;
+ sms_writefile (&h);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ if (answer) {
+ /* set up SMS_EST initial message */
+ h.omsg[0] = 0x93;
+ h.omsg[1] = 0;
+ sms_messagetx (&h);
+ }
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer (chan);
+
+#ifdef OUTALAW
+ res = ast_set_write_format (chan, AST_FORMAT_ALAW);
+#else
+ res = ast_set_write_format (chan, AST_FORMAT_SLINEAR);
+#endif
+ if (res >= 0)
+ res = ast_set_read_format (chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log (LOG_ERROR, "Unable to set to linear mode, giving up\n");
+ LOCAL_USER_REMOVE (u);
+ return -1;
+ }
+
+ if (ast_activate_generator (chan, &smsgen, &h) < 0) {
+ ast_log (LOG_ERROR, "Failed to activate generator on '%s'\n", chan->name);
+ LOCAL_USER_REMOVE (u);
+ return -1;
+ }
+
+ /* Do our thing here */
+ while (ast_waitfor (chan, -1) > -1 && !h.hangup)
+ {
+ f = ast_read (chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_VOICE) {
+ sms_process (&h, f->samples, f->data);
+ }
+
+ ast_frfree (f);
+ }
+
+ sms_log (&h, '?'); /* log incomplete message */
+
+ LOCAL_USER_REMOVE (u);
+ return (h.err);
+}
+
+int unload_module (void)
+{
+ int res;
+
+ res = ast_unregister_application (app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module (void)
+{
+#ifdef OUTALAW
+ {
+ int p;
+ for (p = 0; p < 80; p++)
+ wavea[p] = AST_LIN2A (wave[p]);
+ }
+#endif
+ snprintf (log_file, sizeof (log_file), "%s/sms", ast_config_AST_LOG_DIR);
+ snprintf (spool_dir, sizeof (spool_dir), "%s/sms", ast_config_AST_SPOOL_DIR);
+ return ast_register_application (app, sms_exec, synopsis, descrip);
+}
+
+char *description (void)
+{
+ return tdesc;
+}
+
+int usecount (void)
+{
+ int res;
+ STANDARD_USECOUNT (res);
+ return res;
+}
+
+char *key ()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_softhangup.c b/1.2-netsec/apps/app_softhangup.c
new file mode 100644
index 000000000..804a39259
--- /dev/null
+++ b/1.2-netsec/apps/app_softhangup.c
@@ -0,0 +1,139 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief SoftHangup application
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+
+static char *synopsis = "Soft Hangup Application";
+
+static char *tdesc = "Hangs up the requested channel";
+
+static char *desc = " SoftHangup(Technology/resource|options)\n"
+"Hangs up the requested channel. If there are no channels to hangup,\n"
+"the application will report it.\n"
+"- 'options' may contain the following letter:\n"
+" 'a' : hang up all channels on a specified device instead of a single resource\n";
+
+static char *app = "SoftHangup";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int softhangup_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ struct ast_channel *c=NULL;
+ char *options, *cut, *cdata, *match;
+ char name[AST_CHANNEL_NAME] = "";
+ int all = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SoftHangup requires an argument (Technology/resource)\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ cdata = ast_strdupa(data);
+ match = strsep(&cdata, "|");
+ options = strsep(&cdata, "|");
+ all = options && strchr(options,'a');
+ c = ast_channel_walk_locked(NULL);
+ while (c) {
+ strncpy(name, c->name, sizeof(name)-1);
+ ast_mutex_unlock(&c->lock);
+ /* XXX watch out, i think it is wrong to access c-> after unlocking! */
+ if (all) {
+ /* CAPI is set up like CAPI[foo/bar]/clcnt */
+ if (!strcmp(c->type,"CAPI"))
+ cut = strrchr(name,'/');
+ /* Basically everything else is Foo/Bar-Z */
+ else
+ cut = strchr(name,'-');
+ /* Get rid of what we've cut */
+ if (cut)
+ *cut = 0;
+ }
+ if (!strcasecmp(name, match)) {
+ ast_log(LOG_WARNING, "Soft hanging %s up.\n",c->name);
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ if(!all)
+ break;
+ }
+ c = ast_channel_walk_locked(c);
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, softhangup_exec, synopsis, desc);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_sql_postgres.c b/1.2-netsec/apps/app_sql_postgres.c
new file mode 100644
index 000000000..d9ba1bfb7
--- /dev/null
+++ b/1.2-netsec/apps/app_sql_postgres.c
@@ -0,0 +1,577 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002, Christos Ricudis
+ *
+ * Christos Ricudis <ricudis@itc.auth.gr>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Connect to PostgreSQL
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/lock.h"
+
+#include "libpq-fe.h"
+
+static char *tdesc = "Simple PostgreSQL Interface";
+
+static char *app = "PGSQL";
+
+static char *synopsis = "Do several SQLy things";
+
+static char *descrip =
+"PGSQL(): Do several SQLy things\n"
+"Syntax:\n"
+" PGSQL(Connect var option-string)\n"
+" Connects to a database. Option string contains standard PostgreSQL\n"
+" parameters like host=, dbname=, user=. Connection identifer returned\n"
+" in ${var}\n"
+" PGSQL(Query var ${connection_identifier} query-string)\n"
+" Executes standard SQL query contained in query-string using established\n"
+" connection identified by ${connection_identifier}. Reseult of query is\n"
+" is stored in ${var}.\n"
+" PGSQL(Fetch statusvar ${result_identifier} var1 var2 ... varn)\n"
+" Fetches a single row from a result set contained in ${result_identifier}.\n"
+" Assigns returned fields to ${var1} ... ${varn}. ${statusvar} is set TRUE\n"
+" if additional rows exist in reseult set.\n"
+" PGSQL(Clear ${result_identifier})\n"
+" Frees memory and datastructures associated with result set.\n"
+" PGSQL(Disconnect ${connection_identifier})\n"
+" Disconnects from named connection to PostgreSQL.\n" ;
+
+/*
+
+Syntax of SQL commands :
+
+ Connect var option-string
+
+ Connects to a database using the option-string and stores the
+ connection identifier in ${var}
+
+
+ Query var ${connection_identifier} query-string
+
+ Submits query-string to database backend and stores the result
+ identifier in ${var}
+
+
+ Fetch statusvar ${result_identifier} var1 var2 var3 ... varn
+
+ Fetches a row from the query and stores end-of-table status in
+ ${statusvar} and columns in ${var1}..${varn}
+
+
+ Clear ${result_identifier}
+
+ Clears data structures associated with ${result_identifier}
+
+
+ Disconnect ${connection_identifier}
+
+ Disconnects from named connection
+
+
+EXAMPLES OF USE :
+
+exten => s,2,PGSQL(Connect connid host=localhost user=asterisk dbname=credit)
+exten => s,3,PGSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM})
+exten => s,4,PGSQL(Fetch fetchid ${resultid} datavar1 datavar2)
+exten => s,5,GotoIf(${fetchid}?6:8)
+exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.")
+exten => s,7,Goto(s,4)
+exten => s,8,PGSQL(Clear ${resultid})
+exten => s,9,PGSQL(Disconnect ${connid})
+
+*/
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+#define AST_PGSQL_ID_DUMMY 0
+#define AST_PGSQL_ID_CONNID 1
+#define AST_PGSQL_ID_RESID 2
+#define AST_PGSQL_ID_FETCHID 3
+
+struct ast_PGSQL_id {
+ int identifier_type; /* 0=dummy, 1=connid, 2=resultid */
+ int identifier;
+ void *data;
+ AST_LIST_ENTRY(ast_PGSQL_id) entries;
+} *ast_PGSQL_id;
+
+AST_LIST_HEAD(PGSQLidshead,ast_PGSQL_id) PGSQLidshead;
+
+static void *find_identifier(int identifier,int identifier_type) {
+ struct PGSQLidshead *headp;
+ struct ast_PGSQL_id *i;
+ void *res=NULL;
+ int found=0;
+
+ headp=&PGSQLidshead;
+
+ if (AST_LIST_LOCK(headp)) {
+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
+ } else {
+ AST_LIST_TRAVERSE(headp,i,entries) {
+ if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) {
+ found=1;
+ res=i->data;
+ break;
+ }
+ }
+ if (!found) {
+ ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type);
+ }
+ AST_LIST_UNLOCK(headp);
+ }
+
+ return(res);
+}
+
+static int add_identifier(int identifier_type,void *data) {
+ struct ast_PGSQL_id *i,*j;
+ struct PGSQLidshead *headp;
+ int maxidentifier=0;
+
+ headp=&PGSQLidshead;
+ i=NULL;
+ j=NULL;
+
+ if (AST_LIST_LOCK(headp)) {
+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
+ return(-1);
+ } else {
+ i=malloc(sizeof(struct ast_PGSQL_id));
+ AST_LIST_TRAVERSE(headp,j,entries) {
+ if (j->identifier>maxidentifier) {
+ maxidentifier=j->identifier;
+ }
+ }
+
+ i->identifier=maxidentifier+1;
+ i->identifier_type=identifier_type;
+ i->data=data;
+ AST_LIST_INSERT_HEAD(headp,i,entries);
+ AST_LIST_UNLOCK(headp);
+ }
+ return(i->identifier);
+}
+
+static int del_identifier(int identifier,int identifier_type) {
+ struct ast_PGSQL_id *i;
+ struct PGSQLidshead *headp;
+ int found=0;
+
+ headp=&PGSQLidshead;
+
+ if (AST_LIST_LOCK(headp)) {
+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
+ } else {
+ AST_LIST_TRAVERSE(headp,i,entries) {
+ if ((i->identifier==identifier) &&
+ (i->identifier_type==identifier_type)) {
+ AST_LIST_REMOVE(headp,i,entries);
+ free(i);
+ found=1;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(headp);
+ }
+
+ if (found==0) {
+ ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type);
+ return(-1);
+ } else {
+ return(0);
+ }
+}
+
+static int aPGSQL_connect(struct ast_channel *chan, void *data) {
+
+ char *s1;
+ char s[100] = "";
+ char *optionstring;
+ char *var;
+ int l;
+ int res;
+ PGconn *karoto;
+ int id;
+ char *stringp=NULL;
+
+
+ res=0;
+ l=strlen(data)+2;
+ s1=malloc(l);
+ strncpy(s1, data, l -1);
+ stringp=s1;
+ strsep(&stringp," "); /* eat the first token, we already know it :P */
+ var=strsep(&stringp," ");
+ optionstring=strsep(&stringp,"\n");
+
+ karoto = PQconnectdb(optionstring);
+ if (PQstatus(karoto) == CONNECTION_BAD) {
+ ast_log(LOG_WARNING,"Connection to database using '%s' failed. postgress reports : %s\n", optionstring,
+ PQerrorMessage(karoto));
+ res=-1;
+ } else {
+ ast_log(LOG_WARNING,"adding identifier\n");
+ id=add_identifier(AST_PGSQL_ID_CONNID,karoto);
+ snprintf(s, sizeof(s), "%d", id);
+ pbx_builtin_setvar_helper(chan,var,s);
+ }
+
+ free(s1);
+ return res;
+}
+
+static int aPGSQL_query(struct ast_channel *chan, void *data) {
+
+
+ char *s1,*s2,*s3,*s4;
+ char s[100] = "";
+ char *querystring;
+ char *var;
+ int l;
+ int res,nres;
+ PGconn *karoto;
+ PGresult *PGSQLres;
+ int id,id1;
+ char *stringp=NULL;
+
+
+ res=0;
+ l=strlen(data)+2;
+ s1=malloc(l);
+ s2=malloc(l);
+ strncpy(s1, data, l - 1);
+ stringp=s1;
+ strsep(&stringp," "); /* eat the first token, we already know it :P */
+ s3=strsep(&stringp," ");
+ while (1) { /* ugly trick to make branches with break; */
+ var=s3;
+ s4=strsep(&stringp," ");
+ id=atoi(s4);
+ querystring=strsep(&stringp,"\n");
+ if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) {
+ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_query\n",id);
+ res=-1;
+ break;
+ }
+ PGSQLres=PQexec(karoto,querystring);
+ if (PGSQLres==NULL) {
+ ast_log(LOG_WARNING,"aPGSQL_query: Connection Error (connection identifier = %d, error message : %s)\n",id,PQerrorMessage(karoto));
+ res=-1;
+ break;
+ }
+ if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
+ PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
+ PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
+ ast_log(LOG_WARNING,"aPGSQL_query: Query Error (connection identifier : %d, error message : %s)\n",id,PQcmdStatus(PGSQLres));
+ res=-1;
+ break;
+ }
+ nres=PQnfields(PGSQLres);
+ id1=add_identifier(AST_PGSQL_ID_RESID,PGSQLres);
+ snprintf(s, sizeof(s), "%d", id1);
+ pbx_builtin_setvar_helper(chan,var,s);
+ break;
+ }
+
+ free(s1);
+ free(s2);
+
+ return(res);
+}
+
+
+static int aPGSQL_fetch(struct ast_channel *chan, void *data) {
+
+ char *s1,*s2,*fetchid_var,*s4,*s5,*s6,*s7;
+ char s[100];
+ char *var;
+ int l;
+ int res;
+ PGresult *PGSQLres;
+ int id,id1,i,j,fnd;
+ int *lalares=NULL;
+ int nres;
+ struct ast_var_t *variables;
+ struct varshead *headp;
+ char *stringp=NULL;
+
+ headp=&chan->varshead;
+
+ res=0;
+ l=strlen(data)+2;
+ s7=NULL;
+ s1=malloc(l);
+ s2=malloc(l);
+ strncpy(s1, data, l - 1);
+ stringp=s1;
+ strsep(&stringp," "); /* eat the first token, we already know it :P */
+ fetchid_var=strsep(&stringp," ");
+ while (1) { /* ugly trick to make branches with break; */
+ var=fetchid_var; /* fetchid */
+ fnd=0;
+
+ AST_LIST_TRAVERSE(headp,variables,entries) {
+ if (strncasecmp(ast_var_name(variables),fetchid_var,strlen(fetchid_var))==0) {
+ s7=ast_var_value(variables);
+ fnd=1;
+ break;
+ }
+ }
+
+ if (fnd==0) {
+ s7="0";
+ pbx_builtin_setvar_helper(chan,fetchid_var,s7);
+ }
+
+ s4=strsep(&stringp," ");
+ id=atoi(s4); /* resultid */
+ if ((PGSQLres=find_identifier(id,AST_PGSQL_ID_RESID))==NULL) {
+ ast_log(LOG_WARNING,"Invalid result identifier %d passed in aPGSQL_fetch\n",id);
+ res=-1;
+ break;
+ }
+ id=atoi(s7); /*fetchid */
+ if ((lalares=find_identifier(id,AST_PGSQL_ID_FETCHID))==NULL) {
+ i=0; /* fetching the very first row */
+ } else {
+ i=*lalares;
+ free(lalares);
+ del_identifier(id,AST_PGSQL_ID_FETCHID); /* will re-add it a bit later */
+ }
+
+ if (i<PQntuples(PGSQLres)) {
+ nres=PQnfields(PGSQLres);
+ ast_log(LOG_WARNING,"ast_PGSQL_fetch : nres = %d i = %d ;\n",nres,i);
+ for (j=0;j<nres;j++) {
+ s5=strsep(&stringp," ");
+ if (s5==NULL) {
+ ast_log(LOG_WARNING,"ast_PGSQL_fetch : More tuples (%d) than variables (%d)\n",nres,j);
+ break;
+ }
+ s6=PQgetvalue(PGSQLres,i,j);
+ if (s6==NULL) {
+ ast_log(LOG_WARNING,"PWgetvalue(res,%d,%d) returned NULL in ast_PGSQL_fetch\n",i,j);
+ break;
+ }
+ ast_log(LOG_WARNING,"===setting variable '%s' to '%s'\n",s5,s6);
+ pbx_builtin_setvar_helper(chan,s5,s6);
+ }
+ lalares=malloc(sizeof(int));
+ *lalares = ++i; /* advance to the next row */
+ id1 = add_identifier(AST_PGSQL_ID_FETCHID,lalares);
+ } else {
+ ast_log(LOG_WARNING,"ast_PGSQL_fetch : EOF\n");
+ id1 = 0; /* no more rows */
+ }
+ snprintf(s, sizeof(s), "%d", id1);
+ ast_log(LOG_WARNING,"Setting var '%s' to value '%s'\n",fetchid_var,s);
+ pbx_builtin_setvar_helper(chan,fetchid_var,s);
+ break;
+ }
+
+ free(s1);
+ free(s2);
+ return(res);
+}
+
+static int aPGSQL_reset(struct ast_channel *chan, void *data) {
+
+ char *s1,*s3;
+ int l;
+ PGconn *karoto;
+ int id;
+ char *stringp=NULL;
+
+
+ l=strlen(data)+2;
+ s1=malloc(l);
+ strncpy(s1, data, l - 1);
+ stringp=s1;
+ strsep(&stringp," "); /* eat the first token, we already know it :P */
+ s3=strsep(&stringp," ");
+ id=atoi(s3);
+ if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) {
+ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_reset\n",id);
+ } else {
+ PQreset(karoto);
+ }
+ free(s1);
+ return(0);
+
+}
+
+static int aPGSQL_clear(struct ast_channel *chan, void *data) {
+
+ char *s1,*s3;
+ int l;
+ PGresult *karoto;
+ int id;
+ char *stringp=NULL;
+
+
+ l=strlen(data)+2;
+ s1=malloc(l);
+ strncpy(s1, data, l - 1);
+ stringp=s1;
+ strsep(&stringp," "); /* eat the first token, we already know it :P */
+ s3=strsep(&stringp," ");
+ id=atoi(s3);
+ if ((karoto=find_identifier(id,AST_PGSQL_ID_RESID))==NULL) {
+ ast_log(LOG_WARNING,"Invalid result identifier %d passed in aPGSQL_clear\n",id);
+ } else {
+ PQclear(karoto);
+ del_identifier(id,AST_PGSQL_ID_RESID);
+ }
+ free(s1);
+ return(0);
+
+}
+
+
+
+
+static int aPGSQL_disconnect(struct ast_channel *chan, void *data) {
+
+ char *s1,*s3;
+ int l;
+ PGconn *karoto;
+ int id;
+ char *stringp=NULL;
+
+
+ l=strlen(data)+2;
+ s1=malloc(l);
+ strncpy(s1, data, l - 1);
+ stringp=s1;
+ strsep(&stringp," "); /* eat the first token, we already know it :P */
+ s3=strsep(&stringp," ");
+ id=atoi(s3);
+ if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) {
+ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_disconnect\n",id);
+ } else {
+ PQfinish(karoto);
+ del_identifier(id,AST_PGSQL_ID_CONNID);
+ }
+ free(s1);
+ return(0);
+
+}
+
+static int aPGSQL_debug(struct ast_channel *chan, void *data) {
+ ast_log(LOG_WARNING,"Debug : %s\n",(char *)data);
+ return(0);
+}
+
+static int PGSQL_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int result;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "APP_PGSQL requires an argument (see manual)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ result=0;
+
+ if (strncasecmp("connect",data,strlen("connect"))==0) {
+ result=(aPGSQL_connect(chan,data));
+ } else if (strncasecmp("query",data,strlen("query"))==0) {
+ result=(aPGSQL_query(chan,data));
+ } else if (strncasecmp("fetch",data,strlen("fetch"))==0) {
+ result=(aPGSQL_fetch(chan,data));
+ } else if (strncasecmp("reset",data,strlen("reset"))==0) {
+ result=(aPGSQL_reset(chan,data));
+ } else if (strncasecmp("clear",data,strlen("clear"))==0) {
+ result=(aPGSQL_clear(chan,data));
+ } else if (strncasecmp("debug",data,strlen("debug"))==0) {
+ result=(aPGSQL_debug(chan,data));
+ } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) {
+ result=(aPGSQL_disconnect(chan,data));
+ } else {
+ ast_log(LOG_WARNING, "Unknown APP_PGSQL argument : %s\n",(char *)data);
+ result=-1;
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return result;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ struct PGSQLidshead *headp;
+
+ headp=&PGSQLidshead;
+
+ AST_LIST_HEAD_INIT(headp);
+ return ast_register_application(app, PGSQL_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_stack.c b/1.2-netsec/apps/app_stack.c
new file mode 100644
index 000000000..e3c33d2e5
--- /dev/null
+++ b/1.2-netsec/apps/app_stack.c
@@ -0,0 +1,193 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004-2005 Tilghman Lesher <app_stack_v002@the-tilghman.com>.
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stack applications Gosub, Return, etc.
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+
+#define STACKVAR "~GOSUB~STACK~"
+
+static const char *tdesc = "Stack Routines";
+
+static const char *app_gosub = "Gosub";
+static const char *app_gosubif = "GosubIf";
+static const char *app_return = "Return";
+static const char *app_pop = "StackPop";
+
+static const char *gosub_synopsis = "Jump to label, saving return address";
+static const char *gosubif_synopsis = "Jump to label, saving return address";
+static const char *return_synopsis = "Return from gosub routine";
+static const char *pop_synopsis = "Remove one address from gosub stack";
+
+static const char *gosub_descrip =
+"Gosub([[context|]exten|]priority)\n"
+" Jumps to the label specified, saving the return address.\n";
+static const char *gosubif_descrip =
+"GosubIf(condition?labeliftrue[:labeliffalse])\n"
+" If the condition is true, then jump to labeliftrue. If false, jumps to\n"
+"labeliffalse, if specified. In either case, a jump saves the return point\n"
+"in the dialplan, to be returned to with a Return.\n";
+static const char *return_descrip =
+"Return()\n"
+" Jumps to the last label on the stack, removing it.\n";
+static const char *pop_descrip =
+"StackPop()\n"
+" Removes last label on the stack, discarding it.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int pop_exec(struct ast_channel *chan, void *data)
+{
+ pbx_builtin_setvar_helper(chan, STACKVAR, NULL);
+
+ return 0;
+}
+
+static int return_exec(struct ast_channel *chan, void *data)
+{
+ char *label = pbx_builtin_getvar_helper(chan, STACKVAR);
+
+ if (ast_strlen_zero(label)) {
+ ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
+ return -1;
+ } else if (ast_parseable_goto(chan, label)) {
+ ast_log(LOG_WARNING, "No next statement after Gosub?\n");
+ return -1;
+ }
+
+ pbx_builtin_setvar_helper(chan, STACKVAR, NULL);
+ return 0;
+}
+
+static int gosub_exec(struct ast_channel *chan, void *data)
+{
+ char newlabel[AST_MAX_EXTENSION * 2 + 3 + 11];
+ struct localuser *u;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: %s([[context|]exten|]priority)\n", app_gosub, app_gosub);
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+ snprintf(newlabel, sizeof(newlabel), "%s|%s|%d", chan->context, chan->exten, chan->priority + 1);
+
+ if (ast_parseable_goto(chan, data)) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ pbx_builtin_pushvar_helper(chan, STACKVAR, newlabel);
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+static int gosubif_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *condition="", *label1, *label2, *args;
+ int res=0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "GosubIf requires an argument\n");
+ return 0;
+ }
+
+ args = ast_strdupa((char *)data);
+ if (!args) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ condition = strsep(&args, "?");
+ label1 = strsep(&args, ":");
+ label2 = args;
+
+ if (ast_true(condition)) {
+ if (label1) {
+ res = gosub_exec(chan, label1);
+ }
+ } else if (label2) {
+ res = gosub_exec(chan, label2);
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ ast_unregister_application(app_return);
+ ast_unregister_application(app_pop);
+ ast_unregister_application(app_gosubif);
+ ast_unregister_application(app_gosub);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return 0;
+}
+
+int load_module(void)
+{
+ ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
+ ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
+ ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
+ ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
+
+ return 0;
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_system.c b/1.2-netsec/apps/app_system.c
new file mode 100644
index 000000000..63e9baacb
--- /dev/null
+++ b/1.2-netsec/apps/app_system.c
@@ -0,0 +1,176 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Execute arbitrary system commands
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Generic System() application";
+
+static char *app = "System";
+
+static char *app2 = "TrySystem";
+
+static char *synopsis = "Execute a system command";
+
+static char *synopsis2 = "Try executing a system command";
+
+static char *chanvar = "SYSTEMSTATUS";
+
+static char *descrip =
+" System(command): Executes a command by using system(). If the command\n"
+"fails, the console should report a fallthrough. \n"
+"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
+" FAILURE Could not execute the specified command\n"
+" SUCCESS Specified command successfully executed\n"
+"\n"
+"Old behaviour:\n"
+"If the command itself executes but is in error, and if there exists\n"
+"a priority n + 101, where 'n' is the priority of the current instance,\n"
+"then the channel will be setup to continue at that priority level.\n"
+"Note that this jump functionality has been deprecated and will only occur\n"
+"if the global priority jumping option is enabled in extensions.conf.\n";
+
+static char *descrip2 =
+" TrySystem(command): Executes a command by using system().\n"
+"on any situation.\n"
+"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
+" FAILURE Could not execute the specified command\n"
+" SUCCESS Specified command successfully executed\n"
+" APPERROR Specified command successfully executed, but returned error code\n"
+"\n"
+"Old behaviour:\nIf the command itself executes but is in error, and if\n"
+"there exists a priority n + 101, where 'n' is the priority of the current\n"
+"instance, then the channel will be setup to continue at that\n"
+"priority level. Otherwise, System will terminate.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int system_exec_helper(struct ast_channel *chan, void *data, int failmode)
+{
+ int res=0;
+ struct localuser *u;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "System requires an argument(command)\n");
+ pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
+ return failmode;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* Do our thing here */
+ res = ast_safe_system((char *)data);
+ if ((res < 0) && (errno != ECHILD)) {
+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
+ pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
+ res = failmode;
+ } else if (res == 127) {
+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
+ pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
+ res = failmode;
+ } else {
+ if (res < 0)
+ res = 0;
+ if (option_priority_jumping && res)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+
+ if (res != 0)
+ pbx_builtin_setvar_helper(chan, chanvar, "APPERROR");
+ else
+ pbx_builtin_setvar_helper(chan, chanvar, "SUCCESS");
+ res = 0;
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int system_exec(struct ast_channel *chan, void *data)
+{
+ return system_exec_helper(chan, data, -1);
+}
+
+static int trysystem_exec(struct ast_channel *chan, void *data)
+{
+ return system_exec_helper(chan, data, 0);
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+ res |= ast_unregister_application(app2);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app2, trysystem_exec, synopsis2, descrip2);
+ res |= ast_register_application(app, system_exec, synopsis, descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_talkdetect.c b/1.2-netsec/apps/app_talkdetect.c
new file mode 100644
index 000000000..11efe7068
--- /dev/null
+++ b/1.2-netsec/apps/app_talkdetect.c
@@ -0,0 +1,250 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Playback a file with audio detect
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/dsp.h"
+
+static char *tdesc = "Playback with Talk Detection";
+
+static char *app = "BackgroundDetect";
+
+static char *synopsis = "Background a file with talk detect";
+
+static char *descrip =
+" BackgroundDetect(filename[|sil[|min|[max]]]): Plays back a given\n"
+"filename, waiting for interruption from a given digit (the digit must\n"
+"start the beginning of a valid extension, or it will be ignored).\n"
+"During the playback of the file, audio is monitored in the receive\n"
+"direction, and if a period of non-silence which is greater than 'min' ms\n"
+"yet less than 'max' ms is followed by silence for at least 'sil' ms then\n"
+"the audio playback is aborted and processing jumps to the 'talk' extension\n"
+"if available. If unspecified, sil, min, and max default to 1000, 100, and\n"
+"infinity respectively.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int background_detect_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *tmp;
+ char *options;
+ char *stringp;
+ struct ast_frame *fr;
+ int notsilent=0;
+ struct timeval start = { 0, 0};
+ int sil = 1000;
+ int min = 100;
+ int max = -1;
+ int x;
+ int origrformat=0;
+ struct ast_dsp *dsp;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ tmp = ast_strdupa(data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ stringp=tmp;
+ strsep(&stringp, "|");
+ options = strsep(&stringp, "|");
+ if (options) {
+ if ((sscanf(options, "%d", &x) == 1) && (x > 0))
+ sil = x;
+ options = strsep(&stringp, "|");
+ if (options) {
+ if ((sscanf(options, "%d", &x) == 1) && (x > 0))
+ min = x;
+ options = strsep(&stringp, "|");
+ if (options) {
+ if ((sscanf(options, "%d", &x) == 1) && (x > 0))
+ max = x;
+ }
+ }
+ }
+ ast_log(LOG_DEBUG, "Preparing detect of '%s', sil=%d,min=%d,max=%d\n",
+ tmp, sil, min, max);
+ if (chan->_state != AST_STATE_UP) {
+ /* Otherwise answer unless we're supposed to send this while on-hook */
+ res = ast_answer(chan);
+ }
+ if (!res) {
+ origrformat = chan->readformat;
+ if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)))
+ ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
+ }
+ if (!(dsp = ast_dsp_new())) {
+ ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
+ res = -1;
+ }
+ if (!res) {
+ ast_stopstream(chan);
+ res = ast_streamfile(chan, tmp, chan->language);
+ if (!res) {
+ while(chan->stream) {
+ res = ast_sched_wait(chan->sched);
+ if ((res < 0) && !chan->timingfunc) {
+ res = 0;
+ break;
+ }
+ if (res < 0)
+ res = 1000;
+ res = ast_waitfor(chan, res);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
+ break;
+ } else if (res > 0) {
+ fr = ast_read(chan);
+ if (!fr) {
+ res = -1;
+ break;
+ } else if (fr->frametype == AST_FRAME_DTMF) {
+ char t[2];
+ t[0] = fr->subclass;
+ t[1] = '\0';
+ if (ast_canmatch_extension(chan, chan->context, t, 1, chan->cid.cid_num)) {
+ /* They entered a valid extension, or might be anyhow */
+ res = fr->subclass;
+ ast_frfree(fr);
+ break;
+ }
+ } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR)) {
+ int totalsilence;
+ int ms;
+ res = ast_dsp_silence(dsp, fr, &totalsilence);
+ if (res && (totalsilence > sil)) {
+ /* We've been quiet a little while */
+ if (notsilent) {
+ /* We had heard some talking */
+ ms = ast_tvdiff_ms(ast_tvnow(), start);
+ ms -= sil;
+ if (ms < 0)
+ ms = 0;
+ if ((ms > min) && ((max < 0) || (ms < max))) {
+ char ms_str[10];
+ ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms);
+
+ /* Save detected talk time (in milliseconds) */
+ sprintf(ms_str, "%d", ms );
+ pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
+
+ ast_goto_if_exists(chan, chan->context, "talk", 1);
+ res = 0;
+ ast_frfree(fr);
+ break;
+ } else
+ ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms);
+ notsilent = 0;
+ }
+ } else {
+ if (!notsilent) {
+ /* Heard some audio, mark the begining of the token */
+ start = ast_tvnow();
+ ast_log(LOG_DEBUG, "Start of voice token!\n");
+ notsilent = 1;
+ }
+ }
+
+ }
+ ast_frfree(fr);
+ }
+ ast_sched_runq(chan->sched);
+ }
+ ast_stopstream(chan);
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
+ res = 0;
+ }
+ }
+ if (res > -1) {
+ if (origrformat && ast_set_read_format(chan, origrformat)) {
+ ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
+ chan->name, ast_getformatname(origrformat));
+ }
+ }
+ if (dsp)
+ ast_dsp_free(dsp);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, background_detect_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_test.c b/1.2-netsec/apps/app_test.c
new file mode 100644
index 000000000..dba8bcafb
--- /dev/null
+++ b/1.2-netsec/apps/app_test.c
@@ -0,0 +1,529 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Russell Bryant <russelb@clemson.edu>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Applications to test connection and produce report in text file
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char *tdesc = "Interface Test Application";
+
+static char *tests_descrip =
+ "TestServer(): Perform test server function and write call report.\n"
+ "Results stored in /var/log/asterisk/testreports/<testid>-server.txt";
+static char *tests_app = "TestServer";
+static char *tests_synopsis = "Execute Interface Test Server";
+
+static char *testc_descrip =
+ "TestClient(testid): Executes test client with given testid.\n"
+ "Results stored in /var/log/asterisk/testreports/<testid>-client.txt";
+
+static char *testc_app = "TestClient";
+static char *testc_synopsis = "Execute Interface Test Client";
+
+static int measurenoise(struct ast_channel *chan, int ms, char *who)
+{
+ int res=0;
+ int mssofar;
+ int noise=0;
+ int samples=0;
+ int x;
+ short *foo;
+ struct timeval start;
+ struct ast_frame *f;
+ int rformat;
+ rformat = chan->readformat;
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_NOTICE, "Unable to set to linear mode!\n");
+ return -1;
+ }
+ start = ast_tvnow();
+ for(;;) {
+ mssofar = ast_tvdiff_ms(ast_tvnow(), start);
+ if (mssofar > ms)
+ break;
+ res = ast_waitfor(chan, ms - mssofar);
+ if (res < 1)
+ break;
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
+ foo = (short *)f->data;
+ for (x=0;x<f->samples;x++) {
+ noise += abs(foo[x]);
+ samples++;
+ }
+ }
+ }
+
+ if (rformat) {
+ if (ast_set_read_format(chan, rformat)) {
+ ast_log(LOG_NOTICE, "Unable to restore original format!\n");
+ return -1;
+ }
+ }
+ if (res < 0)
+ return res;
+ if (!samples) {
+ ast_log(LOG_NOTICE, "No samples were received from the other side!\n");
+ return -1;
+ }
+ ast_log(LOG_DEBUG, "%s: Noise: %d, samples: %d, avg: %d\n", who, noise, samples, noise / samples);
+ return (noise / samples);
+}
+
+static int sendnoise(struct ast_channel *chan, int ms)
+{
+ int res;
+ res = ast_tonepair_start(chan, 1537, 2195, ms, 8192);
+ if (!res) {
+ res = ast_waitfordigit(chan, ms);
+ ast_tonepair_stop(chan);
+ }
+ return res;
+}
+
+static int testclient_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int res = 0;
+ char *testid=data;
+ char fn[80];
+ char serverver[80];
+ FILE *f;
+
+ /* Check for test id */
+ if (ast_strlen_zero(testid)) {
+ ast_log(LOG_WARNING, "TestClient requires an argument - the test id\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ /* Wait a few just to be sure things get started */
+ res = ast_safe_sleep(chan, 3000);
+ /* Transmit client version */
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, "8378*1#", 0);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Transmit client version\n");
+
+ /* Read server version */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Read server version\n");
+ if (!res)
+ res = ast_app_getdata(chan, NULL, serverver, sizeof(serverver) - 1, 0);
+ if (res > 0)
+ res = 0;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "server version: %s\n", serverver);
+
+ if (res > 0)
+ res = 0;
+
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ /* Send test id */
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, testid, 0);
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, "#", 0);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "send test identifier: %s\n", testid);
+
+ if ((res >=0) && (!ast_strlen_zero(testid))) {
+ /* Make the directory to hold the test results in case it's not there */
+ snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
+ mkdir(fn, 0777);
+ snprintf(fn, sizeof(fn), "%s/testresults/%s-client.txt", ast_config_AST_LOG_DIR, testid);
+ if ((f = fopen(fn, "w+"))) {
+ setlinebuf(f);
+ fprintf(f, "CLIENTCHAN: %s\n", chan->name);
+ fprintf(f, "CLIENTTEST ID: %s\n", testid);
+ fprintf(f, "ANSWER: PASS\n");
+ res = 0;
+
+ if (!res) {
+ /* Step 1: Wait for "1" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 2. Wait DTMF 1\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 1: %s\n", (res != '1') ? "FAIL" : "PASS");
+ if (res == '1')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ if (!res) {
+ /* Step 2: Send "2" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 2. Send DTMF 2\n");
+ res = ast_dtmf_stream(chan, NULL, "2", 0);
+ fprintf(f, "SEND DTMF 2: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 3: Wait one second */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 3. Wait one second\n");
+ res = ast_safe_sleep(chan, 1000);
+ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 4: Measure noise */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 4. Measure noise\n");
+ res = measurenoise(chan, 5000, "TestClient");
+ fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 5: Wait for "4" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 5. Wait DTMF 4\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 4: %s\n", (res != '4') ? "FAIL" : "PASS");
+ if (res == '4')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 6: Transmit tone noise */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 6. Transmit tone\n");
+ res = sendnoise(chan, 6000);
+ fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
+ }
+ if (!res || (res == '5')) {
+ /* Step 7: Wait for "5" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 7. Wait DTMF 5\n");
+ if (!res)
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 5: %s\n", (res != '5') ? "FAIL" : "PASS");
+ if (res == '5')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 8: Wait one second */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 8. Wait one second\n");
+ res = ast_safe_sleep(chan, 1000);
+ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 9: Measure noise */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 6. Measure tone\n");
+ res = measurenoise(chan, 4000, "TestClient");
+ fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 10: Send "7" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 7. Send DTMF 7\n");
+ res = ast_dtmf_stream(chan, NULL, "7", 0);
+ fprintf(f, "SEND DTMF 7: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res =0;
+ }
+ if (!res) {
+ /* Step 11: Wait for "8" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestClient: 11. Wait DTMF 8\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 8: %s\n", (res != '8') ? "FAIL" : "PASS");
+ if (res == '8')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (option_debug && !res ) {
+ /* Step 12: Hangup! */
+ ast_log(LOG_DEBUG, "TestClient: 12. Hangup\n");
+ }
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "-- TEST COMPLETE--\n");
+ fprintf(f, "-- END TEST--\n");
+ fclose(f);
+ res = -1;
+ } else
+ res = -1;
+ } else {
+ ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
+ res = -1;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static int testserver_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ int res = 0;
+ char testid[80]="";
+ char fn[80];
+ FILE *f;
+ LOCAL_USER_ADD(u);
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ /* Read version */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Read client version\n");
+ if (!res)
+ res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
+ if (res > 0)
+ res = 0;
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "client version: %s\n", testid);
+ ast_log(LOG_DEBUG, "Transmit server version\n");
+ }
+ res = ast_safe_sleep(chan, 1000);
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, "8378*1#", 0);
+ if (res > 0)
+ res = 0;
+
+ if (!res)
+ res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "read test identifier: %s\n", testid);
+ /* Check for sneakyness */
+ if (strchr(testid, '/'))
+ res = -1;
+ if ((res >=0) && (!ast_strlen_zero(testid))) {
+ /* Got a Test ID! Whoo hoo! */
+ /* Make the directory to hold the test results in case it's not there */
+ snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
+ mkdir(fn, 0777);
+ snprintf(fn, sizeof(fn), "%s/testresults/%s-server.txt", ast_config_AST_LOG_DIR, testid);
+ if ((f = fopen(fn, "w+"))) {
+ setlinebuf(f);
+ fprintf(f, "SERVERCHAN: %s\n", chan->name);
+ fprintf(f, "SERVERTEST ID: %s\n", testid);
+ fprintf(f, "ANSWER: PASS\n");
+ ast_log(LOG_DEBUG, "Processing Test ID '%s'\n", testid);
+ res = ast_safe_sleep(chan, 1000);
+ if (!res) {
+ /* Step 1: Send "1" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 1. Send DTMF 1\n");
+ res = ast_dtmf_stream(chan, NULL, "1", 0);
+ fprintf(f, "SEND DTMF 1: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 2: Wait for "2" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 2. Wait DTMF 2\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 2: %s\n", (res != '2') ? "FAIL" : "PASS");
+ if (res == '2')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 3: Measure noise */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 3. Measure noise\n");
+ res = measurenoise(chan, 6000, "TestServer");
+ fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 4: Send "4" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 4. Send DTMF 4\n");
+ res = ast_dtmf_stream(chan, NULL, "4", 0);
+ fprintf(f, "SEND DTMF 4: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 5: Wait one second */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 5. Wait one second\n");
+ res = ast_safe_sleep(chan, 1000);
+ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 6: Measure noise */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 6. Measure tone\n");
+ res = measurenoise(chan, 4000, "TestServer");
+ fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 7: Send "5" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 7. Send DTMF 5\n");
+ res = ast_dtmf_stream(chan, NULL, "5", 0);
+ fprintf(f, "SEND DTMF 5: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 8: Transmit tone noise */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 8. Transmit tone\n");
+ res = sendnoise(chan, 6000);
+ fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
+ }
+
+ if (!res || (res == '7')) {
+ /* Step 9: Wait for "7" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 9. Wait DTMF 7\n");
+ if (!res)
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 7: %s\n", (res != '7') ? "FAIL" : "PASS");
+ if (res == '7')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ if (!res) {
+ /* Step 10: Send "8" */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 10. Send DTMF 8\n");
+ res = ast_dtmf_stream(chan, NULL, "8", 0);
+ fprintf(f, "SEND DTMF 8: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 11: Wait for hangup to arrive! */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "TestServer: 11. Waiting for hangup\n");
+ res = ast_safe_sleep(chan, 10000);
+ fprintf(f, "WAIT HANGUP: %s\n", (res < 0) ? "PASS" : "FAIL");
+ }
+
+ ast_log(LOG_NOTICE, "-- TEST COMPLETE--\n");
+ fprintf(f, "-- END TEST--\n");
+ fclose(f);
+ res = -1;
+ } else
+ res = -1;
+ } else {
+ ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
+ res = -1;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(testc_app);
+ res |= ast_unregister_application(tests_app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(testc_app, testclient_exec, testc_synopsis, testc_descrip);
+ res |= ast_register_application(tests_app, testserver_exec, tests_synopsis, tests_descrip);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key(void)
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_transfer.c b/1.2-netsec/apps/app_transfer.c
new file mode 100644
index 000000000..aaff52be0
--- /dev/null
+++ b/1.2-netsec/apps/app_transfer.c
@@ -0,0 +1,182 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Transfer a caller
+ *
+ * Requires transfer support from channel driver
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static const char *tdesc = "Transfer";
+
+static const char *app = "Transfer";
+
+static const char *synopsis = "Transfer caller to remote extension";
+
+static const char *descrip =
+" Transfer([Tech/]dest[|options]): Requests the remote caller be transferred\n"
+"to a given destination. If TECH (SIP, IAX2, LOCAL etc) is used, only\n"
+"an incoming call with the same channel technology will be transfered.\n"
+"Note that for SIP, if you transfer before call is setup, a 302 redirect\n"
+"SIP message will be returned to the caller.\n"
+"\nThe result of the application will be reported in the TRANSFERSTATUS\n"
+"channel variable:\n"
+" SUCCESS Transfer succeeded\n"
+" FAILURE Transfer failed\n"
+" UNSUPPORTED Transfer unsupported by channel driver\n"
+"The option string many contain the following character:\n"
+"'j' -- jump to n+101 priority if the channel transfer attempt\n"
+" fails\n";
+
+static int transfer_exec(struct ast_channel *chan, void *data)
+{
+ int res;
+ int len;
+ struct localuser *u;
+ char *slash;
+ char *tech = NULL;
+ char *dest = NULL;
+ char *status;
+ char *parse;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(dest);
+ AST_APP_ARG(options);
+ );
+
+ LOCAL_USER_ADD(u);
+
+ if (ast_strlen_zero((char *)data)) {
+ ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination[|options])\n");
+ LOCAL_USER_REMOVE(u);
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+ return 0;
+ } else {
+ parse = ast_strdupa(data);
+ if (!parse) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ dest = args.dest;
+
+ if ((slash = strchr(dest, '/')) && (len = (slash - dest))) {
+ tech = dest;
+ dest = slash + 1;
+ /* Allow execution only if the Tech/destination agrees with the type of the channel */
+ if (strncasecmp(chan->type, tech, len)) {
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ }
+
+ /* Check if the channel supports transfer before we try it */
+ if (!chan->tech->transfer) {
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "UNSUPPORTED");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+
+ res = ast_transfer(chan, dest);
+
+ if (res < 0) {
+ status = "FAILURE";
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ res = 0;
+ } else {
+ status = "SUCCESS";
+ res = 0;
+ }
+
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", status);
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, transfer_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return (char *) tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+
+ STANDARD_USECOUNT(res);
+
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_txtcidname.c b/1.2-netsec/apps/app_txtcidname.c
new file mode 100644
index 000000000..b6e365b54
--- /dev/null
+++ b/1.2-netsec/apps/app_txtcidname.c
@@ -0,0 +1,164 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Caller*id name lookup - Look up the caller's name via DNS
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/module.h"
+#include "asterisk/enum.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static char *tdesc = "TXTCIDName";
+
+static char *app = "TXTCIDName";
+
+static char *synopsis = "Lookup caller name from TXT record";
+
+static char *descrip =
+" TXTCIDName(<CallerIDNumber>[|options]): Looks up a Caller Name via DNS and sets\n"
+"the variable 'TXTCIDNAME'. TXTCIDName will either be blank\n"
+"or return the value found in the TXT record in DNS.\n"
+"The option string may contain the following character:\n"
+"'j' -- jump to n+101 priority if the lookup fails\n"
+"This application sets the following channel variable upon completion:\n"
+" TXTCIDNAMESTATUS The status of the lookup as a text string, one of\n"
+" SUCCESS | FAILED\n";
+
+static int txtcidname_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ char tech[80];
+ char txt[256] = "";
+ char dest[80];
+ struct localuser *u;
+ static int dep_warning = 0;
+ char *parse = NULL;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(cidnum);
+ AST_APP_ARG(options);
+ );
+
+ LOCAL_USER_ADD(u);
+
+ if (!dep_warning) {
+ ast_log(LOG_WARNING, "The TXTCIDName application has been deprecated in favor of the TXTCIDNAME dialplan function.\n");
+ dep_warning = 1;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "TXTCIDName requires an argument (extension[|options])\n");
+ LOCAL_USER_REMOVE(u);
+ return(0);
+ }
+
+ parse = ast_strdupa(data);
+ if (!parse) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args,parse);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if (!res) {
+ res = ast_get_txt(chan, data, dest, sizeof(dest), tech, sizeof(tech), txt, sizeof(txt));
+ }
+
+ /* Parse it out */
+ if (res > 0) {
+ if (!ast_strlen_zero(txt)) {
+ pbx_builtin_setvar_helper(chan, "TXTCIDNAME", txt);
+ pbx_builtin_setvar_helper(chan, "TXTCIDNAMESTATUS", "SUCCESS");
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "TXTCIDNAME got '%s'\n", txt);
+ }
+ }
+ if (!res) {
+ /* Look for a "busy" place */
+ if (priority_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "TXTCIDNAMESTATUS", "FAILED");
+ } else if (res > 0)
+ res = 0;
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, txtcidname_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_url.c b/1.2-netsec/apps/app_url.c
new file mode 100644
index 000000000..7f274b46a
--- /dev/null
+++ b/1.2-netsec/apps/app_url.c
@@ -0,0 +1,196 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief App to transmit a URL
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Send URL Applications";
+
+static char *app = "SendURL";
+
+static char *synopsis = "Send a URL";
+
+static char *descrip =
+" SendURL(URL[|option]): Requests client go to URL (IAX2) or sends the \n"
+"URL to the client (other channels).\n"
+"Result is returned in the SENDURLSTATUS channel variable:\n"
+" SUCCESS URL successfully sent to client\n"
+" FAILURE Failed to send URL\n"
+" NOLOAD Clien failed to load URL (wait enabled)\n"
+" UNSUPPORTED Channel does not support URL transport\n"
+"\n"
+"If the option 'wait' is specified, execution will wait for an\n"
+"acknowledgement that the URL has been loaded before continuing\n"
+"and will return -1 if the peer is unable to load the URL\n"
+"\n"
+"Old behaviour (deprecated): \n"
+" If the client does not support Asterisk \"html\" transport, \n"
+" and there exists a step with priority n + 101, then execution will\n"
+" continue at that step.\n"
+" Otherwise, execution will continue at the next priority level.\n"
+" SendURL only returns 0 if the URL was sent correctly or if\n"
+" the channel does not support HTML transport, and -1 otherwise.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int sendurl_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char *tmp;
+ char *options;
+ int local_option_wait=0;
+ int local_option_jump = 0;
+ struct ast_frame *f;
+ char *stringp=NULL;
+ char *status = "FAILURE";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SendURL requires an argument (URL)\n");
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ tmp = ast_strdupa(data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ stringp=tmp;
+ strsep(&stringp, "|");
+ options = strsep(&stringp, "|");
+ if (options && !strcasecmp(options, "wait"))
+ local_option_wait = 1;
+ if (options && !strcasecmp(options, "j"))
+ local_option_jump = 1;
+
+ if (!ast_channel_supports_html(chan)) {
+ /* Does not support transport */
+ if (local_option_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "UNSUPPORTED");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ res = ast_channel_sendurl(chan, tmp);
+ if (res == -1) {
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "FAILURE");
+ LOCAL_USER_REMOVE(u);
+ return res;
+ }
+ status = "SUCCESS";
+ if (local_option_wait) {
+ for(;;) {
+ /* Wait for an event */
+ res = ast_waitfor(chan, -1);
+ if (res < 0)
+ break;
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ status = "FAILURE";
+ break;
+ }
+ if (f->frametype == AST_FRAME_HTML) {
+ switch(f->subclass) {
+ case AST_HTML_LDCOMPLETE:
+ res = 0;
+ ast_frfree(f);
+ status = "NOLOAD";
+ goto out;
+ break;
+ case AST_HTML_NOSUPPORT:
+ /* Does not support transport */
+ status ="UNSUPPORTED";
+ if (local_option_jump || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ res = 0;
+ goto out;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know what to do with HTML subclass %d\n", f->subclass);
+ };
+ }
+ ast_frfree(f);
+ }
+ }
+out:
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, sendurl_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_userevent.c b/1.2-netsec/apps/app_userevent.c
new file mode 100644
index 000000000..95f5fbc84
--- /dev/null
+++ b/1.2-netsec/apps/app_userevent.c
@@ -0,0 +1,137 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief UserEvent application -- send manager event
+ *
+ * \ingroup applications
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/manager.h"
+
+static char *tdesc = "Custom User Event Application";
+
+static char *app = "UserEvent";
+
+static char *synopsis = "Send an arbitrary event to the manager interface";
+
+static char *descrip =
+" UserEvent(eventname[|body]): Sends an arbitrary event to the\n"
+"manager interface, with an optional body representing additional\n"
+"arguments. The format of the event will be:\n"
+" Event: UserEvent<specified event name>\n"
+" Channel: <channel name>\n"
+" Uniqueid: <call uniqueid>\n"
+" [body]\n"
+"If the body is not specified, only Event, Channel, and Uniqueid fields\n"
+"will be present. Returns 0.";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int userevent_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *info;
+ char eventname[512];
+ char *eventbody;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "UserEvent requires an argument (eventname|optional event body)\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ info = ast_strdupa(data);
+ if (!info) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ snprintf(eventname, sizeof(eventname), "UserEvent%s", info);
+ eventbody = strchr(eventname, '|');
+ if (eventbody) {
+ *eventbody = '\0';
+ eventbody++;
+ }
+
+ if(eventbody) {
+ ast_log(LOG_DEBUG, "Sending user event: %s, %s\n", eventname, eventbody);
+ manager_event(EVENT_FLAG_USER, eventname,
+ "Channel: %s\r\nUniqueid: %s\r\n%s\r\n",
+ chan->name, chan->uniqueid, eventbody);
+ } else {
+ ast_log(LOG_DEBUG, "Sending user event: %s\n", eventname);
+ manager_event(EVENT_FLAG_USER, eventname,
+ "Channel: %s\r\nUniqueid: %s\r\n", chan->name, chan->uniqueid);
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, userevent_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_verbose.c b/1.2-netsec/apps/app_verbose.c
new file mode 100644
index 000000000..6ed1e4820
--- /dev/null
+++ b/1.2-netsec/apps/app_verbose.c
@@ -0,0 +1,135 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_verbose_v001@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Verbose logging application
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+
+static char *tdesc = "Send verbose output";
+
+static char *app_verbose = "Verbose";
+
+static char *verbose_synopsis = "Send arbitrary text to verbose output";
+
+static char *verbose_descrip =
+"Verbose([<level>|]<message>)\n"
+" level must be an integer value. If not specified, defaults to 0.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int verbose_exec(struct ast_channel *chan, void *data)
+{
+ char *vtext;
+ int vsize;
+ struct localuser *u;
+
+ LOCAL_USER_ADD(u);
+
+ if (data) {
+ vtext = ast_strdupa((char *)data);
+ if (vtext) {
+ char *tmp = strsep(&vtext, "|,");
+ if (vtext) {
+ if (sscanf(tmp, "%d", &vsize) != 1) {
+ vsize = 0;
+ ast_log(LOG_WARNING, "'%s' is not a verboser number\n", vtext);
+ }
+ } else {
+ vtext = tmp;
+ vsize = 0;
+ }
+ if (option_verbose >= vsize) {
+ switch (vsize) {
+ case 0:
+ ast_verbose("%s\n", vtext);
+ break;
+ case 1:
+ ast_verbose(VERBOSE_PREFIX_1 "%s\n", vtext);
+ break;
+ case 2:
+ ast_verbose(VERBOSE_PREFIX_2 "%s\n", vtext);
+ break;
+ case 3:
+ ast_verbose(VERBOSE_PREFIX_3 "%s\n", vtext);
+ break;
+ default:
+ ast_verbose(VERBOSE_PREFIX_4 "%s\n", vtext);
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ }
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return 0;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_verbose);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app_verbose, verbose_exec, verbose_synopsis, verbose_descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_voicemail.c b/1.2-netsec/apps/app_voicemail.c
new file mode 100644
index 000000000..55a9c8456
--- /dev/null
+++ b/1.2-netsec/apps/app_voicemail.c
@@ -0,0 +1,6757 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Comedian Mail - Voicemail System
+ *
+ * \par See also
+ * \arg \ref Config_vm
+ * \ingroup applications
+ */
+
+/*
+ * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
+ * George Konstantoulakis <gkon@inaccessnetworks.com>
+ *
+ * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
+ *
+ * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
+ * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
+ * Stojan Sljivic <stojan.sljivic@gdspartners.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <dirent.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/dsp.h"
+#include "asterisk/localtime.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#ifdef USE_ODBC_STORAGE
+#include "asterisk/res_odbc.h"
+#endif
+
+#define COMMAND_TIMEOUT 5000
+#define VOICEMAIL_DIR_MODE 0700
+#define VOICEMAIL_FILE_MODE 0600
+
+#define VOICEMAIL_CONFIG "voicemail.conf"
+#define ASTERISK_USERNAME "asterisk"
+
+/* Default mail command to mail voicemail. Change it with the
+ mailcmd= command in voicemail.conf */
+#define SENDMAIL "/usr/sbin/sendmail -t"
+
+#define INTRO "vm-intro"
+
+#define MAXMSG 100
+#define MAXMSGLIMIT 9999
+
+#define BASEMAXINLINE 256
+#define BASELINELEN 72
+#define BASEMAXINLINE 256
+#define eol "\r\n"
+
+#define MAX_DATETIME_FORMAT 512
+#define MAX_NUM_CID_CONTEXTS 10
+
+#define VM_REVIEW (1 << 0)
+#define VM_OPERATOR (1 << 1)
+#define VM_SAYCID (1 << 2)
+#define VM_SVMAIL (1 << 3)
+#define VM_ENVELOPE (1 << 4)
+#define VM_SAYDURATION (1 << 5)
+#define VM_SKIPAFTERCMD (1 << 6)
+#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
+#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
+#define VM_PBXSKIP (1 << 9)
+#define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
+#define VM_ATTACH (1 << 11)
+#define VM_DELETE (1 << 12)
+#define VM_ALLOCED (1 << 13)
+#define VM_SEARCH (1 << 14)
+
+#define ERROR_LOCK_PATH -100
+
+enum {
+ OPT_SILENT = (1 << 0),
+ OPT_BUSY_GREETING = (1 << 1),
+ OPT_UNAVAIL_GREETING = (1 << 2),
+ OPT_RECORDGAIN = (1 << 3),
+ OPT_PREPEND_MAILBOX = (1 << 4),
+ OPT_PRIORITY_JUMP = (1 << 5),
+} vm_option_flags;
+
+enum {
+ OPT_ARG_RECORDGAIN = 0,
+ OPT_ARG_ARRAY_SIZE = 1,
+} vm_option_args;
+
+AST_APP_OPTIONS(vm_app_options, {
+ AST_APP_OPTION('s', OPT_SILENT),
+ AST_APP_OPTION('b', OPT_BUSY_GREETING),
+ AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
+ AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
+ AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
+ AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
+});
+
+static int load_config(void);
+
+/*! \page vmlang Voicemail Language Syntaxes Supported
+
+ \par Syntaxes supported, not really language codes.
+ \arg \b en - English
+ \arg \b de - German
+ \arg \b es - Spanish
+ \arg \b fr - French
+ \arg \b it = Italian
+ \arg \b nl - Dutch
+ \arg \b pt - Portuguese
+ \arg \b gr - Greek
+ \arg \b no - Norwegian
+ \arg \b se - Swedish
+
+German requires the following additional soundfile:
+\arg \b 1F einE (feminine)
+
+Spanish requires the following additional soundfile:
+\arg \b 1M un (masculine)
+
+Dutch, Portuguese & Spanish require the following additional soundfiles:
+\arg \b vm-INBOXs singular of 'new'
+\arg \b vm-Olds singular of 'old/heard/read'
+
+NB these are plural:
+\arg \b vm-INBOX nieuwe (nl)
+\arg \b vm-Old oude (nl)
+
+Swedish uses:
+\arg \b vm-nytt singular of 'new'
+\arg \b vm-nya plural of 'new'
+\arg \b vm-gammalt singular of 'old'
+\arg \b vm-gamla plural of 'old'
+\arg \b digits/ett 'one', not always same as 'digits/1'
+
+Norwegian uses:
+\arg \b vm-ny singular of 'new'
+\arg \b vm-nye plural of 'new'
+\arg \b vm-gammel singular of 'old'
+\arg \b vm-gamle plural of 'old'
+
+Dutch also uses:
+\arg \b nl-om 'at'?
+
+Spanish also uses:
+\arg \b vm-youhaveno
+
+Italian requires the following additional soundfile:
+
+For vm_intro_it:
+\arg \b vm-nuovo new
+\arg \b vm-nuovi new plural
+\arg \b vm-vecchio old
+\arg \b vm-vecchi old plural
+
+\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
+spelled among others when you have to change folder. For the above reasons, vm-INBOX
+and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
+
+*/
+
+struct baseio {
+ int iocp;
+ int iolen;
+ int linelength;
+ int ateof;
+ unsigned char iobuf[BASEMAXINLINE];
+};
+
+/*! Structure for linked list of users */
+struct ast_vm_user {
+ char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
+ char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
+ char password[80]; /*!< Secret pin code, numbers only */
+ char fullname[80]; /*!< Full name, for directory app */
+ char email[80]; /*!< E-mail address */
+ char pager[80]; /*!< E-mail address to pager (no attachment) */
+ char serveremail[80]; /*!< From: Mail address */
+ char mailcmd[160]; /*!< Configurable mail command */
+ char language[MAX_LANGUAGE]; /*!< Config: Language setting */
+ char zonetag[80]; /*!< Time zone */
+ char callback[80];
+ char dialout[80];
+ char uniqueid[20]; /*!< Unique integer identifier */
+ char exit[80];
+ unsigned int flags; /*!< VM_ flags */
+ int saydurationm;
+ int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
+ struct ast_vm_user *next;
+};
+
+struct vm_zone {
+ char name[80];
+ char timezone[80];
+ char msg_format[512];
+ struct vm_zone *next;
+};
+
+struct vm_state {
+ char curbox[80];
+ char username[80];
+ char curdir[256];
+ char vmbox[256];
+ char fn[256];
+ char fn2[256];
+ int *deleted;
+ int *heard;
+ int curmsg;
+ int lastmsg;
+ int newmessages;
+ int oldmessages;
+ int starting;
+ int repeats;
+};
+static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
+ int option, signed char record_gain);
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
+ char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
+ signed char record_gain);
+static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
+static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
+
+static void apply_options(struct ast_vm_user *vmu, const char *options);
+
+#ifdef USE_ODBC_STORAGE
+static char odbc_database[80];
+static char odbc_table[80];
+#define RETRIEVE(a,b) retrieve_file(a,b)
+#define DISPOSE(a,b) remove_file(a,b)
+#define STORE(a,b,c,d) store_file(a,b,c,d)
+#define EXISTS(a,b,c,d) (message_exists(a,b))
+#define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
+#define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
+#define DELETE(a,b,c) (delete_file(a,b))
+#else
+#define RETRIEVE(a,b)
+#define DISPOSE(a,b)
+#define STORE(a,b,c,d)
+#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
+#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
+#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
+#define DELETE(a,b,c) (vm_delete(c))
+#endif
+
+static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
+
+static char ext_pass_cmd[128];
+
+static char *tdesc = "Comedian Mail (Voicemail System)";
+
+static char *addesc = "Comedian Mail";
+
+static char *synopsis_vm =
+"Leave a Voicemail message";
+
+static char *descrip_vm =
+" VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
+"application allows the calling party to leave a message for the specified\n"
+"list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
+"be taken from the first mailbox specified. Dialplan execution will stop if the\n"
+"specified mailbox does not exist.\n"
+" The Voicemail application will exit if any of the following DTMF digits are\n"
+"received:\n"
+" 0 - Jump to the 'o' extension in the current dialplan context.\n"
+" * - Jump to the 'a' extension in the current dialplan context.\n"
+" This application will set the following channel variable upon completion:\n"
+" VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
+" application. The possible values are:\n"
+" SUCCESS | USEREXIT | FAILED\n\n"
+" Options:\n"
+" b - Play the 'busy' greeting to the calling party.\n"
+" g(#) - Use the specified amount of gain when recording the voicemail\n"
+" message. The units are whole-number decibels (dB).\n"
+" s - Skip the playback of instructions for leaving a message to the\n"
+" calling party.\n"
+" u - Play the 'unavailable greeting.\n"
+" j - Jump to priority n+101 if the mailbox is not found or some other\n"
+" error occurs.\n";
+
+static char *synopsis_vmain =
+"Check Voicemail messages";
+
+static char *descrip_vmain =
+" VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
+"calling party to check voicemail messages. A specific mailbox, and optional\n"
+"corresponding context, may be specified. If a mailbox is not provided, the\n"
+"calling party will be prompted to enter one. If a context is not specified,\n"
+"the 'default' context will be used.\n\n"
+" Options:\n"
+" p - Consider the mailbox parameter as a prefix to the mailbox that\n"
+" is entered by the caller.\n"
+" g(#) - Use the specified amount of gain when recording a voicemail\n"
+" message. The units are whole-number decibels (dB).\n"
+" s - Skip checking the passcode for the mailbox.\n";
+
+static char *synopsis_vm_box_exists =
+"Check to see if Voicemail mailbox exists";
+
+static char *descrip_vm_box_exists =
+" MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
+"mailbox exists. If no voicemail context is specified, the 'default' context\n"
+"will be used.\n"
+" This application will set the following channel variable upon completion:\n"
+" VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
+" MailboxExists application. Possible values include:\n"
+" SUCCESS | FAILED\n\n"
+" Options:\n"
+" j - Jump to priority n+101 if the mailbox is found.\n";
+
+static char *synopsis_vmauthenticate =
+"Authenticate with Voicemail passwords";
+
+static char *descrip_vmauthenticate =
+" VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
+"same way as the Authenticate application, but the passwords are taken from\n"
+"voicemail.conf.\n"
+" If the mailbox is specified, only that mailbox's password will be considered\n"
+"valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
+"be set with the authenticated mailbox.\n\n"
+" Options:\n"
+" s - Skip playing the initial prompts.\n";
+
+/* Leave a message */
+static char *app = "VoiceMail";
+
+/* Check mail, control, etc */
+static char *app2 = "VoiceMailMain";
+
+static char *app3 = "MailboxExists";
+static char *app4 = "VMAuthenticate";
+
+AST_MUTEX_DEFINE_STATIC(vmlock);
+struct ast_vm_user *users;
+struct ast_vm_user *usersl;
+struct vm_zone *zones = NULL;
+struct vm_zone *zonesl = NULL;
+static int maxsilence;
+static int maxmsg;
+static int silencethreshold = 128;
+static char serveremail[80];
+static char mailcmd[160]; /* Configurable mail cmd */
+static char externnotify[160];
+
+static char vmfmts[80];
+static int vmminmessage;
+static int vmmaxmessage;
+static int maxgreet;
+static int skipms;
+static int maxlogins;
+
+static struct ast_flags globalflags = {0};
+
+static int saydurationminfo;
+
+static char dialcontext[AST_MAX_CONTEXT];
+static char callcontext[AST_MAX_CONTEXT];
+static char exitcontext[AST_MAX_CONTEXT];
+
+static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
+
+
+static char *emailbody = NULL;
+static char *emailsubject = NULL;
+static char *pagerbody = NULL;
+static char *pagersubject = NULL;
+static char fromstring[100];
+static char pagerfromstring[100];
+static char emailtitle[100];
+static char charset[32] = "ISO-8859-1";
+
+static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
+static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
+static int adsiver = 1;
+static char emaildateformat[32] = "%A, %B %d, %Y at %r";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static void populate_defaults(struct ast_vm_user *vmu)
+{
+ ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
+ if (saydurationminfo)
+ vmu->saydurationm = saydurationminfo;
+ if (callcontext)
+ ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
+ if (dialcontext)
+ ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
+ if (exitcontext)
+ ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
+ if (maxmsg)
+ vmu->maxmsg = maxmsg;
+}
+
+static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
+{
+ int x;
+ if (!strcasecmp(var, "attach")) {
+ ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
+ } else if (!strcasecmp(var, "serveremail")) {
+ ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
+ } else if (!strcasecmp(var, "language")) {
+ ast_copy_string(vmu->language, value, sizeof(vmu->language));
+ } else if (!strcasecmp(var, "tz")) {
+ ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
+ } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
+ ast_set2_flag(vmu, ast_true(value), VM_DELETE);
+ } else if (!strcasecmp(var, "saycid")){
+ ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
+ } else if (!strcasecmp(var,"sendvoicemail")){
+ ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
+ } else if (!strcasecmp(var, "review")){
+ ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
+ } else if (!strcasecmp(var, "operator")){
+ ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
+ } else if (!strcasecmp(var, "envelope")){
+ ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
+ } else if (!strcasecmp(var, "sayduration")){
+ ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
+ } else if (!strcasecmp(var, "saydurationm")){
+ if (sscanf(value, "%d", &x) == 1) {
+ vmu->saydurationm = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
+ }
+ } else if (!strcasecmp(var, "forcename")){
+ ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
+ } else if (!strcasecmp(var, "forcegreetings")){
+ ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
+ } else if (!strcasecmp(var, "callback")) {
+ ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
+ } else if (!strcasecmp(var, "dialout")) {
+ ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
+ } else if (!strcasecmp(var, "exitcontext")) {
+ ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
+ } else if (!strcasecmp(var, "maxmsg")) {
+ vmu->maxmsg = atoi(value);
+ if (vmu->maxmsg <= 0) {
+ ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
+ vmu->maxmsg = MAXMSG;
+ } else if (vmu->maxmsg > MAXMSGLIMIT) {
+ ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
+ vmu->maxmsg = MAXMSGLIMIT;
+ }
+ } else if (!strcasecmp(var, "options")) {
+ apply_options(vmu, value);
+ }
+}
+
+static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
+{
+ int res;
+ if (!ast_strlen_zero(vmu->uniqueid)) {
+ res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
+ if (res > 0) {
+ ast_copy_string(vmu->password, password, sizeof(vmu->password));
+ res = 0;
+ } else if (!res) {
+ res = -1;
+ }
+ return res;
+ }
+ return -1;
+}
+
+static void apply_options(struct ast_vm_user *vmu, const char *options)
+{ /* Destructively Parse options and apply */
+ char *stringp;
+ char *s;
+ char *var, *value;
+ stringp = ast_strdupa(options);
+ while ((s = strsep(&stringp, "|"))) {
+ value = s;
+ if ((var = strsep(&value, "=")) && value) {
+ apply_option(vmu, var, value);
+ }
+ }
+}
+
+static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
+{
+ struct ast_variable *var, *tmp;
+ struct ast_vm_user *retval;
+
+ if (ivm)
+ retval=ivm;
+ else
+ retval=malloc(sizeof(struct ast_vm_user));
+
+ if (retval) {
+ memset(retval, 0, sizeof(struct ast_vm_user));
+ if (!ivm)
+ ast_set_flag(retval, VM_ALLOCED);
+ if (mailbox)
+ ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
+ populate_defaults(retval);
+ if (!context && ast_test_flag((&globalflags), VM_SEARCH))
+ var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
+ else
+ var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
+ if (var) {
+ tmp = var;
+ while(tmp) {
+ printf("%s => %s\n", tmp->name, tmp->value);
+ if (!strcasecmp(tmp->name, "password")) {
+ ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
+ } else if (!strcasecmp(tmp->name, "uniqueid")) {
+ ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
+ } else if (!strcasecmp(tmp->name, "pager")) {
+ ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
+ } else if (!strcasecmp(tmp->name, "email")) {
+ ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
+ } else if (!strcasecmp(tmp->name, "fullname")) {
+ ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
+ } else if (!strcasecmp(tmp->name, "context")) {
+ ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
+ } else
+ apply_option(retval, tmp->name, tmp->value);
+ tmp = tmp->next;
+ }
+ } else {
+ if (!ivm)
+ free(retval);
+ retval = NULL;
+ }
+ }
+ return retval;
+}
+
+static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
+{
+ /* This function could be made to generate one from a database, too */
+ struct ast_vm_user *vmu=NULL, *cur;
+ ast_mutex_lock(&vmlock);
+ cur = users;
+
+ if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
+ context = "default";
+
+ while (cur) {
+ if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
+ break;
+ if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
+ break;
+ cur=cur->next;
+ }
+ if (cur) {
+ if (ivm)
+ vmu = ivm;
+ else
+ /* Make a copy, so that on a reload, we have no race */
+ vmu = malloc(sizeof(struct ast_vm_user));
+ if (vmu) {
+ memcpy(vmu, cur, sizeof(struct ast_vm_user));
+ ast_set2_flag(vmu, !ivm, VM_ALLOCED);
+ vmu->next = NULL;
+ }
+ } else
+ vmu = find_user_realtime(ivm, context, mailbox);
+ ast_mutex_unlock(&vmlock);
+ return vmu;
+}
+
+static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
+{
+ /* This function could be made to generate one from a database, too */
+ struct ast_vm_user *cur;
+ int res = -1;
+ ast_mutex_lock(&vmlock);
+ cur = users;
+ while (cur) {
+ if ((!context || !strcasecmp(context, cur->context)) &&
+ (!strcasecmp(mailbox, cur->mailbox)))
+ break;
+ cur=cur->next;
+ }
+ if (cur) {
+ ast_copy_string(cur->password, newpass, sizeof(cur->password));
+ res = 0;
+ }
+ ast_mutex_unlock(&vmlock);
+ return res;
+}
+
+static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
+{
+ /* There's probably a better way of doing this. */
+ /* That's why I've put the password change in a separate function. */
+ /* This could also be done with a database function */
+
+ FILE *configin;
+ FILE *configout;
+ int linenum=0;
+ char inbuf[256];
+ char orig[256];
+ char currcontext[256] ="";
+ char tmpin[AST_CONFIG_MAX_PATH];
+ char tmpout[AST_CONFIG_MAX_PATH];
+ struct stat statbuf;
+
+ if (!change_password_realtime(vmu, newpassword))
+ return;
+
+ snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
+ snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
+ configin = fopen(tmpin,"r");
+ if (configin)
+ configout = fopen(tmpout,"w+");
+ else
+ configout = NULL;
+ if (!configin || !configout) {
+ if (configin)
+ fclose(configin);
+ else
+ ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
+ if (configout)
+ fclose(configout);
+ else
+ ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
+ return;
+ }
+
+ while (!feof(configin)) {
+ char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
+
+ /* Read in the line */
+ fgets(inbuf, sizeof(inbuf), configin);
+ linenum++;
+
+ if (ast_strlen_zero(inbuf)) {
+ fprintf(configout, "\n");
+ continue;
+ }
+
+ /* Make a backup of it */
+ ast_copy_string(orig, inbuf, sizeof(orig));
+
+ /*
+ Read the file line by line, split each line into a comment and command section
+ only parse the command portion of the line
+ */
+ if (inbuf[strlen(inbuf) - 1] == '\n')
+ inbuf[strlen(inbuf) - 1] = '\0';
+
+ if ((comment = strchr(inbuf, ';')))
+ *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
+
+ if (ast_strlen_zero(inbuf)) {
+ fprintf(configout, "%s", orig);
+ continue;
+ }
+
+ /* Check for a context, first '[' to first ']' */
+ if ((tmpctx = strchr(inbuf, '['))) {
+ tmpctxend = strchr(tmpctx, ']');
+ if (tmpctxend) {
+ /* Valid context */
+ ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
+ fprintf(configout, "%s", orig);
+ continue;
+ }
+ }
+
+ /* This isn't a context line, check for MBX => PSWD... */
+ user = inbuf;
+ if ((pass = strchr(user, '='))) {
+ /* We have a line in the form of aaaaa=aaaaaa */
+ *pass++ = '\0';
+
+ user = ast_strip(user);
+
+ if (*pass == '>')
+ *pass++ = '\0';
+
+ pass = ast_skip_blanks(pass);
+
+ /*
+ Since no whitespace allowed in fields, or more correctly white space
+ inside the fields is there for a purpose, we can just terminate pass
+ at the comma or EOL whichever comes first.
+ */
+ if ((rest = strchr(pass, ',')))
+ *rest++ = '\0';
+ } else {
+ user = NULL;
+ }
+
+ /* Compare user, pass AND context */
+ if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
+ !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
+ !strcasecmp(currcontext, vmu->context)) {
+ /* This is the line */
+ if (rest) {
+ fprintf(configout, "%s => %s,%s", user, newpassword, rest);
+ } else {
+ fprintf(configout, "%s => %s", user, newpassword);
+ }
+ /* If there was a comment on the line print it out */
+ if (comment) {
+ fprintf(configout, ";%s\n", comment);
+ } else {
+ fprintf(configout, "\n");
+ }
+ } else {
+ /* Put it back like it was */
+ fprintf(configout, "%s", orig);
+ }
+ }
+ fclose(configin);
+ fclose(configout);
+
+ stat(tmpin, &statbuf);
+ chmod(tmpout, statbuf.st_mode);
+ chown(tmpout, statbuf.st_uid, statbuf.st_gid);
+ unlink(tmpin);
+ rename(tmpout, tmpin);
+ reset_user_pw(vmu->context, vmu->mailbox, newpassword);
+ ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
+}
+
+static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
+{
+ char buf[255];
+ snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
+ if (!ast_safe_system(buf))
+ ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
+}
+
+static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
+{
+ return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
+}
+
+static int make_file(char *dest, int len, char *dir, int num)
+{
+ return snprintf(dest, len, "%s/msg%04d", dir, num);
+}
+
+/** basically mkdir -p $dest/$context/$ext/$mailbox
+ * @dest String. base directory.
+ * @context String. Ignored if is null or empty string.
+ * @ext String. Ignored if is null or empty string.
+ * @mailbox String. Ignored if is null or empty string.
+ * @returns 0 on failure, 1 on success.
+ * */
+static int create_dirpath(char *dest, int len, char *context, char *ext, char *mailbox)
+{
+ mode_t mode = VOICEMAIL_DIR_MODE;
+
+ if(context && context[0] != '\0') {
+ make_dir(dest, len, context, "", "");
+ if(mkdir(dest, mode) && errno != EEXIST) {
+ ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
+ return 0;
+ }
+ }
+ if(ext && ext[0] != '\0') {
+ make_dir(dest, len, context, ext, "");
+ if(mkdir(dest, mode) && errno != EEXIST) {
+ ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
+ return 0;
+ }
+ }
+ if(mailbox && mailbox[0] != '\0') {
+ make_dir(dest, len, context, ext, mailbox);
+ if(mkdir(dest, mode) && errno != EEXIST) {
+ ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* only return failure if ast_lock_path returns 'timeout',
+ not if the path does not exist or any other reason
+*/
+static int vm_lock_path(const char *path)
+{
+ switch (ast_lock_path(path)) {
+ case AST_LOCK_TIMEOUT:
+ return -1;
+ default:
+ return 0;
+ }
+}
+
+
+#ifdef USE_ODBC_STORAGE
+static int retrieve_file(char *dir, int msgnum)
+{
+ int x = 0;
+ int res;
+ int fd=-1;
+ size_t fdlen = 0;
+ void *fdm=NULL;
+ SQLSMALLINT colcount=0;
+ SQLHSTMT stmt;
+ char sql[256];
+ char fmt[80]="";
+ char *c;
+ char coltitle[256];
+ SQLSMALLINT collen;
+ SQLSMALLINT datatype;
+ SQLSMALLINT decimaldigits;
+ SQLSMALLINT nullable;
+ SQLULEN colsize;
+ FILE *f=NULL;
+ char rowdata[80];
+ char fn[256];
+ char full_fn[256];
+ char msgnums[80];
+
+ odbc_obj *obj;
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string(fn, dir, sizeof(fn));
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ f = fopen(full_fn, "w+");
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if (res == SQL_NO_DATA) {
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ if (f)
+ fprintf(f, "[message]\n");
+ for (x=0;x<colcount;x++) {
+ rowdata[0] = '\0';
+ collen = sizeof(coltitle);
+ res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
+ &datatype, &colsize, &decimaldigits, &nullable);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ if (!strcasecmp(coltitle, "recording")) {
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
+ fdlen = colsize;
+ fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
+ if (fd > -1) {
+ char tmp[1]="";
+ lseek(fd, fdlen - 1, SEEK_SET);
+ if (write(fd, tmp, 1) != 1) {
+ close(fd);
+ fd = -1;
+ }
+ if (fd > -1)
+ fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ }
+ if (fdm) {
+ memset(fdm, 0, fdlen);
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ }
+ } else {
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
+ fprintf(f, "%s=%s\n", coltitle, rowdata);
+ }
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ if (f)
+ fclose(f);
+ if (fdm)
+ munmap(fdm, fdlen);
+ if (fd > -1)
+ close(fd);
+ return x - 1;
+}
+
+static int remove_file(char *dir, int msgnum)
+{
+ char fn[256];
+ char full_fn[256];
+ char msgnums[80];
+
+ if (msgnum > -1) {
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ make_file(fn, sizeof(fn), dir, msgnum);
+ } else
+ ast_copy_string(fn, dir, sizeof(fn));
+ ast_filedelete(fn, NULL);
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ unlink(full_fn);
+ return 0;
+}
+
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
+{
+ int x = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[256];
+ char rowdata[20];
+
+ odbc_obj *obj;
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ if (sscanf(rowdata, "%d", &x) != 1)
+ ast_log(LOG_WARNING, "Failed to read message count!\n");
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return x - 1;
+}
+
+static int message_exists(char *dir, int msgnum)
+{
+ int x = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[256];
+ char rowdata[20];
+ char msgnums[20];
+
+ odbc_obj *obj;
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ if (sscanf(rowdata, "%d", &x) != 1)
+ ast_log(LOG_WARNING, "Failed to read message count!\n");
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return x;
+}
+
+static int count_messages(struct ast_vm_user *vmu, char *dir)
+{
+ return last_message_index(vmu, dir) + 1;
+}
+
+static void delete_file(char *sdir, int smsg)
+{
+ int res;
+ SQLHSTMT stmt;
+ char sql[256];
+ char msgnums[20];
+
+ odbc_obj *obj;
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return;
+}
+
+static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
+{
+ int res;
+ SQLHSTMT stmt;
+ char sql[512];
+ char msgnums[20];
+ char msgnumd[20];
+ odbc_obj *obj;
+
+ delete_file(ddir, dmsg);
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+#ifdef EXTENDED_ODBC_STORAGE
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
+#else
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
+#endif
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
+#ifdef EXTENDED_ODBC_STORAGE
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
+ SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+ SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+#else
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+#endif
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return;
+}
+
+static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
+{
+ int x = 0;
+ int res;
+ int fd = -1;
+ void *fdm=NULL;
+ size_t fdlen = -1;
+ SQLHSTMT stmt;
+ SQLINTEGER len;
+ char sql[256];
+ char msgnums[20];
+ char fn[256];
+ char full_fn[256];
+ char fmt[80]="";
+ char *c;
+ char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
+ char *category = "";
+ struct ast_config *cfg=NULL;
+ odbc_obj *obj;
+
+ delete_file(dir, msgnum);
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string(fn, dir, sizeof(fn));
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ cfg = ast_config_load(full_fn);
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+ fd = open(full_fn, O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
+ goto yuck;
+ }
+ if (cfg) {
+ context = ast_variable_retrieve(cfg, "message", "context");
+ if (!context) context = "";
+ macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
+ if (!macrocontext) macrocontext = "";
+ callerid = ast_variable_retrieve(cfg, "message", "callerid");
+ if (!callerid) callerid = "";
+ origtime = ast_variable_retrieve(cfg, "message", "origtime");
+ if (!origtime) origtime = "";
+ duration = ast_variable_retrieve(cfg, "message", "duration");
+ if (!duration) duration = "";
+ category = ast_variable_retrieve(cfg, "message", "category");
+ if (!category) category = "";
+ }
+ fdlen = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ printf("Length is %d\n", fdlen);
+ fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
+ if (!fdm) {
+ ast_log(LOG_WARNING, "Memory map failed!\n");
+ goto yuck;
+ }
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ if (!ast_strlen_zero(category))
+#ifdef EXTENDED_ODBC_STORAGE
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
+#else
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
+#endif
+ else
+#ifdef EXTENDED_ODBC_STORAGE
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
+#else
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
+#endif
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
+ SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
+ SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
+ SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
+ SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
+#ifdef EXTENDED_ODBC_STORAGE
+ SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
+ SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
+ if (!ast_strlen_zero(category))
+ SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
+#else
+ if (!ast_strlen_zero(category))
+ SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
+#endif
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ if (cfg)
+ ast_config_destroy(cfg);
+ if (fdm)
+ munmap(fdm, fdlen);
+ if (fd > -1)
+ close(fd);
+ return x;
+}
+
+static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
+{
+ int res;
+ SQLHSTMT stmt;
+ char sql[256];
+ char msgnums[20];
+ char msgnumd[20];
+ odbc_obj *obj;
+
+ delete_file(ddir, dmsg);
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+#ifdef EXTENDED_ODBC_STORAGE
+ snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
+#else
+ snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
+#endif
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
+#ifdef EXTENDED_ODBC_STORAGE
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
+ SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+ SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+#else
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+#endif
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return;
+}
+
+#else
+
+static int count_messages(struct ast_vm_user *vmu, char *dir)
+{
+ /* Find all .txt files - even if they are not in sequence from 0000 */
+
+ int vmcount = 0;
+ DIR *vmdir = NULL;
+ struct dirent *vment = NULL;
+
+ if (vm_lock_path(dir))
+ return ERROR_LOCK_PATH;
+
+ if ((vmdir = opendir(dir))) {
+ while ((vment = readdir(vmdir))) {
+ if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
+ vmcount++;
+ }
+ closedir(vmdir);
+ }
+ ast_unlock_path(dir);
+
+ return vmcount;
+}
+
+static void rename_file(char *sfn, char *dfn)
+{
+ char stxt[256];
+ char dtxt[256];
+ ast_filerename(sfn,dfn,NULL);
+ snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
+ snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
+ rename(stxt, dtxt);
+}
+
+static int copy(char *infile, char *outfile)
+{
+ int ifd;
+ int ofd;
+ int res;
+ int len;
+ char buf[4096];
+
+#ifdef HARDLINK_WHEN_POSSIBLE
+ /* Hard link if possible; saves disk space & is faster */
+ if (link(infile, outfile)) {
+#endif
+ if ((ifd = open(infile, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+ return -1;
+ }
+ if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+ close(ifd);
+ return -1;
+ }
+ do {
+ len = read(ifd, buf, sizeof(buf));
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+ close(ifd);
+ close(ofd);
+ unlink(outfile);
+ }
+ if (len) {
+ res = write(ofd, buf, len);
+ if (errno == ENOMEM || errno == ENOSPC || res != len) {
+ ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+ close(ifd);
+ close(ofd);
+ unlink(outfile);
+ }
+ }
+ } while (len);
+ close(ifd);
+ close(ofd);
+ return 0;
+#ifdef HARDLINK_WHEN_POSSIBLE
+ } else {
+ /* Hard link succeeded */
+ return 0;
+ }
+#endif
+}
+
+static void copy_file(char *frompath, char *topath)
+{
+ char frompath2[256],topath2[256];
+ ast_filecopy(frompath, topath, NULL);
+ snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
+ snprintf(topath2, sizeof(topath2), "%s.txt", topath);
+ copy(frompath2, topath2);
+}
+
+/*
+ * A negative return value indicates an error.
+ */
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
+{
+ int x;
+ char fn[256];
+
+ if (vm_lock_path(dir))
+ return ERROR_LOCK_PATH;
+
+ for (x = 0; x < vmu->maxmsg; x++) {
+ make_file(fn, sizeof(fn), dir, x);
+ if (ast_fileexists(fn, NULL, NULL) < 1)
+ break;
+ }
+ ast_unlock_path(dir);
+
+ return x - 1;
+}
+
+static int vm_delete(char *file)
+{
+ char *txt;
+ int txtsize = 0;
+
+ txtsize = (strlen(file) + 5)*sizeof(char);
+ txt = (char *)alloca(txtsize);
+ /* Sprintf here would safe because we alloca'd exactly the right length,
+ * but trying to eliminate all sprintf's anyhow
+ */
+ snprintf(txt, txtsize, "%s.txt", file);
+ unlink(txt);
+ return ast_filedelete(file, NULL);
+}
+
+
+#endif
+static int
+inbuf(struct baseio *bio, FILE *fi)
+{
+ int l;
+
+ if (bio->ateof)
+ return 0;
+
+ if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+ if (ferror(fi))
+ return -1;
+
+ bio->ateof = 1;
+ return 0;
+ }
+
+ bio->iolen= l;
+ bio->iocp= 0;
+
+ return 1;
+}
+
+static int
+inchar(struct baseio *bio, FILE *fi)
+{
+ if (bio->iocp>=bio->iolen) {
+ if (!inbuf(bio, fi))
+ return EOF;
+ }
+
+ return bio->iobuf[bio->iocp++];
+}
+
+static int
+ochar(struct baseio *bio, int c, FILE *so)
+{
+ if (bio->linelength>=BASELINELEN) {
+ if (fputs(eol,so)==EOF)
+ return -1;
+
+ bio->linelength= 0;
+ }
+
+ if (putc(((unsigned char)c),so)==EOF)
+ return -1;
+
+ bio->linelength++;
+
+ return 1;
+}
+
+static int base_encode(char *filename, FILE *so)
+{
+ unsigned char dtable[BASEMAXINLINE];
+ int i,hiteof= 0;
+ FILE *fi;
+ struct baseio bio;
+
+ memset(&bio, 0, sizeof(bio));
+ bio.iocp = BASEMAXINLINE;
+
+ if (!(fi = fopen(filename, "rb"))) {
+ ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ for (i= 0;i<9;i++) {
+ dtable[i]= 'A'+i;
+ dtable[i+9]= 'J'+i;
+ dtable[26+i]= 'a'+i;
+ dtable[26+i+9]= 'j'+i;
+ }
+ for (i= 0;i<8;i++) {
+ dtable[i+18]= 'S'+i;
+ dtable[26+i+18]= 's'+i;
+ }
+ for (i= 0;i<10;i++) {
+ dtable[52+i]= '0'+i;
+ }
+ dtable[62]= '+';
+ dtable[63]= '/';
+
+ while (!hiteof){
+ unsigned char igroup[3],ogroup[4];
+ int c,n;
+
+ igroup[0]= igroup[1]= igroup[2]= 0;
+
+ for (n= 0;n<3;n++) {
+ if ((c = inchar(&bio, fi)) == EOF) {
+ hiteof= 1;
+ break;
+ }
+
+ igroup[n]= (unsigned char)c;
+ }
+
+ if (n> 0) {
+ ogroup[0]= dtable[igroup[0]>>2];
+ ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
+ ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
+ ogroup[3]= dtable[igroup[2]&0x3F];
+
+ if (n<3) {
+ ogroup[3]= '=';
+
+ if (n<2)
+ ogroup[2]= '=';
+ }
+
+ for (i= 0;i<4;i++)
+ ochar(&bio, ogroup[i], so);
+ }
+ }
+
+ if (fputs(eol,so)==EOF)
+ return 0;
+
+ fclose(fi);
+
+ return 1;
+}
+
+static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize)
+{
+ char callerid[256];
+ /* Prepare variables for substition in email body and subject */
+ pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
+ pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
+ snprintf(passdata, passdatasize, "%d", msgnum);
+ pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
+ pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
+ pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
+ pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
+ pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
+ pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
+ pbx_builtin_setvar_helper(ast, "VM_DATE", date);
+}
+
+static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail)
+{
+ FILE *p=NULL;
+ int pfd;
+ char date[256];
+ char host[MAXHOSTNAMELEN] = "";
+ char who[256];
+ char bound[256];
+ char fname[256];
+ char dur[256];
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[256];
+ time_t t;
+ struct tm tm;
+ struct vm_zone *the_zone = NULL;
+ if (vmu && ast_strlen_zero(vmu->email)) {
+ ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
+ return(0);
+ }
+ if (!strcmp(format, "wav49"))
+ format = "WAV";
+ ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
+ /* Make a temporary file instead of piping directly to sendmail, in case the mail
+ command hangs */
+ pfd = mkstemp(tmp);
+ if (pfd > -1) {
+ p = fdopen(pfd, "w");
+ if (!p) {
+ close(pfd);
+ pfd = -1;
+ }
+ }
+ if (p) {
+ gethostname(host, sizeof(host)-1);
+ if (strchr(srcemail, '@'))
+ ast_copy_string(who, srcemail, sizeof(who));
+ else {
+ snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+ }
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+ time(&t);
+
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ struct vm_zone *z;
+ z = zones;
+ while (z) {
+ if (!strcmp(z->name, vmu->zonetag)) {
+ the_zone = z;
+ break;
+ }
+ z = z->next;
+ }
+ }
+
+ if (the_zone)
+ ast_localtime(&t,&tm,the_zone->timezone);
+ else
+ ast_localtime(&t,&tm,NULL);
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
+ fprintf(p, "Date: %s\n", date);
+
+ /* Set date format for voicemail mail */
+ strftime(date, sizeof(date), emaildateformat, &tm);
+
+ if (*fromstring) {
+ struct ast_channel *ast = ast_channel_alloc(0);
+ if (ast) {
+ char *passdata;
+ int vmlen = strlen(fromstring)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
+ pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
+ fprintf(p, "From: %s <%s>\n",passdata,who);
+ } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ ast_channel_free(ast);
+ } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "From: Asterisk PBX <%s>\n", who);
+ fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
+
+ if (emailsubject) {
+ struct ast_channel *ast = ast_channel_alloc(0);
+ if (ast) {
+ char *passdata;
+ int vmlen = strlen(emailsubject)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
+ pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
+ fprintf(p, "Subject: %s\n",passdata);
+ } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ ast_channel_free(ast);
+ } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ if (*emailtitle) {
+ fprintf(p, emailtitle, msgnum + 1, mailbox) ;
+ fprintf(p,"\n") ;
+ } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
+ fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
+ else
+ fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
+ fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
+ fprintf(p, "MIME-Version: 1.0\n");
+ if (attach_user_voicemail) {
+ /* Something unique. */
+ snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
+
+ fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
+
+ fprintf(p, "--%s\n", bound);
+ }
+ fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
+ if (emailbody) {
+ struct ast_channel *ast = ast_channel_alloc(0);
+ if (ast) {
+ char *passdata;
+ int vmlen = strlen(emailbody)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
+ pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
+ fprintf(p, "%s\n",passdata);
+ } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ ast_channel_free(ast);
+ } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else {
+ fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
+
+ "in mailbox %s from %s, on %s so you might\n"
+ "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
+ dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+ }
+ if (attach_user_voicemail) {
+ /* Eww. We want formats to tell us their own MIME type */
+ char *ctype = "audio/x-";
+ if (!strcasecmp(format, "ogg"))
+ ctype = "application/";
+
+ fprintf(p, "--%s\n", bound);
+ fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
+ fprintf(p, "Content-Transfer-Encoding: base64\n");
+ fprintf(p, "Content-Description: Voicemail sound attachment.\n");
+ fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
+
+ snprintf(fname, sizeof(fname), "%s.%s", attach, format);
+ base_encode(fname, p);
+ fprintf(p, "\n\n--%s--\n.\n", bound);
+ }
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
+ } else {
+ ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
+ return -1;
+ }
+ return 0;
+}
+
+static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
+{
+ FILE *p=NULL;
+ int pfd;
+ char date[256];
+ char host[MAXHOSTNAMELEN]="";
+ char who[256];
+ char dur[256];
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[256];
+ time_t t;
+ struct tm tm;
+ struct vm_zone *the_zone = NULL;
+ pfd = mkstemp(tmp);
+
+ if (pfd > -1) {
+ p = fdopen(pfd, "w");
+ if (!p) {
+ close(pfd);
+ pfd = -1;
+ }
+ }
+
+ if (p) {
+ gethostname(host, sizeof(host)-1);
+ if (strchr(srcemail, '@'))
+ ast_copy_string(who, srcemail, sizeof(who));
+ else {
+ snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+ }
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+ time(&t);
+
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ struct vm_zone *z;
+ z = zones;
+ while (z) {
+ if (!strcmp(z->name, vmu->zonetag)) {
+ the_zone = z;
+ break;
+ }
+ z = z->next;
+ }
+ }
+
+ if (the_zone)
+ ast_localtime(&t,&tm,the_zone->timezone);
+ else
+ ast_localtime(&t,&tm,NULL);
+
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
+ fprintf(p, "Date: %s\n", date);
+
+ if (*pagerfromstring) {
+ struct ast_channel *ast = ast_channel_alloc(0);
+ if (ast) {
+ char *passdata;
+ int vmlen = strlen(fromstring)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
+ pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
+ fprintf(p, "From: %s <%s>\n",passdata,who);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ ast_channel_free(ast);
+ } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "From: Asterisk PBX <%s>\n", who);
+ fprintf(p, "To: %s\n", pager);
+ if (pagersubject) {
+ struct ast_channel *ast = ast_channel_alloc(0);
+ if (ast) {
+ char *passdata;
+ int vmlen = strlen(pagersubject)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
+ pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
+ fprintf(p, "Subject: %s\n\n",passdata);
+ } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ ast_channel_free(ast);
+ } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "Subject: New VM\n\n");
+ strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
+ if (pagerbody) {
+ struct ast_channel *ast = ast_channel_alloc(0);
+ if (ast) {
+ char *passdata;
+ int vmlen = strlen(pagerbody)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
+ pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
+ fprintf(p, "%s\n",passdata);
+ } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ ast_channel_free(ast);
+ } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else {
+ fprintf(p, "New %s long msg in box %s\n"
+ "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+ }
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
+ } else {
+ ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
+ return -1;
+ }
+ return 0;
+}
+
+static int get_date(char *s, int len)
+{
+ struct tm tm;
+ time_t t;
+ t = time(0);
+ localtime_r(&t,&tm);
+ return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
+}
+
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
+{
+ int res;
+ char fn[256];
+ snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
+ RETRIEVE(fn, -1);
+ if (ast_fileexists(fn, NULL, NULL) > 0) {
+ res = ast_streamfile(chan, fn, chan->language);
+ if (res) {
+ DISPOSE(fn, -1);
+ return -1;
+ }
+ res = ast_waitstream(chan, ecodes);
+ if (res) {
+ DISPOSE(fn, -1);
+ return res;
+ }
+ } else {
+ /* Dispose just in case */
+ DISPOSE(fn, -1);
+ res = ast_streamfile(chan, "vm-theperson", chan->language);
+ if (res)
+ return -1;
+ res = ast_waitstream(chan, ecodes);
+ if (res)
+ return res;
+ res = ast_say_digit_str(chan, ext, ecodes, chan->language);
+ if (res)
+ return res;
+ }
+ if (busy)
+ res = ast_streamfile(chan, "vm-isonphone", chan->language);
+ else
+ res = ast_streamfile(chan, "vm-isunavail", chan->language);
+ if (res)
+ return -1;
+ res = ast_waitstream(chan, ecodes);
+ return res;
+}
+
+static void free_user(struct ast_vm_user *vmu)
+{
+ if (ast_test_flag(vmu, VM_ALLOCED))
+ free(vmu);
+}
+
+static void free_zone(struct vm_zone *z)
+{
+ free(z);
+}
+
+static char *mbox(int id)
+{
+ switch(id) {
+ case 0:
+ return "INBOX";
+ case 1:
+ return "Old";
+ case 2:
+ return "Work";
+ case 3:
+ return "Family";
+ case 4:
+ return "Friends";
+ case 5:
+ return "Cust1";
+ case 6:
+ return "Cust2";
+ case 7:
+ return "Cust3";
+ case 8:
+ return "Cust4";
+ case 9:
+ return "Cust5";
+ default:
+ return "Unknown";
+ }
+}
+
+#ifdef USE_ODBC_STORAGE
+static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
+{
+ int x = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[256];
+ char rowdata[20];
+ char tmp[256]="";
+ char *context;
+
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ } else
+ context = "default";
+
+ odbc_obj *obj;
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ *newmsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ *oldmsgs = atoi(rowdata);
+ x = 1;
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+
+yuck:
+ return x;
+}
+
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ int nummsgs = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[256];
+ char rowdata[20];
+ char tmp[256]="";
+ char *context;
+ if (!folder)
+ folder = "INBOX";
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ } else
+ context = "default";
+
+ odbc_obj *obj;
+ obj = fetch_odbc_obj(odbc_database, 0);
+ if (obj) {
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ goto yuck;
+ }
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
+ res = SQLPrepare(stmt, sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ nummsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+
+yuck:
+ if (nummsgs>=1)
+ return 1;
+ else
+ return 0;
+}
+
+#else
+
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ DIR *dir;
+ struct dirent *de;
+ char fn[256];
+ char tmp[256]="";
+ char *mb, *cur;
+ char *context;
+ int ret;
+ if (!folder)
+ folder = "INBOX";
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+ if (strchr(mailbox, ',')) {
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ mb = tmp;
+ ret = 0;
+ while((cur = strsep(&mb, ","))) {
+ if (!ast_strlen_zero(cur)) {
+ if (has_voicemail(cur, folder))
+ return 1;
+ }
+ }
+ return 0;
+ }
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ } else
+ context = "default";
+ snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
+ dir = opendir(fn);
+ if (!dir)
+ return 0;
+ while ((de = readdir(dir))) {
+ if (!strncasecmp(de->d_name, "msg", 3))
+ break;
+ }
+ closedir(dir);
+ if (de)
+ return 1;
+ return 0;
+}
+
+
+static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
+{
+ DIR *dir;
+ struct dirent *de;
+ char fn[256];
+ char tmp[256]="";
+ char *mb, *cur;
+ char *context;
+ int ret;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+ if (strchr(mailbox, ',')) {
+ int tmpnew, tmpold;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ mb = tmp;
+ ret = 0;
+ while((cur = strsep(&mb, ", "))) {
+ if (!ast_strlen_zero(cur)) {
+ if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+ return -1;
+ else {
+ if (newmsgs)
+ *newmsgs += tmpnew;
+ if (oldmsgs)
+ *oldmsgs += tmpold;
+ }
+ }
+ }
+ return 0;
+ }
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ } else
+ context = "default";
+ if (newmsgs) {
+ snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
+ dir = opendir(fn);
+ if (dir) {
+ while ((de = readdir(dir))) {
+ if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
+ !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
+ (*newmsgs)++;
+
+ }
+ closedir(dir);
+ }
+ }
+ if (oldmsgs) {
+ snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
+ dir = opendir(fn);
+ if (dir) {
+ while ((de = readdir(dir))) {
+ if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
+ !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
+ (*oldmsgs)++;
+
+ }
+ closedir(dir);
+ }
+ }
+ return 0;
+}
+
+#endif
+
+static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
+
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt)
+{
+ char fromdir[256], todir[256], frompath[256], topath[256];
+ char *frombox = mbox(imbox);
+ int recipmsgnum;
+
+ ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
+
+ create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
+
+ make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
+ make_file(frompath, sizeof(frompath), fromdir, msgnum);
+
+ if (vm_lock_path(todir))
+ return ERROR_LOCK_PATH;
+
+ recipmsgnum = 0;
+ do {
+ make_file(topath, sizeof(topath), todir, recipmsgnum);
+ if (!EXISTS(todir, recipmsgnum, topath, chan->language))
+ break;
+ recipmsgnum++;
+ } while (recipmsgnum < recip->maxmsg);
+ if (recipmsgnum < recip->maxmsg) {
+ COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
+ } else {
+ ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
+ }
+ ast_unlock_path(todir);
+ notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
+
+ return 0;
+}
+
+static void run_externnotify(char *context, char *extension)
+{
+ char arguments[255];
+ char ext_context[256] = "";
+ int newvoicemails = 0, oldvoicemails = 0;
+
+ if (!ast_strlen_zero(context))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
+ else
+ ast_copy_string(ext_context, extension, sizeof(ext_context));
+
+ if (!ast_strlen_zero(externnotify)) {
+ if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
+ ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
+ } else {
+ snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
+ ast_log(LOG_DEBUG, "Executing %s\n", arguments);
+ ast_safe_system(arguments);
+ }
+ }
+}
+
+struct leave_vm_options {
+ unsigned int flags;
+ signed char record_gain;
+};
+
+static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
+{
+ char tmptxtfile[256], txtfile[256];
+ char callerid[256];
+ FILE *txt;
+ int res = 0;
+ int msgnum;
+ int duration = 0;
+ int ausemacro = 0;
+ int ousemacro = 0;
+ char date[256];
+ char dir[256];
+ char fn[256];
+ char prefile[256]="";
+ char tempfile[256]="";
+ char ext_context[256] = "";
+ char fmt[80];
+ char *context;
+ char ecodes[16] = "#";
+ char tmp[256] = "", *tmpptr;
+ struct ast_vm_user *vmu;
+ struct ast_vm_user svm;
+ char *category = NULL;
+
+ ast_copy_string(tmp, ext, sizeof(tmp));
+ ext = tmp;
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ tmpptr = strchr(context, '&');
+ } else {
+ tmpptr = strchr(ext, '&');
+ }
+
+ if (tmpptr) {
+ *tmpptr = '\0';
+ tmpptr++;
+ }
+
+ category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
+
+ if (!(vmu = find_user(&svm, context, ext))) {
+ ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
+ if (ast_test_flag(options, OPT_PRIORITY_JUMP) || option_priority_jumping)
+ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return res;
+ }
+
+ /* Setup pre-file if appropriate */
+ if (strcmp(vmu->context, "default"))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
+ else
+ ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
+ if (ast_test_flag(options, OPT_BUSY_GREETING))
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
+ else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
+ snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
+ RETRIEVE(tempfile, -1);
+ if (ast_fileexists(tempfile, NULL, NULL) > 0)
+ ast_copy_string(prefile, tempfile, sizeof(prefile));
+ DISPOSE(tempfile, -1);
+ /* It's easier just to try to make it than to check for its existence */
+ create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
+
+ /* Check current or macro-calling context for special extensions */
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ousemacro = 1;
+ }
+
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ ausemacro = 1;
+ }
+
+ /* Play the beginning intro if desired */
+ if (!ast_strlen_zero(prefile)) {
+ RETRIEVE(prefile, -1);
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ if (ast_streamfile(chan, prefile, chan->language) > -1)
+ res = ast_waitstream(chan, ecodes);
+ } else {
+ ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
+ res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
+ }
+ DISPOSE(prefile, -1);
+ if (res < 0) {
+ ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return -1;
+ }
+ }
+ if (res == '#') {
+ /* On a '#' we skip the instructions */
+ ast_set_flag(options, OPT_SILENT);
+ res = 0;
+ }
+ if (!res && !ast_test_flag(options, OPT_SILENT)) {
+ res = ast_streamfile(chan, INTRO, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, ecodes);
+ if (res == '#') {
+ ast_set_flag(options, OPT_SILENT);
+ res = 0;
+ }
+ }
+ if (res > 0)
+ ast_stopstream(chan);
+ /* Check for a '*' here in case the caller wants to escape from voicemail to something
+ other than the operator -- an automated attendant or mailbox login for example */
+ if (res == '*') {
+ chan->exten[0] = 'a';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ chan->priority = 0;
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return 0;
+ }
+ /* Check for a '0' here */
+ if (res == '0') {
+ transfer:
+ if (ast_test_flag(vmu, VM_OPERATOR)) {
+ chan->exten[0] = 'o';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ ast_play_and_wait(chan, "transfer");
+ chan->priority = 0;
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return 0;
+ } else {
+ ast_play_and_wait(chan, "vm-sorry");
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return 0;
+ }
+ }
+ if (res < 0) {
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return -1;
+ }
+ /* The meat of recording the message... All the announcements and beeps have been played*/
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ if (!ast_strlen_zero(fmt)) {
+ msgnum = 0;
+
+ if (vm_lock_path(dir)) {
+ free_user(vmu);
+ return ERROR_LOCK_PATH;
+ }
+
+ /*
+ * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
+ * in the mailbox. So we should get this first so we don't cut off the first few seconds of the
+ * message.
+ */
+ do {
+ make_file(fn, sizeof(fn), dir, msgnum);
+ if (!EXISTS(dir,msgnum,fn,chan->language))
+ break;
+ msgnum++;
+ } while (msgnum < vmu->maxmsg);
+
+ /* Now play the beep once we have the message number for our next message. */
+ if (res >= 0) {
+ /* Unless we're *really* silent, try to send the beep */
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ }
+ if (msgnum < vmu->maxmsg) {
+ /* assign a variable with the name of the voicemail file */
+ pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
+
+ /* Store information */
+ snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
+ snprintf(tmptxtfile, sizeof(tmptxtfile), "%s.txt.tmp", fn);
+ txt = fopen(tmptxtfile, "w+");
+ if (txt) {
+ get_date(date, sizeof(date));
+ fprintf(txt,
+ ";\n"
+ "; Message Information file\n"
+ ";\n"
+ "[message]\n"
+ "origmailbox=%s\n"
+ "context=%s\n"
+ "macrocontext=%s\n"
+ "exten=%s\n"
+ "priority=%d\n"
+ "callerchan=%s\n"
+ "callerid=%s\n"
+ "origdate=%s\n"
+ "origtime=%ld\n"
+ "category=%s\n",
+ ext,
+ chan->context,
+ chan->macrocontext,
+ chan->exten,
+ chan->priority,
+ chan->name,
+ ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
+ date, (long)time(NULL),
+ category ? category : "");
+ } else
+ ast_log(LOG_WARNING, "Error opening text file for output\n");
+ res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
+ if (res == '0') {
+ if (txt)
+ fclose(txt);
+ goto transfer;
+ }
+ if (res > 0)
+ res = 0;
+ if (txt) {
+ fprintf(txt, "duration=%d\n", duration);
+ fclose(txt);
+ rename(tmptxtfile, txtfile);
+ }
+
+ if (duration < vmminmessage) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
+ DELETE(dir,msgnum,fn);
+ /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ goto leave_vm_out;
+ }
+ /* Are there to be more recipients of this message? */
+ while (tmpptr) {
+ struct ast_vm_user recipu, *recip;
+ char *exten, *context;
+
+ exten = strsep(&tmpptr, "&");
+ context = strchr(exten, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+ if ((recip = find_user(&recipu, context, exten))) {
+ copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
+ free_user(recip);
+ }
+ }
+ if (ast_fileexists(fn, NULL, NULL)) {
+ STORE(dir, vmu->mailbox, vmu->context, msgnum);
+ notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
+ DISPOSE(dir, msgnum);
+ }
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
+ } else {
+ ast_unlock_path(dir);
+ res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_log(LOG_WARNING, "No more messages possible\n");
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ }
+ } else
+ ast_log(LOG_WARNING, "No format for saving voicemail?\n");
+ leave_vm_out:
+ free_user(vmu);
+
+ return res;
+}
+
+static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
+{
+ /* we know max messages, so stop process when number is hit */
+
+ int x,dest;
+ char sfn[256];
+ char dfn[256];
+
+ if (vm_lock_path(dir))
+ return ERROR_LOCK_PATH;
+
+ for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
+ make_file(sfn, sizeof(sfn), dir, x);
+ if (EXISTS(dir, x, sfn, NULL)) {
+
+ if(x != dest) {
+ make_file(dfn, sizeof(dfn), dir, dest);
+ RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
+ }
+
+ dest++;
+ }
+ }
+ ast_unlock_path(dir);
+
+ return 0;
+}
+
+
+static int say_and_wait(struct ast_channel *chan, int num, char *language)
+{
+ int d;
+ d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
+ return d;
+}
+
+static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
+{
+ char sfn[256];
+ char dfn[256];
+ char ddir[256];
+ char *dbox = mbox(box);
+ int x;
+ make_file(sfn, sizeof(sfn), dir, msg);
+ create_dirpath(ddir, sizeof(ddir), context, username, dbox);
+
+ if (vm_lock_path(ddir))
+ return ERROR_LOCK_PATH;
+
+ for (x = 0; x < vmu->maxmsg; x++) {
+ make_file(dfn, sizeof(dfn), ddir, x);
+ if (!EXISTS(ddir, x, dfn, NULL))
+ break;
+ }
+ if (x >= vmu->maxmsg) {
+ ast_unlock_path(ddir);
+ return -1;
+ }
+ if (strcmp(sfn, dfn)) {
+ COPY(dir, msg, ddir, x, username, context, sfn, dfn);
+ }
+ ast_unlock_path(ddir);
+
+ return 0;
+}
+
+static int adsi_logo(unsigned char *buf)
+{
+ int bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
+ return bytes;
+}
+
+static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ int x;
+ char num[5];
+
+ *useadsi = 0;
+ bytes += adsi_data_mode(buf + bytes);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+ bytes = 0;
+ bytes += adsi_logo(buf);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
+#ifdef DISPLAY
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
+#endif
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_data_mode(buf + bytes);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+ if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
+ bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return 0;
+ }
+
+#ifdef DISPLAY
+ /* Add a dot */
+ bytes = 0;
+ bytes += adsi_logo(buf);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+ bytes = 0;
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+#ifdef DISPLAY
+ /* Add another dot */
+ bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+
+ bytes = 0;
+ /* These buttons we load but don't use yet */
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+#ifdef DISPLAY
+ /* Add another dot */
+ bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+
+ bytes = 0;
+ for (x=0;x<5;x++) {
+ snprintf(num, sizeof(num), "%d", x);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
+ }
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+#ifdef DISPLAY
+ /* Add another dot */
+ bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+
+ if (adsi_end_download(chan)) {
+ bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return 0;
+ }
+ bytes = 0;
+ bytes += adsi_download_disconnect(buf + bytes);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+ ast_log(LOG_DEBUG, "Done downloading scripts...\n");
+
+#ifdef DISPLAY
+ /* Add last dot */
+ bytes = 0;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+#endif
+ ast_log(LOG_DEBUG, "Restarting session...\n");
+
+ bytes = 0;
+ /* Load the session now */
+ if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
+ *useadsi = 1;
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
+ } else
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return 0;
+}
+
+static void adsi_begin(struct ast_channel *chan, int *useadsi)
+{
+ int x;
+ if (!adsi_available(chan))
+ return;
+ x = adsi_load_session(chan, adsifdn, adsiver, 1);
+ if (x < 0)
+ return;
+ if (!x) {
+ if (adsi_load_vmail(chan, useadsi)) {
+ ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
+ return;
+ }
+ } else
+ *useadsi = 1;
+}
+
+static void adsi_login(struct ast_channel *chan)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+ if (!adsi_available(chan))
+ return;
+
+ for (x=0;x<8;x++)
+ keys[x] = 0;
+ /* Set one key for next */
+ keys[3] = ADSI_KEY_APPS + 3;
+
+ bytes += adsi_logo(buf + bytes);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
+ bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
+ bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
+ bytes += adsi_set_keys(buf + bytes, keys);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_password(struct ast_channel *chan)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+ if (!adsi_available(chan))
+ return;
+
+ for (x=0;x<8;x++)
+ keys[x] = 0;
+ /* Set one key for next */
+ keys[3] = ADSI_KEY_APPS + 3;
+
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
+ bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
+ bytes += adsi_set_keys(buf + bytes, keys);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_folders(struct ast_channel *chan, int start, char *label)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ unsigned char keys[8];
+ int x,y;
+
+ if (!adsi_available(chan))
+ return;
+
+ for (x=0;x<5;x++) {
+ y = ADSI_KEY_APPS + 12 + start + x;
+ if (y > ADSI_KEY_APPS + 12 + 4)
+ y = 0;
+ keys[x] = ADSI_KEY_SKT | y;
+ }
+ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
+ keys[6] = 0;
+ keys[7] = 0;
+
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_set_keys(buf + bytes, keys);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
+{
+ int bytes=0;
+ unsigned char buf[256];
+ char buf1[256], buf2[256];
+ char fn2[256];
+
+ char cid[256]="";
+ char *val;
+ char *name, *num;
+ char datetime[21]="";
+ FILE *f;
+
+ unsigned char keys[8];
+
+ int x;
+
+ if (!adsi_available(chan))
+ return;
+
+ /* Retrieve important info */
+ snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
+ f = fopen(fn2, "r");
+ if (f) {
+ while (!feof(f)) {
+ fgets((char *)buf, sizeof(buf), f);
+ if (!feof(f)) {
+ char *stringp=NULL;
+ stringp = (char *)buf;
+ strsep(&stringp, "=");
+ val = strsep(&stringp, "=");
+ if (!ast_strlen_zero(val)) {
+ if (!strcmp((char *)buf, "callerid"))
+ ast_copy_string(cid, val, sizeof(cid));
+ if (!strcmp((char *)buf, "origdate"))
+ ast_copy_string(datetime, val, sizeof(datetime));
+ }
+ }
+ }
+ fclose(f);
+ }
+ /* New meaning for keys */
+ for (x=0;x<5;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
+ keys[6] = 0x0;
+ keys[7] = 0x0;
+
+ if (!vms->curmsg) {
+ /* No prev key, provide "Folder" instead */
+ keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ }
+ if (vms->curmsg >= vms->lastmsg) {
+ /* If last message ... */
+ if (vms->curmsg) {
+ /* but not only message, provide "Folder" instead */
+ keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ } else {
+ /* Otherwise if only message, leave blank */
+ keys[3] = 1;
+ }
+ }
+
+ if (!ast_strlen_zero(cid)) {
+ ast_callerid_parse(cid, &name, &num);
+ if (!name)
+ name = num;
+ } else
+ name = "Unknown Caller";
+
+ /* If deleted, show "undeleted" */
+
+ if (vms->deleted[vms->curmsg])
+ keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
+
+ /* Except "Exit" */
+ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
+ snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
+ strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
+ snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
+
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_set_keys(buf + bytes, keys);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
+{
+ int bytes=0;
+ unsigned char buf[256];
+ unsigned char keys[8];
+
+ int x;
+
+ if (!adsi_available(chan))
+ return;
+
+ /* New meaning for keys */
+ for (x=0;x<5;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
+
+ keys[6] = 0x0;
+ keys[7] = 0x0;
+
+ if (!vms->curmsg) {
+ /* No prev key, provide "Folder" instead */
+ keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ }
+ if (vms->curmsg >= vms->lastmsg) {
+ /* If last message ... */
+ if (vms->curmsg) {
+ /* but not only message, provide "Folder" instead */
+ keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ } else {
+ /* Otherwise if only message, leave blank */
+ keys[3] = 1;
+ }
+ }
+
+ /* If deleted, show "undeleted" */
+ if (vms->deleted[vms->curmsg])
+ keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
+
+ /* Except "Exit" */
+ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
+ bytes += adsi_set_keys(buf + bytes, keys);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
+{
+ unsigned char buf[256] = "";
+ char buf1[256] = "", buf2[256] = "";
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+
+ char *newm = (vms->newmessages == 1) ? "message" : "messages";
+ char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
+ if (!adsi_available(chan))
+ return;
+ if (vms->newmessages) {
+ snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
+ if (vms->oldmessages) {
+ strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
+ snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
+ } else {
+ snprintf(buf2, sizeof(buf2), "%s.", newm);
+ }
+ } else if (vms->oldmessages) {
+ snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
+ snprintf(buf2, sizeof(buf2), "%s.", oldm);
+ } else {
+ strcpy(buf1, "You have no messages.");
+ buf2[0] = ' ';
+ buf2[1] = '\0';
+ }
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+
+ for (x=0;x<6;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
+ keys[6] = 0;
+ keys[7] = 0;
+
+ /* Don't let them listen if there are none */
+ if (vms->lastmsg < 0)
+ keys[0] = 1;
+ bytes += adsi_set_keys(buf + bytes, keys);
+
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
+{
+ unsigned char buf[256] = "";
+ char buf1[256] = "", buf2[256] = "";
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+
+ char *mess = (vms->lastmsg == 0) ? "message" : "messages";
+
+ if (!adsi_available(chan))
+ return;
+
+ /* Original command keys */
+ for (x=0;x<6;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
+
+ keys[6] = 0;
+ keys[7] = 0;
+
+ if ((vms->lastmsg + 1) < 1)
+ keys[0] = 0;
+
+ snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
+ strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
+
+ if (vms->lastmsg + 1)
+ snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
+ else
+ strcpy(buf2, "no messages.");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_set_keys(buf + bytes, keys);
+
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+}
+
+/*
+static void adsi_clear(struct ast_channel *chan)
+{
+ char buf[256];
+ int bytes=0;
+ if (!adsi_available(chan))
+ return;
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+*/
+
+static void adsi_goodbye(struct ast_channel *chan)
+{
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (!adsi_available(chan))
+ return;
+ bytes += adsi_logo(buf + bytes);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+/*--- get_folder: Folder menu ---*/
+/* Plays "press 1 for INBOX messages" etc
+ Should possibly be internationalized
+ */
+static int get_folder(struct ast_channel *chan, int start)
+{
+ int x;
+ int d;
+ char fn[256];
+ d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
+ if (d)
+ return d;
+ for (x = start; x< 5; x++) { /* For all folders */
+ if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
+ return d;
+ d = ast_play_and_wait(chan, "vm-for"); /* "for" */
+ if (d)
+ return d;
+ snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
+ d = vm_play_folder_name(chan, fn);
+ if (d)
+ return d;
+ d = ast_waitfordigit(chan, 500);
+ if (d)
+ return d;
+ }
+ d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
+ if (d)
+ return d;
+ d = ast_waitfordigit(chan, 4000);
+ return d;
+}
+
+static int get_folder2(struct ast_channel *chan, char *fn, int start)
+{
+ int res = 0;
+ res = ast_play_and_wait(chan, fn); /* Folder name */
+ while (((res < '0') || (res > '9')) &&
+ (res != '#') && (res >= 0)) {
+ res = get_folder(chan, 0);
+ }
+ return res;
+}
+
+static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts,
+ char *context, signed char record_gain)
+{
+ int cmd = 0;
+ int retries = 0;
+ int duration = 0;
+ signed char zero_gain = 0;
+
+ while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
+ if (cmd)
+ retries = 0;
+ switch (cmd) {
+ case '1':
+ /* prepend a message to the current message and return */
+ {
+ char file[200];
+ snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
+ cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
+ break;
+ }
+ case '2':
+ cmd = 't';
+ break;
+ case '*':
+ cmd = '*';
+ break;
+ default:
+ cmd = ast_play_and_wait(chan,"vm-forwardoptions");
+ /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
+ if (!cmd)
+ cmd = ast_play_and_wait(chan,"vm-starmain");
+ /* "press star to return to the main menu" */
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ cmd = 't';
+ }
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
+{
+ char todir[256], fn[256], ext_context[256], *stringp;
+ int newmsgs = 0, oldmsgs = 0;
+
+ make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
+ make_file(fn, sizeof(fn), todir, msgnum);
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
+
+ /* Attach only the first format */
+ fmt = ast_strdupa(fmt);
+ if (fmt) {
+ stringp = fmt;
+ strsep(&stringp, "|");
+
+ if (!ast_strlen_zero(vmu->email)) {
+ int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
+ char *myserveremail = serveremail;
+ attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
+ if (!ast_strlen_zero(vmu->serveremail))
+ myserveremail = vmu->serveremail;
+ sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
+ }
+
+ if (!ast_strlen_zero(vmu->pager)) {
+ char *myserveremail = serveremail;
+ if (!ast_strlen_zero(vmu->serveremail))
+ myserveremail = vmu->serveremail;
+ sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ }
+
+ if (ast_test_flag(vmu, VM_DELETE)) {
+ DELETE(todir, msgnum, fn);
+ }
+
+ /* Leave voicemail for someone */
+ if (ast_app_has_voicemail(ext_context, NULL)) {
+ ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
+ }
+ manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
+ run_externnotify(vmu->context, vmu->mailbox);
+ return 0;
+}
+
+static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender,
+ char *fmt, int flag, signed char record_gain)
+{
+ char username[70]="";
+ char sys[256];
+ char todir[256];
+ int todircount=0;
+ int duration;
+ struct ast_config *mif;
+ char miffile[256];
+ char fn[256];
+ char callerid[512];
+ char ext_context[256]="";
+ int res = 0, cmd = 0;
+ struct ast_vm_user *receiver = NULL, *extensions = NULL, *vmtmp = NULL, *vmfree;
+ char tmp[256];
+ char *stringp, *s;
+ int saved_messages = 0, found = 0;
+ int valid_extensions = 0;
+
+ while (!res && !valid_extensions) {
+ int use_directory = 0;
+ if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
+ int done = 0;
+ int retries = 0;
+ cmd=0;
+ while((cmd >= 0) && !done ){
+ if (cmd)
+ retries = 0;
+ switch (cmd) {
+ case '1':
+ use_directory = 0;
+ done = 1;
+ break;
+ case '2':
+ use_directory = 1;
+ done=1;
+ break;
+ case '*':
+ cmd = 't';
+ done = 1;
+ break;
+ default:
+ /* Press 1 to enter an extension press 2 to use the directory */
+ cmd = ast_play_and_wait(chan,"vm-forward");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,3000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ {
+ cmd = 't';
+ done = 1;
+ }
+
+ }
+ }
+ if( cmd<0 || cmd=='t' )
+ break;
+ }
+
+ if (use_directory) {
+ /* use app_directory */
+
+ char old_context[sizeof(chan->context)];
+ char old_exten[sizeof(chan->exten)];
+ int old_priority;
+ struct ast_app* app;
+
+
+ app = pbx_findapp("Directory");
+ if (app) {
+ /* make mackup copies */
+ memcpy(old_context, chan->context, sizeof(chan->context));
+ memcpy(old_exten, chan->exten, sizeof(chan->exten));
+ old_priority = chan->priority;
+
+ /* call the the Directory, changes the channel */
+ res = pbx_exec(chan, app, context ? context : "default", 1);
+
+ ast_copy_string(username, chan->exten, sizeof(username));
+
+ /* restore the old context, exten, and priority */
+ memcpy(chan->context, old_context, sizeof(chan->context));
+ memcpy(chan->exten, old_exten, sizeof(chan->exten));
+ chan->priority = old_priority;
+
+ } else {
+ ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
+ ast_clear_flag((&globalflags), VM_DIRECFORWARD);
+ }
+ } else {
+ /* Ask for an extension */
+ res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
+ if (res)
+ break;
+ if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
+ break;
+ }
+
+ /* start all over if no username */
+ if (ast_strlen_zero(username))
+ continue;
+ stringp = username;
+ s = strsep(&stringp, "*");
+ /* start optimistic */
+ valid_extensions = 1;
+ while (s) {
+ /* find_user is going to malloc since we have a NULL as first argument */
+ if ((receiver = find_user(NULL, context, s))) {
+ if (!extensions)
+ vmtmp = extensions = receiver;
+ else {
+ vmtmp->next = receiver;
+ vmtmp = receiver;
+ }
+ found++;
+ } else {
+ valid_extensions = 0;
+ break;
+ }
+ s = strsep(&stringp, "*");
+ }
+ /* break from the loop of reading the extensions */
+ if (valid_extensions)
+ break;
+ /* "I am sorry, that's not a valid extension. Please try again." */
+ res = ast_play_and_wait(chan, "pbx-invalid");
+ }
+ /* check if we're clear to proceed */
+ if (!extensions || !valid_extensions)
+ return res;
+ vmtmp = extensions;
+ if (flag==1) {
+ struct leave_vm_options leave_options;
+ char mailbox[AST_MAX_EXTENSION * 2 + 2];
+ snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
+
+ /* Send VoiceMail */
+ memset(&leave_options, 0, sizeof(leave_options));
+ leave_options.record_gain = record_gain;
+ cmd = leave_voicemail(chan, mailbox, &leave_options);
+ } else {
+ /* Forward VoiceMail */
+ RETRIEVE(dir, curmsg);
+ cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context, record_gain);
+ if (!cmd) {
+ while (!res && vmtmp) {
+ /* if (ast_play_and_wait(chan, "vm-savedto"))
+ break;
+ */
+ snprintf(todir, sizeof(todir), "%s%s/%s/INBOX", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
+ snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
+ ast_log(LOG_DEBUG, "%s", sys);
+ ast_safe_system(sys);
+
+ res = count_messages(receiver, todir);
+
+ if ( (res == ERROR_LOCK_PATH) || (res < 0) ) {
+ if (res == ERROR_LOCK_PATH)
+ ast_log(LOG_WARNING, "Unable to lock the directory %s to forward the requested vmail msg!\n", todir);
+ else
+ ast_log(LOG_WARNING, "Unable to determine how many msgs are in the destination folder!\n");
+ break;
+ }
+ todircount = res;
+ ast_copy_string(tmp, fmt, sizeof(tmp));
+ stringp = tmp;
+ while ((s = strsep(&stringp, "|"))) {
+ /* XXX This is a hack -- we should use build_filename or similar XXX */
+ if (!strcasecmp(s, "wav49"))
+ s = "WAV";
+ snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
+ ast_log(LOG_DEBUG, "%s", sys);
+ ast_safe_system(sys);
+ }
+ snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
+ ast_log(LOG_DEBUG, "%s", sys);
+ ast_safe_system(sys);
+ snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
+
+ STORE(todir, vmtmp->mailbox, vmtmp->context, todircount);
+
+ /* load the information on the source message so we can send an e-mail like a new message */
+ snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
+ if ((mif=ast_config_load(miffile))) {
+
+ /* set callerid and duration variables */
+ snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
+ s = ast_variable_retrieve(mif, NULL, "duration");
+ if (s)
+ duration = atoi(s);
+ else
+ duration = 0;
+ if (!ast_strlen_zero(vmtmp->email)) {
+ int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
+ char *myserveremail = serveremail;
+ attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
+ if (!ast_strlen_zero(vmtmp->serveremail))
+ myserveremail = vmtmp->serveremail;
+ sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, tmp, duration, attach_user_voicemail);
+ }
+
+ if (!ast_strlen_zero(vmtmp->pager)) {
+ char *myserveremail = serveremail;
+ if (!ast_strlen_zero(vmtmp->serveremail))
+ myserveremail = vmtmp->serveremail;
+ sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->context, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, duration, vmtmp);
+ }
+
+ ast_config_destroy(mif); /* or here */
+ }
+ /* Leave voicemail for someone */
+ manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
+ run_externnotify(vmtmp->context, vmtmp->mailbox);
+
+ saved_messages++;
+ vmfree = vmtmp;
+ vmtmp = vmtmp->next;
+ free_user(vmfree);
+ }
+ if (saved_messages > 0) {
+ /* give confirmation that the message was saved */
+ /* commented out since we can't forward batches yet
+ if (saved_messages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-saved"); */
+ res = ast_play_and_wait(chan, "vm-msgsaved");
+ }
+ }
+ }
+ return res ? res : cmd;
+}
+
+static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
+{
+ int res;
+ if ((res = ast_streamfile(chan, file, chan->language)))
+ ast_log(LOG_WARNING, "Unable to play message %s\n", file);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ return res;
+}
+
+static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
+{
+ return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
+}
+
+static int play_message_category(struct ast_channel *chan, char *category)
+{
+ int res = 0;
+
+ if (!ast_strlen_zero(category))
+ res = ast_play_and_wait(chan, category);
+
+ return res;
+}
+
+static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
+{
+ int res = 0;
+ struct vm_zone *the_zone = NULL;
+ time_t t;
+ long tin;
+
+ if (sscanf(origtime,"%ld",&tin) < 1) {
+ ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
+ return 0;
+ }
+ t = tin;
+
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ struct vm_zone *z;
+ z = zones;
+ while (z) {
+ if (!strcmp(z->name, vmu->zonetag)) {
+ the_zone = z;
+ break;
+ }
+ z = z->next;
+ }
+ }
+
+/* No internal variable parsing for now, so we'll comment it out for the time being */
+#if 0
+ /* Set the DIFF_* variables */
+ localtime_r(&t, &time_now);
+ tv_now = ast_tvnow();
+ tnow = tv_now.tv_sec;
+ localtime_r(&tnow,&time_then);
+
+ /* Day difference */
+ if (time_now.tm_year == time_then.tm_year)
+ snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
+ else
+ snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
+ pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
+
+ /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
+#endif
+ if (the_zone)
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
+ else if(!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
+ else if(!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
+ else if(!strcasecmp(chan->language,"de")) /* GERMAN syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
+ else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
+ else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
+ else if (!strcasecmp(chan->language,"gr"))
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
+ else
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
+#if 0
+ pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
+#endif
+ return res;
+}
+
+
+
+static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
+{
+ int res = 0;
+ int i;
+ char *callerid, *name;
+ char prefile[256]="";
+
+
+ /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
+ /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
+ if ((cid == NULL)||(context == NULL))
+ return res;
+
+ /* Strip off caller ID number from name */
+ ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
+ ast_callerid_parse(cid, &name, &callerid);
+ if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
+ /* Check for internal contexts and only */
+ /* say extension when the call didn't come from an internal context in the list */
+ for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
+ ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
+ if ((strcmp(cidinternalcontexts[i], context) == 0))
+ break;
+ }
+ if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
+ if (!res) {
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
+ if (!ast_strlen_zero(prefile)) {
+ /* See if we can find a recorded name for this person instead of their extension number */
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
+ if (!callback)
+ res = wait_file2(chan, vms, "vm-from");
+ res = ast_streamfile(chan, prefile, chan->language) > -1;
+ res = ast_waitstream(chan, "");
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
+ /* BB: Say "from extension" as one saying to sound smoother */
+ if (!callback)
+ res = wait_file2(chan, vms, "vm-from-extension");
+ res = ast_say_digit_str(chan, callerid, "", chan->language);
+ }
+ }
+ }
+ }
+
+ else if (!res){
+ ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
+ /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
+ if (!callback)
+ res = wait_file2(chan, vms, "vm-from-phonenumber");
+ res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
+ }
+ } else {
+ /* Number unknown */
+ ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
+ /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
+ res = wait_file2(chan, vms, "vm-unknown-caller");
+ }
+ return res;
+}
+
+static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
+{
+ int res = 0;
+ int durationm;
+ int durations;
+ /* Verify that we have a duration for the message */
+ if((duration == NULL))
+ return res;
+
+ /* Convert from seconds to minutes */
+ durations=atoi(duration);
+ durationm=(durations / 60);
+
+ ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
+
+ if((!res)&&(durationm>=minduration)) {
+ res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ res = wait_file2(chan, vms, "vm-minutes");
+ }
+ return res;
+}
+
+static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
+{
+ int res = 0;
+ char filename[256],*origtime, *cid, *context, *duration;
+ char *category;
+ struct ast_config *msg_cfg;
+
+ vms->starting = 0;
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
+ adsi_message(chan, vms);
+ if (!vms->curmsg)
+ res = wait_file2(chan, vms, "vm-first"); /* "First" */
+ else if (vms->curmsg == vms->lastmsg)
+ res = wait_file2(chan, vms, "vm-last"); /* "last" */
+ if (!res) {
+ if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
+ res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
+ }
+ else {
+ res = wait_file2(chan, vms, "vm-message"); /* "message" */
+ }
+ if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
+ if (!res)
+ res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ }
+ }
+
+ /* Retrieve info from VM attribute file */
+ make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+ snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
+ RETRIEVE(vms->curdir, vms->curmsg);
+ msg_cfg = ast_config_load(filename);
+ if (!msg_cfg) {
+ ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
+ return 0;
+ }
+
+ if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
+ ast_log(LOG_WARNING, "No origtime?!\n");
+ DISPOSE(vms->curdir, vms->curmsg);
+ ast_config_destroy(msg_cfg);
+ return 0;
+ }
+
+ cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
+ duration = ast_variable_retrieve(msg_cfg, "message", "duration");
+ category = ast_variable_retrieve(msg_cfg, "message", "category");
+
+ context = ast_variable_retrieve(msg_cfg, "message", "context");
+ if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
+ context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
+
+ if (!res)
+ res = play_message_category(chan, category);
+ if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
+ res = play_message_datetime(chan, vmu, origtime, filename);
+ if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
+ res = play_message_callerid(chan, vms, cid, context, 0);
+ if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
+ res = play_message_duration(chan, vms, duration, vmu->saydurationm);
+ /* Allow pressing '1' to skip envelope / callerid */
+ if (res == '1')
+ res = 0;
+ ast_config_destroy(msg_cfg);
+
+ if (!res) {
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
+ vms->heard[vms->curmsg] = 1;
+ res = wait_file(chan, vms, vms->fn);
+ }
+ DISPOSE(vms->curdir, vms->curmsg);
+ return res;
+}
+
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
+{
+ int res = 0;
+ int count_msg, last_msg;
+
+ ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
+
+ /* Rename the member vmbox HERE so that we don't try to return before
+ * we know what's going on.
+ */
+ snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
+
+ make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
+ count_msg = count_messages(vmu, vms->curdir);
+ if (count_msg < 0)
+ return count_msg;
+ else
+ vms->lastmsg = count_msg - 1;
+
+ /*
+ The following test is needed in case sequencing gets messed up.
+ There appears to be more than one way to mess up sequence, so
+ we will not try to find all of the root causes--just fix it when
+ detected.
+ */
+
+ last_msg = last_message_index(vmu, vms->curdir);
+ if (last_msg < 0)
+ return last_msg;
+ else if(vms->lastmsg != last_msg)
+ {
+ ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
+ res = resequence_mailbox(vmu, vms->curdir);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int x, nummsg;
+ int res = 0;
+
+ if (vms->lastmsg <= -1)
+ goto done;
+
+ /* Get the deleted messages fixed */
+ if (vm_lock_path(vms->curdir))
+ return ERROR_LOCK_PATH;
+
+ vms->curmsg = -1;
+ for (x = 0; x < vmu->maxmsg; x++) {
+ if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
+ /* Save this message. It's not in INBOX or hasn't been heard */
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+ if (!EXISTS(vms->curdir, x, vms->fn, NULL))
+ break;
+ vms->curmsg++;
+ make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+ if (strcmp(vms->fn, vms->fn2)) {
+ RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
+ }
+ } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
+ /* Move to old folder before deleting */
+ res = save_to_folder(vmu, vms->curdir, x, vmu->context, vms->username, 1);
+ if (res == ERROR_LOCK_PATH) {
+ /* If save failed do not delete the message */
+ vms->deleted[x] = 0;
+ vms->heard[x] = 0;
+ --x;
+ }
+ }
+ }
+
+ /* Delete ALL remaining messages */
+ nummsg = x - 1;
+ for (x = vms->curmsg + 1; x <= nummsg; x++) {
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+ if (EXISTS(vms->curdir, x, vms->fn, NULL))
+ DELETE(vms->curdir, x, vms->fn);
+ }
+ ast_unlock_path(vms->curdir);
+
+done:
+ if (vms->deleted)
+ memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
+ if (vms->heard)
+ memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
+
+ return 0;
+}
+
+/* In Greek even though we CAN use a syntax like "friends messages"
+ * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
+ * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
+ * syntax for the above three categories which is more elegant.
+*/
+
+static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
+{
+ int cmd;
+ char *buf;
+
+ buf = alloca(strlen(mbox)+2);
+ strcpy(buf, mbox);
+ strcat(buf,"s");
+
+ if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
+ cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
+ if (cmd)
+ return cmd;
+ return ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
+ if (cmd)
+ return cmd;
+ return ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
+ }
+}
+
+static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
+{
+ int cmd;
+
+ if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Italian, Spanish, French or Portuguese syntax */
+ cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
+ if (cmd)
+ return cmd;
+ return ast_play_and_wait(chan, mbox);
+ } else if (!strcasecmp(chan->language, "gr")){
+ return vm_play_folder_name_gr(chan, mbox);
+ } else { /* Default English */
+ cmd = ast_play_and_wait(chan, mbox);
+ if (cmd)
+ return cmd;
+ return ast_play_and_wait(chan, "vm-messages"); /* "messages */
+ }
+}
+
+ /* GREEK SYNTAX
+ In greek the plural for old/new is
+ different so we need the following files
+ We also need vm-denExeteMynhmata because
+ this syntax is different.
+
+ -> vm-Olds.wav : "Palia"
+ -> vm-INBOXs.wav : "Nea"
+ -> vm-denExeteMynhmata : "den exete mynhmata"
+ */
+
+
+static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
+{
+ int res = 0;
+
+ if (vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res)
+ res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
+ if (!res) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ } else if (vms->oldmessages){
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res)
+ res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
+ if ((vms->oldmessages == 1)){
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = ast_play_and_wait(chan, "vm-Olds");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ } else if (!vms->oldmessages && !vms->newmessages)
+ res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
+ return res;
+}
+
+/* Default English syntax */
+static int vm_intro_en(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* ITALIAN syntax */
+static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ if (!vms->oldmessages && !vms->newmessages)
+ res = ast_play_and_wait(chan, "vm-no") ||
+ ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res && vms->newmessages) {
+ res = (vms->newmessages == 1) ?
+ ast_play_and_wait(chan, "digits/un") ||
+ ast_play_and_wait(chan, "vm-nuovo") ||
+ ast_play_and_wait(chan, "vm-message") :
+ /* 2 or more new messages */
+ say_and_wait(chan, vms->newmessages, chan->language) ||
+ ast_play_and_wait(chan, "vm-nuovi") ||
+ ast_play_and_wait(chan, "vm-messages");
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ res = (vms->oldmessages == 1) ?
+ ast_play_and_wait(chan, "digits/un") ||
+ ast_play_and_wait(chan, "vm-vecchio") ||
+ ast_play_and_wait(chan, "vm-message") :
+ /* 2 or more old messages */
+ say_and_wait(chan, vms->oldmessages, chan->language) ||
+ ast_play_and_wait(chan, "vm-vecchi") ||
+ ast_play_and_wait(chan, "vm-messages");
+ }
+ return res ? -1 : 0;
+}
+
+/* SWEDISH syntax */
+static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (res)
+ return res;
+
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ return res;
+ }
+
+ if (vms->newmessages) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "digits/ett");
+ res = res ? res : ast_play_and_wait(chan, "vm-nytt");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-nya");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/ett");
+ res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-gamla");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+
+ return res;
+}
+
+/* NORWEGIAN syntax */
+static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (res)
+ return res;
+
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ return res;
+ }
+
+ if (vms->newmessages) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "digits/1");
+ res = res ? res : ast_play_and_wait(chan, "vm-ny");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-nye");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/1");
+ res = res ? res : ast_play_and_wait(chan, "vm-gamel");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-gamle");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+
+ return res;
+}
+
+/* GERMAN syntax */
+static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "digits/1F");
+ else
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "digits/1F");
+ else
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* SPANISH syntax */
+static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-youhaveno");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ } else {
+ res = ast_play_and_wait(chan, "vm-youhave");
+ }
+ if (!res) {
+ if (vms->newmessages) {
+ if (!res) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "digits/1M");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (vms->oldmessages) {
+ if (!res) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/1M");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ }
+ }
+ }
+return res;
+}
+
+/* FRENCH syntax */
+static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* DUTCH syntax */
+static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ else
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ else
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* PORTUGUESE syntax */
+static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
+ if (!res) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ } else {
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
+ if (!res) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ } else {
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+
+/* CZECH syntax */
+/* in czech there must be declension of word new and message
+ * czech : english : czech : english
+ * --------------------------------------------------------
+ * vm-youhave : you have
+ * vm-novou : one new : vm-zpravu : message
+ * vm-nove : 2-4 new : vm-zpravy : messages
+ * vm-novych : 5-infinite new : vm-zprav : messages
+ * vm-starou : one old
+ * vm-stare : 2-4 old
+ * vm-starych : 5-infinite old
+ * jednu : one - falling 4.
+ * vm-no : no ( no messages )
+ */
+
+static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
+{
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ if (vms->newmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/jednu");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ }
+ if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-novou");
+ if ((vms->newmessages) > 1 && (vms->newmessages < 5))
+ res = ast_play_and_wait(chan, "vm-nove");
+ if (vms->newmessages > 4)
+ res = ast_play_and_wait(chan, "vm-novych");
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-zpravu");
+ if ((vms->newmessages) > 1 && (vms->newmessages < 5))
+ res = ast_play_and_wait(chan, "vm-zpravy");
+ if (vms->newmessages > 4)
+ res = ast_play_and_wait(chan, "vm-zprav");
+ }
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res) {
+ if ((vms->oldmessages == 1))
+ res = ast_play_and_wait(chan, "vm-starou");
+ if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
+ res = ast_play_and_wait(chan, "vm-stare");
+ if (vms->oldmessages > 4)
+ res = ast_play_and_wait(chan, "vm-starych");
+ }
+ if (!res) {
+ if ((vms->oldmessages == 1))
+ res = ast_play_and_wait(chan, "vm-zpravu");
+ if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
+ res = ast_play_and_wait(chan, "vm-zpravy");
+ if (vms->oldmessages > 4)
+ res = ast_play_and_wait(chan, "vm-zprav");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-zpravy");
+ }
+ }
+ }
+ return res;
+}
+
+static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Play voicemail intro - syntax is different for different languages */
+ if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
+ return vm_intro_de(chan, vms);
+ } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
+ return vm_intro_es(chan, vms);
+ } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
+ return vm_intro_it(chan, vms);
+ } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
+ return vm_intro_fr(chan, vms);
+ } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
+ return vm_intro_nl(chan, vms);
+ } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
+ return vm_intro_pt(chan, vms);
+ } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
+ return vm_intro_cz(chan, vms);
+ } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
+ return vm_intro_gr(chan, vms);
+ } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
+ return vm_intro_se(chan, vms);
+ } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
+ return vm_intro_no(chan, vms);
+ } else { /* Default to ENGLISH */
+ return vm_intro_en(chan, vms);
+ }
+}
+
+static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
+{
+ int res = 0;
+ /* Play instructions and wait for new command */
+ while (!res) {
+ if (vms->starting) {
+ if (vms->lastmsg > -1) {
+ res = ast_play_and_wait(chan, "vm-onefor");
+ if (!res)
+ res = vm_play_folder_name(chan, vms->vmbox);
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-opts");
+ } else {
+ if (vms->curmsg)
+ res = ast_play_and_wait(chan, "vm-prev");
+ if (!res && !skipadvanced)
+ res = ast_play_and_wait(chan, "vm-advopts");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-repeat");
+ if (!res && (vms->curmsg != vms->lastmsg))
+ res = ast_play_and_wait(chan, "vm-next");
+ if (!res) {
+ if (!vms->deleted[vms->curmsg])
+ res = ast_play_and_wait(chan, "vm-delete");
+ else
+ res = ast_play_and_wait(chan, "vm-undelete");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-toforward");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-savemessage");
+ }
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-helpexit");
+ if (!res)
+ res = ast_waitfordigit(chan, 6000);
+ if (!res) {
+ vms->repeats++;
+ if (vms->repeats > 2) {
+ res = 't';
+ }
+ }
+ }
+ return res;
+}
+
+static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+{
+ int cmd = 0;
+ int duration = 0;
+ int tries = 0;
+ char newpassword[80] = "";
+ char newpassword2[80] = "";
+ char prefile[256]="";
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (adsi_available(chan)) {
+ bytes += adsi_logo(buf + bytes);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ }
+
+ /* First, have the user change their password
+ so they won't get here again */
+ for (;;) {
+ newpassword[1] = '\0';
+ newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
+ if (cmd == '#')
+ newpassword[0] = '\0';
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ newpassword2[1] = '\0';
+ newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
+ if (cmd == '#')
+ newpassword2[0] = '\0';
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ if (!strcmp(newpassword, newpassword2))
+ break;
+ ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
+ cmd = ast_play_and_wait(chan, "vm-mismatch");
+ if (++tries == 3)
+ return -1;
+ }
+ if (ast_strlen_zero(ext_pass_cmd))
+ vm_change_password(vmu,newpassword);
+ else
+ vm_change_password_shell(vmu,newpassword);
+
+ cmd = ast_play_and_wait(chan,"vm-passchanged");
+
+ /* If forcename is set, have the user record their name */
+ if (ast_test_flag(vmu, VM_FORCENAME)) {
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
+ cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ }
+
+ /* If forcegreetings is set, have the user record their greetings */
+ if (ast_test_flag(vmu, VM_FORCEGREET)) {
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
+ cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
+ cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ }
+
+ return cmd;
+}
+
+static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+{
+ int cmd = 0;
+ int retries = 0;
+ int duration = 0;
+ char newpassword[80] = "";
+ char newpassword2[80] = "";
+ char prefile[256]="";
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (adsi_available(chan))
+ {
+ bytes += adsi_logo(buf + bytes);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ }
+ while ((cmd >= 0) && (cmd != 't')) {
+ if (cmd)
+ retries = 0;
+ switch (cmd) {
+ case '1':
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
+ cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ break;
+ case '2':
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
+ cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ break;
+ case '3':
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
+ cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ break;
+ case '4':
+ cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
+ break;
+ case '5':
+ if (vmu->password[0] == '-') {
+ cmd = ast_play_and_wait(chan, "vm-no");
+ break;
+ }
+ newpassword[1] = '\0';
+ newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
+ if (cmd == '#')
+ newpassword[0] = '\0';
+ else {
+ if (cmd < 0)
+ break;
+ if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
+ break;
+ }
+ }
+ newpassword2[1] = '\0';
+ newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
+ if (cmd == '#')
+ newpassword2[0] = '\0';
+ else {
+ if (cmd < 0)
+ break;
+
+ if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
+ break;
+ }
+ }
+ if (strcmp(newpassword, newpassword2)) {
+ ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
+ cmd = ast_play_and_wait(chan, "vm-mismatch");
+ break;
+ }
+ if (ast_strlen_zero(ext_pass_cmd))
+ vm_change_password(vmu,newpassword);
+ else
+ vm_change_password_shell(vmu,newpassword);
+ ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
+ cmd = ast_play_and_wait(chan,"vm-passchanged");
+ break;
+ case '*':
+ cmd = 't';
+ break;
+ default:
+ cmd = ast_play_and_wait(chan,"vm-options");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ cmd = 't';
+ }
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+{
+ int cmd = 0;
+ int retries = 0;
+ int duration = 0;
+ char prefile[256]="";
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (adsi_available(chan))
+ {
+ bytes += adsi_logo(buf + bytes);
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
+ bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
+ bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += adsi_voice_mode(buf + bytes, 0);
+ adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ }
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
+ while((cmd >= 0) && (cmd != 't')) {
+ if (cmd)
+ retries = 0;
+ RETRIEVE(prefile, -1);
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ switch (cmd) {
+ case '1':
+ cmd = play_record_review(chan,"vm-rec-temp",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ break;
+ case '2':
+ DELETE(prefile, -1, prefile);
+ ast_play_and_wait(chan,"vm-tempremoved");
+ cmd = 't';
+ break;
+ case '*':
+ cmd = 't';
+ break;
+ default:
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ cmd = ast_play_and_wait(chan,"vm-tempgreeting2");
+ } else {
+ cmd = ast_play_and_wait(chan,"vm-tempgreeting");
+ } if (!cmd) {
+ cmd = ast_waitfordigit(chan,6000);
+ } if (!cmd) {
+ retries++;
+ } if (retries > 3) {
+ cmd = 't';
+ }
+ }
+ } else {
+ play_record_review(chan,"vm-rec-temp",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = 't';
+ }
+ DISPOSE(prefile, -1);
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+/* GREEK SYNTAX */
+
+static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-youhaveno");
+ if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ } else {
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ }
+ return cmd;
+}
+
+/* Default English syntax */
+static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-youhave");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-no");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ }
+ return cmd;
+}
+
+/* ITALIAN syntax */
+static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-no");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-message");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ return cmd;
+}
+
+/* SPANISH syntax */
+static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-youhaveno");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ return cmd;
+}
+
+/* PORTUGUESE syntax */
+static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-no");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ }
+ return cmd;
+}
+
+static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ if (!strcasecmp(chan->language, "es")) { /* SPANISH */
+ return vm_browse_messages_es(chan, vms, vmu);
+ } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
+ return vm_browse_messages_it(chan, vms, vmu);
+ } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */
+ return vm_browse_messages_pt(chan, vms, vmu);
+ } else if (!strcasecmp(chan->language, "gr")){
+ return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
+ } else { /* Default to English syntax */
+ return vm_browse_messages_en(chan, vms, vmu);
+ }
+}
+
+static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
+ struct ast_vm_user *res_vmu, const char *context, const char *prefix,
+ int skipuser, int maxlogins, int silent)
+{
+ int useadsi=0, valid=0, logretries=0;
+ char password[AST_MAX_EXTENSION]="", *passptr;
+ struct ast_vm_user vmus, *vmu = NULL;
+
+ /* If ADSI is supported, setup login screen */
+ adsi_begin(chan, &useadsi);
+ if (!skipuser && useadsi)
+ adsi_login(chan);
+ if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
+ ast_log(LOG_WARNING, "Couldn't stream login file\n");
+ return -1;
+ }
+
+ /* Authenticate them and get their mailbox/password */
+
+ while (!valid && (logretries < maxlogins)) {
+ /* Prompt for, and read in the username */
+ if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
+ ast_log(LOG_WARNING, "Couldn't read username\n");
+ return -1;
+ }
+ if (ast_strlen_zero(mailbox)) {
+ if (chan->cid.cid_num) {
+ ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
+ return -1;
+ }
+ }
+ if (useadsi)
+ adsi_password(chan);
+
+ if (!ast_strlen_zero(prefix)) {
+ char fullusername[80] = "";
+ ast_copy_string(fullusername, prefix, sizeof(fullusername));
+ strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
+ ast_copy_string(mailbox, fullusername, mailbox_size);
+ }
+
+ vmu = find_user(&vmus, context, mailbox);
+ if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
+ /* saved password is blank, so don't bother asking */
+ password[0] = '\0';
+ } else {
+ if (ast_streamfile(chan, "vm-password", chan->language)) {
+ ast_log(LOG_WARNING, "Unable to stream password file\n");
+ return -1;
+ }
+ if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
+ ast_log(LOG_WARNING, "Unable to read password\n");
+ return -1;
+ }
+ }
+
+ if (vmu) {
+ passptr = vmu->password;
+ if (passptr[0] == '-') passptr++;
+ }
+ if (vmu && !strcmp(passptr, password))
+ valid++;
+ else {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
+ if (!ast_strlen_zero(prefix))
+ mailbox[0] = '\0';
+ }
+ logretries++;
+ if (!valid) {
+ if (skipuser || logretries >= maxlogins) {
+ if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
+ ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
+ return -1;
+ }
+ } else {
+ if (useadsi)
+ adsi_login(chan);
+ if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
+ ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
+ return -1;
+ }
+ }
+ if (ast_waitstream(chan, "")) /* Channel is hung up */
+ return -1;
+ }
+ }
+ if (!valid && (logretries >= maxlogins)) {
+ ast_stopstream(chan);
+ ast_play_and_wait(chan, "vm-goodbye");
+ return -1;
+ }
+ if (vmu && !skipuser) {
+ memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
+ }
+ return 0;
+}
+
+static int vm_execmain(struct ast_channel *chan, void *data)
+{
+ /* XXX This is, admittedly, some pretty horrendus code. For some
+ reason it just seemed a lot easier to do with GOTO's. I feel
+ like I'm back in my GWBASIC days. XXX */
+ int res=-1;
+ int cmd=0;
+ int valid = 0;
+ struct localuser *u;
+ char prefixstr[80] ="";
+ char ext_context[256]="";
+ int box;
+ int useadsi = 0;
+ int skipuser = 0;
+ struct vm_state vms;
+ struct ast_vm_user *vmu = NULL, vmus;
+ char *context=NULL;
+ int silentexit = 0;
+ struct ast_flags flags = { 0 };
+ signed char record_gain = 0;
+
+ LOCAL_USER_ADD(u);
+
+ memset(&vms, 0, sizeof(vms));
+ vms.lastmsg = -1;
+
+ memset(&vmus, 0, sizeof(vmus));
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ if (!ast_strlen_zero(data)) {
+ char *tmp;
+ int argc;
+ char *argv[2];
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ tmp = ast_strdupa(data);
+ argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc == 2) {
+ if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
+ int gain;
+
+ if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
+ ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ } else {
+ record_gain = (signed char) gain;
+ }
+ }
+ } else {
+ /* old style options parsing */
+ while (*argv[0]) {
+ if (*argv[0] == 's') {
+ ast_set_flag(&flags, OPT_SILENT);
+ argv[0]++;
+ } else if (*argv[0] == 'p') {
+ ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
+ argv[0]++;
+ } else
+ break;
+ }
+
+ }
+
+ valid = ast_test_flag(&flags, OPT_SILENT);
+
+ if ((context = strchr(argv[0], '@')))
+ *context++ = '\0';
+
+ if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
+ ast_copy_string(prefixstr, argv[0], sizeof(prefixstr));
+ else
+ ast_copy_string(vms.username, argv[0], sizeof(vms.username));
+
+ if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
+ skipuser++;
+ else
+ valid = 0;
+ }
+
+ if (!valid)
+ res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
+
+ if (!res) {
+ valid = 1;
+ if (!skipuser)
+ vmu = &vmus;
+ } else {
+ res = 0;
+ }
+
+ /* If ADSI is supported, setup login screen */
+ adsi_begin(chan, &useadsi);
+
+ if (!valid)
+ goto out;
+
+ vms.deleted = calloc(vmu->maxmsg, sizeof(int));
+ vms.heard = calloc(vmu->maxmsg, sizeof(int));
+
+ /* Set language from config to override channel language */
+ if (!ast_strlen_zero(vmu->language))
+ ast_copy_string(chan->language, vmu->language, sizeof(chan->language));
+ create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
+ /* Retrieve old and new message counts */
+ res = open_mailbox(&vms, vmu, 1);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ vms.oldmessages = vms.lastmsg + 1;
+ /* Start in INBOX */
+ res = open_mailbox(&vms, vmu, 0);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ vms.newmessages = vms.lastmsg + 1;
+
+ /* Select proper mailbox FIRST!! */
+ if (!vms.newmessages && vms.oldmessages) {
+ /* If we only have old messages start here */
+ res = open_mailbox(&vms, vmu, 1);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ }
+
+ if (useadsi)
+ adsi_status(chan, &vms);
+ res = 0;
+
+ /* Check to see if this is a new user */
+ if (!strcasecmp(vmu->mailbox, vmu->password) &&
+ (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
+ if (ast_play_and_wait(chan, "vm-newuser") == -1)
+ ast_log(LOG_WARNING, "Couldn't stream new user file\n");
+ cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
+ if ((cmd == 't') || (cmd == '#')) {
+ /* Timeout */
+ res = 0;
+ goto out;
+ } else if (cmd < 0) {
+ /* Hangup */
+ res = -1;
+ goto out;
+ }
+ }
+
+ cmd = vm_intro(chan, &vms);
+
+ vms.repeats = 0;
+ vms.starting = 1;
+ while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
+ /* Run main menu */
+ switch(cmd) {
+ case '1':
+ vms.curmsg = 0;
+ /* Fall through */
+ case '5':
+ cmd = vm_browse_messages(chan, &vms, vmu);
+ break;
+ case '2': /* Change folders */
+ if (useadsi)
+ adsi_folders(chan, 0, "Change to folder...");
+ cmd = get_folder2(chan, "vm-changeto", 0);
+ if (cmd == '#') {
+ cmd = 0;
+ } else if (cmd > 0) {
+ cmd = cmd - '0';
+ res = close_mailbox(&vms, vmu);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ res = open_mailbox(&vms, vmu, cmd);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ cmd = 0;
+ }
+ if (useadsi)
+ adsi_status2(chan, &vms);
+
+ if (!cmd)
+ cmd = vm_play_folder_name(chan, vms.vmbox);
+
+ vms.starting = 1;
+ break;
+ case '3': /* Advanced options */
+ cmd = 0;
+ vms.repeats = 0;
+ while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
+ switch(cmd) {
+ case '1': /* Reply */
+ if (vms.lastmsg > -1) {
+ cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+ case '2': /* Callback */
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
+ if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1) {
+ cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
+ if (cmd == 9) {
+ silentexit = 1;
+ goto out;
+ } else if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ }
+ else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+ case '3': /* Envelope */
+ if (vms.lastmsg > -1) {
+ cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+ case '4': /* Dialout */
+ if (!ast_strlen_zero(vmu->dialout)) {
+ cmd = dialout(chan, vmu, NULL, vmu->dialout);
+ if (cmd == 9) {
+ silentexit = 1;
+ goto out;
+ }
+ }
+ else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+
+ case '5': /* Leave VoiceMail */
+ if (ast_test_flag(vmu, VM_SVMAIL)) {
+ cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts, 1, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan,"vm-sorry");
+ cmd='t';
+ break;
+
+ case '*': /* Return to main menu */
+ cmd = 't';
+ break;
+
+ default:
+ cmd = 0;
+ if (!vms.starting) {
+ cmd = ast_play_and_wait(chan, "vm-toreply");
+ }
+ if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
+ cmd = ast_play_and_wait(chan, "vm-tocallback");
+ }
+ if (!cmd && !vms.starting) {
+ cmd = ast_play_and_wait(chan, "vm-tohearenv");
+ }
+ if (!ast_strlen_zero(vmu->dialout) && !cmd) {
+ cmd = ast_play_and_wait(chan, "vm-tomakecall");
+ }
+ if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
+ cmd=ast_play_and_wait(chan, "vm-leavemsg");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-starmain");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ vms.repeats++;
+ if (vms.repeats > 3)
+ cmd = 't';
+ }
+ }
+ if (cmd == 't') {
+ cmd = 0;
+ vms.repeats = 0;
+ }
+ break;
+ case '4':
+ if (vms.curmsg) {
+ vms.curmsg--;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ break;
+ case '6':
+ if (vms.curmsg < vms.lastmsg) {
+ vms.curmsg++;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ break;
+ case '7':
+ vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
+ if (useadsi)
+ adsi_delete(chan, &vms);
+ if (vms.deleted[vms.curmsg])
+ cmd = ast_play_and_wait(chan, "vm-deleted");
+ else
+ cmd = ast_play_and_wait(chan, "vm-undeleted");
+ if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
+ if (vms.curmsg < vms.lastmsg) {
+ vms.curmsg++;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ }
+ break;
+
+ case '8':
+ if (vms.lastmsg > -1) {
+ cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts, 0, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ break;
+ case '9':
+ if (useadsi)
+ adsi_folders(chan, 1, "Save to folder...");
+ cmd = get_folder2(chan, "vm-savefolder", 1);
+ box = 0; /* Shut up compiler */
+ if (cmd == '#') {
+ cmd = 0;
+ break;
+ } else if (cmd > 0) {
+ box = cmd = cmd - '0';
+ cmd = save_to_folder(vmu, vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ } else if (!cmd) {
+ vms.deleted[vms.curmsg] = 1;
+ } else {
+ vms.deleted[vms.curmsg] = 0;
+ vms.heard[vms.curmsg] = 0;
+ }
+ }
+ make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
+ if (useadsi)
+ adsi_message(chan, &vms);
+ snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
+ if (!cmd) {
+ cmd = ast_play_and_wait(chan, "vm-message");
+ if (!cmd)
+ cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-savedto");
+ if (!cmd)
+ cmd = vm_play_folder_name(chan, vms.fn);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-mailboxfull");
+ }
+ if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
+ if (vms.curmsg < vms.lastmsg) {
+ vms.curmsg++;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ }
+ break;
+ case '*':
+ if (!vms.starting) {
+ cmd = ast_play_and_wait(chan, "vm-onefor");
+ if (!cmd)
+ cmd = vm_play_folder_name(chan, vms.vmbox);
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-opts");
+ if (!cmd)
+ cmd = vm_instructions(chan, &vms, 1);
+ } else
+ cmd = 0;
+ break;
+ case '0':
+ cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
+ if (useadsi)
+ adsi_status(chan, &vms);
+ break;
+ default: /* Nothing */
+ cmd = vm_instructions(chan, &vms, 0);
+ break;
+ }
+ }
+ if ((cmd == 't') || (cmd == '#')) {
+ /* Timeout */
+ res = 0;
+ } else {
+ /* Hangup */
+ res = -1;
+ }
+
+out:
+ if (res > -1) {
+ ast_stopstream(chan);
+ adsi_goodbye(chan);
+ if (valid) {
+ if (silentexit)
+ res = ast_play_and_wait(chan, "vm-dialout");
+ else
+ res = ast_play_and_wait(chan, "vm-goodbye");
+ if (res > 0)
+ res = 0;
+ }
+ if (useadsi)
+ adsi_unload_session(chan);
+ }
+ if (vmu)
+ close_mailbox(&vms, vmu);
+ if (valid) {
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
+ manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
+ run_externnotify(vmu->context, vmu->mailbox);
+ }
+ if (vmu)
+ free_user(vmu);
+ if (vms.deleted)
+ free(vms.deleted);
+ if (vms.heard)
+ free(vms.heard);
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int vm_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ char tmp[256];
+ struct leave_vm_options leave_options;
+ int argc;
+ char *argv[2];
+ struct ast_flags flags = { 0 };
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ LOCAL_USER_ADD(u);
+
+ memset(&leave_options, 0, sizeof(leave_options));
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ if (!ast_strlen_zero(data)) {
+ ast_copy_string(tmp, data, sizeof(tmp));
+ argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc == 2) {
+ if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+ ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING);
+ if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
+ int gain;
+
+ if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
+ ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ } else {
+ leave_options.record_gain = (signed char) gain;
+ }
+ }
+ } else {
+ /* old style options parsing */
+ while (*argv[0]) {
+ if (*argv[0] == 's') {
+ ast_set_flag(&leave_options, OPT_SILENT);
+ argv[0]++;
+ } else if (*argv[0] == 'b') {
+ ast_set_flag(&leave_options, OPT_BUSY_GREETING);
+ argv[0]++;
+ } else if (*argv[0] == 'u') {
+ ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
+ argv[0]++;
+ } else if (*argv[0] == 'j') {
+ ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
+ argv[0]++;
+ } else
+ break;
+ }
+ }
+ } else {
+ res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
+ if (res < 0) {
+ LOCAL_USER_REMOVE(u);
+ return res;
+ }
+ if (ast_strlen_zero(tmp)) {
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ argv[0] = ast_strdupa(tmp);
+ }
+
+ res = leave_voicemail(chan, argv[0], &leave_options);
+
+ if (res == ERROR_LOCK_PATH) {
+ ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
+ /*Send the call to n+101 priority, where n is the current priority*/
+ if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || option_priority_jumping)
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
+ ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ res = 0;
+ }
+
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+static int append_mailbox(char *context, char *mbox, char *data)
+{
+ /* Assumes lock is already held */
+ char tmp[256] = "";
+ char *stringp;
+ char *s;
+ struct ast_vm_user *vmu;
+
+ ast_copy_string(tmp, data, sizeof(tmp));
+ vmu = malloc(sizeof(struct ast_vm_user));
+ if (vmu) {
+ memset(vmu, 0, sizeof(struct ast_vm_user));
+ ast_copy_string(vmu->context, context, sizeof(vmu->context));
+ ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
+
+ populate_defaults(vmu);
+
+ stringp = tmp;
+ if ((s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->password, s, sizeof(vmu->password));
+ if (stringp && (s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
+ if (stringp && (s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->email, s, sizeof(vmu->email));
+ if (stringp && (s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
+ if (stringp && (s = strsep(&stringp, ",")))
+ apply_options(vmu, s);
+
+ vmu->next = NULL;
+ if (usersl)
+ usersl->next = vmu;
+ else
+ users = vmu;
+ usersl = vmu;
+ }
+ return 0;
+}
+
+static int vm_box_exists(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ struct ast_vm_user svm;
+ char *context, *box;
+ int priority_jump = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(mbox);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ box = ast_strdupa(data);
+ if (!box) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, box);
+
+ if (args.options) {
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+
+ if ((context = strchr(args.mbox, '@'))) {
+ *context = '\0';
+ context++;
+ }
+
+ if (find_user(&svm, context, args.mbox)) {
+ pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
+ if (priority_jump || option_priority_jumping)
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
+ ast_log(LOG_WARNING, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box, context, chan->exten, chan->priority + 101);
+ } else
+ pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
+ LOCAL_USER_REMOVE(u);
+ return 0;
+}
+
+static int vmauthenticate(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
+ struct ast_vm_user vmus;
+ char *options = NULL;
+ int silent = 0, skipuser = 0;
+ int res = -1;
+
+ LOCAL_USER_ADD(u);
+
+ if (s) {
+ s = ast_strdupa(s);
+ if (!s) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+ user = strsep(&s, "|");
+ options = strsep(&s, "|");
+ if (user) {
+ s = user;
+ user = strsep(&s, "@");
+ context = strsep(&s, "");
+ if (!ast_strlen_zero(user))
+ skipuser++;
+ ast_copy_string(mailbox, user, sizeof(mailbox));
+ }
+ }
+
+ if (options) {
+ silent = (strchr(options, 's')) != NULL;
+ }
+
+ if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
+ pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
+ pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
+ ast_play_and_wait(chan, "auth-thankyou");
+ res = 0;
+ }
+
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+static char show_voicemail_users_help[] =
+"Usage: show voicemail users [for <context>]\n"
+" Lists all mailboxes currently set up\n";
+
+static char show_voicemail_zones_help[] =
+"Usage: show voicemail zones\n"
+" Lists zone message formats\n";
+
+static int handle_show_voicemail_users(int fd, int argc, char *argv[])
+{
+ struct ast_vm_user *vmu = users;
+ char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
+
+ if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
+ else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
+
+ if (vmu) {
+ if (argc == 3)
+ ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
+ else {
+ int count = 0;
+ while (vmu) {
+ if (!strcmp(argv[4],vmu->context))
+ count++;
+ vmu = vmu->next;
+ }
+ if (count) {
+ vmu = users;
+ ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
+ } else {
+ ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
+ return RESULT_FAILURE;
+ }
+ }
+ while (vmu) {
+ char dirname[256];
+ DIR *vmdir;
+ struct dirent *vment;
+ int vmcount = 0;
+ char count[12];
+
+ if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
+ make_dir(dirname, 255, vmu->context, vmu->mailbox, "INBOX");
+ if ((vmdir = opendir(dirname))) {
+ /* No matter what the format of VM, there will always be a .txt file for each message. */
+ while ((vment = readdir(vmdir)))
+ if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
+ vmcount++;
+ closedir(vmdir);
+ }
+ snprintf(count,sizeof(count),"%d",vmcount);
+ ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
+ }
+ vmu = vmu->next;
+ }
+ } else {
+ ast_cli(fd, "There are no voicemail users currently defined\n");
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_voicemail_zones(int fd, int argc, char *argv[])
+{
+ struct vm_zone *zone = zones;
+ char *output_format = "%-15s %-20s %-45s\n";
+
+ if (argc != 3) return RESULT_SHOWUSAGE;
+
+ if (zone) {
+ ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
+ while (zone) {
+ ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
+ zone = zone->next;
+ }
+ } else {
+ ast_cli(fd, "There are no voicemail zones currently defined\n");
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS;
+}
+
+static char *complete_show_voicemail_users(char *line, char *word, int pos, int state)
+{
+ int which = 0;
+ struct ast_vm_user *vmu = users;
+ char *context = "";
+
+ /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
+ if (pos > 4)
+ return NULL;
+ if (pos == 3) {
+ if (state == 0)
+ return strdup("for");
+ else
+ return NULL;
+ }
+ while (vmu) {
+ if (!strncasecmp(word, vmu->context, strlen(word))) {
+ if (context && strcmp(context, vmu->context)) {
+ if (++which > state) {
+ return strdup(vmu->context);
+ }
+ context = vmu->context;
+ }
+ }
+ vmu = vmu->next;
+ }
+ return NULL;
+}
+
+static struct ast_cli_entry show_voicemail_users_cli =
+ { { "show", "voicemail", "users", NULL },
+ handle_show_voicemail_users, "List defined voicemail boxes",
+ show_voicemail_users_help, complete_show_voicemail_users };
+
+static struct ast_cli_entry show_voicemail_zones_cli =
+ { { "show", "voicemail", "zones", NULL },
+ handle_show_voicemail_zones, "List zone message formats",
+ show_voicemail_zones_help, NULL };
+
+static int load_config(void)
+{
+ struct ast_vm_user *cur, *l;
+ struct vm_zone *zcur, *zl;
+ struct ast_config *cfg;
+ char *cat;
+ struct ast_variable *var;
+ char *notifystr = NULL;
+ char *astattach;
+ char *astsearch;
+ char *astsaycid;
+ char *send_voicemail;
+ char *astcallop;
+ char *astreview;
+ char *astskipcmd;
+ char *asthearenv;
+ char *astsaydurationinfo;
+ char *astsaydurationminfo;
+ char *silencestr;
+ char *maxmsgstr;
+ char *astdirfwd;
+ char *thresholdstr;
+ char *fmt;
+ char *astemail;
+ char *astmailcmd = SENDMAIL;
+ char *s,*q,*stringp;
+ char *dialoutcxt = NULL;
+ char *callbackcxt = NULL;
+ char *exitcxt = NULL;
+ char *extpc;
+ char *emaildateformatstr;
+ int x;
+ int tmpadsi[4];
+
+ cfg = ast_config_load(VOICEMAIL_CONFIG);
+ ast_mutex_lock(&vmlock);
+ cur = users;
+ while (cur) {
+ l = cur;
+ cur = cur->next;
+ ast_set_flag(l, VM_ALLOCED);
+ free_user(l);
+ }
+ zcur = zones;
+ while (zcur) {
+ zl = zcur;
+ zcur = zcur->next;
+ free_zone(zl);
+ }
+ zones = NULL;
+ zonesl = NULL;
+ users = NULL;
+ usersl = NULL;
+ memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
+
+ if (cfg) {
+ /* General settings */
+
+ /* Attach voice message to mail message ? */
+ if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
+ astattach = "yes";
+ ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
+
+ if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
+ astsearch = "no";
+ ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
+
+#ifdef USE_ODBC_STORAGE
+ strcpy(odbc_database, "asterisk");
+ if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
+ ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
+ }
+ strcpy(odbc_table, "voicemessages");
+ if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
+ ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
+ }
+#endif
+ /* Mail command */
+ strcpy(mailcmd, SENDMAIL);
+ if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
+ ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
+
+ maxsilence = 0;
+ if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
+ maxsilence = atoi(silencestr);
+ if (maxsilence > 0)
+ maxsilence *= 1000;
+ }
+
+ if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
+ maxmsg = MAXMSG;
+ } else {
+ maxmsg = atoi(maxmsgstr);
+ if (maxmsg <= 0) {
+ ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
+ maxmsg = MAXMSG;
+ } else if (maxmsg > MAXMSGLIMIT) {
+ ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
+ maxmsg = MAXMSGLIMIT;
+ }
+ }
+
+ /* Load date format config for voicemail mail */
+ if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
+ ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
+ }
+
+ /* External password changing command */
+ if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
+ ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
+ }
+
+ /* External voicemail notify application */
+
+ if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
+ ast_copy_string(externnotify, notifystr, sizeof(externnotify));
+ ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
+ } else {
+ externnotify[0] = '\0';
+ }
+
+ /* Silence treshold */
+ silencethreshold = 256;
+ if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
+ silencethreshold = atoi(thresholdstr);
+
+ if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
+ astemail = ASTERISK_USERNAME;
+ ast_copy_string(serveremail, astemail, sizeof(serveremail));
+
+ vmmaxmessage = 0;
+ if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ vmmaxmessage = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max message time length\n");
+ }
+ }
+
+ vmminmessage = 0;
+ if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ vmminmessage = x;
+ if (maxsilence <= vmminmessage)
+ ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
+ } else {
+ ast_log(LOG_WARNING, "Invalid min message time length\n");
+ }
+ }
+ fmt = ast_variable_retrieve(cfg, "general", "format");
+ if (!fmt)
+ fmt = "wav";
+ ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
+
+ skipms = 3000;
+ if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ maxgreet = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max message greeting length\n");
+ }
+ }
+
+ if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ skipms = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid skipms value\n");
+ }
+ }
+
+ maxlogins = 3;
+ if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ maxlogins = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
+ }
+ }
+
+ /* Force new user to record name ? */
+ if (!(astattach = ast_variable_retrieve(cfg, "general", "forcename")))
+ astattach = "no";
+ ast_set2_flag((&globalflags), ast_true(astattach), VM_FORCENAME);
+
+ /* Force new user to record greetings ? */
+ if (!(astattach = ast_variable_retrieve(cfg, "general", "forcegreetings")))
+ astattach = "no";
+ ast_set2_flag((&globalflags), ast_true(astattach), VM_FORCEGREET);
+
+ if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
+ ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
+ stringp = ast_strdupa(s);
+ for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
+ if (!ast_strlen_zero(stringp)) {
+ q = strsep(&stringp,",");
+ while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
+ q++;
+ ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
+ ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
+ } else {
+ cidinternalcontexts[x][0] = '\0';
+ }
+ }
+ }
+ if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
+ ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
+ astreview = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
+
+ if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
+ ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
+ astcallop = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
+
+ if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
+ ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
+ astsaycid = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
+
+ if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
+ ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
+ send_voicemail = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
+
+ if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
+ ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
+ asthearenv = "yes";
+ }
+ ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
+
+ if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
+ ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
+ astsaydurationinfo = "yes";
+ }
+ ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
+
+ saydurationminfo = 2;
+ if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
+ if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
+ saydurationminfo = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
+ }
+ }
+
+ if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
+ ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
+ astskipcmd = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
+
+ if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
+ ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
+ ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
+ } else {
+ dialcontext[0] = '\0';
+ }
+
+ if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
+ ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
+ ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
+ } else {
+ callcontext[0] = '\0';
+ }
+
+ if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
+ ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
+ ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
+ } else {
+ exitcontext[0] = '\0';
+ }
+
+ if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
+ astdirfwd = "no";
+ ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ var = ast_variable_browse(cfg, cat);
+ if (strcasecmp(cat, "zonemessages")) {
+ /* Process mailboxes in this context */
+ while (var) {
+ append_mailbox(cat, var->name, var->value);
+ var = var->next;
+ }
+ } else {
+ /* Timezones in this context */
+ while (var) {
+ struct vm_zone *z;
+ z = malloc(sizeof(struct vm_zone));
+ if (z != NULL) {
+ char *msg_format, *timezone;
+ msg_format = ast_strdupa(var->value);
+ if (msg_format != NULL) {
+ timezone = strsep(&msg_format, "|");
+ if (msg_format) {
+ ast_copy_string(z->name, var->name, sizeof(z->name));
+ ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
+ ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
+ z->next = NULL;
+ if (zones) {
+ zonesl->next = z;
+ zonesl = z;
+ } else {
+ zones = z;
+ zonesl = z;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
+ free(z);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
+ free(z);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
+ return -1;
+ }
+ var = var->next;
+ }
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ memset(fromstring,0,sizeof(fromstring));
+ memset(pagerfromstring,0,sizeof(pagerfromstring));
+ memset(emailtitle,0,sizeof(emailtitle));
+ strcpy(charset, "ISO-8859-1");
+ if (emailbody) {
+ free(emailbody);
+ emailbody = NULL;
+ }
+ if (emailsubject) {
+ free(emailsubject);
+ emailsubject = NULL;
+ }
+ if (pagerbody) {
+ free(pagerbody);
+ pagerbody = NULL;
+ }
+ if (pagersubject) {
+ free(pagersubject);
+ pagersubject = NULL;
+ }
+ if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
+ ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
+ if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
+ ast_copy_string(fromstring,s,sizeof(fromstring));
+ if ((s=ast_variable_retrieve(cfg, "general", "pagerfromstring")))
+ ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
+ if ((s=ast_variable_retrieve(cfg, "general", "charset")))
+ ast_copy_string(charset,s,sizeof(charset));
+ if ((s=ast_variable_retrieve(cfg, "general", "adsifdn"))) {
+ sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
+ for (x=0; x<4; x++) {
+ memcpy(&adsifdn[x], &tmpadsi[x], 1);
+ }
+ }
+ if ((s=ast_variable_retrieve(cfg, "general", "adsisec"))) {
+ sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
+ for (x=0; x<4; x++) {
+ memcpy(&adsisec[x], &tmpadsi[x], 1);
+ }
+ }
+ if ((s=ast_variable_retrieve(cfg, "general", "adsiver")))
+ if (atoi(s)) {
+ adsiver = atoi(s);
+ }
+ if ((s=ast_variable_retrieve(cfg, "general", "emailtitle"))) {
+ ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
+ ast_copy_string(emailtitle,s,sizeof(emailtitle));
+ }
+ if ((s=ast_variable_retrieve(cfg, "general", "emailsubject")))
+ emailsubject = strdup(s);
+ if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
+ char *tmpread, *tmpwrite;
+ emailbody = strdup(s);
+
+ /* substitute strings \t and \n into the apropriate characters */
+ tmpread = tmpwrite = emailbody;
+ while ((tmpwrite = strchr(tmpread,'\\'))) {
+ int len = strlen("\n");
+ switch (tmpwrite[1]) {
+ case 'n':
+ strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
+ strncpy(tmpwrite,"\n",len);
+ break;
+ case 't':
+ strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
+ strncpy(tmpwrite,"\t",len);
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
+ }
+ tmpread = tmpwrite+len;
+ }
+ }
+ if ((s=ast_variable_retrieve(cfg, "general", "pagersubject")))
+ pagersubject = strdup(s);
+ if ((s=ast_variable_retrieve(cfg, "general", "pagerbody"))) {
+ char *tmpread, *tmpwrite;
+ pagerbody = strdup(s);
+
+ /* substitute strings \t and \n into the apropriate characters */
+ tmpread = tmpwrite = pagerbody;
+ while ((tmpwrite = strchr(tmpread,'\\'))) {
+ int len = strlen("\n");
+ switch (tmpwrite[1]) {
+ case 'n':
+ strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
+ strncpy(tmpwrite,"\n",len);
+ break;
+ case 't':
+ strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
+ strncpy(tmpwrite,"\t",len);
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
+ }
+ tmpread = tmpwrite+len;
+ }
+ }
+ ast_mutex_unlock(&vmlock);
+ ast_config_destroy(cfg);
+ return 0;
+ } else {
+ ast_mutex_unlock(&vmlock);
+ ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
+ return 0;
+ }
+}
+
+int reload(void)
+{
+ return(load_config());
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+ res |= ast_unregister_application(app2);
+ res |= ast_unregister_application(app3);
+ res |= ast_unregister_application(app4);
+ res |= ast_cli_unregister(&show_voicemail_users_cli);
+ res |= ast_cli_unregister(&show_voicemail_zones_cli);
+ ast_uninstall_vm_functions();
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+ res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
+ res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
+ res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
+ res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
+ if (res)
+ return(res);
+
+ if ((res=load_config())) {
+ return(res);
+ }
+
+ ast_cli_register(&show_voicemail_users_cli);
+ ast_cli_register(&show_voicemail_zones_cli);
+
+ /* compute the location of the voicemail spool directory */
+ snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
+
+ ast_install_vm_functions(has_voicemail, messagecount);
+
+#if defined(USE_ODBC_STORAGE) && !defined(EXTENDED_ODBC_STORAGE)
+ ast_log(LOG_WARNING, "The current ODBC storage table format will be changed soon."
+ "Please update your tables as per the README and edit the apps/Makefile "
+ "and uncomment the line containing EXTENDED_ODBC_STORAGE to enable the "
+ "new table format.\n");
+#endif
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
+{
+ int cmd = 0;
+ char destination[80] = "";
+ int retries = 0;
+
+ if (!num) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
+ while (retries < 3 && cmd != 't') {
+ destination[1] = '\0';
+ destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
+ if (!cmd)
+ destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
+ if (!cmd)
+ destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
+ if (!cmd) {
+ cmd = ast_waitfordigit(chan, 6000);
+ if (cmd)
+ destination[0] = cmd;
+ }
+ if (!cmd) {
+ retries++;
+ } else {
+
+ if (cmd < 0)
+ return 0;
+ if (cmd == '*') {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
+ return 0;
+ }
+ if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
+ retries++;
+ else
+ cmd = 't';
+ }
+ }
+ if (retries >= 3) {
+ return 0;
+ }
+
+ } else {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
+ ast_copy_string(destination, num, sizeof(destination));
+ }
+
+ if (!ast_strlen_zero(destination)) {
+ if (destination[strlen(destination) -1 ] == '*')
+ return 0;
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
+ ast_copy_string(chan->exten, destination, sizeof(chan->exten));
+ ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
+ chan->priority = 0;
+ return 9;
+ }
+ return 0;
+}
+
+static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
+ int option, signed char record_gain)
+{
+ int res = 0;
+ char filename[256],*origtime, *cid, *context, *name, *num;
+ struct ast_config *msg_cfg;
+ int retries = 0;
+
+ vms->starting = 0;
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
+
+ /* Retrieve info from VM attribute file */
+
+ make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+ snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
+ RETRIEVE(vms->curdir, vms->curmsg);
+ msg_cfg = ast_config_load(filename);
+ DISPOSE(vms->curdir, vms->curmsg);
+ if (!msg_cfg) {
+ ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
+ return 0;
+ }
+
+ if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
+ return 0;
+
+ cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
+
+ context = ast_variable_retrieve(msg_cfg, "message", "context");
+ if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
+ context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
+
+ if (option == 3) {
+
+ if (!res)
+ res = play_message_datetime(chan, vmu, origtime, filename);
+ if (!res)
+ res = play_message_callerid(chan, vms, cid, context, 0);
+ } else if (option == 2) { /* Call back */
+
+ if (!ast_strlen_zero(cid)) {
+ ast_callerid_parse(cid, &name, &num);
+ while ((res > -1) && (res != 't')) {
+ switch(res) {
+ case '1':
+ if (num) {
+ /* Dial the CID number */
+ res = dialout(chan, vmu, num, vmu->callback);
+ if (res)
+ return 9;
+ } else {
+ res = '2';
+ }
+ break;
+
+ case '2':
+ /* Want to enter a different number, can only do this if there's a dialout context for this user */
+ if (!ast_strlen_zero(vmu->dialout)) {
+ res = dialout(chan, vmu, NULL, vmu->dialout);
+ if (res)
+ return 9;
+ } else {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
+ res = ast_play_and_wait(chan, "vm-sorry");
+ }
+ return res;
+ case '*':
+ res = 't';
+ break;
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+
+ res = ast_play_and_wait(chan, "vm-sorry");
+ retries++;
+ break;
+ default:
+ if (num) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
+ res = ast_play_and_wait(chan, "vm-num-i-have");
+ if (!res)
+ res = play_message_callerid(chan, vms, num, vmu->context, 1);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-tocallnum");
+ /* Only prompt for a caller-specified number if there is a dialout context specified */
+ if (!ast_strlen_zero(vmu->dialout)) {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-calldiffnum");
+ }
+ } else {
+ res = ast_play_and_wait(chan, "vm-nonumber");
+ if (!ast_strlen_zero(vmu->dialout)) {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-toenternumber");
+ }
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-star-cancel");
+ if (!res)
+ res = ast_waitfordigit(chan, 6000);
+ if (!res)
+ retries++;
+ if (retries > 3)
+ res = 't';
+ break;
+
+ }
+ if (res == 't')
+ res = 0;
+ else if (res == '*')
+ res = -1;
+ }
+ }
+
+ }
+ else if (option == 1) { /* Reply */
+ /* Send reply directly to sender */
+ if (!ast_strlen_zero(cid)) {
+ ast_callerid_parse(cid, &name, &num);
+ if (!num) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-nonumber");
+ return res;
+ } else {
+ if (find_user(NULL, vmu->context, num)) {
+ struct leave_vm_options leave_options;
+ char mailbox[AST_MAX_EXTENSION * 2 + 2];
+ snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
+
+ memset(&leave_options, 0, sizeof(leave_options));
+ leave_options.record_gain = record_gain;
+ res = leave_voicemail(chan, mailbox, &leave_options);
+ if (!res)
+ res = 't';
+ return res;
+ } else {
+ /* Sender has no mailbox, can't reply */
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
+ ast_play_and_wait(chan, "vm-nobox");
+ res = 't';
+ return res;
+ }
+ }
+ res = 0;
+ }
+ }
+
+ ast_config_destroy(msg_cfg);
+
+ if (!res) {
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
+ vms->heard[msg] = 1;
+ res = wait_file(chan, vms, vms->fn);
+ }
+ return res;
+}
+
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
+ int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
+ signed char record_gain)
+{
+ /* Record message & let caller review or re-record it, or set options if applicable */
+ int res = 0;
+ int cmd = 0;
+ int max_attempts = 3;
+ int attempts = 0;
+ int recorded = 0;
+ int message_exists = 0;
+ signed char zero_gain = 0;
+ /* Note that urgent and private are for flagging messages as such in the future */
+
+ /* barf if no pointer passed to store duration in */
+ if (duration == NULL) {
+ ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
+ return -1;
+ }
+
+ cmd = '3'; /* Want to start by recording */
+
+ while ((cmd >= 0) && (cmd != 't')) {
+ switch (cmd) {
+ case '1':
+ if (!message_exists) {
+ /* In this case, 1 is to record a message */
+ cmd = '3';
+ break;
+ } else {
+ /* Otherwise 1 is to save the existing message */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
+ ast_streamfile(chan, "vm-msgsaved", chan->language);
+ ast_waitstream(chan, "");
+ STORE(recordfile, vmu->mailbox, vmu->context, -1);
+ DISPOSE(recordfile, -1);
+ cmd = 't';
+ return res;
+ }
+ case '2':
+ /* Review */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
+ ast_streamfile(chan, recordfile, chan->language);
+ cmd = ast_waitstream(chan, AST_DIGIT_ANY);
+ break;
+ case '3':
+ message_exists = 0;
+ /* Record */
+ if (recorded == 1) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
+ }
+ if (recorded && outsidecaller) {
+ cmd = ast_play_and_wait(chan, INTRO);
+ cmd = ast_play_and_wait(chan, "beep");
+ }
+ recorded = 1;
+ /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
+ cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir);
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
+ if (cmd == -1) {
+ /* User has hung up, no options to give */
+ return cmd;
+ }
+ if (cmd == '0') {
+ break;
+ } else if (cmd == '*') {
+ break;
+ }
+#if 0
+ else if (vmu->review && (*duration < 5)) {
+ /* Message is too short */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
+ cmd = ast_play_and_wait(chan, "vm-tooshort");
+ cmd = vm_delete(recordfile);
+ break;
+ }
+ else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
+ /* Message is all silence */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
+ cmd = vm_delete(recordfile);
+ cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-speakup");
+ break;
+ }
+#endif
+ else {
+ /* If all is well, a message exists */
+ message_exists = 1;
+ cmd = 0;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+#if 0
+/* XXX Commented out for the moment because of the dangers of deleting
+ a message while recording (can put the message numbers out of sync) */
+ case '*':
+ /* Cancel recording, delete message, offer to take another message*/
+ cmd = ast_play_and_wait(chan, "vm-deleted");
+ cmd = vm_delete(recordfile);
+ if (outsidecaller) {
+ res = vm_exec(chan, NULL);
+ return res;
+ }
+ else
+ return 1;
+#endif
+ case '0':
+ if (message_exists || recorded) {
+ cmd = ast_play_and_wait(chan, "vm-saveoper");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 3000);
+ if (cmd == '1') {
+ ast_play_and_wait(chan, "vm-msgsaved");
+ cmd = '0';
+ } else {
+ ast_play_and_wait(chan, "vm-deleted");
+ DELETE(recordfile, -1, recordfile);
+ cmd = '0';
+ }
+ }
+ return cmd;
+ default:
+ /* If the caller is an ouside caller, and the review option is enabled,
+ allow them to review the message, but let the owner of the box review
+ their OGM's */
+ if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
+ return cmd;
+ if (message_exists) {
+ cmd = ast_play_and_wait(chan, "vm-review");
+ }
+ else {
+ cmd = ast_play_and_wait(chan, "vm-torerecord");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+
+ if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
+ cmd = ast_play_and_wait(chan, "vm-reachoper");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+#if 0
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
+#endif
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 6000);
+ if (!cmd) {
+ attempts++;
+ }
+ if (attempts > max_attempts) {
+ cmd = 't';
+ }
+ }
+ }
+ if (outsidecaller)
+ ast_play_and_wait(chan, "vm-goodbye");
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+ }
+
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_waitforring.c b/1.2-netsec/apps/app_waitforring.c
new file mode 100644
index 000000000..25e651d9e
--- /dev/null
+++ b/1.2-netsec/apps/app_waitforring.c
@@ -0,0 +1,154 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Wait for Ring Application
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/lock.h"
+
+static char *synopsis = "Wait for Ring Application";
+
+static char *tdesc = "Waits until first ring after time";
+
+static char *desc = " WaitForRing(timeout)\n"
+"Returns 0 after waiting at least timeout seconds. and\n"
+"only after the next ring has completed. Returns 0 on\n"
+"success or -1 on hangup\n";
+
+static char *app = "WaitForRing";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int waitforring_exec(struct ast_channel *chan, void *data)
+{
+ struct localuser *u;
+ struct ast_frame *f;
+ int res = 0;
+ int ms;
+
+ if (!data || (sscanf(data, "%d", &ms) != 1)) {
+ ast_log(LOG_WARNING, "WaitForRing requires an argument (minimum seconds)\n");
+ return 0;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ ms *= 1000;
+ while(ms > 0) {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ res = ms;
+ break;
+ }
+ if (ms > 0) {
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Got a ring but still waiting for timeout\n");
+ }
+ ast_frfree(f);
+ }
+ }
+ /* Now we're really ready for the ring */
+ if (!res) {
+ ms = 99999999;
+ while(ms > 0) {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ res = ms;
+ break;
+ }
+ if (ms > 0) {
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Got a ring after the timeout\n");
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, waitforring_exec, synopsis, desc);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_waitforsilence.c b/1.2-netsec/apps/app_waitforsilence.c
new file mode 100644
index 000000000..615eddbce
--- /dev/null
+++ b/1.2-netsec/apps/app_waitforsilence.c
@@ -0,0 +1,210 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * WaitForSilence Application by David C. Troy <dave@popvox.com>
+ * Version 1.00 2004-01-29
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Wait for Silence
+ * - Waits for up to 'x' milliseconds of silence, 'y' times \n
+ * - WaitForSilence(500,2) will wait for 1/2 second of silence, twice \n
+ * - WaitForSilence(1000,1) will wait for 1 second of silence, once \n
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/dsp.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Wait For Silence";
+static char *app = "WaitForSilence";
+static char *synopsis = "Waits for a specified amount of silence";
+static char *descrip =
+" WaitForSilence(x[|y]) Wait for Silence: Waits for up to 'x' \n"
+"milliseconds of silence, 'y' times or 1 if omitted\n"
+"Set the channel variable WAITSTATUS with to one of these values:"
+"SILENCE - if silence of x ms was detected"
+"TIMEOUT - if silence of x ms was not detected."
+"Examples:\n"
+" - WaitForSilence(500|2) will wait for 1/2 second of silence, twice\n"
+" - WaitForSilence(1000) will wait for 1 second of silence, once\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int do_waiting(struct ast_channel *chan, int maxsilence) {
+
+ struct ast_frame *f;
+ int totalsilence = 0;
+ int dspsilence = 0;
+ int gotsilence = 0;
+ static int silencethreshold = 64;
+ int rfmt = 0;
+ int res = 0;
+ struct ast_dsp *sildet; /* silence detector dsp */
+ time_t start, now;
+ time(&start);
+
+ rfmt = chan->readformat; /* Set to linear mode */
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ return -1;
+ }
+
+ sildet = ast_dsp_new(); /* Create the silence detector */
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, silencethreshold);
+
+ /* Await silence... */
+ f = NULL;
+ for(;;) {
+ res = ast_waitfor(chan, 2000);
+ if (!res) {
+ ast_log(LOG_WARNING, "One waitfor failed, trying another\n");
+ /* Try one more time in case of masq */
+ res = ast_waitfor(chan, 2000);
+ if (!res) {
+ ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
+ res = -1;
+ }
+ }
+
+ if (res < 0) {
+ f = NULL;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_VOICE) {
+ dspsilence = 0;
+ ast_dsp_silence(sildet, f, &dspsilence);
+ if (dspsilence) {
+ totalsilence = dspsilence;
+ time(&start);
+ } else {
+ totalsilence = 0;
+ }
+
+ if (totalsilence >= maxsilence) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Exiting with %dms silence > %dms required\n", totalsilence, maxsilence);
+ /* Ended happily with silence */
+ gotsilence = 1;
+ pbx_builtin_setvar_helper(chan, "WAITSTATUS", "SILENCE");
+ ast_log(LOG_DEBUG, "WAITSTATUS was set to SILENCE\n");
+ ast_frfree(f);
+ break;
+ } else if ( difftime(time(&now),start) >= maxsilence/1000 ) {
+ pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT");
+ ast_log(LOG_DEBUG, "WAITSTATUS was set to TIMEOUT\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ if (rfmt && ast_set_read_format(chan, rfmt)) {
+ ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
+ }
+ ast_dsp_free(sildet);
+ return gotsilence;
+}
+
+static int waitforsilence_exec(struct ast_channel *chan, void *data)
+{
+ int res = 1;
+ struct localuser *u;
+ int maxsilence = 1000;
+ int iterations = 1, i;
+
+ LOCAL_USER_ADD(u);
+
+ res = ast_answer(chan); /* Answer the channel */
+
+ if (!data || ((sscanf(data, "%d|%d", &maxsilence, &iterations) != 2) &&
+ (sscanf(data, "%d", &maxsilence) != 1))) {
+ ast_log(LOG_WARNING, "Using default value of 1000ms, 1 iteration\n");
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Waiting %d time(s) for %d ms silence\n", iterations, maxsilence);
+
+ res = 1;
+ for (i=0; (i<iterations) && (res == 1); i++) {
+ res = do_waiting(chan, maxsilence);
+ }
+ LOCAL_USER_REMOVE(u);
+ if (res > 0)
+ res = 0;
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, waitforsilence_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_while.c b/1.2-netsec/apps/app_while.c
new file mode 100644
index 000000000..7c98afe80
--- /dev/null
+++ b/1.2-netsec/apps/app_while.c
@@ -0,0 +1,369 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2004 - 2005, Anthony Minessale <anthmct@yahoo.com>
+ *
+ * Anthony Minessale <anthmct@yahoo.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief While Loop and ExecIf Implementations
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.h"
+
+#define ALL_DONE(u,ret) {LOCAL_USER_REMOVE(u); return ret;}
+
+
+static char *exec_app = "ExecIf";
+static char *exec_desc =
+"Usage: ExecIF (<expr>|<app>|<data>)\n"
+"If <expr> is true, execute and return the result of <app>(<data>).\n"
+"If <expr> is true, but <app> is not found, then the application\n"
+"will return a non-zero value.";
+static char *exec_synopsis = "Conditional exec";
+
+static char *start_app = "While";
+static char *start_desc =
+"Usage: While(<expr>)\n"
+"Start a While Loop. Execution will return to this point when\n"
+"EndWhile is called until expr is no longer true.\n";
+
+static char *start_synopsis = "Start A While Loop";
+
+
+static char *stop_app = "EndWhile";
+static char *stop_desc =
+"Usage: EndWhile()\n"
+"Return to the previous called While\n\n";
+
+static char *stop_synopsis = "End A While Loop";
+
+static char *tdesc = "While Loops and Conditional Execution";
+
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int execif_exec(struct ast_channel *chan, void *data) {
+ int res=0;
+ struct localuser *u;
+ char *myapp = NULL;
+ char *mydata = NULL;
+ char *expr = NULL;
+ struct ast_app *app = NULL;
+
+ LOCAL_USER_ADD(u);
+
+ expr = ast_strdupa(data);
+ if (!expr) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ if ((myapp = strchr(expr,'|'))) {
+ *myapp = '\0';
+ myapp++;
+ if ((mydata = strchr(myapp,'|'))) {
+ *mydata = '\0';
+ mydata++;
+ } else
+ mydata = "";
+
+ if (ast_true(expr)) {
+ if ((app = pbx_findapp(myapp))) {
+ res = pbx_exec(chan, app, mydata, 1);
+ } else {
+ ast_log(LOG_WARNING, "Count not find application! (%s)\n", myapp);
+ res = -1;
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR,"Invalid Syntax.\n");
+ res = -1;
+ }
+
+ ALL_DONE(u,res);
+}
+
+#define VAR_SIZE 64
+
+
+static char *get_index(struct ast_channel *chan, const char *prefix, int index) {
+ char varname[VAR_SIZE];
+
+ snprintf(varname, VAR_SIZE, "%s_%d", prefix, index);
+ return pbx_builtin_getvar_helper(chan, varname);
+}
+
+static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
+{
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_context *c2;
+
+ for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
+ if (ast_extension_match(ast_get_extension_name(e), exten)) {
+ int needmatch = ast_get_extension_matchcid(e);
+ if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
+ (!needmatch)) {
+ /* This is the matching extension we want */
+ struct ast_exten *p;
+ for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
+ if (priority != ast_get_extension_priority(p))
+ continue;
+ return p;
+ }
+ }
+ }
+ }
+
+ /* No match; run through includes */
+ for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
+ for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
+ if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
+ e = find_matching_priority(c2, exten, priority, callerid);
+ if (e)
+ return e;
+ }
+ }
+ }
+ return NULL;
+}
+
+static int find_matching_endwhile(struct ast_channel *chan)
+{
+ struct ast_context *c;
+ int res=-1;
+
+ if (ast_lock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock contexts list\n");
+ return -1;
+ }
+
+ for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
+ struct ast_exten *e;
+
+ if (!ast_lock_context(c)) {
+ if (!strcmp(ast_get_context_name(c), chan->context)) {
+ /* This is the matching context we want */
+ int cur_priority = chan->priority + 1, level=1;
+
+ for (e = find_matching_priority(c, chan->exten, cur_priority, chan->cid.cid_num); e; e = find_matching_priority(c, chan->exten, ++cur_priority, chan->cid.cid_num)) {
+ if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
+ level++;
+ } else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
+ level--;
+ }
+
+ if (level == 0) {
+ res = cur_priority;
+ break;
+ }
+ }
+ }
+ ast_unlock_context(c);
+ if (res > 0) {
+ break;
+ }
+ }
+ }
+ ast_unlock_contexts();
+ return res;
+}
+
+static int _while_exec(struct ast_channel *chan, void *data, int end)
+{
+ int res=0;
+ struct localuser *u;
+ char *while_pri = NULL;
+ char *goto_str = NULL, *my_name = NULL;
+ char *condition = NULL, *label = NULL;
+ char varname[VAR_SIZE], end_varname[VAR_SIZE];
+ const char *prefix = "WHILE";
+ size_t size=0;
+ int used_index_i = -1, x=0;
+ char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
+
+ if (!chan) {
+ /* huh ? */
+ return -1;
+ }
+
+ LOCAL_USER_ADD(u);
+
+ /* dont want run away loops if the chan isn't even up
+ this is up for debate since it slows things down a tad ......
+ */
+ if (ast_waitfordigit(chan,1) < 0)
+ ALL_DONE(u,-1);
+
+
+ for (x=0;;x++) {
+ if (get_index(chan, prefix, x)) {
+ used_index_i = x;
+ } else
+ break;
+ }
+
+ snprintf(used_index, VAR_SIZE, "%d", used_index_i);
+ snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
+
+ if (!end) {
+ condition = ast_strdupa((char *) data);
+ }
+
+ size = strlen(chan->context) + strlen(chan->exten) + 32;
+ my_name = alloca(size);
+ memset(my_name, 0, size);
+ snprintf(my_name, size, "%s_%s_%d", chan->context, chan->exten, chan->priority);
+
+ if (ast_strlen_zero(label)) {
+ if (end)
+ label = used_index;
+ else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
+ label = new_index;
+ pbx_builtin_setvar_helper(chan, my_name, label);
+ }
+
+ }
+
+ snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
+ while_pri = pbx_builtin_getvar_helper(chan, varname);
+
+ if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
+ snprintf(end_varname,VAR_SIZE,"END_%s",varname);
+ }
+
+
+ if (!end && !ast_true(condition)) {
+ /* Condition Met (clean up helper vars) */
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ pbx_builtin_setvar_helper(chan, my_name, NULL);
+ snprintf(end_varname,VAR_SIZE,"END_%s",varname);
+ if ((goto_str=pbx_builtin_getvar_helper(chan, end_varname))) {
+ pbx_builtin_setvar_helper(chan, end_varname, NULL);
+ ast_parseable_goto(chan, goto_str);
+ } else {
+ int pri = find_matching_endwhile(chan);
+ if (pri > 0) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Jumping to priority %d\n", pri);
+ chan->priority = pri;
+ } else {
+ ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", chan->context, chan->exten, chan->priority);
+ }
+ }
+ ALL_DONE(u,res);
+ }
+
+ if (!end && !while_pri) {
+ size = strlen(chan->context) + strlen(chan->exten) + 32;
+ goto_str = alloca(size);
+ memset(goto_str, 0, size);
+ snprintf(goto_str, size, "%s|%s|%d", chan->context, chan->exten, chan->priority);
+ pbx_builtin_setvar_helper(chan, varname, goto_str);
+ }
+
+ else if (end && while_pri) {
+ /* END of loop */
+ snprintf(end_varname, VAR_SIZE, "END_%s", varname);
+ if (! pbx_builtin_getvar_helper(chan, end_varname)) {
+ size = strlen(chan->context) + strlen(chan->exten) + 32;
+ goto_str = alloca(size);
+ memset(goto_str, 0, size);
+ snprintf(goto_str, size, "%s|%s|%d", chan->context, chan->exten, chan->priority+1);
+ pbx_builtin_setvar_helper(chan, end_varname, goto_str);
+ }
+ ast_parseable_goto(chan, while_pri);
+ }
+
+
+
+
+ ALL_DONE(u, res);
+}
+
+static int while_start_exec(struct ast_channel *chan, void *data) {
+ return _while_exec(chan, data, 0);
+}
+
+static int while_end_exec(struct ast_channel *chan, void *data) {
+ return _while_exec(chan, data, 1);
+}
+
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(start_app);
+ res |= ast_unregister_application(exec_app);
+ res |= ast_unregister_application(stop_app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(start_app, while_start_exec, start_synopsis, start_desc);
+ res |= ast_register_application(exec_app, execif_exec, exec_synopsis, exec_desc);
+ res |= ast_register_application(stop_app, while_end_exec, stop_synopsis, stop_desc);
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/app_zapateller.c b/1.2-netsec/apps/app_zapateller.c
new file mode 100644
index 000000000..3120a4267
--- /dev/null
+++ b/1.2-netsec/apps/app_zapateller.c
@@ -0,0 +1,138 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Playback the special information tone to get rid of telemarketers
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+static char *tdesc = "Block Telemarketers with Special Information Tone";
+
+static char *app = "Zapateller";
+
+static char *synopsis = "Block telemarketers with SIT";
+
+static char *descrip =
+" Zapateller(options): Generates special information tone to block\n"
+"telemarketers from calling you. Options is a pipe-delimited list of\n"
+"options. The following options are available:\n"
+"'answer' causes the line to be answered before playing the tone,\n"
+"'nocallerid' causes Zapateller to only play the tone if there\n"
+"is no callerid information available. Options should be separated by |\n"
+"characters\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int zapateller_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct localuser *u;
+ int answer = 0, nocallerid = 0;
+ char *c;
+ char *stringp=NULL;
+
+ LOCAL_USER_ADD(u);
+
+ stringp=data;
+ c = strsep(&stringp, "|");
+ while(!ast_strlen_zero(c)) {
+ if (!strcasecmp(c, "answer"))
+ answer = 1;
+ else if (!strcasecmp(c, "nocallerid"))
+ nocallerid = 1;
+
+ c = strsep(&stringp, "|");
+ }
+
+ ast_stopstream(chan);
+ if (chan->_state != AST_STATE_UP) {
+
+ if (answer)
+ res = ast_answer(chan);
+ if (!res) {
+ res = ast_safe_sleep(chan, 500);
+ }
+ }
+ if (chan->cid.cid_num && nocallerid) {
+ LOCAL_USER_REMOVE(u);
+ return res;
+ }
+ if (!res)
+ res = ast_tonepair(chan, 950, 0, 330, 0);
+ if (!res)
+ res = ast_tonepair(chan, 1400, 0, 330, 0);
+ if (!res)
+ res = ast_tonepair(chan, 1800, 0, 330, 0);
+ if (!res)
+ res = ast_tonepair(chan, 0, 0, 1000, 0);
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, zapateller_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_zapbarge.c b/1.2-netsec/apps/app_zapbarge.c
new file mode 100644
index 000000000..10c5c49e5
--- /dev/null
+++ b/1.2-netsec/apps/app_zapbarge.c
@@ -0,0 +1,335 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Special thanks to comphealth.com for sponsoring this
+ * GPL application.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Zap Barge support
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#ifdef __linux__
+#include <linux/zaptel.h>
+#else
+#include <zaptel.h>
+#endif /* __linux__ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+
+static char *tdesc = "Barge in on Zap channel application";
+
+static char *app = "ZapBarge";
+
+static char *synopsis = "Barge in (monitor) Zap channel";
+
+static char *descrip =
+" ZapBarge([channel]): Barges in on a specified zap\n"
+"channel or prompts if one is not specified. Returns\n"
+"-1 when caller user hangs up and is independent of the\n"
+"state of the channel being monitored.";
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+
+#define CONF_SIZE 160
+
+static int careful_write(int fd, unsigned char *data, int len)
+{
+ int res;
+ while(len) {
+ res = write(fd, data, len);
+ if (res < 1) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
+ return -1;
+ } else
+ return 0;
+ }
+ len -= res;
+ data += res;
+ }
+ return 0;
+}
+
+static int conf_run(struct ast_channel *chan, int confno, int confflags)
+{
+ int fd;
+ struct zt_confinfo ztc;
+ struct ast_frame *f;
+ struct ast_channel *c;
+ struct ast_frame fr;
+ int outfd;
+ int ms;
+ int nfds;
+ int res;
+ int flags;
+ int retryzap;
+ int origfd;
+ int ret = -1;
+
+ ZT_BUFFERINFO bi;
+ char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+
+ /* Set it into U-law mode (write) */
+ if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
+ goto outrun;
+ }
+
+ /* Set it into U-law mode (read) */
+ if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
+ goto outrun;
+ }
+ ast_indicate(chan, -1);
+ retryzap = strcasecmp(chan->type, "Zap");
+zapretry:
+ origfd = chan->fds[0];
+ if (retryzap) {
+ fd = open("/dev/zap/pseudo", O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ goto outrun;
+ }
+ /* Make non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ /* Setup buffering information */
+ memset(&bi, 0, sizeof(bi));
+ bi.bufsize = CONF_SIZE;
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
+ ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ nfds = 1;
+ } else {
+ /* XXX Make sure we're not running on a pseudo channel XXX */
+ fd = chan->fds[0];
+ nfds = 0;
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Check to see if we're in a conference... */
+ ztc.chan = 0;
+ if (ioctl(fd, ZT_GETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error getting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (ztc.confmode) {
+ /* Whoa, already in a conference... Retry... */
+ if (!retryzap) {
+ ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
+ retryzap = 1;
+ goto zapretry;
+ }
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = confno;
+ ztc.confmode = ZT_CONF_MONITORBOTH;
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
+
+ for(;;) {
+ outfd = -1;
+ ms = -1;
+ c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+ if (c) {
+ if (c->fds[0] != origfd) {
+ if (retryzap) {
+ /* Kill old pseudo */
+ close(fd);
+ }
+ ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
+ retryzap = 0;
+ goto zapretry;
+ }
+ f = ast_read(c);
+ if (!f)
+ break;
+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
+ ret = 0;
+ break;
+ } else if (fd != chan->fds[0]) {
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass == AST_FORMAT_ULAW) {
+ /* Carefully write */
+ careful_write(fd, f->data, f->datalen);
+ } else
+ ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else if (outfd > -1) {
+ res = read(outfd, buf, CONF_SIZE);
+ if (res > 0) {
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_ULAW;
+ fr.datalen = res;
+ fr.samples = res;
+ fr.data = buf;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ if (ast_write(chan, &fr) < 0) {
+ ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+ /* break; */
+ }
+ } else
+ ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+ }
+ }
+ if (fd != chan->fds[0])
+ close(fd);
+ else {
+ /* Take out of conference */
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = 0;
+ ztc.confmode = 0;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ }
+ }
+
+outrun:
+
+ return ret;
+}
+
+static int conf_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ int retrycnt = 0;
+ int confflags = 0;
+ int confno = 0;
+ char confstr[80] = "";
+
+ LOCAL_USER_ADD(u);
+
+ if (!ast_strlen_zero(data)) {
+ if ((sscanf(data, "Zap/%d", &confno) != 1) &&
+ (sscanf(data, "%d", &confno) != 1)) {
+ ast_log(LOG_WARNING, "ZapBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
+ LOCAL_USER_REMOVE(u);
+ return 0;
+ }
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ while(!confno && (++retrycnt < 4)) {
+ /* Prompt user for conference number */
+ confstr[0] = '\0';
+ res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
+ if (res <0) goto out;
+ if (sscanf(confstr, "%d", &confno) != 1)
+ confno = 0;
+ }
+ if (confno) {
+ /* XXX Should prompt user for pin if pin is required XXX */
+ /* Run the conference */
+ res = conf_run(chan, confno, confflags);
+ }
+out:
+ /* Do the conference */
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, conf_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_zapras.c b/1.2-netsec/apps/app_zapras.c
new file mode 100644
index 000000000..4a94f34ba
--- /dev/null
+++ b/1.2-netsec/apps/app_zapras.c
@@ -0,0 +1,276 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Execute an ISDN RAS
+ *
+ * \ingroup applications
+ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#ifdef __linux__
+#include <sys/signal.h>
+#else
+#include <signal.h>
+#endif /* __linux__ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+/* Need some zaptel help here */
+#ifdef __linux__
+#include <linux/zaptel.h>
+#else
+#include <zaptel.h>
+#endif /* __linux__ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+
+static char *tdesc = "Zap RAS Application";
+
+static char *app = "ZapRAS";
+
+static char *synopsis = "Executes Zaptel ISDN RAS application";
+
+static char *descrip =
+" ZapRAS(args): Executes a RAS server using pppd on the given channel.\n"
+"The channel must be a clear channel (i.e. PRI source) and a Zaptel\n"
+"channel to be able to use this function (No modem emulation is included).\n"
+"Your pppd must be patched to be zaptel aware. Arguments should be\n"
+"separated by | characters.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+#define PPP_MAX_ARGS 32
+#define PPP_EXEC "/usr/sbin/pppd"
+
+static pid_t spawn_ras(struct ast_channel *chan, char *args)
+{
+ pid_t pid;
+ int x;
+ char *c;
+
+ char *argv[PPP_MAX_ARGS];
+ int argc = 0;
+ char *stringp=NULL;
+
+ /* Start by forking */
+ pid = fork();
+ if (pid)
+ return pid;
+
+ /* Execute RAS on File handles */
+ dup2(chan->fds[0], STDIN_FILENO);
+
+ /* Close other file descriptors */
+ for (x=STDERR_FILENO + 1;x<1024;x++)
+ close(x);
+
+ /* Restore original signal handlers */
+ for (x=0;x<NSIG;x++)
+ signal(x, SIG_DFL);
+
+ /* Reset all arguments */
+ memset(argv, 0, sizeof(argv));
+
+ /* First argument is executable, followed by standard
+ arguments for zaptel PPP */
+ argv[argc++] = PPP_EXEC;
+ argv[argc++] = "nodetach";
+
+ /* And all the other arguments */
+ stringp=args;
+ c = strsep(&stringp, "|");
+ while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) {
+ argv[argc++] = c;
+ c = strsep(&stringp, "|");
+ }
+
+ argv[argc++] = "plugin";
+ argv[argc++] = "zaptel.so";
+ argv[argc++] = "stdin";
+
+#if 0
+ for (x=0;x<argc;x++) {
+ fprintf(stderr, "Arg %d: %s\n", x, argv[x]);
+ }
+#endif
+
+ /* Finally launch PPP */
+ execv(PPP_EXEC, argv);
+ fprintf(stderr, "Failed to exec PPPD!\n");
+ exit(1);
+}
+
+static void run_ras(struct ast_channel *chan, char *args)
+{
+ pid_t pid;
+ int status;
+ int res;
+ int signalled = 0;
+ struct zt_bufferinfo savebi;
+ int x;
+
+ res = ioctl(chan->fds[0], ZT_GET_BUFINFO, &savebi);
+ if(res) {
+ ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", chan->name);
+ return;
+ }
+
+ pid = spawn_ras(chan, args);
+ if (pid < 0) {
+ ast_log(LOG_WARNING, "Failed to spawn RAS\n");
+ } else {
+ for (;;) {
+ res = wait4(pid, &status, WNOHANG, NULL);
+ if (!res) {
+ /* Check for hangup */
+ if (chan->_softhangup && !signalled) {
+ ast_log(LOG_DEBUG, "Channel '%s' hungup. Signalling RAS at %d to die...\n", chan->name, pid);
+ kill(pid, SIGTERM);
+ signalled=1;
+ }
+ /* Try again */
+ sleep(1);
+ continue;
+ }
+ if (res < 0) {
+ ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno));
+ }
+ if (option_verbose > 2) {
+ if (WIFEXITED(status)) {
+ ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with status %d\n", chan->name, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with signal %d\n",
+ chan->name, WTERMSIG(status));
+ } else {
+ ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated weirdly.\n", chan->name);
+ }
+ }
+ /* Throw back into audio mode */
+ x = 1;
+ ioctl(chan->fds[0], ZT_AUDIOMODE, &x);
+
+ /* Restore saved values */
+ res = ioctl(chan->fds[0], ZT_SET_BUFINFO, &savebi);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name);
+ }
+ break;
+ }
+ }
+}
+
+static int zapras_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ char *args;
+ struct localuser *u;
+ ZT_PARAMS ztp;
+
+ if (!data)
+ data = "";
+
+ LOCAL_USER_ADD(u);
+
+ args = ast_strdupa(data);
+ if (!args) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ LOCAL_USER_REMOVE(u);
+ return -1;
+ }
+
+ /* Answer the channel if it's not up */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+ if (strcasecmp(chan->type, "Zap")) {
+ /* If it's not a zap channel, we're done. Wait a couple of
+ seconds and then hangup... */
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a Zap channel\n", chan->name);
+ sleep(2);
+ } else {
+ memset(&ztp, 0, sizeof(ztp));
+ if (ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp)) {
+ ast_log(LOG_WARNING, "Unable to get zaptel parameters\n");
+ } else if (ztp.sigtype != ZT_SIG_CLEAR) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a clear channel\n", chan->name);
+ } else {
+ /* Everything should be okay. Run PPP. */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Starting RAS on %s\n", chan->name);
+ /* Execute RAS */
+ run_ras(chan, args);
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, zapras_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
diff --git a/1.2-netsec/apps/app_zapscan.c b/1.2-netsec/apps/app_zapscan.c
new file mode 100644
index 000000000..ed0c77977
--- /dev/null
+++ b/1.2-netsec/apps/app_zapscan.c
@@ -0,0 +1,399 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Modified from app_zapbarge by David Troy <dave@toad.net>
+ *
+ * Special thanks to comphealth.com for sponsoring this
+ * GPL application.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Zap Scanner
+ *
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#ifdef __linux__
+#include <linux/zaptel.h>
+#else
+#include <zaptel.h>
+#endif /* __linux__ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+
+static char *tdesc = "Scan Zap channels application";
+
+static char *app = "ZapScan";
+
+static char *synopsis = "Scan Zap channels to monitor calls";
+
+static char *descrip =
+" ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
+"a convenient way. Use '#' to select the next channel and use '*' to exit\n"
+"Limit scanning to a channel GROUP by setting the option group argument.\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+
+#define CONF_SIZE 160
+
+static struct ast_channel *get_zap_channel_locked(int num) {
+ char name[80];
+
+ snprintf(name,sizeof(name),"Zap/%d-1",num);
+ return ast_get_channel_by_name_locked(name);
+}
+
+static int careful_write(int fd, unsigned char *data, int len)
+{
+ int res;
+ while(len) {
+ res = write(fd, data, len);
+ if (res < 1) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
+ return -1;
+ } else
+ return 0;
+ }
+ len -= res;
+ data += res;
+ }
+ return 0;
+}
+
+static int conf_run(struct ast_channel *chan, int confno, int confflags)
+{
+ int fd;
+ struct zt_confinfo ztc;
+ struct ast_frame *f;
+ struct ast_channel *c;
+ struct ast_frame fr;
+ int outfd;
+ int ms;
+ int nfds;
+ int res;
+ int flags;
+ int retryzap;
+ int origfd;
+ int ret = -1;
+ char input[4];
+ int ic=0;
+
+ ZT_BUFFERINFO bi;
+ char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+
+ /* Set it into U-law mode (write) */
+ if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
+ goto outrun;
+ }
+
+ /* Set it into U-law mode (read) */
+ if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
+ goto outrun;
+ }
+ ast_indicate(chan, -1);
+ retryzap = strcasecmp(chan->type, "Zap");
+ zapretry:
+ origfd = chan->fds[0];
+ if (retryzap) {
+ fd = open("/dev/zap/pseudo", O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ goto outrun;
+ }
+ /* Make non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ /* Setup buffering information */
+ memset(&bi, 0, sizeof(bi));
+ bi.bufsize = CONF_SIZE;
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
+ ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ nfds = 1;
+ } else {
+ /* XXX Make sure we're not running on a pseudo channel XXX */
+ fd = chan->fds[0];
+ nfds = 0;
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Check to see if we're in a conference... */
+ ztc.chan = 0;
+ if (ioctl(fd, ZT_GETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error getting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (ztc.confmode) {
+ /* Whoa, already in a conference... Retry... */
+ if (!retryzap) {
+ ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
+ retryzap = 1;
+ goto zapretry;
+ }
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = confno;
+ ztc.confmode = ZT_CONF_MONITORBOTH;
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
+
+ for(;;) {
+ outfd = -1;
+ ms = -1;
+ c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+ if (c) {
+ if (c->fds[0] != origfd) {
+ if (retryzap) {
+ /* Kill old pseudo */
+ close(fd);
+ }
+ ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
+ retryzap = 0;
+ goto zapretry;
+ }
+ f = ast_read(c);
+ if (!f)
+ break;
+ if(f->frametype == AST_FRAME_DTMF) {
+ if(f->subclass == '#') {
+ ret = 0;
+ break;
+ }
+ else if (f->subclass == '*') {
+ ret = -1;
+ break;
+
+ }
+ else {
+ input[ic++] = f->subclass;
+ }
+ if(ic == 3) {
+ input[ic++] = '\0';
+ ic=0;
+ ret = atoi(input);
+ ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
+ break;
+ }
+ }
+
+ if (fd != chan->fds[0]) {
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass == AST_FORMAT_ULAW) {
+ /* Carefully write */
+ careful_write(fd, f->data, f->datalen);
+ } else
+ ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else if (outfd > -1) {
+ res = read(outfd, buf, CONF_SIZE);
+ if (res > 0) {
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_ULAW;
+ fr.datalen = res;
+ fr.samples = res;
+ fr.data = buf;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ if (ast_write(chan, &fr) < 0) {
+ ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+ /* break; */
+ }
+ } else
+ ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+ }
+ }
+ if (fd != chan->fds[0])
+ close(fd);
+ else {
+ /* Take out of conference */
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = 0;
+ ztc.confmode = 0;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ }
+ }
+
+ outrun:
+
+ return ret;
+}
+
+static int conf_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ int confflags = 0;
+ int confno = 0;
+ char confstr[80] = "", *tmp = NULL;
+ struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
+ struct ast_frame *f;
+ char *mygroup;
+ char *desired_group;
+ int input=0,search_group=0;
+
+ LOCAL_USER_ADD(u);
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ desired_group = ast_strdupa((char *) data);
+ if(!ast_strlen_zero(desired_group)) {
+ ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group);
+ search_group = 1;
+ }
+
+ for (;;) {
+ if (ast_waitfor(chan, 100) < 0)
+ break;
+
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ ichan = NULL;
+ if(input) {
+ ichan = get_zap_channel_locked(input);
+ input = 0;
+ }
+
+ tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
+
+ if ( !tempchan && !lastchan )
+ break;
+
+ if (tempchan && search_group) {
+ if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
+ ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
+ } else {
+ ast_mutex_unlock(&tempchan->lock);
+ lastchan = tempchan;
+ continue;
+ }
+ }
+ if ( tempchan && tempchan->type && (!strcmp(tempchan->type, "Zap")) && (tempchan != chan) ) {
+ ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
+ ast_copy_string(confstr, tempchan->name, sizeof(confstr));
+ ast_mutex_unlock(&tempchan->lock);
+ if ((tmp = strchr(confstr,'-'))) {
+ *tmp = '\0';
+ }
+ confno = atoi(strchr(confstr,'/') + 1);
+ ast_stopstream(chan);
+ ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ res = conf_run(chan, confno, confflags);
+ if (res<0) break;
+ input = res;
+ } else if (tempchan)
+ ast_mutex_unlock(&tempchan->lock);
+ lastchan = tempchan;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return res;
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, conf_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
+
diff --git a/1.2-netsec/apps/enter.h b/1.2-netsec/apps/enter.h
new file mode 100644
index 000000000..ac765984a
--- /dev/null
+++ b/1.2-netsec/apps/enter.h
@@ -0,0 +1,287 @@
+/*
+ * U-law 8-bit audio data
+ *
+ * Source: enter.raw
+ *
+ * Copyright (C) 1999, Mark Spencer and Linux Support Services
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char enter[] = {
+0xba, 0xba, 0xb0, 0xa6, 0xa9, 0xb8, 0xfe, 0x46, 0x42, 0x46,
+0x4a, 0xfe, 0xac, 0xa2, 0x9f, 0x9f, 0xa8, 0xb8, 0x3b, 0x29,
+0x35, 0x4a, 0xfe, 0xc1, 0xad, 0xa2, 0xad, 0xc5, 0x4e, 0x68,
+0x68, 0xe7, 0xb8, 0xb0, 0xb2, 0xc1, 0xc1, 0xb0, 0xae, 0xcd,
+0xfe, 0xfe, 0xcd, 0xcd, 0xfe, 0x68, 0xd3, 0xb2, 0xae, 0xab,
+0xb2, 0xfe, 0x35, 0x31, 0xdb, 0xac, 0xab, 0xaf, 0xab, 0xaa,
+0xb4, 0x68, 0x3b, 0x39, 0x3f, 0x68, 0xb4, 0xa8, 0xa8, 0xb0,
+0xbc, 0xbc, 0xc5, 0x3f, 0x31, 0x37, 0xfe, 0xc1, 0xbc, 0xb0,
+0xa5, 0xa2, 0xa8, 0xaf, 0xbe, 0x3b, 0x28, 0x26, 0x3d, 0xbc,
+0xb0, 0xae, 0xa2, 0x9f, 0xa2, 0xfe, 0x29, 0x24, 0x29, 0x4a,
+0xc5, 0xaa, 0xa8, 0xa9, 0xa8, 0xa5, 0xa7, 0xdb, 0x2c, 0x27,
+0x2d, 0x4a, 0xfe, 0xdb, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0xe7,
+0x2c, 0x22, 0x2b, 0xfe, 0xba, 0xb0, 0xaa, 0x9f, 0xa3, 0xb0,
+0x5c, 0x33, 0x33, 0x39, 0x5c, 0xdb, 0xc1, 0xb4, 0xb0, 0xaa,
+0xad, 0xba, 0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xe7, 0xaf,
+0xa6, 0xa7, 0xb0, 0xfe, 0x46, 0x39, 0x5c, 0xe7, 0xdb, 0xfe,
+0xba, 0xac, 0xa8, 0xc5, 0x46, 0x33, 0x54, 0xc5, 0xae, 0xad,
+0xb2, 0xc1, 0xcd, 0xc1, 0xbc, 0xfe, 0x3f, 0x37, 0xfe, 0xb4,
+0xb6, 0xcd, 0xdb, 0xc1, 0xb0, 0xb6, 0xcd, 0x4e, 0x39, 0x37,
+0xfe, 0xb0, 0xab, 0xa9, 0xa9, 0xa9, 0xb0, 0x5c, 0x29, 0x25,
+0x31, 0xfe, 0xc1, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xcd, 0x3b,
+0x2a, 0x2c, 0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8,
+0xfe, 0x33, 0x27, 0x2a, 0x39, 0xfe, 0xc1, 0xbe, 0xb0, 0xa2,
+0x9f, 0xb0, 0x33, 0x22, 0x25, 0x46, 0xc1, 0xb8, 0xb0, 0xab,
+0xa8, 0xa8, 0xb0, 0xbe, 0x42, 0x2c, 0x2e, 0x4a, 0xfe, 0x5c,
+0xfe, 0xb4, 0xa8, 0xa8, 0xba, 0xfe, 0x4a, 0x39, 0x39, 0x46,
+0xfe, 0xbc, 0xaf, 0xa5, 0xa5, 0xae, 0x68, 0x37, 0x4a, 0xfe,
+0xfe, 0x4a, 0x4a, 0xd3, 0xb0, 0xb0, 0xc1, 0x5c, 0x46, 0x46,
+0xd3, 0xb6, 0xbe, 0x54, 0x54, 0xc9, 0xab, 0xae, 0xc5, 0x46,
+0x4a, 0xfe, 0xcd, 0xc9, 0xcd, 0xe7, 0xe7, 0xc9, 0xb4, 0xc5,
+0x4a, 0x2c, 0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb,
+0xfe, 0x4a, 0x46, 0x3f, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6,
+0xb2, 0xcd, 0x33, 0x2e, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xaf,
+0xa7, 0xa7, 0xad, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xcd,
+0xc5, 0xac, 0xa6, 0xac, 0xfe, 0x3b, 0x2c, 0x2d, 0x3d, 0xc1,
+0xb4, 0xbe, 0xcd, 0xaf, 0xa5, 0xa8, 0xe7, 0x31, 0x2f, 0x39,
+0x46, 0x5c, 0xdb, 0xbc, 0xba, 0xaf, 0xa9, 0xad, 0xfe, 0x2f,
+0x2d, 0xba, 0xad, 0xba, 0xfe, 0x3d, 0x42, 0x5c, 0xc9, 0xc1,
+0xcd, 0xfe, 0xc1, 0xae, 0xa6, 0xcd, 0x33, 0x25, 0x3b, 0xdb,
+0xb0, 0xb6, 0xb8, 0xb6, 0xb4, 0xb8, 0xba, 0xfe, 0x3d, 0x37,
+0xfe, 0xba, 0xc1, 0x54, 0x54, 0xd3, 0xb0, 0xb4, 0xe7, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xb6, 0xa9, 0xa7, 0xba,
+0x3d, 0x35, 0xfe, 0xc1, 0xcd, 0x4a, 0x54, 0xbe, 0xb2, 0xb8,
+0xfe, 0x46, 0x3b, 0xfe, 0xba, 0xab, 0xc5, 0x46, 0x3b, 0xbc,
+0xaa, 0xab, 0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xdb, 0x54, 0x3d,
+0x4a, 0xbc, 0xac, 0xb4, 0x3f, 0x2e, 0x3d, 0xba, 0xb0, 0xb8,
+0xba, 0xb6, 0xba, 0xcd, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4,
+0xbe, 0x54, 0x54, 0xcd, 0xb6, 0xc9, 0x46, 0x54, 0xcd, 0xc5,
+0xdb, 0xfe, 0xfe, 0xc1, 0xae, 0xa9, 0xac, 0xfe, 0x35, 0x2e,
+0xfe, 0xba, 0xc1, 0x5c, 0xfe, 0xb6, 0xaa, 0xb0, 0xe7, 0x35,
+0x2e, 0x39, 0xc1, 0xac, 0xb0, 0xfe, 0xfe, 0xbc, 0xa6, 0xac,
+0xc1, 0x42, 0x46, 0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xc9, 0xae,
+0xa9, 0xb0, 0x54, 0x35, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xae,
+0xab, 0xb6, 0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x4e,
+0xfe, 0xb0, 0xac, 0xb8, 0xfe, 0xfe, 0xc1, 0xb6, 0xc5, 0x46,
+0x3d, 0xe7, 0xb4, 0xa7, 0xab, 0xbc, 0x3f, 0x37, 0x54, 0xba,
+0xcd, 0x54, 0x42, 0xc5, 0xae, 0xac, 0xc9, 0x46, 0x3d, 0x54,
+0xba, 0xb0, 0xb0, 0xfe, 0x5c, 0xcd, 0xb0, 0xb0, 0xc9, 0x54,
+0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xc1, 0xba, 0xc5,
+0xfe, 0x42, 0x46, 0xfe, 0xc5, 0xba, 0xb2, 0xa7, 0xa7, 0xb0,
+0xfe, 0x3d, 0x4a, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xb0,
+0xae, 0xc5, 0x4e, 0x39, 0xfe, 0xc5, 0xbe, 0xfe, 0x54, 0xc9,
+0xa9, 0xa2, 0xa5, 0xbc, 0x3b, 0x2f, 0x35, 0xfe, 0xc9, 0xfe,
+0xfe, 0xc5, 0xa9, 0xa6, 0xb0, 0x54, 0x31, 0x31, 0x3f, 0xd3,
+0xbc, 0xc1, 0xcd, 0xb8, 0xae, 0xa8, 0xb4, 0xd3, 0x54, 0x4e,
+0x5c, 0x54, 0xfe, 0xdb, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c,
+0x3d, 0x3f, 0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0xfe,
+0xfe, 0xe7, 0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xb8, 0xaf, 0xb0,
+0xe7, 0x42, 0x4a, 0xcd, 0xbc, 0xdb, 0x46, 0x68, 0xcd, 0xb0,
+0xab, 0xbc, 0xfe, 0x3d, 0x46, 0xfe, 0xb8, 0xbc, 0xd3, 0xd3,
+0xb6, 0xb0, 0xb6, 0x5c, 0x3b, 0x35, 0x54, 0xdb, 0xba, 0xb4,
+0xc1, 0xc9, 0xc1, 0xba, 0xc9, 0x5c, 0x3d, 0x46, 0xfe, 0xcd,
+0xc5, 0xb8, 0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3d, 0x35, 0x46,
+0xfe, 0xdb, 0xbc, 0xb2, 0xa9, 0xab, 0xba, 0x3f, 0x31, 0x39,
+0xfe, 0xe7, 0xdb, 0xcd, 0xb8, 0xae, 0xab, 0xac, 0xe7, 0x3d,
+0x2d, 0x3f, 0xfe, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa8, 0xb0,
+0xfe, 0x31, 0x2d, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xb4, 0xa8,
+0xad, 0xc5, 0x46, 0x39, 0x3f, 0x5c, 0xfe, 0xd3, 0xc5, 0xc1,
+0xb6, 0xb0, 0xbc, 0x68, 0x46, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe,
+0xc1, 0xaf, 0xb0, 0xb8, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe,
+0xfe, 0xe7, 0xb0, 0xab, 0xb2, 0x4a, 0x37, 0x3f, 0xcd, 0xbe,
+0xc1, 0xe7, 0xe7, 0xd3, 0xb6, 0xb4, 0xc9, 0x3b, 0x33, 0x4a,
+0xba, 0xb4, 0xc5, 0xfe, 0xc9, 0xb6, 0xb4, 0xcd, 0xfe, 0x3b,
+0x3b, 0xfe, 0xc1, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xba, 0x4a,
+0x31, 0x35, 0x68, 0xcd, 0xc5, 0xba, 0xb4, 0xb0, 0xb0, 0xba,
+0x5c, 0x35, 0x2f, 0x4e, 0xd3, 0xc1, 0xdb, 0xd3, 0xb4, 0xa9,
+0xab, 0xcd, 0x3b, 0x2f, 0x35, 0xfe, 0xd3, 0xd3, 0xdb, 0xbc,
+0xad, 0xa4, 0xb0, 0xfe, 0x2d, 0x2f, 0x3f, 0xe7, 0xe7, 0xe7,
+0xcd, 0xb4, 0xaf, 0xad, 0xc5, 0x3d, 0x31, 0x3d, 0xe7, 0xd3,
+0xe7, 0xe7, 0xc1, 0xaf, 0xad, 0xb6, 0xfe, 0x4a, 0x42, 0x54,
+0xfe, 0x68, 0xfe, 0xd3, 0xb2, 0xae, 0xb4, 0xfe, 0x42, 0x4e,
+0xcd, 0xc5, 0xcd, 0xdb, 0xc9, 0xb4, 0xb0, 0xb6, 0xfe, 0x3b,
+0x42, 0xe7, 0xb0, 0xb8, 0xcd, 0xfe, 0xc9, 0xb6, 0xb8, 0xfe,
+0x42, 0x3d, 0xfe, 0xc1, 0xb0, 0xba, 0xd3, 0xfe, 0xc1, 0xb0,
+0xb6, 0xfe, 0x3b, 0x3f, 0xe7, 0xba, 0xb8, 0xbc, 0xc5, 0xc1,
+0xc1, 0xcd, 0xfe, 0x3b, 0x37, 0xfe, 0xc1, 0xb4, 0xb6, 0xb8,
+0xb6, 0xb8, 0xc5, 0x5c, 0x3f, 0x46, 0xfe, 0xcd, 0xc5, 0xcd,
+0xcd, 0xc1, 0xb2, 0xb2, 0xfe, 0x3f, 0x35, 0x54, 0xdb, 0xc1,
+0xcd, 0xcd, 0xbc, 0xaf, 0xac, 0xb6, 0x54, 0x35, 0x31, 0x68,
+0xba, 0xb8, 0xcd, 0xdb, 0xc9, 0xb2, 0xb4, 0xc9, 0x46, 0x39,
+0x42, 0xdb, 0xbc, 0xbc, 0xcd, 0xcd, 0xbe, 0xb2, 0xb8, 0xe7,
+0x54, 0x46, 0xfe, 0xfe, 0xdb, 0xc9, 0xc5, 0xbe, 0xbe, 0xc9,
+0xfe, 0x5c, 0x5c, 0xfe, 0xd3, 0xcd, 0xcd, 0xc5, 0xb6, 0xb2,
+0xc5, 0x68, 0x4e, 0xfe, 0xc5, 0xc1, 0xcd, 0x68, 0x5c, 0xe7,
+0xb8, 0xb6, 0xd3, 0x4a, 0x46, 0xfe, 0xbc, 0xb8, 0xc1, 0xe7,
+0xe7, 0xc1, 0xb4, 0xbe, 0xfe, 0x3f, 0x3f, 0xfe, 0xba, 0xb2,
+0xba, 0xe7, 0xfe, 0xcd, 0xcd, 0xfe, 0x4e, 0x46, 0xfe, 0xc5,
+0xb8, 0xb2, 0xba, 0xc1, 0xcd, 0xd3, 0xe7, 0xfe, 0x5c, 0x5c,
+0xfe, 0xe7, 0xc5, 0xbe, 0xb6, 0xba, 0xc5, 0xfe, 0x3f, 0x3f,
+0x54, 0xfe, 0xd3, 0xc1, 0xbc, 0xb6, 0xb0, 0xb0, 0xd3, 0x54,
+0x39, 0x46, 0xfe, 0xc1, 0xcd, 0xe7, 0xe7, 0xc5, 0xb8, 0xb4,
+0xd3, 0x54, 0x37, 0x42, 0xdb, 0xbe, 0xc1, 0xd3, 0xcd, 0xb8,
+0xb0, 0xb0, 0xcd, 0x4a, 0x3b, 0x42, 0xe7, 0xc5, 0xbe, 0xcd,
+0xe7, 0xd3, 0xc5, 0xcd, 0xfe, 0x54, 0x54, 0x68, 0xe7, 0xc5,
+0xc1, 0xc1, 0xcd, 0xcd, 0xc9, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe,
+0xfe, 0xe7, 0xc5, 0xbe, 0xc1, 0xfe, 0x5c, 0x5c, 0xfe, 0xcd,
+0xcd, 0xcd, 0xdb, 0xd3, 0xc1, 0xbc, 0xbe, 0xfe, 0x4e, 0x54,
+0xcd, 0xb6, 0xb8, 0xd3, 0x5c, 0x5c, 0xfe, 0xc5, 0xc9, 0xfe,
+0x46, 0x4a, 0xe7, 0xb4, 0xb6, 0xc5, 0xfe, 0xe7, 0xcd, 0xc9,
+0xdb, 0xfe, 0x4e, 0x68, 0xd3, 0xb6, 0xb2, 0xbc, 0xfe, 0x68,
+0xfe, 0xfe, 0x68, 0x54, 0x68, 0xe7, 0xc5, 0xbc, 0xb8, 0xbe,
+0xcd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3, 0xcd,
+0xc1, 0xb8, 0xbc, 0xdb, 0x4e, 0x42, 0x4a, 0xfe, 0xc9, 0xc1,
+0xcd, 0xd3, 0xcd, 0xba, 0xb8, 0xcd, 0x46, 0x3b, 0xfe, 0xc9,
+0xba, 0xcd, 0xe7, 0xfe, 0xd3, 0xc1, 0xba, 0xdb, 0x54, 0x3d,
+0x68, 0xd3, 0xbc, 0xcd, 0xfe, 0xfe, 0xc5, 0xbe, 0xc1, 0xe7,
+0x54, 0x4a, 0xfe, 0xc9, 0xc1, 0xcd, 0xfe, 0xfe, 0xd3, 0xd3,
+0xd3, 0xfe, 0xe7, 0xe7, 0xe7, 0xdb, 0xd3, 0xe7, 0xe7, 0xe7,
+0xfe, 0xfe, 0xfe, 0xfe, 0xcd, 0xc9, 0xdb, 0xfe, 0xfe, 0xdb,
+0xbe, 0xc9, 0xfe, 0x5c, 0xfe, 0xc9, 0xbc, 0xbe, 0xdb, 0x68,
+0x5c, 0xdb, 0xc5, 0xd3, 0x54, 0x46, 0xfe, 0xbc, 0xb2, 0xb8,
+0xdb, 0x68, 0x68, 0xe7, 0xcd, 0xdb, 0x5c, 0x54, 0xfe, 0xc1,
+0xb8, 0xc1, 0xe7, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
+0xd3, 0xc5, 0xc1, 0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0x54, 0x4e,
+0xfe, 0xd3, 0xcd, 0xd3, 0xd3, 0xc5, 0xc1, 0xc1, 0xe7, 0x5c,
+0x4e, 0x5c, 0xd3, 0xc1, 0xcd, 0xfe, 0xfe, 0xcd, 0xba, 0xba,
+0xe7, 0x4a, 0x4a, 0x68, 0xcd, 0xc5, 0xcd, 0xfe, 0xfe, 0xcd,
+0xb8, 0xc1, 0xe7, 0x4e, 0x5c, 0xe7, 0xc1, 0xc9, 0xdb, 0xfe,
+0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0x68, 0xfe, 0xdb, 0xd3, 0xe7,
+0xfe, 0xfe, 0xcd, 0xc9, 0xcd, 0xd3, 0xd3, 0xd3, 0xcd, 0xe7,
+0xfe, 0xfe, 0xe7, 0xc5, 0xc5, 0xe7, 0x68, 0x68, 0xe7, 0xc1,
+0xc5, 0xfe, 0x5c, 0xfe, 0xd3, 0xc1, 0xd3, 0xfe, 0x68, 0xe7,
+0xc5, 0xb6, 0xc5, 0xe7, 0x68, 0xfe, 0xcd, 0xc5, 0xe7, 0xfe,
+0x54, 0xfe, 0xc9, 0xc5, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3,
+0xfe, 0xfe, 0xfe, 0xcd, 0xc1, 0xc1, 0xc9, 0xd3, 0xd3, 0xe7,
+0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xd3, 0xdb, 0xe7, 0xe7, 0xd3,
+0xcd, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xd3, 0xe7, 0xe7,
+0xc9, 0xbc, 0xbe, 0xe7, 0x68, 0x4a, 0xfe, 0xdb, 0xcd, 0xfe,
+0xfe, 0xfe, 0xcd, 0xc1, 0xc9, 0xfe, 0x54, 0x5c, 0xe7, 0xc9,
+0xc5, 0xe7, 0xfe, 0xfe, 0xcd, 0xc5, 0xc5, 0xe7, 0xfe, 0xfe,
+0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xdb, 0xd3, 0xd3, 0xdb, 0xe7,
+0xfe, 0xfe, 0xe7, 0xe7, 0xdb, 0xd3, 0xc9, 0xd3, 0xe7, 0xfe,
+0xfe, 0xd3, 0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xcd, 0xcd,
+0xfe, 0xfe, 0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd,
+0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xdb, 0xc9, 0xcd, 0xe7, 0xfe,
+0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7, 0xd3, 0xc5, 0xcd,
+0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe,
+0xe7, 0xcd, 0xcd, 0xd3, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe,
+0xe7, 0xe7, 0xdb, 0xc9, 0xc1, 0xc5, 0xfe, 0x5c, 0x68, 0xfe,
+0xd3, 0xdb, 0xe7, 0xe7, 0xe7, 0xd3, 0xc5, 0xcd, 0xe7, 0x68,
+0xfe, 0xe7, 0xcd, 0xd3, 0xe7, 0xfe, 0xe7, 0xcd, 0xc1, 0xc1,
+0xdb, 0xfe, 0x54, 0xfe, 0xe7, 0xcd, 0xe7, 0xfe, 0xe7, 0xd3,
+0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xcd, 0xfe,
+0xfe, 0xe7, 0xcd, 0xd3, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7,
+0xd3, 0xd3, 0xe7, 0xfe, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
+0xfe, 0xdb, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9,
+0xcd, 0xe7, 0xfe, 0xfe, 0xd3, 0xcd, 0xdb, 0xfe, 0x5c, 0xfe,
+0xcd, 0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9, 0xcd, 0xfe,
+0x68, 0xfe, 0xd3, 0xc1, 0xc1, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7,
+0xfe, 0xfe, 0x68, 0xfe, 0xe7, 0xc5, 0xc9, 0xdb, 0xfe, 0xfe,
+0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xdb, 0xc5, 0xc5,
+0xd3, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe,
+0xc9, 0xc1, 0xc5, 0xfe, 0x54, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd,
+0xfe, 0xfe, 0xdb, 0xc5, 0xc9, 0xfe, 0x5c, 0x68, 0xfe, 0xcd,
+0xcd, 0xfe, 0xfe, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe,
+0xdb, 0xc9, 0xc5, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe,
+0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe,
+0xe7, 0xd3, 0xcd, 0xd3, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7,
+0xfe, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xd3, 0xc5,
+0xc9, 0xfe, 0x5c, 0x54, 0xfe, 0xcd, 0xc1, 0xcd, 0xe7, 0xfe,
+0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x5c, 0xe7, 0xc1, 0xc1, 0xd3,
+0xfe, 0xfe, 0xe7, 0xd3, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd,
+0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xe7, 0xd3, 0xcd, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe, 0xfe, 0xdb,
+0xc9, 0xcd, 0xe7, 0xfe, 0xe7, 0xc9, 0xc5, 0xdb, 0xfe, 0x5c,
+0xfe, 0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3,
+0xfe, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7,
+0xd3, 0xe7, 0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe,
+0xfe, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xe7, 0xdb, 0xcd, 0xd3,
+0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe, 0xdb, 0xd3, 0xdb, 0xe7,
+0xe7, 0xdb, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xc5,
+0xcd, 0xe7, 0xfe, 0xdb, 0xd3, 0xe7, 0xfe, 0x68, 0xfe, 0xe7,
+0xcd, 0xcd, 0xd3, 0xfe, 0xfe, 0xe7, 0xdb, 0xe7, 0xfe, 0x68,
+0xfe, 0xdb, 0xfe, 0x68, 0xbe, 0xb2, 0xae, 0xab, 0xb2, 0xfe,
+0x2f, 0x31, 0xdb, 0xac, 0xad, 0xaf, 0xab, 0xab, 0xb4, 0x68,
+0x37, 0x39, 0x3f, 0xe7, 0xb4, 0xa8, 0xaa, 0xb0, 0xbc, 0xbc,
+0xc5, 0x3f, 0x31, 0x3d, 0xfe, 0xc1, 0xb8, 0xb0, 0xa5, 0xa2,
+0xa8, 0xaf, 0xdb, 0x3b, 0x28, 0x2a, 0x3d, 0xbc, 0xb0, 0xaa,
+0xa2, 0x9f, 0xab, 0xfe, 0x29, 0x24, 0x29, 0x4a, 0xb4, 0xaa,
+0xa8, 0xa9, 0xa8, 0xa5, 0xac, 0xdb, 0x2c, 0x27, 0x35, 0x4a,
+0xfe, 0xcd, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0x4e, 0x2c, 0x22,
+0x33, 0xfe, 0xba, 0xb0, 0xa6, 0x9f, 0xa3, 0xbc, 0x5c, 0x33,
+0x31, 0x39, 0x5c, 0xcd, 0xc1, 0xb4, 0xad, 0xaa, 0xad, 0xcd,
+0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xc5, 0xaf, 0xa6, 0xa9,
+0xb0, 0xfe, 0x3d, 0x39, 0x5c, 0xdb, 0xdb, 0xfe, 0xba, 0xac,
+0xa8, 0xc5, 0x39, 0x33, 0x54, 0xb8, 0xae, 0xad, 0xb8, 0xc1,
+0xcd, 0xbe, 0xbc, 0xfe, 0x39, 0x37, 0xfe, 0xb4, 0xba, 0xcd,
+0xdb, 0xb8, 0xb0, 0xb6, 0xfe, 0x4e, 0x39, 0x3d, 0xfe, 0xb0,
+0xaa, 0xa9, 0xa9, 0xaa, 0xb0, 0x5c, 0x29, 0x28, 0x31, 0xfe,
+0xba, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xfe, 0x3b, 0x2a, 0x2f,
+0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8, 0xfe, 0x2c,
+0x27, 0x2a, 0x46, 0xfe, 0xc1, 0xbc, 0xb0, 0xa2, 0xa2, 0xb0,
+0x33, 0x22, 0x2b, 0x46, 0xc1, 0xb4, 0xb0, 0xab, 0xa8, 0xa8,
+0xb0, 0xdb, 0x42, 0x2c, 0x33, 0x4a, 0xfe, 0x5c, 0xdb, 0xb4,
+0xa8, 0xad, 0xba, 0xfe, 0x46, 0x39, 0x39, 0x4a, 0xfe, 0xbc,
+0xab, 0xa5, 0xa5, 0xb8, 0x68, 0x37, 0x4a, 0xe7, 0xfe, 0x4a,
+0x5c, 0xd3, 0xb0, 0xb2, 0xc1, 0x5c, 0x42, 0x46, 0xd3, 0xb4,
+0xbe, 0x54, 0x54, 0xb6, 0xab, 0xae, 0xe7, 0x46, 0x4a, 0xfe,
+0xcd, 0xc9, 0xd3, 0xe7, 0xe7, 0xbe, 0xb4, 0xc5, 0x37, 0x2c,
+0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb, 0x54, 0x4a,
+0x46, 0x42, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6, 0xb6, 0xcd,
+0x33, 0x2f, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xac, 0xa7, 0xa7,
+0xb2, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xd3, 0xc5, 0xac,
+0xa6, 0xac, 0xfe, 0x33, 0x2c, 0x2d, 0x54, 0xc1, 0xb4, 0xcd,
+0xcd, 0xaf, 0xa4, 0xa8, 0xe7, 0x31, 0x31, 0x39, 0x46, 0xfe,
+0xdb, 0xbc, 0xb6, 0xaf, 0xa9, 0xb2, 0xfe, 0x2f, 0xfe, 0xba,
+0xad, 0xba, 0x4e, 0x3d, 0x42, 0xfe, 0xc9, 0xc1, 0xe7, 0xfe,
+0xc1, 0xa9, 0xa6, 0xcd, 0x2a, 0x25, 0x3b, 0xbc, 0xb0, 0xb6,
+0xb8, 0xb4, 0xb4, 0xb8, 0xc1, 0xfe, 0x3d, 0x3d, 0xfe, 0xba,
+0xd3, 0x54, 0x54, 0xbe, 0xb0, 0xb4, 0xe7, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xc5, 0xb6, 0xa9, 0xaa, 0xba, 0x3d, 0x39,
+0xfe, 0xc1, 0xfe, 0x4a, 0x54, 0xbe, 0xb2, 0xb8, 0xfe, 0x3d,
+0x3b, 0xfe, 0xb0, 0xab, 0xc5, 0x39, 0x3b, 0xbc, 0xa7, 0xab,
+0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x3d, 0xfe, 0xbc,
+0xac, 0xc9, 0x3f, 0x2e, 0xfe, 0xba, 0xb0, 0xba, 0xba, 0xb6,
+0xba, 0xd3, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4, 0xbe, 0x54,
+0x68, 0xcd, 0xb6, 0xfe, 0x46, 0x54, 0xcd, 0xc5, 0xdb, 0xfe,
+0xe7, 0xc1, 0xae, 0xa8, 0xac, 0xfe, 0x2e, 0x2e, 0xfe, 0xb6,
+0xc1, 0x5c, 0xe7, 0xb6, 0xaa, 0xb0, 0x54, 0x35, 0x2e, 0x4a,
+0xc1, 0xac, 0xbc, 0xfe, 0xfe, 0xaf, 0xa6, 0xac, 0xfe, 0x42,
+0x46, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xae, 0xa9, 0xb0,
+0x54, 0x31, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xac, 0xab, 0xb6,
+0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x54, 0xfe, 0xb0,
+0xae, 0xb8, 0xfe, 0xe7, 0xc1, 0xb6, 0xe7, 0x46, 0x3d, 0xe7,
+0xae, 0xa7, 0xab, 0xdb, 0x3f, 0x37, 0xfe, 0xba, 0xcd, 0x3f,
+0x42, 0xc5, 0xab, 0xac, 0xc9, 0x46, 0x3d, 0x54, 0xba, 0xad,
+0xb0, 0xfe, 0x68, 0xcd, 0xb0, 0xb0, 0xc9, 0x54, 0x54, 0xfe,
+0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xbe, 0xba, 0xc5, 0x68, 0x42,
+0x46, 0xe7, 0xc5, 0xba, 0xaf, 0xa7, 0xa7, 0xbc, 0xfe, 0x3d,
+0x4a, 0x68, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xaf, 0xae, 0xc5,
+0x3d, 0x39, 0xfe, 0xbc, 0xbe, 0xfe, 0x68, 0xc9, 0xa9, 0xa2,
+0xaa, 0xbc, 0x3b, 0x2d, 0x35, 0xfe, 0xcd, 0xfe, 0xfe, 0xb4,
+0xa9, 0xa6, 0xbc, 0x54, 0x31, 0x31, 0x54, 0xd3, 0xbc, 0xc5,
+0xcd, 0xb8, 0xab, 0xa8, 0xb4, 0xfe, 0x54, 0x4e, 0x68, 0x54,
+0xfe, 0xc9, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c, 0x3b, 0x3f,
+0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0x68, 0xfe, 0xe7,
+0xdb, 0xfe, 0xfe, 0xfe, 0xcd, 0xb8, 0xaf, 0xb6, 0xe7, 0x42,
+0x5c, 0xcd, 0xbc, 0xfe, 0x46, 0x68, 0xba, 0xb0, 0xab, 0xbc,
+0x54, 0x3d, 0x46, 0xc9, 0xb8, 0xbc, 0xdb, 0xd3, 0xb6, 0xb0,
+0xb6, 0x5c, 0x37, 0x35, 0x54, 0xc9, 0xba, 0xb4, 0xc1, 0xc9,
+0xc1, 0xba, 0xe7, 0x5c, 0x3d, 0x54, 0xfe, 0xcd, 0xc5, 0xb8,
+0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3b, 0x35, 0x46, 0xfe, 0xdb,
+0xbc, 0xaf, 0xa9, 0xab, 0xd3, 0x3f, 0x31, 0x3f, 0xfe, 0xe7,
+0xdb, 0xcd, 0xb8, 0xae, 0xaa, 0xac, 0xe7, 0x33, 0x2d, 0x3f,
+0xd3, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa9, 0xb0, 0xfe, 0x31,
+0x2f, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xae, 0xa8, 0xad, 0xfe,
+0x46, 0x39, 0x46, 0x5c, 0xfe, 0xcd, 0xc5, 0xc1, 0xb6, 0xb0,
+0xbc, 0x68, 0x42, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe, 0xb6, 0xaf,
+0xb0, 0xc5, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe, 0x68, 0xe7,
+0xb0, 0xac, 0xb2, 0x4a, 0x35, 0x3f, 0xcd, 0xbc, 0xc1, 0xe7,
+0xe7, 0xd3, 0xb6, 0xb4, 0xfe, 0x3b, 0x33, 0xfe, 0xba, 0xb4,
+0xd3, 0xfe, 0xc9, 0xb4, 0xb4, 0xcd, 0x4a, 0x3b, 0x3b, 0xfe,
+0xb8, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xcd, 0x4a, 0x31, 0x3b,
+0x68, 0xcd, 0xc1, 0xba, 0xb4, 0xb0, 0xb0, 0xba, 0x5c, 0x2f,
+0x2f, 0x4e, 0xc9, 0xc1, 0xdb, 0xc9, 0xb4 };
diff --git a/1.2-netsec/apps/leave.h b/1.2-netsec/apps/leave.h
new file mode 100644
index 000000000..238976f20
--- /dev/null
+++ b/1.2-netsec/apps/leave.h
@@ -0,0 +1,207 @@
+/*
+ * U-law 8-bit audio data
+ *
+ * Source: leave.raw
+ *
+ * Copyright (C) 1999, Mark Spencer and Linux Support Services
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char leave[] = {
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc1, 0x3d,
+0x42, 0x46, 0x3f, 0x3f, 0x46, 0x3f, 0x4e, 0xba, 0xbe, 0xbe,
+0xbc, 0xba, 0xbe, 0xc5, 0xb6, 0x2e, 0x2c, 0x33, 0x2f, 0x2e,
+0x2f, 0x33, 0x2b, 0x54, 0xac, 0xb0, 0xb0, 0xad, 0xaf, 0xb0,
+0xae, 0xcd, 0x3b, 0x2f, 0x31, 0x2e, 0x2f, 0x31, 0x2e, 0x46,
+0xc5, 0xaf, 0xb0, 0xaf, 0xae, 0xaf, 0xaf, 0xb0, 0xfe, 0x2d,
+0x31, 0x31, 0x2e, 0x31, 0x2f, 0x31, 0xfe, 0xae, 0xaf, 0xaf,
+0xae, 0xb0, 0xae, 0xaf, 0xfe, 0xdb, 0x2e, 0x2e, 0x31, 0x31,
+0x2d, 0x2e, 0xdb, 0x68, 0xaf, 0xad, 0xb0, 0xb0, 0xae, 0xaf,
+0x5c, 0xe7, 0x39, 0x2d, 0x31, 0x31, 0x31, 0x2d, 0xfe, 0xfe,
+0x68, 0xad, 0xaf, 0xb0, 0xaf, 0xac, 0xbc, 0xfe, 0xd3, 0x2f,
+0x2e, 0x33, 0x31, 0x2d, 0x4e, 0xdb, 0xfe, 0xfe, 0xac, 0xaf,
+0xb0, 0xac, 0xb6, 0x68, 0xe7, 0xdb, 0x2e, 0x2f, 0x35, 0x2f,
+0x31, 0xe7, 0xe7, 0x68, 0xad, 0xac, 0xb0, 0xae, 0xac, 0xfe,
+0xfe, 0xdb, 0xfe, 0x2d, 0x33, 0x31, 0x2e, 0xfe, 0xfe, 0xfe,
+0xfe, 0xbc, 0xaf, 0xb0, 0xad, 0xfe, 0xfe, 0xfe, 0xe7, 0x5c,
+0x2e, 0x33, 0x2e, 0x35, 0xe7, 0xfe, 0xfe, 0xfe, 0xad, 0xb0,
+0xaf, 0xc1, 0xfe, 0xe7, 0xfe, 0xe7, 0x3d, 0x31, 0x2f, 0x37,
+0xe7, 0xfe, 0xfe, 0xe7, 0xfe, 0xaf, 0xad, 0xbe, 0xfe, 0xdb,
+0xfe, 0xfe, 0xdb, 0x35, 0x2d, 0x39, 0xdb, 0xfe, 0xfe, 0xdb,
+0xfe, 0xfe, 0xad, 0xaf, 0xfe, 0xfe, 0xe7, 0x68, 0xfe, 0xd3,
+0x2e, 0x2c, 0xdb, 0xdb, 0x2c, 0x35, 0xd3, 0x68, 0xaf, 0xad,
+0xb0, 0xb0, 0xad, 0xba, 0x68, 0xe7, 0xe7, 0x2e, 0x2f, 0x33,
+0x31, 0x2d, 0xdb, 0xd3, 0x5c, 0xae, 0xaa, 0xe7, 0x68, 0xaa,
+0xe7, 0xfe, 0xdb, 0xe7, 0xfe, 0xe7, 0xd3, 0x2d, 0xfe, 0xdb,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc5, 0xfe, 0xe7, 0xe7,
+0xfe, 0xfe, 0xe7, 0xe7, 0x3b, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xc5, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0x3b,
+0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xb0, 0xfe, 0xfe,
+0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0x2e, 0x5c, 0xdb, 0xfe, 0xfe,
+0xe7, 0xe7, 0x68, 0xb0, 0xbe, 0x68, 0xe7, 0xe7, 0xfe, 0xfe,
+0xdb, 0x39, 0x2f, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xbe,
+0xaf, 0xe7, 0x68, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0x33, 0x33,
+0xdb, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xb0, 0xb0, 0xfe, 0xfe,
+0xe7, 0xfe, 0xfe, 0xfe, 0x35, 0x33, 0xe7, 0xe7, 0xfe, 0xe7,
+0xe7, 0xfe, 0xb0, 0xb2, 0xb0, 0xfe, 0xfe, 0xe7, 0xfe, 0xe7,
+0x46, 0x35, 0x35, 0x3f, 0xe7, 0xfe, 0xe7, 0xfe, 0xb2, 0xb0,
+0xb2, 0xb0, 0xfe, 0xfe, 0xfe, 0xfe, 0x42, 0x35, 0x37, 0x33,
+0xe7, 0xfe, 0xfe, 0xfe, 0xb8, 0xb0, 0xb6, 0xb0, 0xba, 0xfe,
+0xfe, 0xe7, 0xe7, 0x33, 0x39, 0x39, 0x33, 0xe7, 0xdb, 0xfe,
+0xe7, 0xb0, 0xb4, 0xb6, 0xb0, 0xcd, 0xfe, 0xe7, 0xe7, 0x33,
+0x39, 0x3b, 0x33, 0x46, 0xd3, 0xfe, 0xfe, 0xb0, 0xb2, 0xb6,
+0xb4, 0xb0, 0xfe, 0xfe, 0xdb, 0x35, 0x37, 0x39, 0x39, 0x35,
+0x37, 0xdb, 0x68, 0xcd, 0xb2, 0xb6, 0xb6, 0xb4, 0xb4, 0x68,
+0xe7, 0x42, 0x37, 0x3b, 0x3b, 0x39, 0x37, 0xdb, 0xfe, 0xcd,
+0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb4, 0xfe, 0x54, 0x37, 0x3b,
+0x39, 0x3b, 0x3b, 0x39, 0xe7, 0xfe, 0xb6, 0xb6, 0xb6, 0xb4,
+0xb6, 0xb6, 0xbc, 0xfe, 0x3f, 0x3b, 0x3b, 0x39, 0x3b, 0x3b,
+0x39, 0xe7, 0xb6, 0xb8, 0xb8, 0xb6, 0xb8, 0xb8, 0xb4, 0xfe,
+0x3b, 0x3d, 0x3d, 0x3b, 0x39, 0x3d, 0x3b, 0x39, 0xbe, 0xb8,
+0xba, 0xb8, 0xb6, 0xb8, 0xba, 0xb4, 0xfe, 0x39, 0x3f, 0x3d,
+0x3b, 0x3d, 0x3f, 0x39, 0xdb, 0xb4, 0xba, 0xb8, 0xb6, 0xb8,
+0xbc, 0xb4, 0xba, 0x39, 0x42, 0x3f, 0x3d, 0x3d, 0x3f, 0x3f,
+0x3b, 0xb8, 0xb6, 0xbc, 0xb8, 0xb8, 0xba, 0xbc, 0xb8, 0xe7,
+0x3d, 0x42, 0x3f, 0x3d, 0x3f, 0x42, 0x3d, 0xfe, 0xb8, 0xbc,
+0xbc, 0xba, 0xba, 0xbc, 0xba, 0xe7, 0x3d, 0x3f, 0x42, 0x3f,
+0x3f, 0x42, 0x42, 0xfe, 0xfe, 0xbc, 0xbc, 0xbe, 0xbc, 0xbe,
+0xbc, 0xc5, 0xe7, 0x68, 0x42, 0x46, 0x42, 0x46, 0x42, 0x46,
+0xfe, 0xfe, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xc5, 0xfe, 0xdb,
+0x46, 0x46, 0x4a, 0x4a, 0x46, 0x46, 0xe7, 0xfe, 0xd3, 0xbe,
+0xc9, 0xc9, 0xc5, 0xc5, 0xe7, 0xdb, 0xd3, 0x4a, 0x4e, 0x54,
+0x4e, 0x4e, 0xfe, 0x5c, 0x54, 0xd3, 0xcd, 0xd3, 0xd3, 0xcd,
+0xd3, 0xd3, 0xcd, 0xfe, 0x5c, 0x68, 0x5c, 0x5c, 0x5c, 0x68,
+0x5c, 0x5c, 0xcd, 0xcd, 0xd3, 0xcd, 0xdb, 0xe7, 0xe7, 0xdb,
+0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7,
+0xfe, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0xfe, 0x46, 0x35, 0x35,
+0x37, 0x39, 0x3b, 0x39, 0x35, 0x33, 0x35, 0x5c, 0xd3, 0xcd,
+0xdb, 0xfe, 0xfe, 0xd3, 0xb0, 0xb0, 0xb0, 0xb4, 0xb4, 0xb6,
+0xb2, 0xb0, 0xb0, 0xb6, 0xcd, 0x5c, 0x68, 0xfe, 0xfe, 0xfe,
+0x3b, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x39, 0x37, 0x35, 0x35,
+0x3f, 0xdb, 0xcd, 0xcd, 0xdb, 0xfe, 0xe7, 0xc5, 0xb0, 0xb0,
+0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb0, 0xb0, 0xcd, 0x5c, 0x5c,
+0xfe, 0xe7, 0xe7, 0xfe, 0x35, 0x35, 0x35, 0x39, 0x3b, 0x3b,
+0x39, 0x35, 0x33, 0x39, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe,
+0xba, 0xb0, 0xb0, 0xb2, 0xb4, 0xb6, 0xb6, 0xb4, 0xb2, 0xb0,
+0xb4, 0xc9, 0x5c, 0x68, 0xfe, 0xfe, 0x5c, 0x3b, 0x35, 0x37,
+0x39, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37, 0x3d, 0xe7, 0xcd,
+0xdb, 0xfe, 0xe7, 0xbe, 0xb2, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6,
+0xb6, 0xb4, 0xb0, 0xb0, 0xc5, 0x5c, 0x5c, 0xfe, 0xe7, 0xe7,
+0x4e, 0x35, 0x35, 0x37, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37,
+0x3b, 0xe7, 0xc9, 0xcd, 0xe7, 0xfe, 0xd3, 0xb4, 0xb2, 0xb2,
+0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2, 0xb4, 0xc1, 0x68,
+0x68, 0xfe, 0xfe, 0x42, 0x39, 0x37, 0x39, 0x3b, 0x3b, 0x3b,
+0x3b, 0x3b, 0x39, 0x37, 0x3b, 0xfe, 0xd3, 0xdb, 0xfe, 0xcd,
+0xb4, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2,
+0xb2, 0xc1, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0x3d, 0x37, 0x37,
+0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x37, 0x37, 0x39, 0xfe, 0xcd,
+0xd3, 0xfe, 0xfe, 0xc1, 0xb2, 0xb2, 0xb4, 0xb6, 0xb6, 0xb6,
+0xb6, 0xb6, 0xb6, 0xb4, 0xb4, 0xbc, 0x68, 0xfe, 0xfe, 0xfe,
+0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x39,
+0x39, 0x3b, 0xfe, 0xdb, 0xe7, 0xfe, 0xbc, 0xb6, 0xb6, 0xb6,
+0xb8, 0xb6, 0xb6, 0xb6, 0xb8, 0xb6, 0xb4, 0xb4, 0xbc, 0xfe,
+0x68, 0xfe, 0xe7, 0x5c, 0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b,
+0x3d, 0x3d, 0x3b, 0x39, 0x3b, 0x68, 0xdb, 0xdb, 0xfe, 0xe7,
+0xb8, 0xb6, 0xb6, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb6,
+0xb4, 0xb6, 0xdb, 0x68, 0xfe, 0xfe, 0x46, 0x3b, 0x3b, 0x3b,
+0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3b, 0x5c,
+0xdb, 0xdb, 0xc9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xbc, 0xcd, 0xfe, 0xfe, 0x3d,
+0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d,
+0x3b, 0x3d, 0x46, 0xfe, 0xe7, 0xe7, 0xc5, 0xb8, 0xb8, 0xb8,
+0xba, 0xba, 0xb8, 0xb8, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xcd,
+0xfe, 0xfe, 0x68, 0x3f, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+0x3d, 0x3d, 0x3f, 0x3f, 0x3d, 0x3b, 0x4a, 0xfe, 0xdb, 0xbc,
+0xb8, 0xba, 0xba, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xba, 0xba,
+0xba, 0xba, 0xba, 0xc5, 0xfe, 0x54, 0x3f, 0x3f, 0x3f, 0x3f,
+0x3f, 0x3f, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
+0xfe, 0xe7, 0xdb, 0xbc, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+0xba, 0xba, 0xbc, 0xba, 0xba, 0xba, 0xc5, 0xfe, 0xfe, 0x4e,
+0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
+0x42, 0x42, 0x3f, 0x46, 0xfe, 0xcd, 0xb8, 0xba, 0xbc, 0xbc,
+0xbc, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xba, 0xb8,
+0xbe, 0xfe, 0x42, 0x3d, 0x3f, 0x42, 0x42, 0x42, 0x3f, 0x3f,
+0x3f, 0x42, 0x42, 0x42, 0x42, 0x3f, 0x3f, 0x68, 0xdb, 0xc5,
+0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+0xbc, 0xbc, 0xba, 0xc1, 0xfe, 0xfe, 0x3f, 0x42, 0x46, 0x46,
+0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x46, 0x42, 0x3f,
+0x42, 0x68, 0xbe, 0xba, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xbc,
+0xbc, 0xbc, 0xbe, 0xc1, 0xbe, 0xbc, 0xba, 0xbe, 0x68, 0x3f,
+0x42, 0x46, 0x4a, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x46, 0x46,
+0x46, 0x46, 0x42, 0x42, 0x68, 0xd3, 0xbc, 0xbc, 0xbe, 0xc1,
+0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc1, 0xbe, 0xbe,
+0xc1, 0xfe, 0x4e, 0x42, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46,
+0x46, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46, 0x68, 0xdb, 0xbe,
+0xbe, 0xc1, 0xc5, 0xc1, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1,
+0xc5, 0xc5, 0xbe, 0xbc, 0xc1, 0x4e, 0x46, 0x46, 0x4a, 0x4e,
+0x4e, 0x4a, 0x46, 0x46, 0x46, 0x4a, 0x4a, 0x4e, 0x4a, 0x46,
+0x46, 0xfe, 0xbe, 0xbe, 0xc1, 0xc9, 0xc5, 0xc5, 0xc1, 0xc1,
+0xc1, 0xc1, 0xc5, 0xc5, 0xc5, 0xc5, 0xbe, 0xc1, 0xfe, 0x4a,
+0x4a, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4a, 0x4a, 0x4a, 0x4e,
+0x54, 0x4e, 0x4a, 0x4a, 0x4e, 0xcd, 0xc1, 0xc5, 0xc5, 0xc9,
+0xc5, 0xc5, 0xc5, 0xc5, 0xc9, 0xcd, 0xcd, 0xcd, 0xcd, 0xc9,
+0xc9, 0xd3, 0x68, 0x54, 0x5c, 0x68, 0x68, 0x68, 0x5c, 0x5c,
+0x5c, 0x5c, 0x5c, 0x68, 0x68, 0x5c, 0x54, 0x5c, 0xdb, 0xcd,
+0xcd, 0xdb, 0xdb, 0xdb, 0xdb, 0xd3, 0xd3, 0xe7, 0xe7, 0xe7,
+0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe };
diff --git a/1.2-netsec/apps/rpt_flow.pdf b/1.2-netsec/apps/rpt_flow.pdf
new file mode 100644
index 000000000..2085af0f2
--- /dev/null
+++ b/1.2-netsec/apps/rpt_flow.pdf
Binary files differ