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; }