Asterisk.org: Developer Documentation (28 Oct 2009)

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 }