Version 0.1.12 from FTP
authorMark Spencer <markster@digium.com>
Fri, 21 Jun 2002 01:40:13 +0000 (01:40 +0000)
committerMark Spencer <markster@digium.com>
Fri, 21 Jun 2002 01:40:13 +0000 (01:40 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@465 65c4cc65-6c06-0410-ace0-fbb531ad65f3

14 files changed:
Makefile
apps/app_adsiprog.c [new file with mode: 0755]
apps/app_system.c
channel.c
channels/adtranvofr.h
channels/chan_phone.c
codecs/Makefile
codecs/lpc10/Makefile
formats/format_jpeg.c
formats/format_wav.c
include/asterisk/logger.h
logger.c
pbx.c
res/res_adsi.c

index 68e8430..0dc2486 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,6 @@
 # the GNU General Public License
 #
 
-
 .EXPORT_ALL_VARIABLES:
 
 INSTALL_PREFIX=
@@ -22,14 +21,20 @@ AGI_DIR=$(INSTALL_PREFIX)/var/lib/asterisk/agi-bin
 # Pentium Pro Optimize
 #PROC=i686
 # Pentium Optimize
-PROC=i586
+#PROC=i586
+#PROC=k6
+#PROC=ppc
+PROC=$(shell uname -m)
 
 DEBUG=-g #-pg
 INCLUDE=-Iinclude -I../include
-CFLAGS=-pipe  -Wall -Wmissing-prototypes -Wmissing-declarations -O6 $(DEBUG) $(INCLUDE) -D_REENTRANT
+CFLAGS=-pipe  -Wall -Wmissing-prototypes -Wmissing-declarations -O6 $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE
 #CFLAGS+=-Werror
 CFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
+CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi)
+
 ASTERISKVERSION=$(shell if [ -f .version ]; then cat .version; fi)
+HTTPDIR=$(shell if [ -d /var/www ]; then echo "/var/www"; else echo "/home/httpd"; fi)
 RPMVERSION=$(shell sed 's/[-\/:]/_/g' .version)
 CFLAGS+=-DASTERISK_VERSION=\"$(ASTERISKVERSION)\"
 # Optional debugging parameters
@@ -38,11 +43,11 @@ CFLAGS+= -DDO_CRASH -DDEBUG_THREADS
 #CLFAGS+= -DTRACE_FRAMES
 CFLAGS+=# -fomit-frame-pointer 
 SUBDIRS=res channels pbx apps codecs formats agi cdr
-LIBS=-ldl -lpthread -lreadline -lncurses -lm
+LIBS=-ldl -lpthread -lreadline -lncurses -lm #-lnjamd
 OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
-       translate.o file.o say.o pbx.o cli.o md5.o \
+       translate.o file.o say.o pbx.o cli.o md5.o term.o \
        ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
-       cdr.o tdd.o asterisk.o 
+       cdr.o tdd.o acl.o rtp.o asterisk.o 
 CC=gcc
 INSTALL=install
 
