Wed Oct 28 15:47:47 2009

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: 211526 $")
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, "%30o", (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, "%30x", (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, "%30d", (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;
00920       }
00921    }
00922    return -1;
00923 }
00924 
00925 static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
00926 {
00927    int x;
00928    char *unused;
00929    int res;
00930    int max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
00931    for (x=0;x<sizeof(opcmds) / sizeof(opcmds[0]);x++) {
00932       if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
00933          if (opcmds[x].add_args) {
00934             res = opcmds[x].add_args(sub->data + sub->datalen,
00935                   code, opcmds[x].id, args, state, script, lineno);
00936             if ((sub->datalen + res + 1) <= max) 
00937                sub->datalen += res;
00938             else {
00939                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);
00940                return -1;
00941             }
00942          } else {
00943             if ((unused = get_token(&args, script, lineno))) 
00944                ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
00945             if ((sub->datalen + 2) <= max) {
00946                sub->data[sub->datalen] = opcmds[x].id;
00947                sub->datalen++;
00948             } else {
00949                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);
00950                return -1;
00951             }
00952          }
00953          /* Separate commands with 0xff */
00954          sub->data[sub->datalen] = 0xff;
00955          sub->datalen++;
00956          sub->inscount++;
00957          return 0;
00958       }
00959    }
00960    return -1;
00961 }
00962 
00963 static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
00964 {
00965    char *keyword;
00966    char *args;
00967    char vname[256];
00968    char tmp[80];
00969    char tmp2[80];
00970    int lrci;
00971    int wi;
00972    int event;
00973    struct adsi_display *disp;
00974    struct adsi_subscript *newsub;
00975    /* Find the first keyword */
00976    keyword = get_token(&buf, script, lineno);
00977    if (!keyword) 
00978       return 0;
00979    switch(state->state) {
00980    case STATE_NORMAL:
00981       if (!strcasecmp(keyword, "DESCRIPTION")) {
00982          args = get_token(&buf, script, lineno);
00983          if (args) {
00984             if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
00985                ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
00986          } else
00987             ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
00988       } else if (!strcasecmp(keyword, "VERSION")) {
00989          args = get_token(&buf, script, lineno);
00990          if (args) {
00991             if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
00992                ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
00993          } else
00994             ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
00995       } else if (!strcasecmp(keyword, "SECURITY")) {
00996          args = get_token(&buf, script, lineno);
00997          if (args) {
00998             if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
00999                ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
01000          } else
01001             ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
01002       } else if (!strcasecmp(keyword, "FDN")) {
01003          args = get_token(&buf, script, lineno);
01004          if (args) {
01005             if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
01006                ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
01007          } else
01008             ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
01009       } else if (!strcasecmp(keyword, "KEY")) {
01010          args = get_token(&buf, script, lineno);
01011          if (!args) {
01012             ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
01013             break;
01014          }
01015          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01016             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
01017             break;
01018          }
01019          state->key = getkeybyname(state, vname, script, lineno);
01020          if (!state->key) {
01021             ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
01022             break;
01023          }
01024          if (state->key->defined) {
01025             ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
01026             break;
01027          }
01028          args = get_token(&buf, script, lineno);
01029          if (!args || strcasecmp(args, "IS")) {
01030             ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
01031             break;
01032          }
01033          args = get_token(&buf, script, lineno);
01034          if (!args) {
01035             ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
01036             break;
01037          }
01038          if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01039             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
01040             break;
01041          }
01042          args = get_token(&buf, script, lineno);
01043          if (args) {
01044             if (strcasecmp(args, "OR")) {
01045                ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
01046                break;
01047             }
01048             args = get_token(&buf, script, lineno);
01049             if (!args) {
01050                ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
01051                break;
01052             }
01053             if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
01054                ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
01055                break;
01056             }
01057          } else {
01058             ast_copy_string(tmp2, tmp, sizeof(tmp2));
01059          }
01060          if (strlen(tmp2) > 18) {
01061             ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
01062             tmp2[18] = '\0';
01063          }
01064          if (strlen(tmp) > 7) {
01065             ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
01066             tmp[7] = '\0';
01067          }
01068          /* Setup initial stuff */
01069          state->key->retstr[0] = 128;
01070          /* 1 has the length */
01071          state->key->retstr[2] = state->key->id;
01072          /* Put the Full name in */
01073          memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
01074          /* Update length */
01075          state->key->retstrlen = strlen(tmp2) + 3;
01076          /* Put trailing 0xff */
01077          state->key->retstr[state->key->retstrlen++] = 0xff;
01078          /* Put the short name */
01079          memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
01080          /* Update length */
01081          state->key->retstrlen += strlen(tmp);
01082          /* Put trailing 0xff */
01083          state->key->retstr[state->key->retstrlen++] = 0xff;
01084          /* Record initial length */
01085          state->key->initlen = state->key->retstrlen;
01086          state->state = STATE_INKEY;
01087       } else if (!strcasecmp(keyword, "SUB")) {
01088          args = get_token(&buf, script, lineno);
01089          if (!args) {
01090             ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
01091             break;
01092          }
01093          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01094             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
01095             break;
01096          }
01097          state->sub = getsubbyname(state, vname, script, lineno);
01098          if (!state->sub) {
01099             ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
01100             break;
01101          }
01102          if (state->sub->defined) {
01103             ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
01104             break;
01105          }
01106          /* Setup sub */
01107          state->sub->data[0] = 130;
01108          /* 1 is the length */
01109          state->sub->data[2] = 0x0; /* Clear extensibility bit */
01110          state->sub->datalen = 3;
01111          if (state->sub->id) {
01112             /* If this isn't the main subroutine, make a subroutine label for it */
01113             state->sub->data[3] = 9;
01114             state->sub->data[4] = state->sub->id;
01115             /* 5 is length */
01116             state->sub->data[6] = 0xff;
01117             state->sub->datalen = 7;
01118          }
01119          args = get_token(&buf, script, lineno);
01120          if (!args || strcasecmp(args, "IS")) {
01121             ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
01122             break;
01123          }
01124          state->state = STATE_INSUB;
01125       } else if (!strcasecmp(keyword, "STATE")) {
01126          args = get_token(&buf, script, lineno);
01127          if (!args) {
01128             ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
01129             break;
01130          }
01131          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01132             ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
01133             break;
01134          }
01135          if (getstatebyname(state, vname, script, lineno, 0)) {
01136             ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
01137             break;
01138          }
01139          getstatebyname(state, vname, script, lineno, 1);
01140       } else if (!strcasecmp(keyword, "FLAG")) {
01141          args = get_token(&buf, script, lineno);
01142          if (!args) {
01143             ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
01144             break;
01145          }
01146          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01147             ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
01148             break;
01149          }
01150          if (getflagbyname(state, vname, script, lineno, 0)) {
01151             ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
01152             break;
01153          }
01154          getflagbyname(state, vname, script, lineno, 1);
01155       } else if (!strcasecmp(keyword, "DISPLAY")) {
01156          lrci = 0;
01157          wi = 0;
01158          args = get_token(&buf, script, lineno);
01159          if (!args) {
01160             ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
01161             break;
01162          }
01163          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01164             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
01165             break;
01166          }
01167          if (getdisplaybyname(state, vname, script, lineno, 0)) {
01168             ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
01169             break;
01170          }
01171          disp = getdisplaybyname(state, vname, script, lineno, 1);
01172          if (!disp)
01173             break;
01174          args = get_token(&buf, script, lineno);
01175          if (!args || strcasecmp(args, "IS")) {
01176             ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
01177             break;
01178          }
01179          args = get_token(&buf, script, lineno);
01180          if (!args) {
01181             ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
01182             break;
01183          }
01184          if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01185             ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
01186             break;
01187          }
01188          if (strlen(tmp) > 20) {
01189             ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
01190             tmp[20] = '\0';
01191          }
01192          memcpy(disp->data + 5, tmp, strlen(tmp));
01193          disp->datalen = strlen(tmp) + 5;
01194          disp->data[disp->datalen++] = 0xff;
01195 
01196          args = get_token(&buf, script, lineno);
01197          if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01198             /* Got a column two */
01199             if (strlen(tmp) > 20) {
01200                ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
01201                tmp[20] = '\0';
01202             }
01203             memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
01204             disp->datalen += strlen(tmp);
01205             args = get_token(&buf, script, lineno);
01206          }
01207          while(args) {
01208             if (!strcasecmp(args, "JUSTIFY")) {
01209                args = get_token(&buf, script, lineno);
01210                if (!args) {
01211                   ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
01212                   break;
01213                }
01214                lrci = getjustifybyname(args);
01215                if (lrci < 0) {
01216                   ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
01217                   break;
01218                }
01219             } else if (!strcasecmp(args, "WRAP")) {
01220                wi = 0x80;
01221             } else {
01222                ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
01223                break;
01224             }
01225             args = get_token(&buf, script, lineno);
01226          }
01227          if (args) {
01228             /* Something bad happened */
01229             break;
01230          }
01231          disp->data[0] = 129;
01232          disp->data[1] = disp->datalen - 2;
01233          disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
01234          disp->data[3] = wi;
01235          disp->data[4] = 0xff;
01236       } else {
01237          ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
01238       }
01239       break;
01240    case STATE_INKEY:
01241       if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
01242          if (!strcasecmp(keyword, "ENDKEY")) {
01243             /* Return to normal operation and increment current key */
01244             state->state = STATE_NORMAL;
01245             state->key->defined = 1;
01246             state->key->retstr[1] = state->key->retstrlen - 2;
01247             state->key = NULL;
01248          } else {
01249             ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
01250          }
01251       }
01252       break;
01253    case STATE_INIF:
01254       if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
01255          if (!strcasecmp(keyword, "ENDIF")) {
01256             /* Return to normal SUB operation and increment current key */
01257             state->state = STATE_INSUB;
01258             state->sub->defined = 1;
01259             /* Store the proper number of instructions */
01260             state->sub->ifdata[2] = state->sub->ifinscount;
01261          } else if (!strcasecmp(keyword, "GOTO")) {
01262             args = get_token(&buf, script, lineno);
01263             if (!args) {
01264                ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
01265                break;
01266             }
01267             if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01268                ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
01269                break;
01270             }
01271             newsub = getsubbyname(state, tmp, script, lineno);
01272             if (!newsub) 
01273                break;
01274             /* Somehow you use GOTO to go to another place */
01275             state->sub->data[state->sub->datalen++] = 0x8;
01276             state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
01277             state->sub->data[state->sub->datalen++] = newsub->id;
01278             /* Terminate */
01279             state->sub->data[state->sub->datalen++] = 0xff;
01280             /* Increment counters */
01281             state->sub->inscount++;
01282             state->sub->ifinscount++;
01283          } else {
01284             ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
01285          }
01286       } else
01287          state->sub->ifinscount++;
01288       break;
01289    case STATE_INSUB:
01290       if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
01291          if (!strcasecmp(keyword, "ENDSUB")) {
01292             /* Return to normal operation and increment current key */
01293             state->state = STATE_NORMAL;
01294             state->sub->defined = 1;
01295             /* Store the proper length */
01296             state->sub->data[1] = state->sub->datalen - 2;
01297             if (state->sub->id) {
01298                /* if this isn't main, store number of instructions, too */
01299                state->sub->data[5] = state->sub->inscount;
01300             }
01301             state->sub = NULL;
01302          } else if (!strcasecmp(keyword, "IFEVENT")) {
01303             args = get_token(&buf, script, lineno);
01304             if (!args) {
01305                ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
01306                break;
01307             }
01308             event = geteventbyname(args);
01309             if (event < 1) {
01310                ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
01311                break;
01312             }
01313             args = get_token(&buf, script, lineno);
01314             if (!args || strcasecmp(args, "THEN")) {
01315                ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
01316                break;
01317             }
01318             state->sub->ifinscount = 0;
01319             state->sub->ifdata = state->sub->data + 
01320                   state->sub->datalen;
01321             /* Reserve header and insert op codes */
01322             state->sub->ifdata[0] = 0x1;
01323             state->sub->ifdata[1] = event;
01324             /* 2 is for the number of instructions */
01325             state->sub->ifdata[3] = 0xff;
01326             state->sub->datalen += 4;
01327             /* Update Subscript instruction count */
01328             state->sub->inscount++;
01329             state->state = STATE_INIF;
01330          } else {
01331             ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
01332          }
01333       }
01334       break;
01335    default:
01336       ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
01337    }
01338    return 0;
01339 }
01340 
01341 static struct adsi_script *compile_script(char *script)
01342 {
01343    FILE *f;
01344    char fn[256];
01345    char buf[256];
01346    char *c;
01347    int lineno=0;
01348    int x, err;
01349    struct adsi_script *scr;
01350    if (script[0] == '/')
01351       ast_copy_string(fn, script, sizeof(fn));
01352    else
01353       snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script);
01354    f = fopen(fn, "r");
01355    if (!f) {
01356       ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
01357       return NULL;
01358    }
01359    scr = malloc(sizeof(struct adsi_script));
01360    if (!scr) {
01361       fclose(f);
01362       ast_log(LOG_WARNING, "Out of memory loading script '%s'\n", fn);
01363       return NULL;
01364    }
01365    memset(scr, 0, sizeof(struct adsi_script));
01366    /* Create "main" as first subroutine */
01367    getsubbyname(scr, "main", NULL, 0);
01368    while(!feof(f)) {
01369       fgets(buf, sizeof(buf), f);
01370       if (!feof(f)) {
01371          lineno++;
01372          /* Trim off trailing return */
01373          buf[strlen(buf) - 1] = '\0';
01374          c = strchr(buf, ';');
01375          /* Strip comments */
01376          if (c)
01377             *c = '\0';
01378          if (!ast_strlen_zero(buf))
01379             adsi_process(scr, buf, script, lineno);
01380       }
01381    }
01382    fclose(f);
01383    /* Make sure we're in the main routine again */
01384    switch(scr->state) {
01385    case STATE_NORMAL:
01386       break;
01387    case STATE_INSUB:
01388       ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
01389       free(scr);
01390       return NULL;
01391    case STATE_INKEY:
01392       ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
01393       free(scr);
01394       return NULL;
01395    }
01396    err = 0;
01397 
01398    /* Resolve all keys and record their lengths */
01399    for (x=0;x<scr->numkeys;x++) {
01400       if (!scr->keys[x].defined) {
01401          ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
01402          err++;
01403       }
01404    }
01405 
01406    /* Resolve all subs */
01407    for (x=0;x<scr->numsubs;x++) {
01408       if (!scr->subs[x].defined) {
01409          ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
01410          err++;
01411       }
01412       if (x == (scr->numsubs - 1)) {
01413          /* Clear out extension bit on last message */
01414          scr->subs[x].data[2] = 0x80;
01415       }
01416    }
01417 
01418    if (err) {
01419       free(scr);
01420       return NULL;
01421    }
01422    return scr;
01423 }
01424 
01425 #ifdef DUMP_MESSAGES
01426 static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
01427 {
01428    int x;
01429    printf("%s %s: [ ", type, vname);
01430    for (x=0;x<buflen;x++)
01431       printf("%02x ", buf[x]);
01432    printf("]\n");
01433 }
01434 #endif
01435 
01436 static int adsi_prog(struct ast_channel *chan, char *script)
01437 {
01438    struct adsi_script *scr;
01439    int x;
01440    unsigned char buf[1024];
01441    int bytes;
01442    scr = compile_script(script);
01443    if (!scr) 
01444       return -1;
01445 
01446    /* Start an empty ADSI Session */
01447    if (adsi_load_session(chan, NULL, 0, 1) < 1) 
01448       return -1;
01449 
01450    /* Now begin the download attempt */
01451    if (adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
01452       /* User rejected us for some reason */
01453       if (option_verbose > 2)
01454          ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n");
01455       ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
01456       free(scr);
01457       return -1;
01458    }
01459 
01460    bytes = 0;
01461    /* Start with key definitions */
01462    for (x=0;x<scr->numkeys;x++) {
01463       if (bytes + scr->keys[x].retstrlen > 253) {
01464          /* Send what we've collected so far */
01465          if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01466             ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01467             return -1;
01468          }
01469          bytes =0;
01470       }
01471       memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
01472       bytes += scr->keys[x].retstrlen;
01473 #ifdef DUMP_MESSAGES
01474       dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
01475 #endif
01476    }
01477    if (bytes) {
01478       if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01479          ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01480          return -1;
01481       }
01482    }
01483 
01484    bytes = 0;
01485    /* Continue with the display messages */
01486    for (x=0;x<scr->numdisplays;x++) {
01487       if (bytes + scr->displays[x].datalen > 253) {
01488          /* Send what we've collected so far */
01489          if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01490             ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01491             return -1;
01492          }
01493          bytes =0;
01494       }
01495       memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
01496       bytes += scr->displays[x].datalen;
01497 #ifdef DUMP_MESSAGES
01498       dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
01499 #endif
01500    }
01501    if (bytes) {
01502       if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01503          ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01504          return -1;
01505       }
01506    }
01507 
01508    bytes = 0;
01509    /* Send subroutines */
01510    for (x=0;x<scr->numsubs;x++) {
01511       if (bytes + scr->subs[x].datalen > 253) {
01512          /* Send what we've collected so far */
01513          if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01514             ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01515             return -1;
01516          }
01517          bytes =0;
01518       }
01519       memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
01520       bytes += scr->subs[x].datalen;
01521 #ifdef DUMP_MESSAGES
01522       dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
01523 #endif
01524    }
01525    if (bytes) {
01526       if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01527          ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01528          return -1;
01529       }
01530    }
01531 
01532 
01533    bytes = 0;
01534    bytes += adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
01535    bytes += adsi_set_line(buf, ADSI_INFO_PAGE, 1);
01536    if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
01537       return -1;
01538    if (adsi_end_download(chan)) {
01539       /* Download failed for some reason */
01540       if (option_verbose > 2)
01541          ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n");
01542       ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
01543       free(scr);
01544       return -1;
01545    }
01546    free(scr);
01547    adsi_unload_session(chan);
01548    return 0;
01549 }
01550 
01551 static int adsi_exec(struct ast_channel *chan, void *data)
01552 {
01553    int res=0;
01554    struct localuser *u;
01555 
01556    LOCAL_USER_ADD(u);
01557    
01558    if (ast_strlen_zero(data))
01559       data = "asterisk.adsi";
01560    
01561    if (!adsi_available(chan)) {
01562       if (option_verbose > 2)
01563          ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE.  Not bothering to try.\n");
01564    } else {
01565       if (option_verbose > 2)
01566          ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE.  Attempting Upload.\n");
01567       res = adsi_prog(chan, data);
01568    }
01569 
01570    LOCAL_USER_REMOVE(u);
01571    
01572    return res;
01573 }
01574 
01575 int unload_module(void)
01576 {
01577    int res;
01578 
01579    res = ast_unregister_application(app); 
01580    
01581    STANDARD_HANGUP_LOCALUSERS;
01582 
01583    return res;
01584 }
01585 
01586 int load_module(void)
01587 {
01588    return ast_register_application(app, adsi_exec, synopsis, descrip);
01589 }
01590 
01591 char *description(void)
01592 {
01593    return tdesc;
01594 }
01595 
01596 int usecount(void)
01597 {
01598    int res;
01599    STANDARD_USECOUNT(res);
01600    return res;
01601 }
01602 
01603 char *key()
01604 {
01605    return ASTERISK_GPL_KEY;
01606 }

Generated on Wed Oct 28 15:47:47 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6