diff --git a/Makefile b/Makefile
index 68e8430870d0526cc1e095611786640093c7e9e4..0dc248632aca21fcdbe03a15f5abd7f29e0acb2b 100755
--- 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
index 0000000000000000000000000000000000000000..531b2c42316e55dc12bcb92a33607a0ad40bf033
--- /dev/null
+++ b/apps/app_adsiprog.c
@@ -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;
+}
diff --git a/apps/app_system.c b/apps/app_system.c
index a4369125f2213b0794f1614973b6869192b23430..539515e4e69d0dc3ae9ce32782628e7230c0f4c5 100755
--- a/apps/app_system.c
+++ b/apps/app_system.c
@@ -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)) 
diff --git a/channel.c b/channel.c
index fdd7c8f66a1c2a7be402169f96584f20076734be..f975db52f55993fc82c52e2d977164b69c790ad7 100755
--- 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;
+}
diff --git a/channels/adtranvofr.h b/channels/adtranvofr.h
index 9a45402b86a0807e79d70c498ca91907ce167572..88fd428ee599f777bea92237b2d7e06afefecf03 100755
--- a/channels/adtranvofr.h
+++ b/channels/adtranvofr.h
@@ -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 */
diff --git a/channels/chan_phone.c b/channels/chan_phone.c
index 7300646b60e72d813237336d3b09ddde1606467a..ee45e39d0dc783635a38308a2ce78257db9f7e2e 100755
--- a/channels/chan_phone.c
+++ b/channels/chan_phone.c
@@ -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;
diff --git a/codecs/Makefile b/codecs/Makefile
index 027225eb86ddcb98168395bc6e60a88acb3a025c..39aa85ec18d2effa6441b8b6a262022092657adf 100755
--- a/codecs/Makefile
+++ b/codecs/Makefile
@@ -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)
diff --git a/codecs/lpc10/Makefile b/codecs/lpc10/Makefile
index cce4c4d9259092c9fa06a72f57631228b1782252..af5131d77862eee661ffbf5c4145034a584fbe67 100755
--- a/codecs/lpc10/Makefile
+++ b/codecs/lpc10/Makefile
@@ -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
 
diff --git a/formats/format_jpeg.c b/formats/format_jpeg.c
index 7e10af88412d4a19354afb45cd49be3a7469b18e..4e9f0f4c34e1bda8a628b7d02b7b74a826cf2de1 100755
--- a/formats/format_jpeg.c
+++ b/formats/format_jpeg.c
@@ -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;
 		}
 	}
diff --git a/formats/format_wav.c b/formats/format_wav.c
index 77ac71916d0bf34f4fb1e8260677ebe5aac7dc09..667f66f24e237eaee4ed577fa09d4154ff2308d4 100755
--- a/formats/format_wav.c
+++ b/formats/format_wav.c
@@ -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 */
diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h
index c819d7ac5de83877bebd5dfb7e0194d3c79e991f..793ed30c7c0fd7e72e4813168eab83db9548631a 100755
--- a/include/asterisk/logger.h
+++ b/include/asterisk/logger.h
@@ -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));
diff --git a/logger.c b/logger.c
index ba4f493eadf0cc0653efbff5aa47fa2187688082..9da8b62e7ea6726bd00461a71f49f17ef10f4fd1 100755
--- a/logger.c
+++ b/logger.c
@@ -15,10 +15,12 @@
 #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 55df966dd79ff9c4e3236da688e8566ce2fb21fd..ec52c05983fbfc01fba5b730da68aed64ca34188 100755
--- 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;
 }
diff --git a/res/res_adsi.c b/res/res_adsi.c
index e464c01516b2e23b401278fd8928ab9893250c5f..11a23c9876cd0236b22a00f6fc3322dc077b11e9 100755
--- a/res/res_adsi.c
+++ b/res/res_adsi.c
@@ -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;
 
 }