@@ -81,9 +86,10 @@ datafiles: all
        for x in sounds/digits/*; do \
                install $$x $(INSTALL_PREFIX)/var/lib/asterisk/sounds/digits ; \
        done
-       for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-*; do \
+       for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-*; do \
                install $$x $(INSTALL_PREFIX)/var/lib/asterisk/sounds ; \
        done
+       mkdir -p $(INSTALL_PREFIX)/var/lib/asterisk/mohmp3
        mkdir -p $(INSTALL_PREFIX)/var/lib/asterisk/images
        for x in images/*.jpg; do \
                install $$x $(INSTALL_PREFIX)/var/lib/asterisk/images ; \
@@ -93,8 +99,10 @@ datafiles: all
 install: all datafiles
        mkdir -p $(MODULES_DIR)
        mkdir -p $(INSTALL_PREFIX)/usr/sbin
+       mkdir -p $(INSTALL_PREFIX)/etc/asterisk
        install -m 755 asterisk $(INSTALL_PREFIX)/usr/sbin/
        install -m 755 astgenkey $(INSTALL_PREFIX)/usr/sbin/
+       install -m 755 safe_asterisk $(INSTALL_PREFIX)/usr/sbin/
        for x in $(SUBDIRS); do $(MAKE) -C $$x install || exit 1 ; done
        install -d $(INSTALL_PREFIX)/usr/include/asterisk
        install include/asterisk/*.h $(INSTALL_PREFIX)/usr/include/asterisk
@@ -128,7 +136,15 @@ install: all datafiles
        @echo " + **Note** This requires that you have      +"
        @echo " + doxygen installed on your local system    +"
        @echo " +-------------------------------------------+"
-samples: all datafiles
+adsi: all
+       mkdir -p /etc/asterisk
+       for x in configs/*.adsi; do \
+               if ! [ -f $(INSTALL_PREFIX)/etc/asterisk/$$x ]; then \
+                       install -m 644 $$x $(INSTALL_PREFIX)/etc/asterisk/`basename $$x` ; \
+               fi ; \
+       done
+
+samples: all datafiles adsi
        mkdir -p $(INSTALL_PREFIX)/etc/asterisk
        for x in configs/*.sample; do \
                if [ -f $(INSTALL_PREFIX)/etc/asterisk/`basename $$x .sample` ]; then \
@@ -139,6 +155,9 @@ samples: all datafiles
        for x in sounds/demo-*; do \
                install $$x $(INSTALL_PREFIX)/var/lib/asterisk/sounds; \
        done
+       for x in sounds/*.mp3; do \
+               install $$x $(INSTALL_PREFIX)/var/lib/asterisk/mohmp3 ; \
+       done
        mkdir -p $(INSTALL_PREFIX)/var/spool/asterisk/vm/1234/INBOX
        :> $(INSTALL_PREFIX)/var/lib/asterisk/sounds/vm/1234/unavail.gsm
        for x in vm-theperson digits/1 digits/2 digits/3 digits/4 vm-isunavail; do \
@@ -149,6 +168,24 @@ samples: all datafiles
                cat $(INSTALL_PREFIX)/var/lib/asterisk/sounds/$$x.gsm >> $(INSTALL_PREFIX)/var/lib/asterisk/sounds/vm/1234/busy.gsm ; \
        done
 
+webvmail:
+       @[ -d $(HTTPDIR) ] || ( echo "No HTTP directory" && exit 1 )
+       @[ -d $(HTTPDIR)/html ] || ( echo "No http directory" && exit 1 )
+       @[ -d $(HTTPDIR)/cgi-bin ] || ( echo "No cgi-bin directory" && exit 1 )
+       install -m 4755 -o root -g root vmail.cgi $(HTTPDIR)/cgi-bin/vmail.cgi
+       mkdir -p $(HTTPDIR)/html/_asterisk
+       for x in images/*.gif; do \
+               install -m 644 $$x $(HTTPDIR)/html/_asterisk/; \
+       done
+       @echo " +--------- Asterisk Web Voicemail ----------+"  
+       @echo " +                                           +"
+       @echo " + Asterisk Web Voicemail is installed in    +"
+       @echo " + your cgi-bin directory.  IT USES A SETUID +"
+       @echo " + ROOT PERL SCRIPT, SO IF YOU DON'T LIKE    +"
+       @echo " + THAT, UNINSTALL IT!                       +"
+       @echo " +                                           +"
+       @echo " +-------------------------------------------+"  
+
 mailbox:
        ./addmailbox 
        
@@ -171,3 +208,13 @@ __rpm: _version
 
 progdocs:
        doxygen asterisk-ng-doxygen
+
+config:
+       if [ -d /etc/rc.d/init.d ]; then \
+               install -m 755 init.asterisk /etc/rc.d/init.d/asterisk; \
+               /sbin/chkconfig --add asterisk; \
+       elif [ -d /etc/init.d ]; then \
+               install -m 755 init.asterisk /etc/init.d/asterisk; \
+       fi 
+
+       
diff --git a/apps/app_adsiprog.c b/apps/app_adsiprog.c
new file mode 100755 (executable)
index 0000000..531b2c4
--- /dev/null
@@ -0,0 +1,1567 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Program Asterisk ADSI Scripts into phone
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#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 <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pthread.h>
+
+#include "../asterisk.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): Programs an ADSI Phone with the given script.\n"
+"If none is specified, the default is used.  Returns 0 unless CPE\n" 
+"is hungup.\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 */
+       char sec[5];
+       char desc[19];
+       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 (strlen(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 ((strlen(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;
+       }
+       strncpy(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname) - 1);
+       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;
+       }
+       strncpy(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname) - 1);
+       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;
+       }
+       strncpy(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname) - 1);
+       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;
+       }
+       strncpy(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname) - 1);
+       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;
+       }
+       strncpy(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname) - 1);
+       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 & 0x2f); 
+       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 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' */
+       { "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 {
+                               strncpy(tmp2, tmp, sizeof(tmp2) - 1);
+                       }
+                       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] == '/')
+               strncpy(fn, script, sizeof(fn) - 1);
+       else
+               snprintf(fn, sizeof(fn), "%s/%s", 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 (strlen(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;
+       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 > 254) {
+                       /* 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 > 254) {
+                       /* 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 > 254) {
+                       /* 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;
+       if (!data || !strlen(data))
+               data = "asterisk.adsi";
+       LOCAL_USER_ADD(u);
+       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)
+{
+       STANDARD_HANGUP_LOCALUSERS;
+       return ast_unregister_application(app);
+}
+
+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;
+}
index a436912..539515e 100755 (executable)
@@ -11,6 +11,7 @@
  * the GNU General Public License
  */
 
+#include <asterisk/lock.h>
 #include <asterisk/file.h>
 #include <asterisk/logger.h>
 #include <asterisk/channel.h>
@@ -53,10 +54,10 @@ static int skel_exec(struct ast_channel *chan, void *data)
        /* Do our thing here */
        res = system((char *)data);
        if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to execute '%s'\n", data);
+               ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
                res = -1;
        } else if (res == 127) {
-               ast_log(LOG_WARNING, "Unable to execute '%s'\n", data);
+               ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
                res = -1;
        } else {
                if (res && ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) 
index fdd7c8f..f975db5 100755 (executable)
--- a/channel.c
+++ b/channel.c
@@ -18,7 +18,9 @@
 #include <sys/time.h>
 #include <signal.h>
 #include <errno.h>
+#include <asterisk/lock.h>
 #include <unistd.h>
+#include <math.h>                      /* For PI */
 #include <asterisk/frame.h>
 #include <asterisk/sched.h>
 #include <asterisk/options.h>
@@ -28,6 +30,7 @@
 #include <asterisk/file.h>
 #include <asterisk/translate.h>
 
+static int shutting_down = 0;
 
 /* XXX Lock appropriately in more functions XXX */
 
@@ -62,7 +65,7 @@ struct ast_channel *channels = NULL;
 /* Protect the channel list (highly unlikely that two things would change
    it at the same time, but still! */
    
-static pthread_mutex_t chlock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t chlock = AST_MUTEX_INITIALIZER;
 
 int ast_check_hangup(struct ast_channel *chan)
 {
@@ -79,6 +82,45 @@ time_t       myt;
        return 1;
 }
 
+void ast_begin_shutdown(int hangup)
+{
+       struct ast_channel *c;
+       shutting_down = 1;
+       if (hangup) {
+               PTHREAD_MUTEX_LOCK(&chlock);
+               c = channels;
+               while(c) {
+                       c->softhangup = 1;
+                       c = c->next;
+               }
+               PTHREAD_MUTEX_UNLOCK(&chlock);
+       }
+}
+
+int ast_active_channels(void)
+{
+       struct ast_channel *c;
+       int cnt = 0;
+       PTHREAD_MUTEX_LOCK(&chlock);
+       c = channels;
+       while(c) {
+               cnt++;
+               c = c->next;
+       }
+       PTHREAD_MUTEX_UNLOCK(&chlock);
+       return cnt;
+}
+
+void ast_cancel_shutdown(void)
+{
+       shutting_down = 0;
+}
+
+int ast_shutting_down(void)
+{
+       return shutting_down;
+}
+
 void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
 {
 time_t myt;
@@ -189,11 +231,15 @@ int ast_best_codec(int fmts)
        return 0;
 }
 
-struct ast_channel *ast_channel_alloc(void)
+struct ast_channel *ast_channel_alloc(int needqueue)
 {
        struct ast_channel *tmp;
        struct ast_channel_pvt *pvt;
        int x;
+       int flags;
+       /* If shutting down, don't allocate any new channels */
+       if (shutting_down)
+               return NULL;
        PTHREAD_MUTEX_LOCK(&chlock);
        tmp = malloc(sizeof(struct ast_channel));
        memset(tmp, 0, sizeof(struct ast_channel));
@@ -203,24 +249,43 @@ struct ast_channel *ast_channel_alloc(void)
                        memset(pvt, 0, sizeof(struct ast_channel_pvt));
                        tmp->sched = sched_context_create();
                        if (tmp->sched) {
-                               for (x=0;x<AST_MAX_FDS;x++)
+                               for (x=0;x<AST_MAX_FDS - 1;x++)
                                        tmp->fds[x] = -1;
-                               strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
-                               tmp->pvt = pvt;
-                               tmp->state = AST_STATE_DOWN;
-                               tmp->stack = -1;
-                               tmp->streamid = -1;
-                               tmp->appl = NULL;
-                               tmp->data = NULL;
-                               pthread_mutex_init(&tmp->lock, NULL);
-                               strncpy(tmp->context, "default", sizeof(tmp->context)-1);
-                               strncpy(tmp->language, defaultlanguage, sizeof(tmp->language)-1);
-                               strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
-                               tmp->priority=1;
-                               tmp->amaflags = ast_default_amaflags;
-                               strncpy(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode)-1);
-                               tmp->next = channels;
-                               channels= tmp;
+                               if (needqueue &&  
+                                       pipe(pvt->alertpipe)) {
+                                       ast_log(LOG_WARNING, "Alert pipe creation failed!\n");
+                                       free(pvt);
+                                       free(tmp);
+                                       tmp = NULL;
+                                       pvt = NULL;
+                               } else {
+                                       /* Make sure we've got it done right if they don't */
+                                       if (needqueue) {
+                                               flags = fcntl(pvt->alertpipe[0], F_GETFL);
+                                               fcntl(pvt->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
+                                               flags = fcntl(pvt->alertpipe[1], F_GETFL);
+                                               fcntl(pvt->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
+                                       } else
+                                               pvt->alertpipe[0] = pvt->alertpipe[1] = -1;
+                                       /* Always watch the alertpipe */
+                                       tmp->fds[AST_MAX_FDS-1] = pvt->alertpipe[0];
+                                       strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
+                                       tmp->pvt = pvt;
+                                       tmp->state = AST_STATE_DOWN;
+                                       tmp->stack = -1;
+                                       tmp->streamid = -1;
+                                       tmp->appl = NULL;
+                                       tmp->data = NULL;
+                                       ast_pthread_mutex_init(&tmp->lock);
+                                       strncpy(tmp->context, "default", sizeof(tmp->context)-1);
+                                       strncpy(tmp->language, defaultlanguage, sizeof(tmp->language)-1);
+                                       strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
+                                       tmp->priority=1;
+                                       tmp->amaflags = ast_default_amaflags;
+                                       strncpy(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode)-1);
+                                       tmp->next = channels;
+                                       channels= tmp;
+                               }
                        } else {
                                ast_log(LOG_WARNING, "Unable to create schedule context\n");
                                free(tmp);
@@ -237,6 +302,56 @@ struct ast_channel *ast_channel_alloc(void)
        return tmp;
 }
 
+int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int lock)
+{
+       struct ast_frame *f;
+       struct ast_frame *prev, *cur;
+       int blah = 1;
+       int qlen = 0;
+       f = ast_frdup(fin);
+       if (!f) {
+               ast_log(LOG_WARNING, "Unable to duplicate frame\n");
+               return -1;
+       }
+       if (lock)
+               ast_pthread_mutex_lock(&chan->lock);
+       prev = NULL;
+       cur = chan->pvt->readq;
+       while(cur) {
+               prev = cur;
+               cur = cur->next;
+               qlen++;
+       }
+       if (prev)
+               prev->next = f;
+       else
+               chan->pvt->readq = f;
+       if (chan->pvt->alertpipe[1] > -1) {
+               if (write(chan->pvt->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
+                       ast_log(LOG_WARNING, "Unable to write to alert pipe, frametype/subclass %d/%d (qlen = %d): %s!\n",
+                               f->frametype, f->subclass, qlen, strerror(errno));
+       }
+       if (qlen  > 128) {
+               ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
+       }
+       if (lock)
+               ast_pthread_mutex_unlock(&chan->lock);
+       return 0;
+}
+
+int ast_queue_hangup(struct ast_channel *chan, int lock)
+{
+       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+       return ast_queue_frame(chan, &f, lock);
+}
+
+int ast_queue_control(struct ast_channel *chan, int control, int lock)
+{
+       struct ast_frame f = { AST_FRAME_CONTROL, };
+       f.subclass = control;
+       return ast_queue_frame(chan, &f, lock);
+}
+
 int ast_channel_defer_dtmf(struct ast_channel *chan)
 {
        int pre = 0;
@@ -272,9 +387,28 @@ struct ast_channel *ast_channel_walk(struct ast_channel *prev)
        
 }
 
+int ast_safe_sleep(struct ast_channel *chan, int ms)
+{
+       struct ast_frame *f;
+       while(ms > 0) {
+               ms = ast_waitfor(chan, ms);
+               if (ms <0)
+                       return -1;
+               if (ms > 0) {
+                       f = ast_read(chan);
+                       if (!f)
+                               return -1;
+                       ast_frfree(f);
+               }
+       }
+       return 0;
+}
+
 void ast_channel_free(struct ast_channel *chan)
 {
        struct ast_channel *last=NULL, *cur;
+       int fd;
+       struct ast_frame *f, *fp;
        PTHREAD_MUTEX_LOCK(&chlock);
        cur = channels;
        while(cur) {
@@ -306,6 +440,17 @@ void ast_channel_free(struct ast_channel *chan)
        if (chan->ani)
                free(chan->ani);
        pthread_mutex_destroy(&chan->lock);
+       /* Close pipes if appropriate */
+       if ((fd = chan->pvt->alertpipe[0]) > -1)
+               close(fd);
+       if ((fd = chan->pvt->alertpipe[1]) > -1)
+               close(fd);
+       f = chan->pvt->readq;
+       while(f) {
+               fp = f;
+               f = f->next;
+               ast_frfree(fp);
+       }
        free(chan->pvt);
        free(chan);
        PTHREAD_MUTEX_UNLOCK(&chlock);
@@ -359,6 +504,11 @@ int ast_hangup(struct ast_channel *chan)
                ast_stopstream(chan);
        if (chan->sched)
                sched_context_destroy(chan->sched);
+       /* Clear any tone stuff remaining */
+       if (chan->generatordata)
+               chan->generator->release(chan, chan->generatordata);
+       chan->generatordata = NULL;
+       chan->generator = NULL;
        if (chan->cdr) {
                /* End the CDR if it hasn't already */
                ast_cdr_end(chan->cdr);
@@ -434,6 +584,29 @@ int ast_answer(struct ast_channel *chan)
        return 0;
 }
 
+void ast_deactivate_generator(struct ast_channel *chan)
+{
+       if (chan->generatordata) {
+               chan->generator->release(chan, chan->generatordata);
+               chan->generatordata = NULL;
+               chan->writeinterrupt = 0;
+       }
+}
+
+int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
+{
+       if (chan->generatordata) {
+               chan->generator->release(chan, chan->generatordata);
+               chan->generatordata = NULL;
+       }
+       if ((chan->generatordata = gen->alloc(chan, params))) {
+               chan->generator = gen;
+       } else {
+               return -1;
+       }
+       return 0;
+}
+
 int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
 {
        /* Wait for x amount of time on a file descriptor to have input.  */
@@ -511,9 +684,10 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
        tv.tv_usec = (*ms % 1000) * 1000;
        FD_ZERO(&rfds);
        FD_ZERO(&efds);
+
        for (x=0;x<n;x++) {
                for (y=0;y<AST_MAX_FDS;y++) {
-                       if (c[x]->fds[y] > 0) {
+                       if (c[x]->fds[y] > -1) {
                                FD_SET(c[x]->fds[y], &rfds);
                                FD_SET(c[x]->fds[y], &efds);
                                if (c[x]->fds[y] > max)
@@ -623,12 +797,13 @@ char ast_waitfordigit(struct ast_channel *c, int ms)
 struct ast_frame *ast_read(struct ast_channel *chan)
 {
        struct ast_frame *f = NULL;
+       int blah;
        static struct ast_frame null_frame = 
        {
                AST_FRAME_NULL,
        };
        
-       pthread_mutex_lock(&chan->lock);
+       ast_pthread_mutex_lock(&chan->lock);
        if (chan->masq) {
                if (ast_do_masquerade(chan)) {
                        ast_log(LOG_WARNING, "Failed to perform masquerade\n");
@@ -644,7 +819,7 @@ struct ast_frame *ast_read(struct ast_channel *chan)
                pthread_mutex_unlock(&chan->lock);
                return NULL;
        }
-       
+
        if (!chan->deferdtmf && strlen(chan->dtmfq)) {
                /* We have DTMF that has been deferred.  Return it now */
                chan->dtmff.frametype = AST_FRAME_DTMF;
@@ -655,19 +830,35 @@ struct ast_frame *ast_read(struct ast_channel *chan)
                return &chan->dtmff;
        }
        
-       chan->blocker = pthread_self();
-       if (chan->exception) {
-               if (chan->pvt->exception) 
-                       f = chan->pvt->exception(chan);
+       /* Read and ignore anything on the alertpipe, but read only
+          one sizeof(blah) per frame that we send from it */
+       if (chan->pvt->alertpipe[0] > -1) {
+               read(chan->pvt->alertpipe[0], &blah, sizeof(blah));
+       }
+
+       /* Check for pending read queue */
+       if (chan->pvt->readq) {
+               f = chan->pvt->readq;
+               chan->pvt->readq = f->next;
+               /* Interpret hangup and return NULL */
+               if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))
+                       f = NULL;
+       } else {
+               chan->blocker = pthread_self();
+               if (chan->exception) {
+                       if (chan->pvt->exception) 
+                               f = chan->pvt->exception(chan);
+                       else
+                               ast_log(LOG_WARNING, "Exception flag set, but no exception handler\n");
+                       /* Clear the exception flag */
+                       chan->exception = 0;
+               } else
+               if (chan->pvt->read)
+                       f = chan->pvt->read(chan);
                else
-                       ast_log(LOG_WARNING, "Exception flag set, but no exception handler\n");
-               /* Clear the exception flag */
-               chan->exception = 0;
-       } else
-       if (chan->pvt->read)
-               f = chan->pvt->read(chan);
-       else
-               ast_log(LOG_WARNING, "No read routine on channel %s\n", chan);
+                       ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
+       }
+
        if (f && (f->frametype == AST_FRAME_VOICE)) {
                if (chan->pvt->readtrans) {
                        f = ast_translate(chan->pvt->readtrans, f, 1);
@@ -675,6 +866,7 @@ struct ast_frame *ast_read(struct ast_channel *chan)
                                f = &null_frame;
                }
        }
+
        /* Make sure we always return NULL in the future */
        if (!f) {
                chan->softhangup = 1;
@@ -689,10 +881,26 @@ struct ast_frame *ast_read(struct ast_channel *chan)
                f = &null_frame;
        } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
                /* Answer the CDR */
+               chan->state = AST_STATE_UP;
                ast_cdr_answer(chan->cdr);
        }
        pthread_mutex_unlock(&chan->lock);
 
+       /* Run any generator sitting on the line */
+       if (f && (f->frametype == AST_FRAME_VOICE) && chan->generatordata) {
+               /* Mask generator data temporarily */
+               void *tmp;
+               int res;
+               tmp = chan->generatordata;
+               chan->generatordata = NULL;
+               res = chan->generator->generate(chan, tmp, f->datalen);
+               chan->generatordata = tmp;
+               if (res) {
+                       ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
+                       ast_deactivate_generator(chan);
+               }
+       }
+
        return f;
 }
 
@@ -769,6 +977,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
        }
        if (chan->masqr)
                return 0;
+       if (chan->generatordata) {
+               if (chan->writeinterrupt)
+                       ast_deactivate_generator(chan);
+               else
+                       return 0;
+       }
        CHECK_BLOCKING(chan);
        switch(fr->frametype) {
        case AST_FRAME_CONTROL:
@@ -796,6 +1010,9 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                }
        }
        chan->blocking = 0;
+       /* Consider a write failure to force a soft hangup */
+       if (res < 0)
+               chan->softhangup = 1;
        return res;
 }
 
@@ -899,7 +1116,7 @@ int ast_call(struct ast_channel *chan, char *addr, int timeout)
           return anyway.  */
        int res = -1;
        /* Stop if we're a zombie or need a soft hangup */
-       pthread_mutex_lock(&chan->lock);
+       ast_pthread_mutex_lock(&chan->lock);
        if (!chan->zombie && !ast_check_hangup(chan)) 
                if (chan->pvt->call)
                        res = chan->pvt->call(chan, addr, timeout);
@@ -975,19 +1192,19 @@ int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *pe
        chanf = chan->nativeformats;
        res = ast_translator_best_choice(&peerf, &chanf);
        if (res < 0) {
-               ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan, chan->nativeformats, peer, peer->nativeformats);
+               ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, chan->nativeformats, peer->name, peer->nativeformats);
                return -1;
        }
        /* Set read format on channel */
        res = ast_set_read_format(chan, peerf);
        if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan, chanf);
+               ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, chanf);
                return -1;
        }
        /* Set write format on peer channel */
        res = ast_set_write_format(peer, peerf);
        if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer, peerf);
+               ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, peerf);
                return -1;
        }
        /* Now we go the other way */
