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