Sun Oct 12 06:28:35 2008

Asterisk developer's documentation


app_adsiprog.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Program Asterisk ADSI Scripts into phone
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  * 
00025  * \ingroup applications
00026  */
00027 
00028 /*** MODULEINFO
00029    <depend>res_adsi</depend>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53780 $")
00035 
00036 #include <sys/types.h>
00037 #include <netinet/in.h>
00038 #include <stdlib.h>
00039 #include <unistd.h>
00040 #include <string.h>
00041 #include <stdlib.h>
00042 #include <ctype.h>
00043 #include <stdio.h>
00044 #include <errno.h>
00045 
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/adsi.h"
00052 #include "asterisk/options.h"
00053 #include "asterisk/utils.h"
00054 #include "asterisk/lock.h"
00055 
00056 static char *app = "ADSIProg";
00057 
00058 static char *synopsis = "Load Asterisk ADSI Scripts into phone";
00059 
00060 /* #define DUMP_MESSAGES */
00061 
00062 static char *descrip =
00063 "  ADSIProg(script): This application programs an ADSI Phone with the given\n"
00064 "script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
00065 
00066 struct adsi_event {
00067    int id;
00068    char *name;
00069 };
00070 
00071 static struct adsi_event events[] = {
00072    { 1, "CALLERID" },
00073    { 2, "VMWI" },
00074    { 3, "NEARANSWER" },
00075    { 4, "FARANSWER" },
00076    { 5, "ENDOFRING" },
00077    { 6, "IDLE" },
00078    { 7, "OFFHOOK" },
00079    { 8, "CIDCW" },
00080    { 9, "BUSY" },
00081    { 10, "FARRING" },
00082    { 11, "DIALTONE" },
00083    { 12, "RECALL" },
00084    { 13, "MESSAGE" },
00085    { 14, "REORDER" },
00086    { 15, "DISTINCTIVERING" },
00087    { 16, "RING" },
00088    { 17, "REMINDERRING" },
00089    { 18, "SPECIALRING" },
00090    { 19, "CODEDRING" },
00091    { 20, "TIMER" },
00092    { 21, "INUSE" },
00093    { 22, "EVENT22" },
00094    { 23, "EVENT23" },
00095    { 24, "CPEID" },
00096 };
00097 
00098 static struct adsi_event justify[] = {
00099    { 0, "CENTER" },
00100    { 1, "RIGHT" },
00101    { 2, "LEFT" },
00102    { 3, "INDENT" },
00103 };
00104 
00105 #define STATE_NORMAL    0
00106 #define STATE_INKEY     1
00107 #define STATE_INSUB     2
00108 #define STATE_INIF      3
00109 
00110 #define MAX_RET_CODE    20
00111 #define MAX_SUB_LEN     255
00112 #define MAX_MAIN_LEN    1600
00113 
00114 #define ARG_STRING      (1 << 0)
00115 #define ARG_NUMBER      (1 << 1)
00116 
00117 struct adsi_soft_key {
00118    char vname[40];      /* Which "variable" is associated with it */
00119    int retstrlen;    /* Length of return string */
00120    int initlen;      /* initial length */
00121    int id;
00122    int defined;
00123    char retstr[80];  /* Return string data */
00124 };
00125 
00126 struct adsi_subscript {
00127    char vname[40];
00128    int id;
00129    int defined;
00130    int datalen;
00131    int inscount;
00132    int ifinscount;
00133    char *ifdata;
00134    char data[2048];
00135 };
00136 
00137 struct adsi_state {
00138    char vname[40];
00139    int id;
00140 };
00141 
00142 struct adsi_flag {
00143    char vname[40];
00144    int id;
00145 };
00146 
00147 struct adsi_display {
00148    char vname[40];
00149    int id;
00150    char data[70];
00151    int datalen;
00152 };
00153 
00154 struct adsi_script {
00155    int state;
00156    int numkeys;
00157    int numsubs;
00158    int numstates;
00159    int numdisplays;
00160    int numflags;
00161    struct adsi_soft_key *key;
00162    struct adsi_subscript *sub;
00163    /* Pre-defined displays */
00164    struct adsi_display displays[63];
00165    /* ADSI States 1 (initial) - 254 */
00166    struct adsi_state states[256];
00167    /* Keys 2-63 */
00168    struct adsi_soft_key keys[62];
00169    /* Subscripts 0 (main) to 127 */
00170    struct adsi_subscript subs[128];
00171    /* Flags 1-7 */
00172    struct adsi_flag flags[7];
00173 
00174    /* Stuff from adsi script */
00175    unsigned char sec[5];
00176    char desc[19];
00177    unsigned char fdn[5];
00178    int ver;
00179 };
00180 
00181 
00182 static int process_token(void *out, char *src, int maxlen, int argtype)
00183 {
00184    if ((strlen(src) > 1) && src[0] == '\"') {
00185       /* This is a quoted string */
00186       if (!(argtype & ARG_STRING))
00187          return -1;
00188       src++;
00189       /* Don't take more than what's there */
00190       if (maxlen > strlen(src) - 1)
00191          maxlen = strlen(src) - 1;
00192       memcpy(out, src, maxlen);
00193       ((char *)out)[maxlen] = '\0';
00194    } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
00195       if (!(argtype & ARG_NUMBER))
00196          return -1;
00197       /* Octal value */
00198       if (sscanf(src, "%o", (int *)out) != 1)
00199          return -1;
00200       if (argtype & ARG_STRING) {
00201          /* Convert */
00202          *((unsigned int *)out) = htonl(*((unsigned int *)out));
00203       }
00204    } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
00205       if (!(argtype & ARG_NUMBER))
00206          return -1;
00207       /* Hex value */
00208       if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
00209          return -1;
00210       if (argtype & ARG_STRING) {
00211          /* Convert */
00212          *((unsigned int *)out) = htonl(*((unsigned int *)out));
00213       }
00214    } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
00215       if (!(argtype & ARG_NUMBER))
00216          return -1;
00217       /* Hex value */
00218       if (sscanf(src, "%d", (int *)out) != 1)
00219          return -1;
00220       if (argtype & ARG_STRING) {
00221          /* Convert */
00222          *((unsigned int *)out) = htonl(*((unsigned int *)out));
00223       }
00224    } else
00225       return -1;
00226    return 0;
00227 }
00228 
00229 static char *get_token(char **buf, char *script, int lineno)
00230 {
00231    char *tmp = *buf;
00232    char *keyword;
00233    int quoted = 0;
00234    /* Advance past any white space */
00235    while(*tmp && (*tmp < 33))
00236       tmp++;
00237    if (!*tmp)
00238       return NULL;
00239    keyword = tmp;
00240    while(*tmp && ((*tmp > 32)  || quoted)) {
00241       if (*tmp == '\"') {
00242          quoted = !quoted;
00243       }
00244       tmp++;
00245    }
00246    if (quoted) {
00247       ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
00248       return NULL;
00249    }
00250    *tmp = '\0';
00251    tmp++;
00252    while(*tmp && (*tmp < 33))
00253       tmp++;
00254    /* Note where we left off */
00255    *buf = tmp;
00256    return keyword;
00257 }
00258 
00259 static char *validdtmf = "123456789*0#ABCD";
00260 
00261 static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00262 {
00263    char dtmfstr[80];
00264    char *a;
00265    int bytes=0;
00266    a = get_token(&args, script, lineno);
00267    if (!a) {
00268       ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
00269       return 0;
00270    }
00271    if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
00272       ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
00273       return 0;
00274    }
00275    a = dtmfstr;
00276    while(*a) {
00277       if (strchr(validdtmf, *a)) {
00278          *buf = *a;
00279          buf++;
00280          bytes++;
00281       } else
00282          ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
00283       a++;
00284    }
00285    return bytes;
00286 }
00287 
00288 static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00289 {
00290    char *page;
00291    char *gline;
00292    int line;
00293    unsigned char cmd;
00294    page = get_token(&args, script, lineno);
00295    gline = get_token(&args, script, lineno);
00296    if (!page || !gline) {
00297       ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
00298       return 0;
00299    }
00300    if (!strcasecmp(page, "INFO")) {
00301       cmd = 0;
00302    } else if (!strcasecmp(page, "COMM")) {
00303       cmd = 0x80;
00304    } else {
00305       ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
00306       return 0;
00307    }
00308    if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
00309       ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
00310       return 0;
00311    }
00312    cmd |= line;
00313    buf[0] = 0x8b;
00314    buf[1] = cmd;
00315    return 2;
00316 }
00317 
00318 static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00319 {
00320    char *dir;
00321    char *gline;
00322    int line;
00323    unsigned char cmd;
00324    dir = get_token(&args, script, lineno);
00325    gline = get_token(&args, script, lineno);
00326    if (!dir || !gline) {
00327       ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
00328       return 0;
00329    }
00330    if (!strcasecmp(dir, "UP")) {
00331       cmd = 0;
00332    } else if (!strcasecmp(dir, "DOWN")) {
00333       cmd = 0x20;
00334    } else {
00335       ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
00336       return 0;
00337    }
00338    if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
00339       ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
00340       return 0;
00341    }
00342    cmd |= line;
00343    buf[0] = 0x8c;
00344    buf[1] = cmd;
00345    return 2;
00346 }
00347 
00348 static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00349 {
00350    char *gtime;
00351    int ms;
00352    gtime = get_token(&args, script, lineno);
00353    if (!gtime) {
00354       ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
00355       return 0;
00356    }
00357    if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
00358       ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
00359       return 0;
00360    }
00361    buf[0] = 0x90;
00362    if (id == 11)
00363       buf[1] = ms / 100;
00364    else
00365       buf[1] = ms / 10;
00366    return 2;
00367 }
00368 
00369 static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00370 {
00371    char *gstate;
00372    int state;
00373    gstate = get_token(&args, script, lineno);
00374    if (!gstate) {
00375       ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
00376       return 0;
00377    }
00378    if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
00379       ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
00380       return 0;
00381    }
00382    buf[0] = id;
00383    buf[1] = state;
00384    return 2;
00385 }
00386 
00387 static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00388 {
00389    char *tok;
00390    tok = get_token(&args, script, lineno);
00391    if (tok) 
00392       ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00393 
00394    buf[0] = id;
00395    /* For some reason the clear code is different slightly */
00396    if (id == 7)
00397       buf[1] = 0x10;
00398    else
00399       buf[1] = 0x00;
00400    return 2;
00401 }
00402 
00403 static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
00404 {
00405    int x;
00406    for (x=0;x<state->numflags;x++) 
00407       if (!strcasecmp(state->flags[x].vname, name)) 
00408          return &state->flags[x];
00409    /* Return now if we're not allowed to create */
00410    if (!create)
00411       return NULL;
00412    if (state->numflags > 6) {
00413       ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
00414       return NULL;
00415    }
00416    ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
00417    state->flags[state->numflags].id = state->numflags + 1;
00418    state->numflags++;
00419    return &state->flags[state->numflags-1];
00420 }
00421 
00422 static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00423 {
00424    char *tok;
00425    char sname[80];
00426    struct adsi_flag *flag;
00427    tok = get_token(&args, script, lineno);
00428    if (!tok) {
00429       ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
00430       return 0;
00431    }
00432    if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
00433       ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
00434       return 0;
00435    }
00436    flag = getflagbyname(state, sname, script, lineno, 0);
00437    if (!flag) {
00438       ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
00439       return 0;
00440    }
00441    buf[0] = id;
00442    buf[1] = ((flag->id & 0x7) << 4) | 1;
00443    return 2;
00444 }
00445 
00446 static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00447 {
00448    char *tok;
00449    struct adsi_flag *flag;
00450    char sname[80];
00451    tok = get_token(&args, script, lineno);
00452    if (!tok) {
00453       ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
00454       return 0;
00455    }
00456    if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
00457       ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
00458       return 0;
00459    }
00460    flag = getflagbyname(state, sname, script, lineno, 0);
00461    if (!flag) {
00462       ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
00463       return 0;
00464    }
00465    buf[0] = id;
00466    buf[1] = ((flag->id & 0x7) << 4);
00467    return 2;
00468 }
00469 
00470 static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00471 {
00472    char *tok;
00473    int secs;
00474    tok = get_token(&args, script, lineno);
00475    if (!tok) {
00476       ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
00477       return 0;
00478    }
00479    if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
00480       ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
00481       return 0;
00482    }
00483    buf[0] = id;
00484    buf[1] = 0x1;
00485    buf[2] = secs;
00486    return 3;
00487 }
00488 
00489 static int geteventbyname(char *name)
00490 {
00491    int x;
00492    for (x=0;x<sizeof(events) / sizeof(events[0]); x++) {
00493       if (!strcasecmp(events[x].name, name))
00494          return events[x].id;
00495    }
00496    return 0;
00497 }
00498 
00499 static int getjustifybyname(char *name)
00500 {
00501    int x;
00502    for (x=0;x<sizeof(justify) / sizeof(justify[0]); x++) {
00503       if (!strcasecmp(justify[x].name, name))
00504          return justify[x].id;
00505    }
00506    return -1;
00507 }
00508 
00509 static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
00510 {
00511    int x;
00512    for (x=0;x<state->numkeys;x++) 
00513       if (!strcasecmp(state->keys[x].vname, name)) 
00514          return &state->keys[x];
00515    if (state->numkeys > 61) {
00516       ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
00517       return NULL;
00518    }
00519    ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
00520    state->keys[state->numkeys].id = state->numkeys + 2;
00521    state->numkeys++;
00522    return &state->keys[state->numkeys-1];
00523 }
00524 
00525 static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
00526 {
00527    int x;
00528    for (x=0;x<state->numsubs;x++) 
00529       if (!strcasecmp(state->subs[x].vname, name)) 
00530          return &state->subs[x];
00531    if (state->numsubs > 127) {
00532       ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
00533       return NULL;
00534    }
00535    ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
00536    state->subs[state->numsubs].id = state->numsubs;
00537    state->numsubs++;
00538    return &state->subs[state->numsubs-1];
00539 }
00540 
00541 static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
00542 {
00543    int x;
00544    for (x=0;x<state->numstates;x++) 
00545       if (!strcasecmp(state->states[x].vname, name)) 
00546          return &state->states[x];
00547    /* Return now if we're not allowed to create */
00548    if (!create)
00549       return NULL;
00550    if (state->numstates > 253) {
00551       ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
00552       return NULL;
00553    }
00554    ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
00555    state->states[state->numstates].id = state->numstates + 1;
00556    state->numstates++;
00557    return &state->states[state->numstates-1];
00558 }
00559 
00560 static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
00561 {
00562    int x;
00563    for (x=0;x<state->numdisplays;x++) 
00564       if (!strcasecmp(state->displays[x].vname, name)) 
00565          return &state->displays[x];
00566    /* Return now if we're not allowed to create */
00567    if (!create)
00568       return NULL;
00569    if (state->numdisplays > 61) {
00570       ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
00571       return NULL;
00572    }
00573    ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
00574    state->displays[state->numdisplays].id = state->numdisplays + 1;
00575    state->numdisplays++;
00576    return &state->displays[state->numdisplays-1];
00577 }
00578 
00579 static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00580 {
00581    char *tok;
00582    char newkey[80];
00583    int bytes;
00584    unsigned char keyid[6];
00585    int x;
00586    int flagid=0;
00587    struct adsi_soft_key *key;
00588    struct adsi_flag *flag;
00589 
00590    for (x=0;x<7;x++) {
00591       /* Up to 6 key arguments */
00592       tok = get_token(&args, script, lineno);
00593       if (!tok)
00594          break;
00595       if (!strcasecmp(tok, "UNLESS")) {
00596          /* Check for trailing UNLESS flag */
00597          tok = get_token(&args, script, lineno);
00598          if (!tok) {
00599             ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
00600          } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
00601             ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
00602          } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) {
00603             ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
00604          } else
00605             flagid = flag->id;
00606          if ((tok = get_token(&args, script, lineno)))
00607             ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
00608          break;
00609       }
00610       if (x > 5) {
00611          ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
00612          break;
00613       }
00614       if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
00615          ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok); 
00616          continue;
00617       }
00618                
00619       key = getkeybyname(state, newkey, script, lineno);
00620       if (!key)
00621          break;
00622       keyid[x] = key->id;
00623    }
00624    buf[0] = id;
00625    buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
00626    for (bytes=0;bytes<x;bytes++) {
00627       buf[bytes + 2] = keyid[bytes];
00628    }
00629    return 2 + x;
00630 }
00631 
00632 static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00633 {
00634    char *tok;
00635    char dispname[80];
00636    int line=0;
00637    int flag=0;
00638    int cmd = 3;
00639    struct adsi_display *disp;
00640 
00641    /* Get display */
00642    tok = get_token(&args, script, lineno);
00643    if (!tok || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
00644       ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
00645       return 0;
00646    }
00647    disp = getdisplaybyname(state, dispname, script, lineno, 0);
00648    if (!disp) {
00649       ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
00650       return 0;
00651    }
00652 
00653    tok = get_token(&args, script, lineno);
00654    if (!tok || strcasecmp(tok, "AT")) {
00655       ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
00656       return 0;
00657    }
00658    /* Get line number */
00659    tok = get_token(&args, script, lineno);
00660    if (!tok || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
00661       ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
00662       return 0;
00663    }
00664    tok = get_token(&args, script, lineno);
00665    if (tok && !strcasecmp(tok, "NOUPDATE")) {
00666       cmd = 1;
00667       tok = get_token(&args, script, lineno);
00668    }
00669    if (tok && !strcasecmp(tok, "UNLESS")) {
00670       /* Check for trailing UNLESS flag */
00671       tok = get_token(&args, script, lineno);
00672       if (!tok) {
00673          ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
00674       } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) {
00675          ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
00676       }
00677       if ((tok = get_token(&args, script, lineno)))
00678          ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
00679    }
00680                
00681    buf[0] = id;
00682    buf[1] = (cmd << 6) | (disp->id & 0x3f); 
00683    buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
00684    return 3;
00685 }
00686 
00687 static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00688 {
00689    char *tok;
00690    tok = get_token(&args, script, lineno);
00691    if (tok) 
00692       ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00693 
00694    buf[0] = id;
00695    buf[1] = 0x00;
00696    return 2;
00697 }
00698 
00699 static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00700 {
00701    char *tok;
00702    tok = get_token(&args, script, lineno);
00703    if (tok) 
00704       ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00705 
00706    buf[0] = id;
00707    buf[1] = 0x7;
00708    return 2;
00709 }
00710 
00711 static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00712 {
00713    char *tok;
00714    tok = get_token(&args, script, lineno);
00715    if (tok)
00716       ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00717 
00718    buf[0] = id;
00719    buf[1] = 0;
00720    return 2;
00721 }
00722 
00723 static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
00724 {
00725    char *tok;
00726    tok = get_token(&args, script, lineno);
00727    if (tok) 
00728       ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00729 
00730    buf[0] = id;
00731    buf[1] = 0xf;
00732    return 2;
00733 }
00734 
00735 static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00736 {
00737    char *tok;
00738    char subscript[80];
00739    struct adsi_subscript *sub;
00740    tok = get_token(&args, script, lineno);
00741    if (!tok) {
00742       ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
00743       return 0;
00744    }
00745    if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
00746       ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
00747       return 0;
00748    }
00749    sub = getsubbyname(state, subscript, script, lineno);
00750    if (!sub) 
00751       return 0;
00752    buf[0] = 0x9d;
00753    buf[1] = sub->id;
00754    return 2;
00755 }
00756 
00757 static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
00758 {
00759    char *tok;
00760    char subscript[80];
00761    char sname[80];
00762    int sawin=0;
00763    int event;
00764    int snums[8];
00765    int scnt = 0;
00766    int x;
00767    struct adsi_subscript *sub;
00768    tok = get_token(&args, script, lineno);
00769    if (!tok) {
00770       ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
00771       return 0;
00772    }
00773    event = geteventbyname(tok);
00774    if (event < 1) {
00775       ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
00776       return 0;
00777    }
00778    tok = get_token(&args, script, lineno);
00779    while ((!sawin && !strcasecmp(tok, "IN")) ||
00780           (sawin && !strcasecmp(tok, "OR"))) {
00781       sawin = 1;
00782       if (scnt > 7) {
00783          ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
00784          return 0;
00785       }
00786       /* Process 'in' things */
00787       tok = get_token(&args, script, lineno);
00788       if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
00789          ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
00790          return 0;
00791       }
00792       if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
00793          ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
00794          return 0;
00795       }
00796       scnt++;
00797       tok = get_token(&args, script, lineno);
00798       if (!tok)
00799          break;
00800    }
00801    if (!tok || strcasecmp(tok, "GOTO")) {
00802       if (!tok)
00803          tok = "<nothing>";
00804       if (sawin) 
00805          ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
00806       else
00807          ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
00808    }
00809    tok = get_token(&args, script, lineno);
00810    if (!tok) {
00811       ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
00812       return 0;
00813    }
00814    if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
00815       ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
00816       return 0;
00817    }
00818    sub = getsubbyname(state, subscript, script, lineno);
00819    if (!sub) 
00820       return 0;
00821    buf[0] = 8;
00822    buf[1] = event;
00823    buf[2] = sub->id | 0x80;
00824    for (x=0;x<scnt;x++)
00825       buf[3 + x] = snums[x];
00826    return 3 + scnt;
00827 }
00828 
00829 struct adsi_key_cmd {
00830    char *name;
00831    int id;
00832    int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
00833 };
00834 
00835 static struct adsi_key_cmd kcmds[] = {
00836    { "SENDDTMF", 0, send_dtmf },
00837    /* Encoded DTMF would go here */
00838    { "ONHOOK", 0x81 },
00839    { "OFFHOOK", 0x82 },
00840    { "FLASH", 0x83 },
00841    { "WAITDIALTONE", 0x84 },
00842    /* Send line number */
00843    { "BLANK", 0x86 },
00844    { "SENDCHARS", 0x87 },
00845    { "CLEARCHARS", 0x88 },
00846    { "BACKSPACE", 0x89 },
00847    /* Tab column */
00848    { "GOTOLINE", 0x8b, goto_line },
00849    { "GOTOLINEREL", 0x8c, goto_line_rel },
00850    { "PAGEUP", 0x8d },
00851    { "PAGEDOWN", 0x8e },
00852    /* Extended DTMF */
00853    { "DELAY", 0x90, send_delay },
00854    { "DIALPULSEONE", 0x91 },
00855    { "DATAMODE", 0x92 },
00856    { "VOICEMODE", 0x93 },
00857    /* Display call buffer 'n' */
00858    /* Clear call buffer 'n' */
00859    { "CLEARCB1", 0x95, clearcbone },
00860    { "DIGITCOLLECT", 0x96, digitcollect },
00861    { "DIGITDIRECT", 0x96, digitdirect },
00862    { "CLEAR", 0x97 },
00863    { "SHOWDISPLAY", 0x98, showdisplay },
00864    { "CLEARDISPLAY", 0x98, cleardisplay },
00865    { "SHOWKEYS", 0x99, showkeys },
00866    { "SETSTATE", 0x9a, set_state },
00867    { "TIMERSTART", 0x9b, starttimer },
00868    { "TIMERCLEAR", 0x9b, cleartimer },
00869    { "SETFLAG", 0x9c, setflag },
00870    { "CLEARFLAG", 0x9c, clearflag },
00871    { "GOTO", 0x9d, subscript },
00872    { "EVENT22", 0x9e },
00873    { "EVENT23", 0x9f },
00874    { "EXIT", 0xa0 },
00875 };
00876 
00877 static struct adsi_key_cmd opcmds[] = {
00878    
00879    /* 1 - Branch on event -- handled specially */
00880    { "SHOWKEYS", 2, showkeys },
00881    /* Display Control */
00882    { "SHOWDISPLAY", 3, showdisplay },
00883    { "CLEARDISPLAY", 3, cleardisplay },
00884    { "CLEAR", 5 },
00885    { "SETSTATE", 6, set_state },
00886    { "TIMERSTART", 7, starttimer },
00887    { "TIMERCLEAR", 7, cleartimer },
00888    { "ONEVENT", 8, onevent },
00889    /* 9 - Subroutine label, treated specially */
00890    { "SETFLAG", 10, setflag },
00891    { "CLEARFLAG", 10, clearflag },
00892    { "DELAY", 11, send_delay },
00893    { "EXIT", 12 },
00894 };
00895 
00896 
00897 static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
00898 {
00899    int x;
00900    char *unused;
00901    int res;
00902    for (x=0;x<sizeof(kcmds) / sizeof(kcmds[0]);x++) {
00903       if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
00904          if (kcmds[x].add_args) {
00905             res = kcmds[x].add_args(key->retstr + key->retstrlen,
00906                   code, kcmds[x].id, args, state, script, lineno);
00907             if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE) 
00908                key->retstrlen += res;
00909             else 
00910                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);
00911          } else {
00912             if ((unused = get_token(&args, script, lineno))) 
00913                ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);