@@ -995,19 +1212,19 @@ int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *pe
        chanf = chan->nativeformats;
        res = ast_translator_best_choice(&chanf, &peerf);
        if (res < 0) {
-               ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer, peer->nativeformats, chan, chan->nativeformats);
+               ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, peer->nativeformats, chan->name, chan->nativeformats);
                return -1;
        }
        /* Set writeformat on channel */
        res = ast_set_write_format(chan, chanf);
        if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan, chanf);
+               ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, chanf);
                return -1;
        }
        /* Set read format on peer channel */
        res = ast_set_read_format(peer, chanf);
        if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer, peerf);
+               ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, peerf);
                return -1;
        }
        return 0;
@@ -1057,7 +1274,7 @@ static int ast_do_masquerade(struct ast_channel *original)
        free_translation(original);
 
        /* We need the clone's lock, too */
-       pthread_mutex_lock(&clone->lock);
+       ast_pthread_mutex_lock(&clone->lock);
 
        /* Unlink the masquerade */
        original->masq = NULL;
@@ -1224,7 +1441,8 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags
                }
        
                        
-               if ((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat)) {
+               if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat)) &&
+                       !(c0->generator || c1->generator))  {
                        if (ast_channel_make_compatible(c0, c1)) {
                                ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
                                return -1;
@@ -1282,10 +1500,14 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags
                                last = who;
 #endif
 tackygoto:
-                               if (who == c0) 
-                                       ast_write(c1, f);
-                               else 
-                                       ast_write(c0, f);
+                               /* Don't copy packets if there is a generator on either one, since they're
+                                  not supposed to be listening anyway */
+                               if (!c0->generator && !c1->generator) {
+                                       if (who == c0) 
+                                               ast_write(c1, f);
+                                       else 
+                                               ast_write(c0, f);
+                               }
                        }
                        ast_frfree(f);
                } else
@@ -1320,3 +1542,136 @@ int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int
        return 0;
 }
 
+struct tonepair_def {
+       int freq1;
+       int freq2;
+       int duration;
+       int vol;
+};
+
+struct tonepair_state {
+       float freq1;
+       float freq2;
+       float vol;
+       int duration;
+       int pos;
+       int origrfmt;
+       int origwfmt;
+       struct ast_frame f;
+       unsigned char offset[AST_FRIENDLY_OFFSET];
+       short data[4000];
+};
+
+static void tonepair_release(struct ast_channel *chan, void *params)
+{
+       struct tonepair_state *ts = params;
+       if (chan) {
+               ast_set_write_format(chan, ts->origwfmt);
+               ast_set_read_format(chan, ts->origrfmt);
+       }
+       free(ts);
+}
+
+static void * tonepair_alloc(struct ast_channel *chan, void *params)
+{
+       struct tonepair_state *ts;
+       struct tonepair_def *td = params;
+       ts = malloc(sizeof(struct tonepair_state));
+       if (!ts)
+               return NULL;
+       memset(ts, 0, sizeof(struct tonepair_state));
+       ts->origrfmt = chan->readformat;
+       ts->origwfmt = chan->writeformat;
+       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+               ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
+               tonepair_release(NULL, ts);
+               ts = NULL;
+       } else if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+               ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (read)\n", chan->name);
+               ast_set_write_format(chan, ts->origwfmt);
+               tonepair_release(NULL, ts);
+               ts = NULL;
+       } else {
+               ts->freq1 = td->freq1;
+               ts->freq2 = td->freq2;
+               ts->duration = td->duration;
+               ts->vol = td->vol;
+       }
+       /* Let interrupts interrupt :) */
+       chan->writeinterrupt = 1;
+       return ts;
+}
+
+static int tonepair_generator(struct ast_channel *chan, void *data, int len)
+{
+       struct tonepair_state *ts = data;
+       int x;
+       if (len > sizeof(ts->data) / 2 - 1) {
+               ast_log(LOG_WARNING, "Can't generate that much data!\n");
+               return -1;
+       }
+       memset(&ts->f, 0, sizeof(ts->f));
+       for (x=0;x<len/2;x++) {
+               ts->data[x] = ts->vol * (
+                               sin((ts->freq1 * 2.0 * M_PI / 8000.0) * (ts->pos + x)) +
+                               sin((ts->freq2 * 2.0 * M_PI / 8000.0) * (ts->pos + x))
+                       );
+       }
+       ts->f.frametype = AST_FRAME_VOICE;
+       ts->f.subclass = AST_FORMAT_SLINEAR;
+       ts->f.datalen = len;
+       ts->f.timelen = len/8;
+       ts->f.offset = AST_FRIENDLY_OFFSET;
+       ts->f.data = ts->data;
+       ast_write(chan, &ts->f);
+       ts->pos += x;
+       if (ts->duration > 0) {
+               if (ts->pos >= ts->duration * 8)
+                       return -1;
+       }
+       return 0;
+}
+
+static struct ast_generator tonepair = {
+       alloc: tonepair_alloc,
+       release: tonepair_release,
+       generate: tonepair_generator,
+};
+
+int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
+{
+       struct tonepair_def d = { 0, };
+       d.freq1 = freq1;
+       d.freq2 = freq2;
+       d.duration = duration;
+       if (vol < 1)
+               d.vol = 8192;
+       else
+               d.vol = vol;
+       if (ast_activate_generator(chan, &tonepair, &d))
+               return -1;
+       return 0;
+}
+
+void ast_tonepair_stop(struct ast_channel *chan)
+{
+       ast_deactivate_generator(chan);
+}
+
+int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
+{
+       struct ast_frame *f;
+       int res;
+       if ((res = ast_tonepair_start(chan, freq1, freq2, duration, vol)))
+               return res;
+
+       /* Give us some wiggle room */
+       while(chan->generatordata && (ast_waitfor(chan, 100) >= 0)) {
+               f = ast_read(chan);
+               if (f)
+                       ast_frfree(f);
+               else
+                       return -1;
+       }
+       return 0;
+}
index 9a45402..88fd428 100755 (executable)
@@ -80,7 +80,7 @@ struct vofr_hdr {
        u_int8_t cid;                   /* Channel ID */
        u_int8_t mod:4;                 /* Modulation */
        u_int8_t remid:4;               /* Remote ID */
-#elif __BYTE__ORDER == __BIG_ENDIAN
+#elif __BYTE_ORDER == __BIG_ENDIAN
        u_int8_t ctag:4;                /* Connect tag */
        u_int8_t dtype:4;               /* Data type */
        u_int8_t vflags:4;              /* Voice Routing Flags */
index 7300646..ee45e39 100755 (executable)
@@ -14,6 +14,7 @@
 #include <stdio.h>
 #include <pthread.h>
 #include <string.h>
+#include <asterisk/lock.h>
 #include <asterisk/channel.h>
 #include <asterisk/channel_pvt.h>
 #include <asterisk/config.h>
@@ -55,14 +56,14 @@ static int silencesupression = 0;
 
 static int prefformat = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW;
 
-static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
 
 /* Protect the interface list (of phone_pvt's) */
-static pthread_mutex_t iflock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t iflock = AST_MUTEX_INITIALIZER;
 
 /* Protect the monitoring thread, so only one process can kill or start it, and not
    when it's doing something critical. */
-static pthread_mutex_t monlock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t monlock = AST_MUTEX_INITIALIZER;
 
 /* This is the thread for the monitor which checks for input on the channels
    which are not currently in use.  */
@@ -140,7 +141,31 @@ static int phone_digit(struct ast_channel *ast, char digit)
 static int phone_call(struct ast_channel *ast, char *dest, int timeout)
 {
        struct phone_pvt *p;
+       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_RINGING };
+
+       // CID stuff for the phonejack...
+
+       PHONE_CID cid;
+       time_t UtcTime;
+       struct tm *t;
+
+
+       if (ast->callerid) {
+               time(&UtcTime);
+               t = localtime(&UtcTime);
+
+               if(t != NULL) {
+                       sprintf(cid.month, "%02d",(t->tm_mon + 1));
+                       sprintf(cid.day, "%02d", t->tm_mday);
+                       sprintf(cid.hour, "%02d", t->tm_hour);
+                       sprintf(cid.min, "%02d", t->tm_min);
+               }
+               strcpy(cid.name, "Unknown");
+               sprintf(cid.number,"%s",ast->callerid);
+       }
+
        p = ast->pvt->pvt;
+
        if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
                ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name);
                return -1;
@@ -149,7 +174,9 @@ static int phone_call(struct ast_channel *ast, char *dest, int timeout)
           ring the phone and wait for someone to answer */
        if (option_debug)
                ast_log(LOG_DEBUG, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fds[0]);
-       ioctl(p->fd, PHONE_RING_START);
+
+       ioctl(p->fd, PHONE_RING_START,&cid);
+       ast_queue_frame(ast, &f, 0);
        ast->state = AST_STATE_RINGING;
        return 0;
 }
@@ -336,7 +363,7 @@ static struct ast_frame  *phone_exception(struct ast_channel *ast)
        if (phonee.bits.pstn_ring)
                ast_verbose("Unit is ringing\n");
        if (phonee.bits.caller_id) {
-               ast_verbose("We have caller ID: %s\n");
+               ast_verbose("We have caller ID\n");
        }
        if (phonee.bits.pstn_wink)
                ast_verbose("Detected Wink\n");
@@ -351,6 +378,7 @@ static struct ast_frame  *phone_read(struct ast_channel *ast)
 {
        int res;
        struct phone_pvt *p = ast->pvt->pvt;
+       
 
        /* Some nice norms */
        p->fr.datalen = 0;
@@ -448,14 +476,13 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
        char tmpbuf[4];
        /* Write a frame of (presumably voice) data */
        if (frame->frametype != AST_FRAME_VOICE) {
-               ast_log(LOG_WARNING, "Don't know what to do with  frame type '%d'\n", frame->frametype);
-               ast_frfree(frame);
-               return -1;
+               if (frame->frametype != AST_FRAME_IMAGE)
+                       ast_log(LOG_WARNING, "Don't know what to do with  frame type '%d'\n", frame->frametype);
+               return 0;
        }
        if (!(frame->subclass &
                (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW))) {
                ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
-               ast_frfree(frame);
                return -1;
        }
        /* If we're not in up mode, go into up mode now */
@@ -560,17 +587,20 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
                        res = phone_write_buf(p, pos, expected, maxfr);
                }
                if (res != expected) {
-                       if (res < 0) 
-                               ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
-/*
- * Card is in non-blocking mode now and it works well now, but there are
- * lot of messages like this. So, this message is temporarily disabled.
- */
+                       if (errno != EAGAIN) {
+                               if (res < 0) 
+                                       ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
+       /*
+        * Card is in non-blocking mode now and it works well now, but there are
+        * lot of messages like this. So, this message is temporarily disabled.
+        */
 #if 0
-                       else
-                               ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
+                               else
+                                       ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
 #endif
-                       return -1;
+                               return -1;
+                       } else /* Pretend it worked */
+                               res = expected;
                }
                sofar += res;
                pos += res;
@@ -581,7 +611,7 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
 static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context)
 {
        struct ast_channel *tmp;
-       tmp = ast_channel_alloc();
+       tmp = ast_channel_alloc(1);
        if (tmp) {
                snprintf(tmp->name, sizeof(tmp->name), "Phone/%s", i->dev + 5);
                tmp->type = type;
index 027225e..39aa85e 100755 (executable)
@@ -25,6 +25,8 @@ CFLAGS+=
 LIBG723=g723.1/libg723.a
 LIBG723B=g723.1b/libg723b.a
 LIBGSM=gsm/lib/libgsm.a
+LIBGSM=$(shell if uname -m | grep -q 86; then echo gsm/lib/libgsm.a; else echo "-lgsm" ; fi)
+LIBGSMT=$(shell if uname -m | grep -q 86; then echo gsm/lib/libgsm.a; fi)
 LIBMP3=mp3/libmp3.a
 LIBLPC10=lpc10/liblpc10.a
 
@@ -44,7 +46,7 @@ clean:
 $(LIBG723):
        make -C g723.1 all
 
-$(LIBGSM):
+gsm/lib/libgsm.a:
        make -C gsm lib/libgsm.a
 
 $(LIBG723B):
@@ -65,7 +67,7 @@ codec_g723_1b.o : codec_g723_1.c
 codec_g723_1b.so : codec_g723_1b.o $(LIBG723B)
        $(CC) -shared -Xlinker -x -o $@ $< $(LIBG723B) -lm
 
-codec_gsm.so: codec_gsm.o $(LIBGSM)
+codec_gsm.so: codec_gsm.o $(LIBGSMT) 
        $(CC) -shared -Xlinker -x -o $@ $< $(LIBGSM)
 
 codec_lpc10.so: codec_lpc10.o $(LIBLPC10)
index cce4c4d..af5131d 100755 (executable)
@@ -22,7 +22,8 @@ LIB_TARGET_DIR = .
 # 
 
 WARNINGS = -Wall -Wno-comment -Wno-error
-CFLAGS = -O6 -mpentium -I$(LIB_TARGET_DIR) $(WARNINGS)
+CFLAGS = -O6 -I$(LIB_TARGET_DIR) $(WARNINGS)
+CFLAGS+= $(shell if uname -m | grep -q 86; then echo "-mpentium" ; fi)
 
 LIB = $(LIB_TARGET_DIR)/liblpc10.a
 
index 7e10af8..4e9f0f4 100755 (executable)
@@ -80,7 +80,7 @@ static int jpeg_write_image(int fd, struct ast_frame *fr)
        if (fr->datalen) {
                res = write(fd, fr->data, fr->datalen);
                if (res != fr->datalen) {
-                       ast_log(LOG_WARNING, "Only wrote %d of %d bytes: %s\n", res, fr->datalen);
+                       ast_log(LOG_WARNING, "Only wrote %d of %d bytes: %s\n", res, fr->datalen, strerror(errno));
                        return -1;
                }
        }
index 77ac719..667f66f 100755 (executable)
@@ -11,6 +11,7 @@
  * the GNU General Public License
  */
  
+#include <asterisk/lock.h>
 #include <asterisk/channel.h>
 #include <asterisk/file.h>
 #include <asterisk/logger.h>
@@ -51,7 +52,7 @@ struct ast_filestream {
 
 
 static struct ast_filestream *glist = NULL;
-static pthread_mutex_t wav_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t wav_lock = AST_MUTEX_INITIALIZER;
 static int glistcnt = 0;
 
 static char *name = "wav";
@@ -167,7 +168,7 @@ static int check_header(int fd)
                return -1;
        }
        if (read(fd, &bisam, 2) != 2) {
-               ast_log(LOG_WARNING, "Read failed (Bits Per Sample): &d\n", ltohs(bisam));
+               ast_log(LOG_WARNING, "Read failed (Bits Per Sample): %d\n", ltohs(bisam));
                return -1;
        }
         /* Begin data chunk */
index c819d7a..793ed30 100755 (executable)
@@ -37,7 +37,8 @@ extern "C" {
  * ast_log(LOG_WHATEVER, "Problem with the %s Captain.  We should get some more.  Will %d be enough?", "flux capacitor", 10);
  * where WHATEVER is one of ERROR, DEBUG, EVENT, NOTICE, or WARNING depending on which log you wish to output to.
  */
-extern void ast_log(int level, char *file, int line, char *function, char *fmt, ...);
+extern void ast_log(int level, char *file, int line, char *function, char *fmt, ...)
+       __attribute__ ((format (printf, 5, 6)));
 
 //! Send a verbose message (based on verbose level)
 /*!
@@ -47,7 +48,8 @@ extern void ast_log(int level, char *file, int line, char *function, char *fmt,
  * Note the abscence of a comma after the VERBOSE_PREFIX_3.  This is important.
  * VERBOSE_PREFIX_1 through VERBOSE_PREFIX_3 are defined.
  */
-extern void ast_verbose(char *fmt, ...);
+extern void ast_verbose(char *fmt, ...)
+       __attribute__ ((format (printf, 1, 2)));
 
 extern int ast_register_verbose(void (*verboser)(char *string, int opos, int replacelast, int complete));
 extern int ast_unregister_verbose(void (*verboser)(char *string, int opos, int replacelast, int complete));
index ba4f493..9da8b62 100755 (executable)
--- a/logger.c
+++ b/logger.c
 #include <stdio.h>
 #include <unistd.h>
 #include <time.h>
+#include <asterisk/lock.h>
 #include <asterisk/logger.h>
 #include <asterisk/options.h>
 #include <asterisk/channel.h>
 #include <asterisk/config.h>
+#include <asterisk/term.h>
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -30,8 +32,8 @@
 
 #define MAX_MSG_QUEUE 200
 
-static pthread_mutex_t msglist_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t msglist_lock = AST_MUTEX_INITIALIZER;
+static pthread_mutex_t loglock = AST_MUTEX_INITIALIZER;
 
 static struct msglist {
        char *msg;
@@ -59,6 +61,14 @@ static char *levels[] = {
        "ERROR"
 };
 
+static int colors[] = {
+       COLOR_BRGREEN,
+       COLOR_BRBLUE,
+       COLOR_YELLOW,
+       COLOR_BRRED,
+       COLOR_RED
+};
+
 static int make_components(char *s, int lineno)
 {
        char *w;
@@ -207,14 +217,21 @@ int reload_logger(void)
 extern void ast_log(int level, char *file, int line, char *function, char *fmt, ...)
 {
        char date[256];
+       char tmp[80];
+       char tmp2[80];
+       char tmp3[80];
+       char tmp4[80];
+       char linestr[80];
        time_t t;
        struct tm *tm;
        struct logfile *f;
 
        va_list ap;
        va_start(ap, fmt);
-       if (!option_verbose && !option_debug && (!level))
+       if (!option_verbose && !option_debug && (!level)) {
+               va_end(ap);
                return;
+       }
        ast_pthread_mutex_lock(&loglock);
        if (level == 1 /* Event */) {
                time(&t);
@@ -226,7 +243,9 @@ extern void ast_log(int level, char *file, int line, char *function, char *fmt,
                        vfprintf(eventlog, fmt, ap);
                        fflush(eventlog);
                } else
-                       ast_log(LOG_WARNING, "Unable to retrieve local time?\n");
+                       /** Cannot use ast_log() from locked section of ast_log()!
+                           ast_log(LOG_WARNING, "Unable to retrieve local time?\n"); **/
+                       fprintf(stderr, "ast_log: Unable to retrieve local time for %d?\n", t);
        } else {
                if (logfiles) {
                        f = logfiles;
@@ -238,16 +257,26 @@ extern void ast_log(int level, char *file, int line, char *function, char *fmt,
                                                strftime(date, sizeof(date), "%b %e %T", tm);
                                                fprintf(f->f, "%s %s[%ld]: File %s, Line %d (%s): ", date, levels[level], pthread_self(), file, line, function);
                                        } else {
-                                               fprintf(f->f, "%s[%ld]: File %s, Line %d (%s): ", levels[level], pthread_self(), file, line, function);
+                                               sprintf(linestr, "%d", line);
+                                               fprintf(f->f, "%s[%ld]: File %s, Line %s (%s): ",
+                                                                                                                               term_color(tmp, levels[level], colors[level], 0, sizeof(tmp)),
+                                                                                                                               pthread_self(),
+                                                                                                                               term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
+                                                                                                                               term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
+                                                                                                                               term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
                                        }
                                        vfprintf(f->f, fmt, ap);
+                                       va_start(ap, fmt);
                                        fflush(f->f);
+                                       va_end(ap);
                                }
                                f = f->next;
                        }
                } else {
                        fprintf(stdout, "%s[%ld]: File %s, Line %d (%s): ", levels[level], pthread_self(), file, line, function);
+                       va_start(ap, fmt);
                        vfprintf(stdout, fmt, ap);
+                       va_end(ap);
                        fflush(stdout);
                }
        }
diff --git a/pbx.c b/pbx.c
index 55df966..ec52c05 100755 (executable)
--- a/pbx.c
+++ b/pbx.c
@@ -11,7 +11,7 @@
  * the GNU General Public License
  */
 
-#include <pthread.h>
+#include <asterisk/lock.h>
 #include <asterisk/cli.h>
 #include <asterisk/pbx.h>
 #include <asterisk/channel.h>
@@ -20,6 +20,7 @@
 #include <asterisk/file.h>
 #include <asterisk/callerid.h>
 #include <asterisk/cdr.h>
+#include <asterisk/term.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -71,7 +72,13 @@ struct ast_exten {
 
 struct ast_include {
        char name[AST_MAX_EXTENSION];
+       char rname[AST_MAX_EXTENSION];
        char *registrar;
+       int hastime;
+       unsigned int monthmask;
+       unsigned int daymask;
+       unsigned int dowmask;
+       unsigned int minmask[24];
        struct ast_include *next;
 };
 
@@ -244,14 +251,14 @@ static struct pbx_builtin {
 };
 
 /* Lock for the application list */
-static pthread_mutex_t applock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t applock = AST_MUTEX_INITIALIZER;
 static struct ast_context *contexts = NULL;
 /* Lock for the ast_context list */
-static pthread_mutex_t conlock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t conlock = AST_MUTEX_INITIALIZER;
 static struct ast_app *apps = NULL;
 
 /* Lock for switches */
-static pthread_mutex_t switchlock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t switchlock = AST_MUTEX_INITIALIZER;
 struct ast_switch *switches = NULL;
 
 int pbx_exec(struct ast_channel *c, /* Channel */
@@ -342,6 +349,47 @@ static struct ast_switch *pbx_findswitch(char *sw)
        return asw;
 }
 
+static inline int include_valid(struct ast_include *i)
+{
+       struct tm *tm;
+       time_t t;
+       if (!i->hastime)
+               return 1;
+       time(&t);
+       tm = localtime(&t);
+       if (!tm) {
+               ast_log(LOG_WARNING, "Failed to get local time\n");
+               return 0;
+       }
+
+       /* If it's not the right month, return */
+       if (!(i->monthmask & (1 << tm->tm_mon))) {
+               return 0;
+       }
+
+       /* If it's not that time of the month.... */
+       if (!(i->daymask & (1 << tm->tm_mday)))
+               return 0;
+
+       /* If it's not the right day of the week */
+       if (!(i->dowmask & (1 << tm->tm_wday)))
+               return 0;
+
+       /* Sanity check the hour just to be safe */
+       if ((tm->tm_hour < 0) || (tm->tm_hour > 23)) {
+               ast_log(LOG_WARNING, "Insane time...\n");
+               return 0;
+       }
+
+       /* Now the tough part, we calculate if it fits
+          in the right time based on min/hour */
+       if (!(i->minmask[tm->tm_hour] & (1 << (tm->tm_min / 2))))
+               return 0;
+
+       /* If we got this far, then we're good */
+       return 1;
+}
+
 static void pbx_destroy(struct ast_pbx *p)
 {
        free(p);
@@ -561,10 +609,12 @@ static struct ast_exten *pbx_find_extension(struct ast_channel *chan, char *cont
                        /* Now try any includes we have in this context */
                        i = tmp->includes;
                        while(i) {
-                               if ((e = pbx_find_extension(chan, i->name, exten, priority, callerid, action, incstack, stacklen, status, swo, data))) 
-                                       return e;
-                               if (*swo) 
-                                       return NULL;
+                               if (include_valid(i)) {
+                                       if ((e = pbx_find_extension(chan, i->rname, exten, priority, callerid, action, incstack, stacklen, status, swo, data))) 
+                                               return e;
+                                       if (*swo) 
+                                               return NULL;
+                               }
                                i = i->next;
                        }
                }
@@ -584,6 +634,9 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
        int status = 0;
        char *incstack[AST_PBX_MAX_STACK];
        int stacklen = 0;
+       char tmp[80];
+       char tmp2[80];
+       char tmp3[256];
        if (ast_pthread_mutex_lock(&conlock)) {
                ast_log(LOG_WARNING, "Unable to obtain lock\n");
                if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH))
@@ -614,7 +667,10 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
                                else if (option_verbose > 2)
                                                ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n", 
-                                                               app->name, c->name, (e->data ? (char *)e->data : NULL), (newstack ? "in new stack" : "in same stack"));
+                                                               term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
+                                                               term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
+                                                               term_color(tmp3, (e->data ? (char *)e->data : NULL), COLOR_BRMAGENTA, 0, sizeof(tmp3)),
+                                                               (newstack ? "in new stack" : "in same stack"));
                                res = pbx_exec(c, app, e->data, newstack);
                                return res;
                        } else {
@@ -675,41 +731,6 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
 
 }
 
-#if 0
-int ast_pbx_longest_extension(char *context) 
-{
-       /* XXX Not include-aware XXX */
-       struct ast_context *tmp;
-       struct ast_exten *e;
-       int len = 0;
-       if (ast_pthread_mutex_lock(&conlock)) {
-               ast_log(LOG_WARNING, "Unable to obtain lock\n");
-               return -1;
-       }
-       tmp = contexts;
-       while(tmp) {
-               if (!strcasecmp(tmp->name, context)) {
-                       /* By locking tmp, not only can the state of its entries not
-                          change, but it cannot be destroyed either. */
-                       ast_pthread_mutex_lock(&tmp->lock);
-                       /* But we can relieve the conlock, as tmp will not change */
-                       ast_pthread_mutex_unlock(&conlock);
-                       e = tmp->root;
-                       while(e) {
-                               if (strlen(e->exten) > len)
-                                       len = strlen(e->exten);
-                               e = e->next;
-                       }
-                       ast_pthread_mutex_unlock(&tmp->lock);
-                       return len;
-               }
-               tmp = tmp->next;
-       }
-       ast_log(LOG_WARNING, "No such context '%s'\n", context);
-       return -1;
-}
-#endif
-
 int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int priority, char *callerid) 
 {
        return pbx_extension_helper(c, context, exten, priority, callerid, HELPER_EXISTS);
@@ -736,7 +757,7 @@ int ast_pbx_run(struct ast_channel *c)
 
        /* A little initial setup here */
        if (c->pbx)
-               ast_log(LOG_WARNING, "%s already has PBX structure??\n");
+               ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
        c->pbx = malloc(sizeof(struct ast_pbx));
        if (!c->pbx) {
                ast_log(LOG_WARNING, "Out of memory\n");
@@ -744,7 +765,7 @@ int ast_pbx_run(struct ast_channel *c)
        }
        if (c->amaflags) {
                if (c->cdr) {
-                       ast_log(LOG_WARNING, "%s already has a call record??\n");
+                       ast_log(LOG_WARNING, "%s already has a call record??\n", c->name);
                } else {
                        c->cdr = ast_cdr_alloc();
                        if (!c->cdr) {
@@ -890,6 +911,22 @@ int ast_pbx_run(struct ast_channel *c)
        if (firstpass) 
                ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
 out:
+       if (ast_exists_extension(c, c->context, "h", 1, c->callerid)) {
+               strcpy(c->exten, "h");
+               c->priority = 1;
+               while(ast_exists_extension(c, c->context, c->exten, c->priority, c->callerid)) {
+                       if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->callerid))) {
+                               /* Something bad happened, or a hangup has been requested. */
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+                               else if (option_verbose > 1)
+                                       ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+                               break;
+                       }
+                       c->priority++;
+               }
+       }
+
        pbx_destroy(c->pbx);
        c->pbx = NULL;
        if (res != AST_PBX_KEEPALIVE)
@@ -973,7 +1010,7 @@ int ast_context_remove_include2(struct ast_context *con, char *include, char *re
 {
        struct ast_include *i, *pi = NULL;
 
-       if (pthread_mutex_lock(&con->lock)) return -1;
+       if (ast_pthread_mutex_lock(&con->lock)) return -1;
 
        /* walk includes */
        i = con->includes;
@@ -1045,7 +1082,7 @@ int ast_context_remove_switch2(struct ast_context *con, char *sw, char *data, ch
 {
        struct ast_sw *i, *pi = NULL;
 
-       if (pthread_mutex_lock(&con->lock)) return -1;
+       if (ast_pthread_mutex_lock(&con->lock)) return -1;
 
        /* walk switchs */
        i = con->alts;
@@ -1215,6 +1252,7 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int
 int ast_register_application(char *app, int (*execute)(struct ast_channel *, void *), char *synopsis, char *description)
 {
        struct ast_app *tmp;
+       char tmps[80];
        if (ast_pthread_mutex_lock(&applock)) {
                ast_log(LOG_ERROR, "Unable to lock application list\n");
                return -1;
@@ -1230,6 +1268,7 @@ int ast_register_application(char *app, int (*execute)(struct ast_channel *, voi
        }
        tmp = malloc(sizeof(struct ast_app));
        if (tmp) {
+               memset(tmp, 0, sizeof(struct ast_app));
                strncpy(tmp->name, app, sizeof(tmp->name)-1);
                tmp->execute = execute;
                tmp->synopsis = synopsis;
@@ -1242,7 +1281,7 @@ int ast_register_application(char *app, int (*execute)(struct ast_channel *, voi
                return -1;
        }
        if (option_verbose > 1)
-               ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", tmp->name);
+               ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
        ast_pthread_mutex_unlock(&applock);
        return 0;
 }
@@ -1769,7 +1808,7 @@ struct ast_context *ast_context_create(char *name, char *registrar)
        tmp = malloc(sizeof(struct ast_context));
        if (tmp) {
                memset(tmp, 0, sizeof(struct ast_context));
-               pthread_mutex_init(&tmp->lock, NULL);
+               ast_pthread_mutex_init(&tmp->lock);
                strncpy(tmp->name, name, sizeof(tmp->name)-1);
                tmp->root = NULL;
                tmp->registrar = registrar;
@@ -1821,6 +1860,255 @@ int ast_context_add_include(char *context, char *include, char *registrar)
        return -1;
 }
 
+#define FIND_NEXT \
+do { \
+       c = info; \
+       while(*c && (*c != '|')) c++; \
+       if (*c) *c = '\0'; else c = NULL; \
+} while(0)
+
+static void get_timerange(struct ast_include *i, char *times)
+{
+       char *e;
+       int x;
+       int s1, s2;
+       int e1, e2;
+       /* Star is all times */
+       if (!strlen(times) || !strcmp(times, "*")) {
+               for (x=0;x<24;x++)
+                       i->minmask[x] = (1 << 30) - 1;
+               return;
+       }
+       /* Otherwise expect a range */
+       e = strchr(times, '-');
+       if (!e) {
+               ast_log(LOG_WARNING, "Time range is not valid. Assuming no time.\n");
+               return;
+       }
+       *e = '\0';
+       e++;
+       while(*e && !isdigit(*e)) e++;
+       if (!*e) {
+               ast_log(LOG_WARNING, "Invalid time range.  Assuming no time.\n");
+               return;
+       }
+       if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+               ast_log(LOG_WARNING, "%s isn't a time.  Assuming no time.\n", times);
+               return;
+       }
+       if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+               ast_log(LOG_WARNING, "%s isn't a time.  Assuming no time.\n", e);
+               return;
+       }
+       s1 = s1 * 30 + s2/2;
+       if ((s1 < 0) || (s1 >= 24*30)) {
+               ast_log(LOG_WARNING, "%s isn't a valid star time. Assuming no time.\n", times);
+               return;
+       }
+       e1 = e1 * 30 + e2/2;
+       if ((e1 < 0) || (e2 >= 24*30)) {
+               ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
+               return;
+       }
+       /* Go through the time and enable each appropriate bit */
+       for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
+               i->minmask[x/30] |= (1 << (x % 30));
+       }
+       /* Do the last one */
+       i->minmask[x/30] |= (1 << (x % 30));
+       /* All done */
+}
+
+static char *days[] =
+{
+       "sun",
+       "mon",
+       "tue",
+       "wed",
+       "thu",
+       "fri",
+       "sat",
+};
+
+static unsigned int get_dow(char *dow)
+{
+       char *c;
+       /* The following line is coincidence, really! */
+       int s, e, x;
+       unsigned mask;
+       /* Check for all days */
+       if (!strlen(dow) || !strcmp(dow, "*"))
+               return (1 << 7) - 1;
+       /* Get start and ending days */
+       c = strchr(dow, '-');
+       if (c) {
+               *c = '\0';
+               c++;
+       }
+       /* Find the start */
+       s = 0;
+       while((s < 7) && strcasecmp(dow, days[s])) s++;
+       if (s >= 7) {
+               ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", dow);
+               return 0;
+       }
+       if (c) {
+               e = 0;
+               while((e < 7) && strcasecmp(dow, days[e])) e++;
+               if (e >= 7) {
+                       ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
+                       return 0;
+               }
+       } else
+               e = s;
+       mask = 0;
+       for (x=s;x!=e;x = (x + 1) % 7) {
+               mask |= (1 << x);
+       }
+       /* One last one */
+       mask |= (1 << x);
+       return mask;
+}
+
+static unsigned int get_day(char *day)
+{
+       char *c;
+       /* The following line is coincidence, really! */
+       int s, e, x;
+       unsigned int mask;
+       /* Check for all days */
+       if (!strlen(day) || !strcmp(day, "*")) {
+               mask = (1 << 30)  + ((1 << 30) - 1);
+       }
+       /* Get start and ending days */
+       c = strchr(day, '-');
+       if (c) {
+               *c = '\0';
+               c++;
+       }
+       /* Find the start */
+       if (sscanf(day, "%d", &s) != 1) {
+               ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", day);
+               return 0;
+       }
+       if ((s < 1) || (s > 31)) {
+               ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", day);
+               return 0;
+       }
+       s--;
+       if (c) {
+               if (sscanf(c, "%d", &e) != 1) {
+                       ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
+                       return 0;
+               }
+               if ((e < 1) || (e > 31)) {
+                       ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
+                       return 0;
+               }
+               e--;
+       } else
+               e = s;
+       mask = 0;
+       for (x=s;x!=e;x = (x + 1) % 31) {
+               mask |= (1 << x);
+       }
+       mask |= (1 << x);
+       return mask;
+}
+
+static char *months[] =
+{
+       "jan",
+       "feb",
+       "mar",
+       "apr",
+       "may",
+       "jun",
+       "jul",
+       "aug",
+       "sep",
+       "oct",
+       "nov",
+       "dec",
+};
+
+static unsigned int get_month(char *mon)
+{
+       char *c;
+       /* The following line is coincidence, really! */
+       int s, e, x;
+       unsigned int mask;
+       /* Check for all days */
+       if (!strlen(mon) || !strcmp(mon, "*")) 
+               return (1 << 12) - 1;
+       /* Get start and ending days */
+       c = strchr(mon, '-');
+       if (c) {
+               *c = '\0';
+               c++;
+       }
+       /* Find the start */
+       s = 0;
+       while((s < 12) && strcasecmp(mon, months[s])) s++;
+       if (s >= 12) {
+               ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", mon);
+               return 0;
+       }
+       if (c) {
+               e = 0;
+               while((e < 12) && strcasecmp(mon, months[e])) e++;
+               if (e >= 12) {
+                       ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", c);
+                       return 0;
+               }
+       } else
+               e = s;
+       mask = 0;
+       for (x=s;x!=e;x = (x + 1) % 12) {
+               mask |= (1 << x);
+       }
+       /* One last one */
+       mask |= (1 << x);
+       return mask;
+}
+
+static void build_timing(struct ast_include *i, char *info)
+{
+       char *c;
+       /* Check for empty just in case */
+       if (!strlen(info))
+               return;
+       i->hastime = 1;
+       /* Assume everything except time */
+       i->monthmask = (1 << 12) - 1;
+       i->daymask = (1 << 30) - 1 + (1 << 30);
+       i->dowmask = (1 << 7) - 1;
+       /* Avoid using strtok */
+       FIND_NEXT;
+
+       /* Info has the time range, start with that */
+       get_timerange(i, info);
+       info = c;
+       if (!info)
+               return;
+       FIND_NEXT;
+       /* Now check for day of week */
+       i->dowmask = get_dow(info);
+
+       info = c;
+       if (!info)
+               return;
+       FIND_NEXT;
+       /* Now check for the day of the month */
+       i->daymask = get_day(info);
+       info = c;
+       if (!info)
+               return;
+       FIND_NEXT;
+       /* And finally go for the month */
+       i->monthmask = get_month(info);
+}
+
 /*
  * errno values
  *  ENOMEM - out of memory
@@ -1832,6 +2120,7 @@ int ast_context_add_include2(struct ast_context *con, char *value,
        char *registrar)
 {
        struct ast_include *new_include;
+       char *c;
        struct ast_include *i, *il = NULL; /* include, include_last */
 
        /* allocate new include structure ... */
@@ -1842,7 +2131,17 @@ int ast_context_add_include2(struct ast_context *con, char *value,
        }
        
        /* ... fill in this structure ... */
+       memset(new_include, 0, sizeof(struct ast_include));
        strncpy(new_include->name, value, sizeof(new_include->name)-1);
+       strncpy(new_include->rname, value, sizeof(new_include->rname)-1);
+       c = new_include->rname;
+       /* Strip off timing info */
+       while(*c && (*c != '|')) c++; 
+       /* Process if it's there */
+       if (*c) {
+               build_timing(new_include, c+1);
+               *c = '\0';
+       }
        new_include->next      = NULL;
        new_include->registrar = registrar;
 
@@ -1932,6 +2231,7 @@ int ast_context_add_switch2(struct ast_context *con, char *value,
        }
        
        /* ... fill in this structure ... */
+       memset(new_sw, 0, sizeof(struct ast_sw));
        strncpy(new_sw->name, value, sizeof(new_sw->name)-1);
        if (data)
                strncpy(new_sw->data, data, sizeof(new_sw->data)-1);
@@ -2068,10 +2368,11 @@ int ast_context_add_ignorepat2(struct ast_context *con, char *value, char *regis
                errno = ENOMEM;
                return -1;
        }
+       memset(ignorepat, 0, sizeof(struct ast_ignorepat));
        strncpy(ignorepat->pattern, value, sizeof(ignorepat->pattern)-1);
        ignorepat->next = NULL;
        ignorepat->registrar = registrar;
-       pthread_mutex_lock(&con->lock);
+       ast_pthread_mutex_lock(&con->lock);
        ignorepatc = con->ignorepats;
        while(ignorepatc) {
                ignorepatl = ignorepatc;
@@ -2194,6 +2495,7 @@ int ast_add_extension2(struct ast_context *con,
        /* Be optimistic:  Build the extension structure first */
        tmp = malloc(sizeof(struct ast_exten));
        if (tmp) {
+               memset(tmp, 0, sizeof(struct ast_exten));
                ext_strncpy(tmp->exten, extension, sizeof(tmp->exten));
                tmp->priority = priority;
                if (callerid) {
@@ -2474,21 +2776,10 @@ static int pbx_builtin_prefix(struct ast_channel *chan, void *data)
 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
 {
        int ms;
-       struct ast_frame *f;
        /* Wait for "n" seconds */
        if (data && atoi((char *)data)) {
                ms = atoi((char *)data) * 1000;
-               while(ms > 0) {
-                       ms = ast_waitfor(chan, ms);
-                       if (ms <0)
-                               return -1;
-                       if (ms > 0) {
-                               f = ast_read(chan);
-                               if (!f)
-                                       return -1;
-                               ast_frfree(f);
-                       }
-               }
+               return ast_safe_sleep(chan, ms);
        }
        return 0;
 }
index e464c01..11a23c9 100755 (executable)
@@ -99,27 +99,6 @@ static int adsi_generate(unsigned char *buf, int msgtype, char *msg, int msglen,
 
 }
 
-#if 0
-static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int len, int *remainder)
-{
-       int res;
-       fd_set fds;
-       while(len) {
-               FD_ZERO(&fds);
-               FD_SET(chan->fds[0], &fds);
-               select(chan->fds[0] + 1, NULL, &fds, NULL, NULL);
-               res = write(chan->fds[0], buf, len);
-               if (res < 1) {
-                       ast_log(LOG_WARNING, "Failed to write: %s\n", strerror(errno));
-                       return -1;
-               }
-               buf += res;
-               len -= res;
-       }
-       return 0;
-}
-
-#else
 static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int len, int *remainder)
 {
        /* Sends carefully on a full duplex channel by using reading for
@@ -190,7 +169,6 @@ static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int l
        }
        return 0;
 }
-#endif
 
 static int __adsi_transmit_messages(struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype)
 {
@@ -305,7 +283,7 @@ static int __adsi_transmit_messages(struct ast_channel *chan, unsigned char **ms
                                break;
                        else {
                                retries++;
-                               ast_log(LOG_DEBUG, "Retransmitting (%d), from %d\n", start + 1);
+                               ast_log(LOG_DEBUG, "Retransmitting (%d), from %d\n", retries, start + 1);
                        }
                } else {
                        retries++;
@@ -502,14 +480,15 @@ int adsi_download_connect(unsigned char *buf, unsigned char *service,  unsigned
        /* Delimiter */
        buf[bytes++] = 0xff;
        
-       for (x=0;x<4;x++)
+       for (x=0;x<4;x++) {
                buf[bytes++] = fdn[x];
+       }
        for (x=0;x<4;x++)
                buf[bytes++] = sec[x];
-       if (ver > -1)
-               buf[bytes++] = ver & 0xff;
+       buf[bytes++] = ver & 0xff;
 
        buf[1] = bytes - 2;
+
        return bytes;
 
 }
@@ -529,6 +508,158 @@ int adsi_disconnect_session(unsigned char *buf)
 
 }
 
+int adsi_query_cpeid(unsigned char *buf)
+{
+       int bytes = 0;
+       buf[bytes++] = ADSI_QUERY_CPEID;
+       /* Reserve space for length */
+       bytes++;
+       buf[1] = bytes - 2;
+       return bytes;
+}
+
+int adsi_query_cpeinfo(unsigned char *buf)
+{
+       int bytes = 0;
+       buf[bytes++] = ADSI_QUERY_CONFIG;
+       /* Reserve space for length */
+       bytes++;
+       buf[1] = bytes - 2;
+       return bytes;
+}
+
+int adsi_read_encoded_dtmf(struct ast_channel *chan, unsigned char *buf, int maxlen)
+{
+       int bytes = 0;
+       int res;
+       unsigned char current = 0;
+       int gotstar = 0;
+       int pos = 0;
+       memset(buf, 0, sizeof(buf));
+       while(bytes <= maxlen) {
+               /* Wait up to a second for a digit */
+               res = ast_waitfordigit(chan, 1000);
+               if (!res)
+                       break;
+               if (res == '*') {
+                       gotstar = 1;    
+                       continue;
+               }
+               /* Ignore anything other than a digit */
+               if ((res < '0') || (res > '9'))
+                       continue;
+               res -= '0';
+               if (gotstar)
+                       res += 9;
+               if (pos)  {
+                       pos = 0;
+                       buf[bytes++] = (res << 4) | current;
+               } else {
+                       pos = 1;
+                       current = res;
+               }
+               gotstar = 0;
+       }
+       return bytes;
+}
+
+int adsi_get_cpeid(struct ast_channel *chan, unsigned char *cpeid, int voice)
+{
+       char buf[256];
+       int bytes = 0;
+       int res;
+       bytes += adsi_data_mode(buf);
+       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+       bytes = 0;
+       bytes += adsi_query_cpeid(buf);
+       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+       /* Get response */
+       memset(buf, 0, sizeof(buf));
+       res = adsi_read_encoded_dtmf(chan, cpeid, 4);
+       if (res != 4) {
+               ast_log(LOG_WARNING, "Got %d bytes back of encoded DTMF, expecting 4\n", res);
+               res = 0;
+       } else {
+               res = 1;
+       }
+
+       if (voice) {
+               bytes = 0;
+               bytes += adsi_voice_mode(buf, 0);
+               adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+               /* Ignore the resulting DTMF B announcing it's in voice mode */
+               ast_waitfordigit(chan, 1000);
+       }
+       return res;
+}
+
+int adsi_get_cpeinfo(struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
+{
+       char buf[256];
+       int bytes = 0;
+       int res;
+       bytes += adsi_data_mode(buf);
+       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+       bytes = 0;
+       bytes += adsi_query_cpeinfo(buf);
+       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+       /* Get width */
+       memset(buf, 0, sizeof(buf));
+       res = ast_readstring(chan, buf, 2, 1000, 500, "");
+       if (res < 0)
+               return res;
+       if (strlen(buf) != 2) {
+               ast_log(LOG_WARNING, "Got %d bytes of width, expecting 2\n", res);
+               res = 0;
+       } else {
+               res = 1;
+       }
+       if (width)
+               *width = atoi(buf);
+       /* Get height */
+       memset(buf, 0, sizeof(buf));
+       if (res) {
+               res = ast_readstring(chan, buf, 2, 1000, 500, "");
+               if (res < 0)
+                       return res;
+               if (strlen(buf) != 2) {
+                       ast_log(LOG_WARNING, "Got %d bytes of height, expecting 2\n", res);
+                       res = 0;
+               } else {
+                       res = 1;
+               }       
+               if (height)
+                       *height= atoi(buf);
+       }
+       /* Get buttons */
+       memset(buf, 0, sizeof(buf));
+       if (res) {
+               res = ast_readstring(chan, buf, 1, 1000, 500, "");
+               if (res < 0)
+                       return res;
+               if (strlen(buf) != 1) {
+                       ast_log(LOG_WARNING, "Got %d bytes of buttons, expecting 1\n", res);
+                       res = 0;
+               } else {
+                       res = 1;
+               }       
+               if (buttons)
+                       *buttons = atoi(buf);
+       }
+       if (voice) {
+               bytes = 0;
+               bytes += adsi_voice_mode(buf, 0);
+               adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+               /* Ignore the resulting DTMF B announcing it's in voice mode */
+               ast_waitfordigit(chan, 1000);
+       }
+       return res;
+}
+
 int adsi_data_mode(unsigned char *buf)
 {
        int bytes=0;
@@ -544,6 +675,36 @@ int adsi_data_mode(unsigned char *buf)
 
 }
 
+int adsi_clear_soft_keys(unsigned char *buf)
+{
+       int bytes=0;
+
+       /* Message type */
+       buf[bytes++] = ADSI_CLEAR_SOFTKEY;
+
+       /* Reserve space for length */
+       bytes++;
+
+       buf[1] = bytes - 2;
+       return bytes;
+
+}
+
+int adsi_clear_screen(unsigned char *buf)
+{
+       int bytes=0;
+
+       /* Message type */
+       buf[bytes++] = ADSI_CLEAR_SCREEN;
+
+       /* Reserve space for length */
+       bytes++;
+
+       buf[1] = bytes - 2;
+       return bytes;
+
+}
+
 int adsi_voice_mode(unsigned char *buf, int when)
 {
        int bytes=0;
@@ -719,53 +880,7 @@ int adsi_set_line(unsigned char *buf, int page, int line)
 static int total = 0;
 static int speeds = 0;
 
-#if 0
-int adsi_channel_init(struct ast_channel *chan)
-{
-       char dsp[256];
-       char keys[256];
-       int bytes;
-       int x;
-       unsigned char *msgs[3];
-       unsigned char keyd[6];
-       int lens[3];
-       int types[3];
-       memset(dsp, 0, sizeof(dsp));
-       memset(keys, 0, sizeof(keys));
-       memset(msgs, 0, sizeof(msgs));
-
-       /* Start with initial display setup */
-       bytes = 0;
-       bytes += adsi_connect_session(dsp, NULL, -1);
-       for (x=0;x<total;x++) {
-               bytes += adsi_display(dsp + bytes, ADSI_INFO_PAGE, x + 1, aligns[x], 0,
-                       strlen(intro[x]) ? intro[x] : " ", "");
-       }
-       bytes += adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
-       msgs[0] = dsp;
-       lens[0] = bytes;
-       types[0] = ADSI_MSG_DISPLAY;
-
-       /* Prepare key setup messages */
-
-       if (speeds) {
-               bytes = 0;
-               memset(keyd, 0, sizeof(keyd));
-               for (x=0;x<speeds;x++) {
-                       bytes += adsi_load_soft_key(keys + bytes, ADSI_SPEED_DIAL + x,
-                                       speeddial[x][1], speeddial[x][2], speeddial[x][0]);
-                       keyd[x] = ADSI_SPEED_DIAL + x;
-               }
-               bytes += adsi_set_keys(keys + bytes, keyd);
-               msgs[1] = keys;
-               lens[1] = bytes;
-               types[1] = ADSI_MSG_DISPLAY;
-       }
-       adsi_transmit_messages(chan, msgs, lens, types);
-       return 0;
-}
-#else
-int adsi_channel_init(struct ast_channel *chan)
+int adsi_channel_restore(struct ast_channel *chan)
 {
        char dsp[256];
        int bytes;
@@ -776,11 +891,6 @@ int adsi_channel_init(struct ast_channel *chan)
 
        /* Start with initial display setup */
        bytes = 0;
-       bytes += adsi_connect_session(dsp, NULL, -1);
-       for (x=0;x<total;x++) {
-               bytes += adsi_display(dsp + bytes, ADSI_INFO_PAGE, x + 1, aligns[x], 0,
-                       strlen(intro[x]) ? intro[x] : " ", "");
-       }
        bytes += adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
 
        /* Prepare key setup messages */
@@ -788,42 +898,33 @@ int adsi_channel_init(struct ast_channel *chan)
        if (speeds) {
                memset(keyd, 0, sizeof(keyd));
                for (x=0;x<speeds;x++) {
-                       bytes += adsi_load_soft_key(dsp + bytes, ADSI_SPEED_DIAL + x,
-                                       speeddial[x][1], speeddial[x][2], speeddial[x][0], 0);
                        keyd[x] = ADSI_SPEED_DIAL + x;
                }
                bytes += adsi_set_keys(dsp + bytes, keyd);
        }
        adsi_transmit_message(chan, dsp, bytes, ADSI_MSG_DISPLAY);
        return 0;
+
 }
-#endif
 
-int adsi_channel_restore(struct ast_channel *chan)
+int adsi_print(struct ast_channel *chan, char **lines, int *aligns, int voice)
 {
-       char dsp[256];
-       int bytes;
+       char buf[4096];
+       int bytes=0;
+       int res;
        int x;
-       unsigned char keyd[6];
-
-       memset(dsp, 0, sizeof(dsp));
-
-       /* Start with initial display setup */
-       bytes = 0;
-       bytes += adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
-
-       /* Prepare key setup messages */
-
-       if (speeds) {
-               memset(keyd, 0, sizeof(keyd));
-               for (x=0;x<speeds;x++) {
-                       keyd[x] = ADSI_SPEED_DIAL + x;
-               }
-               bytes += adsi_set_keys(dsp + bytes, keyd);
+       for(x=0;lines[x];x++) 
+               bytes += adsi_display(buf + bytes, ADSI_INFO_PAGE, x+1, aligns[x],0, lines[x], "");
+       bytes += adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
+       if (voice) {
+               bytes += adsi_voice_mode(buf + bytes, 0);
        }
-       adsi_transmit_message(chan, dsp, bytes, ADSI_MSG_DISPLAY);
-       return 0;
-
+       res = adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+       if (voice) {
+               /* Ignore the resulting DTMF B announcing it's in voice mode */
+               ast_waitfordigit(chan, 1000);
+       }
+       return res;
 }
 
 int adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
@@ -845,21 +946,24 @@ int adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int
        /* Prepare key setup messages */
        if (adsi_transmit_message(chan, dsp, bytes, ADSI_MSG_DISPLAY))
                return -1;
-       res = ast_readstring(chan, resp, 1, 1200, 1200, "");
-       if (res < 0)
-               return -1;
-       if (res) {
-               ast_log(LOG_DEBUG, "No response from CPE about version.  Assuming not there.\n");
-               return 0;
-       }
-       if (!strcmp(resp, "B")) {
-               ast_log(LOG_DEBUG, "CPE has script '%s' version %d already loaded\n", app, ver);
+       if (app) {
+               res = ast_readstring(chan, resp, 1, 1200, 1200, "");
+               if (res < 0)
+                       return -1;
+               if (res) {
+                       ast_log(LOG_DEBUG, "No response from CPE about version.  Assuming not there.\n");
+                       return 0;
+               }
+               if (!strcmp(resp, "B")) {
+                       ast_log(LOG_DEBUG, "CPE has script '%s' version %d already loaded\n", app, ver);
+                       return 1;
+               } else if (!strcmp(resp, "A")) {
+                       ast_log(LOG_DEBUG, "CPE hasn't script '%s' version %d already loaded\n", app, ver);
+               } else {
+                       ast_log(LOG_WARNING, "Unexpected CPE response to script query: %s\n", resp);
+               }
+       } else
                return 1;
-       } else if (!strcmp(resp, "A")) {
-               ast_log(LOG_DEBUG, "CPE hasn't script '%s' version %d already loaded\n", app, ver);
-       } else {
-               ast_log(LOG_WARNING, "Unexpected CPE response to script query: %s\n", resp);
-       }
        return 0;
 
 }