Thu Oct 11 06:33:31 2012

Asterisk developer's documentation


app_adsiprog.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Program Asterisk ADSI Scripts into phone
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \ingroup applications
00026  */
00027 
00028 /*! 
00029  * \li The application app_adsiprog uses the configuration file \ref adsi.conf
00030  * \addtogroup configuration_file Configuration Files
00031  */
00032 
00033 /*! 
00034  * \page adsi.conf adsi.conf
00035  * \verbinclude adsi.conf.sample
00036  */
00037 
00038 /*** MODULEINFO
00039    <depend>res_adsi</depend>
00040    <support_level>extended</support_level>
00041  ***/
00042 
00043 #include "asterisk.h"
00044 
00045 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374164 $")
00046 
00047 #include <netinet/in.h>
00048 #include <ctype.h>
00049 
00050 #include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
00051 #include "asterisk/file.h"
00052 #include "asterisk/channel.h"
00053 #include "asterisk/pbx.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/adsi.h"
00056 #include "asterisk/utils.h"
00057 #include "asterisk/lock.h"
00058 
00059 static const char app[] = "ADSIProg";
00060 
00061 /*** DOCUMENTATION
00062    <application name="ADSIProg" language="en_US">
00063       <synopsis>
00064          Load Asterisk ADSI Scripts into phone
00065       </synopsis>
00066       <syntax>
00067          <parameter name="script" required="false">
00068             <para>adsi script to use. If not given uses the default script <filename>asterisk.adsi</filename></para>
00069          </parameter>
00070       </syntax>
00071       <description>
00072          <para>This application programs an ADSI Phone with the given script</para>
00073       </description>
00074       <see-also>
00075          <ref type="application">GetCPEID</ref>
00076          <ref type="filename">adsi.conf</ref>
00077       </see-also>
00078    </application>
00079  ***/
00080 
00081 /* #define DUMP_MESSAGES */
00082 
00083 struct adsi_event {
00084    int id;
00085    const char *name;
00086 };
00087 
00088 static const struct adsi_event events[] = {
00089    { 1, "CALLERID" },
00090    { 2, "VMWI" },
00091    { 3, "NEARANSWER" },
00092    { 4, "FARANSWER" },
00093    { 5, "ENDOFRING" },
00094    { 6, "IDLE" },
00095    { 7, "OFFHOOK" },
00096    { 8, "CIDCW" },
00097    { 9, "BUSY" },
00098    { 10, "FARRING" },
00099    { 11, "DIALTONE" },
00100    { 12, "RECALL" },
00101    { 13, "MESSAGE" },
00102    { 14, "REORDER" },
00103    { 15, "DISTINCTIVERING" },
00104    { 16, "RING" },
00105    { 17, "REMINDERRING" },
00106    { 18, "SPECIALRING" },
00107    { 19, "CODEDRING" },
00108    { 20, "TIMER" },
00109    { 21, "INUSE" },
00110    { 22, "EVENT22" },
00111    { 23, "EVENT23" },
00112    { 24, "CPEID" },
00113 };
00114 
00115 static const struct adsi_event justify[] = {
00116    { 0, "CENTER" },
00117    { 1, "RIGHT" },
00118    { 2, "LEFT" },
00119    { 3, "INDENT" },
00120 };
00121 
00122 #define STATE_NORMAL 0
00123 #define STATE_INKEY  1
00124 #define STATE_INSUB  2
00125 #define STATE_INIF   3
00126 
00127 #define MAX_RET_CODE 20
00128 #define MAX_SUB_LEN  255
00129 #define MAX_MAIN_LEN 1600
00130 
00131 #define ARG_STRING (1 << 0)
00132 #define ARG_NUMBER (1 << 1)
00133 
00134 struct adsi_soft_key {
00135    char vname[40];  /* Which "variable" is associated with it */
00136    int retstrlen;   /* Length of return string */
00137    int initlen;     /* initial length */
00138    int id;
00139    int defined;
00140    char retstr[80]; /* Return string data */
00141 };
00142 
00143 struct adsi_subscript {
00144    char vname[40];
00145    int id;
00146    int defined;
00147    int datalen;
00148    int inscount;
00149    int ifinscount;
00150    char *ifdata;
00151    char data[2048];
00152 };
00153 
00154 struct adsi_state {
00155    char vname[40];
00156    int id;
00157 };
00158 
00159 struct adsi_flag {
00160    char vname[40];
00161    int id;
00162 };
00163 
00164 struct adsi_display {
00165    char vname[40];
00166    int id;
00167    char data[70];
00168    int datalen;
00169 };
00170 
00171 struct adsi_script {
00172    int state;
00173    int numkeys;
00174    int numsubs;
00175    int numstates;
00176    int numdisplays;
00177    int numflags;
00178    struct adsi_soft_key *key;
00179    struct adsi_subscript *sub;
00180    /* Pre-defined displays */
00181    struct adsi_display displays[63];
00182    /* ADSI States 1 (initial) - 254 */
00183    struct adsi_state states[256];
00184    /* Keys 2-63 */
00185    struct adsi_soft_key keys[62];
00186    /* Subscripts 0 (main) to 127 */
00187    struct adsi_subscript subs[128];
00188    /* Flags 1-7 */
00189    struct adsi_flag flags[7];
00190 
00191    /* Stuff from adsi script */
00192    unsigned char sec[5];
00193    char desc[19];
00194    unsigned char fdn[5];
00195    int ver;
00196 };
00197 
00198 
00199 static int process_token(void *out, char *src, int maxlen, int argtype)
00200 {
00201    if ((strlen(src) > 1) && src[0] == '\"') {
00202       /* This is a quoted string */
00203       if (!(argtype & ARG_STRING))
00204          return -1;
00205       src++;
00206       /* Don't take more than what's there */
00207       if (maxlen > strlen(src) - 1)
00208          maxlen = strlen(src) - 1;
00209       memcpy(out, src, maxlen);
00210       ((char *)out)[maxlen] = '\0';
00211    } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
00212       if (!(argtype & ARG_NUMBER))
00213          return -1;
00214       /* Octal value */
00215       if (sscanf(src, "%30o", (int *)out) != 1)
00216          return -1;
00217       if (argtype & ARG_STRING) {
00218          /* Convert */
00219          *((unsigned int *)out) = htonl(*((unsigned int *)out));
00220       }
00221    } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
00222       if (!(argtype & ARG_NUMBER))
00223          return -1;
00224       /* Hex value */
00225       if (sscanf(src + 2, "%30x", (unsigned int *)out) != 1)
00226          return -1;
00227       if (argtype & ARG_STRING) {
00228          /* Convert */
00229          *((unsigned int *)out) = htonl(*((unsigned int *)out));
00230       }
00231    } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
00232       if (!(argtype & ARG_NUMBER))
00233          return -1;
00234       /* Hex value */
00235       if (sscanf(src, "%30d", (int *)out) != 1)
00236          return -1;
00237       if (argtype & ARG_STRING) {
00238          /* Convert */
00239          *((unsigned int *)out) = htonl(*((unsigned int *)out));
00240       }
00241    } else
00242       return -1;
00243    return 0;
00244 }
00245 
00246 static char *get_token(char **buf, const char *script, int lineno)
00247 {
00248    char *tmp = *buf, *keyword;
00249    int quoted = 0;
00250 
00251    /* Advance past any white space */
00252    while(*tmp && (*tmp < 33))
00253       tmp++;
00254    if (!*tmp)
00255       return NULL;
00256    keyword = tmp;
00257    while(*tmp && ((*tmp > 32)  || quoted)) {
00258       if (*tmp == '\"') {
00259          quoted = !quoted;
00260       }
00261       tmp++;
00262    }
00263    if (quoted) {
00264       ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
00265       return NULL;
00266    }
00267    *tmp = '\0';
00268    tmp++;
00269    while(*tmp && (*tmp < 33))
00270       tmp++;
00271    /* Note where we left off */
00272    *buf = tmp;
00273    return keyword;
00274 }
00275 
00276 static char *validdtmf = "123456789*0#ABCD";
00277 
00278 static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00279 {
00280    char dtmfstr[80], *a;
00281    int bytes = 0;
00282 
00283    if (!(a = get_token(&args, script, lineno))) {
00284       ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
00285       return 0;
00286    }
00287 
00288    if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
00289       ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
00290       return 0;
00291    }
00292 
00293    a = dtmfstr;
00294 
00295    while (*a) {
00296       if (strchr(validdtmf, *a)) {
00297          *buf = *a;
00298          buf++;
00299          bytes++;
00300       } else
00301          ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
00302       a++;
00303    }
00304 
00305    return bytes;
00306 }
00307 
00308 static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00309 {
00310    char *page = get_token(&args, script, lineno);
00311    char *gline = get_token(&args, script, lineno);
00312    int line;
00313    unsigned char cmd;
00314 
00315    if (!page || !gline) {
00316       ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
00317       return 0;
00318    }
00319 
00320    if (!strcasecmp(page, "INFO"))
00321       cmd = 0;
00322    else if (!strcasecmp(page, "COMM"))
00323       cmd = 0x80;
00324    else {
00325       ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
00326       return 0;
00327    }
00328 
00329    if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
00330       ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
00331       return 0;
00332    }
00333 
00334    cmd |= line;
00335    buf[0] = 0x8b;
00336    buf[1] = cmd;
00337 
00338    return 2;
00339 }
00340 
00341 static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00342 {
00343    char *dir = get_token(&args, script, lineno);
00344    char *gline = get_token(&args, script, lineno);
00345    int line;
00346    unsigned char cmd;
00347 
00348    if (!dir || !gline) {
00349       ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
00350       return 0;
00351    }
00352 
00353    if (!strcasecmp(dir, "UP"))
00354       cmd = 0;
00355    else if (!strcasecmp(dir, "DOWN"))
00356       cmd = 0x20;
00357    else {
00358       ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
00359       return 0;
00360    }
00361 
00362    if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
00363       ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
00364       return 0;
00365    }
00366 
00367    cmd |= line;
00368    buf[0] = 0x8c;
00369    buf[1] = cmd;
00370 
00371    return 2;
00372 }
00373 
00374 static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00375 {
00376    char *gtime = get_token(&args, script, lineno);
00377    int ms;
00378 
00379    if (!gtime) {
00380       ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
00381       return 0;
00382    }
00383 
00384    if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
00385       ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
00386       return 0;
00387    }
00388 
00389    buf[0] = 0x90;
00390 
00391    if (id == 11)
00392       buf[1] = ms / 100;
00393    else
00394       buf[1] = ms / 10;
00395 
00396    return 2;
00397 }
00398 
00399 static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00400 {
00401    char *gstate = get_token(&args, script, lineno);
00402    int state;
00403 
00404    if (!gstate) {
00405       ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
00406       return 0;
00407    }
00408 
00409    if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
00410       ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
00411       return 0;
00412    }
00413 
00414    buf[0] = id;
00415    buf[1] = state;
00416 
00417    return 2;
00418 }
00419 
00420 static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00421 {
00422    char *tok = get_token(&args, script, lineno);
00423 
00424    if (tok)
00425       ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00426 
00427    buf[0] = id;
00428 
00429    /* For some reason the clear code is different slightly */
00430    if (id == 7)
00431       buf[1] = 0x10;
00432    else
00433       buf[1] = 0x00;
00434 
00435    return 2;
00436 }
00437 
00438 static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, const char *script, int lineno, int create)
00439 {
00440    int x;
00441 
00442    for (x = 0; x < state->numflags; x++) {
00443       if (!strcasecmp(state->flags[x].vname, name))
00444          return &state->flags[x];
00445    }
00446 
00447    /* Return now if we're not allowed to create */
00448    if (!create)
00449       return NULL;
00450 
00451    if (state->numflags > 6) {
00452       ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
00453       return NULL;
00454    }
00455 
00456    ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
00457    state->flags[state->numflags].id = state->numflags + 1;
00458    state->numflags++;
00459 
00460    return &state->flags[state->numflags-1];
00461 }
00462 
00463 static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00464 {
00465    char *tok = get_token(&args, script, lineno);
00466    char sname[80];
00467    struct adsi_flag *flag;
00468 
00469    if (!tok) {
00470       ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
00471       return 0;
00472    }
00473 
00474    if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
00475       ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
00476       return 0;
00477    }
00478 
00479    if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
00480       ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
00481       return 0;
00482    }
00483 
00484    buf[0] = id;
00485    buf[1] = ((flag->id & 0x7) << 4) | 1;
00486 
00487    return 2;
00488 }
00489 
00490 static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00491 {
00492    char *tok = get_token(&args, script, lineno);
00493    struct adsi_flag *flag;
00494    char sname[80];
00495 
00496    if (!tok) {
00497       ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
00498       return 0;
00499    }
00500 
00501    if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
00502       ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
00503       return 0;
00504    }
00505 
00506    if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
00507       ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
00508       return 0;
00509    }
00510 
00511    buf[0] = id;
00512    buf[1] = ((flag->id & 0x7) << 4);
00513 
00514    return 2;
00515 }
00516 
00517 static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00518 {
00519    char *tok = get_token(&args, script, lineno);
00520    int secs;
00521 
00522    if (!tok) {
00523       ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
00524       return 0;
00525    }
00526 
00527    if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
00528       ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
00529       return 0;
00530    }
00531 
00532    buf[0] = id;
00533    buf[1] = 0x1;
00534    buf[2] = secs;
00535 
00536    return 3;
00537 }
00538 
00539 static int geteventbyname(char *name)
00540 {
00541    int x;
00542 
00543    for (x = 0; x < ARRAY_LEN(events); x++) {
00544       if (!strcasecmp(events[x].name, name))
00545          return events[x].id;
00546    }
00547 
00548    return 0;
00549 }
00550 
00551 static int getjustifybyname(char *name)
00552 {
00553    int x;
00554 
00555    for (x = 0; x < ARRAY_LEN(justify); x++) {
00556       if (!strcasecmp(justify[x].name, name))
00557          return justify[x].id;
00558    }
00559 
00560    return -1;
00561 }
00562 
00563 static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, const char *script, int lineno)
00564 {
00565    int x;
00566 
00567    for (x = 0; x < state->numkeys; x++) {
00568       if (!strcasecmp(state->keys[x].vname, name))
00569          return &state->keys[x];
00570    }
00571 
00572    if (state->numkeys > 61) {
00573       ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
00574       return NULL;
00575    }
00576 
00577    ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
00578    state->keys[state->numkeys].id = state->numkeys + 2;
00579    state->numkeys++;
00580 
00581    return &state->keys[state->numkeys-1];
00582 }
00583 
00584 static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, const char *script, int lineno)
00585 {
00586    int x;
00587 
00588    for (x = 0; x < state->numsubs; x++) {
00589       if (!strcasecmp(state->subs[x].vname, name))
00590          return &state->subs[x];
00591    }
00592 
00593    if (state->numsubs > 127) {
00594       ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
00595       return NULL;
00596    }
00597 
00598    ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
00599    state->subs[state->numsubs].id = state->numsubs;
00600    state->numsubs++;
00601 
00602    return &state->subs[state->numsubs-1];
00603 }
00604 
00605 static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, const char *script, int lineno, int create)
00606 {
00607    int x;
00608 
00609    for (x = 0; x <state->numstates; x++) {
00610       if (!strcasecmp(state->states[x].vname, name))
00611          return &state->states[x];
00612    }
00613 
00614    /* Return now if we're not allowed to create */
00615    if (!create)
00616       return NULL;
00617 
00618    if (state->numstates > 253) {
00619       ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
00620       return NULL;
00621    }
00622 
00623    ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
00624    state->states[state->numstates].id = state->numstates + 1;
00625    state->numstates++;
00626 
00627    return &state->states[state->numstates-1];
00628 }
00629 
00630 static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, const char *script, int lineno, int create)
00631 {
00632    int x;
00633 
00634    for (x = 0; x < state->numdisplays; x++) {
00635       if (!strcasecmp(state->displays[x].vname, name))
00636          return &state->displays[x];
00637    }
00638 
00639    /* Return now if we're not allowed to create */
00640    if (!create)
00641       return NULL;
00642 
00643    if (state->numdisplays > 61) {
00644       ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
00645       return NULL;
00646    }
00647 
00648    ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
00649    state->displays[state->numdisplays].id = state->numdisplays + 1;
00650    state->numdisplays++;
00651 
00652    return &state->displays[state->numdisplays-1];
00653 }
00654 
00655 static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00656 {
00657    char *tok, newkey[80];
00658    int bytes, x, flagid = 0;
00659    unsigned char keyid[6];
00660    struct adsi_soft_key *key;
00661    struct adsi_flag *flag;
00662 
00663    for (x = 0; x < 7; x++) {
00664       /* Up to 6 key arguments */
00665       if (!(tok = get_token(&args, script, lineno)))
00666          break;
00667       if (!strcasecmp(tok, "UNLESS")) {
00668          /* Check for trailing UNLESS flag */
00669          if (!(tok = get_token(&args, script, lineno)))
00670             ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
00671          else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING))
00672             ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
00673          else if (!(flag = getflagbyname(state, newkey, script, lineno, 0)))
00674             ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
00675          else
00676             flagid = flag->id;
00677          if ((tok = get_token(&args, script, lineno)))
00678             ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
00679          break;
00680       }
00681       if (x > 5) {
00682          ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
00683          break;
00684       }
00685       if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
00686          ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
00687          continue;
00688       }
00689 
00690       if (!(key = getkeybyname(state, newkey, script, lineno)))
00691          break;
00692       keyid[x] = key->id;
00693    }
00694    buf[0] = id;
00695    buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
00696    for (bytes = 0; bytes < x; bytes++)
00697       buf[bytes + 2] = keyid[bytes];
00698 
00699    return 2 + x;
00700 }
00701 
00702 static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00703 {
00704    char *tok, dispname[80];
00705    int line = 0, flag = 0, cmd = 3;
00706    struct adsi_display *disp;
00707 
00708    /* Get display */
00709    if (!(tok = get_token(&args, script, lineno)) || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
00710       ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
00711       return 0;
00712    }
00713 
00714    if (!(disp = getdisplaybyname(state, dispname, script, lineno, 0))) {
00715       ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
00716       return 0;
00717    }
00718 
00719    if (!(tok = get_token(&args, script, lineno)) || strcasecmp(tok, "AT")) {
00720       ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
00721       return 0;
00722    }
00723 
00724    /* Get line number */
00725    if (!(tok = get_token(&args, script, lineno)) || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
00726       ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
00727       return 0;
00728    }
00729 
00730    if ((tok = get_token(&args, script, lineno)) && !strcasecmp(tok, "NOUPDATE")) {
00731       cmd = 1;
00732       tok = get_token(&args, script, lineno);
00733    }
00734 
00735    if (tok && !strcasecmp(tok, "UNLESS")) {
00736       /* Check for trailing UNLESS flag */
00737       if (!(tok = get_token(&args, script, lineno)))
00738          ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
00739       else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER))
00740          ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
00741 
00742       if ((tok = get_token(&args, script, lineno)))
00743          ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
00744    }
00745 
00746    buf[0] = id;
00747    buf[1] = (cmd << 6) | (disp->id & 0x3f);
00748    buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
00749 
00750    return 3;
00751 }
00752 
00753 static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00754 {
00755    char *tok = get_token(&args, script, lineno);
00756 
00757    if (tok)
00758       ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00759 
00760    buf[0] = id;
00761    buf[1] = 0x00;
00762    return 2;
00763 }
00764 
00765 static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00766 {
00767    char *tok = get_token(&args, script, lineno);
00768 
00769    if (tok)
00770       ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00771 
00772    buf[0] = id;
00773    buf[1] = 0x7;
00774    return 2;
00775 }
00776 
00777 static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00778 {
00779    char *tok = get_token(&args, script, lineno);
00780 
00781    if (tok)
00782       ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00783 
00784    buf[0] = id;
00785    buf[1] = 0;
00786    return 2;
00787 }
00788 
00789 static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno)
00790 {
00791    char *tok = get_token(&args, script, lineno);
00792 
00793    if (tok)
00794       ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
00795 
00796    buf[0] = id;
00797    buf[1] = 0xf;
00798    return 2;
00799 }
00800 
00801 static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00802 {
00803    char *tok = get_token(&args, script, lineno);
00804    char subscr[80];
00805    struct adsi_subscript *sub;
00806 
00807    if (!tok) {
00808       ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
00809       return 0;
00810    }
00811 
00812    if (process_token(subscr, tok, sizeof(subscr) - 1, ARG_STRING)) {
00813       ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
00814       return 0;
00815    }
00816 
00817    if (!(sub = getsubbyname(state, subscr, script, lineno)))
00818       return 0;
00819 
00820    buf[0] = 0x9d;
00821    buf[1] = sub->id;
00822 
00823    return 2;
00824 }
00825 
00826 static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno)
00827 {
00828    char *tok = get_token(&args, script, lineno);
00829    char subscr[80], sname[80];
00830    int sawin = 0, event, snums[8], scnt = 0, x;
00831    struct adsi_subscript *sub;
00832 
00833    if (!tok) {
00834       ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
00835       return 0;
00836    }
00837 
00838    if ((event = geteventbyname(tok)) < 1) {
00839       ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
00840       return 0;
00841    }
00842 
00843    tok = get_token(&args, script, lineno);
00844    while ((!sawin && !strcasecmp(tok, "IN")) || (sawin && !strcasecmp(tok, "OR"))) {
00845       sawin = 1;
00846       if (scnt > 7) {
00847          ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
00848          return 0;
00849       }
00850       /* Process 'in' things */
00851       tok = get_token(&args, script, lineno);
00852       if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
00853          ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
00854          return 0;
00855       }
00856       if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) == NULL)) {
00857          ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
00858          return 0;
00859       }
00860       scnt++;
00861       if (!(tok = get_token(&args, script, lineno)))
00862          break;
00863    }
00864    if (!tok || strcasecmp(tok, "GOTO")) {
00865       if (!tok)
00866          tok = "<nothing>";
00867       if (sawin)
00868          ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
00869       else
00870          ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
00871    }
00872    if (!(tok = get_token(&args, script, lineno))) {
00873       ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
00874       return 0;
00875    }
00876    if (process_token(subscr, tok, sizeof(subscr) - 1, ARG_STRING)) {
00877       ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
00878       return 0;
00879    }
00880    if (!(sub = getsubbyname(state, subscr, script, lineno)))
00881       return 0;
00882    buf[0] = 8;
00883    buf[1] = event;
00884    buf[2] = sub->id | 0x80;
00885    for (x = 0; x < scnt; x++)
00886       buf[3 + x] = snums[x];
00887    return 3 + scnt;
00888 }
00889 
00890 struct adsi_key_cmd {
00891    char *name;
00892    int id;
00893    int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno);
00894 };
00895 
00896 static const struct adsi_key_cmd kcmds[] = {
00897    { "SENDDTMF", 0, send_dtmf },
00898    /* Encoded DTMF would go here */
00899    { "ONHOOK", 0x81 },
00900    { "OFFHOOK", 0x82 },
00901    { "FLASH", 0x83 },
00902    { "WAITDIALTONE", 0x84 },
00903    /* Send line number */
00904    { "BLANK", 0x86 },
00905    { "SENDCHARS", 0x87 },
00906    { "CLEARCHARS", 0x88 },
00907    { "BACKSPACE", 0x89 },
00908    /* Tab column */
00909    { "GOTOLINE", 0x8b, goto_line },
00910    { "GOTOLINEREL", 0x8c, goto_line_rel },
00911    { "PAGEUP", 0x8d },
00912    { "PAGEDOWN", 0x8e },
00913    /* Extended DTMF */
00914    { "DELAY", 0x90, send_delay },
00915    { "DIALPULSEONE", 0x91 },
00916    { "DATAMODE", 0x92 },
00917    { "VOICEMODE", 0x93 },
00918    /* Display call buffer 'n' */
00919    /* Clear call buffer 'n' */
00920    { "CLEARCB1", 0x95, clearcbone },
00921    { "DIGITCOLLECT", 0x96, digitcollect },
00922    { "DIGITDIRECT", 0x96, digitdirect },
00923    { "CLEAR", 0x97 },
00924    { "SHOWDISPLAY", 0x98, showdisplay },
00925    { "CLEARDISPLAY", 0x98, cleardisplay },
00926    { "SHOWKEYS", 0x99, showkeys },
00927    { "SETSTATE", 0x9a, set_state },
00928    { "TIMERSTART", 0x9b, starttimer },
00929    { "TIMERCLEAR", 0x9b, cleartimer },
00930    { "SETFLAG", 0x9c, setflag },
00931    { "CLEARFLAG", 0x9c, clearflag },
00932    { "GOTO", 0x9d, subscript },
00933    { "EVENT22", 0x9e },
00934    { "EVENT23", 0x9f },
00935    { "EXIT", 0xa0 },
00936 };
00937 
00938 static const struct adsi_key_cmd opcmds[] = {
00939    
00940    /* 1 - Branch on event -- handled specially */
00941    { "SHOWKEYS", 2, showkeys },
00942    /* Display Control */
00943    { "SHOWDISPLAY", 3, showdisplay },
00944    { "CLEARDISPLAY", 3, cleardisplay },
00945    { "CLEAR", 5 },
00946    { "SETSTATE", 6, set_state },
00947    { "TIMERSTART", 7, starttimer },
00948    { "TIMERCLEAR", 7, cleartimer },
00949    { "ONEVENT", 8, onevent },
00950    /* 9 - Subroutine label, treated specially */
00951    { "SETFLAG", 10, setflag },
00952    { "CLEARFLAG", 10, clearflag },
00953    { "DELAY", 11, send_delay },
00954    { "EXIT", 12 },
00955 };
00956 
00957 
00958 static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, const char *script, int lineno)
00959 {
00960    int x, res;
00961    char *unused;
00962 
00963    for (x = 0; x < ARRAY_LEN(kcmds); x++) {
00964       if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
00965          if (kcmds[x].add_args) {
00966             res = kcmds[x].add_args(key->retstr + key->retstrlen,
00967                   code, kcmds[x].id, args, state, script, lineno);
00968             if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
00969                key->retstrlen += res;
00970             else
00971                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);
00972          } else {
00973             if ((unused = get_token(&args, script, lineno)))
00974                ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
00975             if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
00976                key->retstr[key->retstrlen] = kcmds[x].id;
00977                key->retstrlen++;
00978             } else
00979                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);
00980          }
00981          return 0;
00982       }
00983    }
00984    return -1;
00985 }
00986 
00987 static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, const char *script, int lineno)
00988 {
00989    int x, res, max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
00990    char *unused;
00991 
00992    for (x = 0; x < ARRAY_LEN(opcmds); x++) {
00993       if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
00994          if (opcmds[x].add_args) {
00995             res = opcmds[x].add_args(sub->data + sub->datalen,
00996                   code, opcmds[x].id, args, state, script, lineno);
00997             if ((sub->datalen + res + 1) <= max)
00998                sub->datalen += res;
00999             else {
01000                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);
01001                return -1;
01002             }
01003          } else {
01004             if ((unused = get_token(&args, script, lineno)))
01005                ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
01006             if ((sub->datalen + 2) <= max) {
01007                sub->data[sub->datalen] = opcmds[x].id;
01008                sub->datalen++;
01009             } else {
01010                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);
01011                return -1;
01012             }
01013          }
01014          /* Separate commands with 0xff */
01015          sub->data[sub->datalen] = 0xff;
01016          sub->datalen++;
01017          sub->inscount++;
01018          return 0;
01019       }
01020    }
01021    return -1;
01022 }
01023 
01024 static int adsi_process(struct adsi_script *state, char *buf, const char *script, int lineno)
01025 {
01026    char *keyword = get_token(&buf, script, lineno);
01027    char *args, vname[256], tmp[80], tmp2[80];
01028    int lrci, wi, event;
01029    struct adsi_display *disp;
01030    struct adsi_subscript *newsub;
01031 
01032    if (!keyword)
01033       return 0;
01034 
01035    switch(state->state) {
01036    case STATE_NORMAL:
01037       if (!strcasecmp(keyword, "DESCRIPTION")) {
01038          if ((args = get_token(&buf, script, lineno))) {
01039             if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
01040                ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
01041          } else
01042             ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
01043       } else if (!strcasecmp(keyword, "VERSION")) {
01044          if ((args = get_token(&buf, script, lineno))) {
01045             if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
01046                ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
01047          } else
01048             ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
01049       } else if (!strcasecmp(keyword, "SECURITY")) {
01050          if ((args = get_token(&buf, script, lineno))) {
01051             if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
01052                ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
01053          } else
01054             ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
01055       } else if (!strcasecmp(keyword, "FDN")) {
01056          if ((args = get_token(&buf, script, lineno))) {
01057             if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
01058                ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
01059          } else
01060             ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
01061       } else if (!strcasecmp(keyword, "KEY")) {
01062          if (!(args = get_token(&buf, script, lineno))) {
01063             ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
01064             break;
01065          }
01066          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01067             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
01068             break;
01069          }
01070          if (!(state->key = getkeybyname(state, vname, script, lineno))) {
01071             ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
01072             break;
01073          }
01074          if (state->key->defined) {
01075             ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
01076             break;
01077          }
01078          if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
01079             ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
01080             break;
01081          }
01082          if (!(args = get_token(&buf, script, lineno))) {
01083             ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
01084             break;
01085          }
01086          if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01087             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
01088             break;
01089          }
01090          if ((args = get_token(&buf, script, lineno))) {
01091             if (strcasecmp(args, "OR")) {
01092                ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
01093                break;
01094             }
01095             if (!(args = get_token(&buf, script, lineno))) {
01096                ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
01097                break;
01098             }
01099             if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
01100                ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
01101                break;
01102             }
01103          } else {
01104             ast_copy_string(tmp2, tmp, sizeof(tmp2));
01105          }
01106          if (strlen(tmp2) > 18) {
01107             ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
01108             tmp2[18] = '\0';
01109          }
01110          if (strlen(tmp) > 7) {
01111             ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
01112             tmp[7] = '\0';
01113          }
01114          /* Setup initial stuff */
01115          state->key->retstr[0] = 128;
01116          /* 1 has the length */
01117          state->key->retstr[2] = state->key->id;
01118          /* Put the Full name in */
01119          memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
01120          /* Update length */
01121          state->key->retstrlen = strlen(tmp2) + 3;
01122          /* Put trailing 0xff */
01123          state->key->retstr[state->key->retstrlen++] = 0xff;
01124          /* Put the short name */
01125          memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
01126          /* Update length */
01127          state->key->retstrlen += strlen(tmp);
01128          /* Put trailing 0xff */
01129          state->key->retstr[state->key->retstrlen++] = 0xff;
01130          /* Record initial length */
01131          state->key->initlen = state->key->retstrlen;
01132          state->state = STATE_INKEY;
01133       } else if (!strcasecmp(keyword, "SUB")) {
01134          if (!(args = get_token(&buf, script, lineno))) {
01135             ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
01136             break;
01137          }
01138          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01139             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
01140             break;
01141          }
01142          if (!(state->sub = getsubbyname(state, vname, script, lineno))) {
01143             ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
01144             break;
01145          }
01146          if (state->sub->defined) {
01147             ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
01148             break;
01149          }
01150          /* Setup sub */
01151          state->sub->data[0] = 130;
01152          /* 1 is the length */
01153          state->sub->data[2] = 0x0; /* Clear extensibility bit */
01154          state->sub->datalen = 3;
01155          if (state->sub->id) {
01156             /* If this isn't the main subroutine, make a subroutine label for it */
01157             state->sub->data[3] = 9;
01158             state->sub->data[4] = state->sub->id;
01159             /* 5 is length */
01160             state->sub->data[6] = 0xff;
01161             state->sub->datalen = 7;
01162          }
01163          if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
01164             ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
01165             break;
01166          }
01167          state->state = STATE_INSUB;
01168       } else if (!strcasecmp(keyword, "STATE")) {
01169          if (!(args = get_token(&buf, script, lineno))) {
01170             ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
01171             break;
01172          }
01173          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01174             ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
01175             break;
01176          }
01177          if (getstatebyname(state, vname, script, lineno, 0)) {
01178             ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
01179             break;
01180          }
01181          getstatebyname(state, vname, script, lineno, 1);
01182       } else if (!strcasecmp(keyword, "FLAG")) {
01183          if (!(args = get_token(&buf, script, lineno))) {
01184             ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
01185             break;
01186          }
01187          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01188             ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
01189             break;
01190          }
01191          if (getflagbyname(state, vname, script, lineno, 0)) {
01192             ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
01193             break;
01194          }
01195          getflagbyname(state, vname, script, lineno, 1);
01196       } else if (!strcasecmp(keyword, "DISPLAY")) {
01197          lrci = 0;
01198          wi = 0;
01199          if (!(args = get_token(&buf, script, lineno))) {
01200             ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
01201             break;
01202          }
01203          if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
01204             ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
01205             break;
01206          }
01207          if (getdisplaybyname(state, vname, script, lineno, 0)) {
01208             ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
01209             break;
01210          }
01211          if (!(disp = getdisplaybyname(state, vname, script, lineno, 1)))
01212             break;
01213          if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
01214             ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
01215             break;
01216          }
01217          if (!(args = get_token(&buf, script, lineno))) {
01218             ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
01219             break;
01220          }
01221          if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01222             ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
01223             break;
01224          }
01225          if (strlen(tmp) > 20) {
01226             ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
01227             tmp[20] = '\0';
01228          }
01229          memcpy(disp->data + 5, tmp, strlen(tmp));
01230          disp->datalen = strlen(tmp) + 5;
01231          disp->data[disp->datalen++] = 0xff;
01232 
01233          args = get_token(&buf, script, lineno);
01234          if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01235             /* Got a column two */
01236             if (strlen(tmp) > 20) {
01237                ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
01238                tmp[20] = '\0';
01239             }
01240             memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
01241             disp->datalen += strlen(tmp);
01242             args = get_token(&buf, script, lineno);
01243          }
01244          while (args) {
01245             if (!strcasecmp(args, "JUSTIFY")) {
01246                args = get_token(&buf, script, lineno);
01247                if (!args) {
01248                   ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
01249                   break;
01250                }
01251                lrci = getjustifybyname(args);
01252                if (lrci < 0) {
01253                   ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
01254                   break;
01255                }
01256             } else if (!strcasecmp(args, "WRAP")) {
01257                wi = 0x80;
01258             } else {
01259                ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
01260                break;
01261             }
01262             args = get_token(&buf, script, lineno);
01263          }
01264          if (args) {
01265             /* Something bad happened */
01266             break;
01267          }
01268          disp->data[0] = 129;
01269          disp->data[1] = disp->datalen - 2;
01270          disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
01271          disp->data[3] = wi;
01272          disp->data[4] = 0xff;
01273       } else {
01274          ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
01275       }
01276       break;
01277    case STATE_INKEY:
01278       if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
01279          if (!strcasecmp(keyword, "ENDKEY")) {
01280             /* Return to normal operation and increment current key */
01281             state->state = STATE_NORMAL;
01282             state->key->defined = 1;
01283             state->key->retstr[1] = state->key->retstrlen - 2;
01284             state->key = NULL;
01285          } else {
01286             ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
01287          }
01288       }
01289       break;
01290    case STATE_INIF:
01291       if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
01292          if (!strcasecmp(keyword, "ENDIF")) {
01293             /* Return to normal SUB operation and increment current key */
01294             state->state = STATE_INSUB;
01295             state->sub->defined = 1;
01296             /* Store the proper number of instructions */
01297             state->sub->ifdata[2] = state->sub->ifinscount;
01298          } else if (!strcasecmp(keyword, "GOTO")) {
01299             if (!(args = get_token(&buf, script, lineno))) {
01300                ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
01301                break;
01302             }
01303             if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
01304                ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
01305                break;
01306             }
01307             if (!(newsub = getsubbyname(state, tmp, script, lineno)))
01308                break;
01309             /* Somehow you use GOTO to go to another place */
01310             state->sub->data[state->sub->datalen++] = 0x8;
01311             state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
01312             state->sub->data[state->sub->datalen++] = newsub->id;
01313             /* Terminate */
01314             state->sub->data[state->sub->datalen++] = 0xff;
01315             /* Increment counters */
01316             state->sub->inscount++;
01317             state->sub->ifinscount++;
01318          } else {
01319             ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
01320          }
01321       } else
01322          state->sub->ifinscount++;
01323       break;
01324    case STATE_INSUB:
01325       if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
01326          if (!strcasecmp(keyword, "ENDSUB")) {
01327             /* Return to normal operation and increment current key */
01328             state->state = STATE_NORMAL;
01329             state->sub->defined = 1;
01330             /* Store the proper length */
01331             state->sub->data[1] = state->sub->datalen - 2;
01332             if (state->sub->id) {
01333                /* if this isn't main, store number of instructions, too */
01334                state->sub->data[5] = state->sub->inscount;
01335             }
01336             state->sub = NULL;
01337          } else if (!strcasecmp(keyword, "IFEVENT")) {
01338             if (!(args = get_token(&buf, script, lineno))) {
01339                ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
01340                break;
01341             }
01342             if ((event = geteventbyname(args)) < 1) {
01343                ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
01344                break;
01345             }
01346             if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "THEN")) {
01347                ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
01348                break;
01349             }
01350             state->sub->ifinscount = 0;
01351             state->sub->ifdata = state->sub->data + state->sub->datalen;
01352             /* Reserve header and insert op codes */
01353             state->sub->ifdata[0] = 0x1;
01354             state->sub->ifdata[1] = event;
01355             /* 2 is for the number of instructions */
01356             state->sub->ifdata[3] = 0xff;
01357             state->sub->datalen += 4;
01358             /* Update Subscript instruction count */
01359             state->sub->inscount++;
01360             state->state = STATE_INIF;
01361          } else {
01362             ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
01363          }
01364       }
01365       break;
01366    default:
01367       ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
01368    }
01369    return 0;
01370 }
01371 
01372 static struct adsi_script *compile_script(const char *script)
01373 {
01374    FILE *f;
01375    char fn[256], buf[256], *c;
01376    int lineno = 0, x, err;
01377    struct adsi_script *scr;
01378 
01379    if (script[0] == '/')
01380       ast_copy_string(fn, script, sizeof(fn));
01381    else
01382       snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, script);
01383 
01384    if (!(f = fopen(fn, "r"))) {
01385       ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
01386       return NULL;
01387    }
01388 
01389    if (!(scr = ast_calloc(1, sizeof(*scr)))) {
01390       fclose(f);
01391       return NULL;
01392    }
01393 
01394    /* Create "main" as first subroutine */
01395    getsubbyname(scr, "main", NULL, 0);
01396    while (!feof(f)) {
01397       if (!fgets(buf, sizeof(buf), f)) {
01398          continue;
01399       }
01400       if (!feof(f)) {
01401          lineno++;
01402          /* Trim off trailing return */
01403          buf[strlen(buf) - 1] = '\0';
01404          /* Strip comments */
01405          if ((c = strchr(buf, ';')))
01406             *c = '\0';
01407          if (!ast_strlen_zero(buf))
01408             adsi_process(scr, buf, script, lineno);
01409       }
01410    }
01411    fclose(f);
01412    /* Make sure we're in the main routine again */
01413    switch(scr->state) {
01414    case STATE_NORMAL:
01415       break;
01416    case STATE_INSUB:
01417       ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
01418       ast_free(scr);
01419       return NULL;
01420    case STATE_INKEY:
01421       ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
01422       ast_free(scr);
01423       return NULL;
01424    }
01425    err = 0;
01426 
01427    /* Resolve all keys and record their lengths */
01428    for (x = 0; x < scr->numkeys; x++) {
01429       if (!scr->keys[x].defined) {
01430          ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
01431          err++;
01432       }
01433    }
01434 
01435    /* Resolve all subs */
01436    for (x = 0; x < scr->numsubs; x++) {
01437       if (!scr->subs[x].defined) {
01438          ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
01439          err++;
01440       }
01441       if (x == (scr->numsubs - 1)) {
01442          /* Clear out extension bit on last message */
01443          scr->subs[x].data[2] = 0x80;
01444       }
01445    }
01446 
01447    if (err) {
01448       ast_free(scr);
01449       return NULL;
01450    }
01451    return scr;
01452 }
01453 
01454 #ifdef DUMP_MESSAGES
01455 static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
01456 {
01457    int x;
01458    printf("%s %s: [ ", type, vname);
01459    for (x = 0; x < buflen; x++)
01460       printf("%02x ", buf[x]);
01461    printf("]\n");
01462 }
01463 #endif
01464 
01465 static int adsi_prog(struct ast_channel *chan, const char *script)
01466 {
01467    struct adsi_script *scr;
01468    int x, bytes;
01469    unsigned char buf[1024];
01470 
01471    if (!(scr = compile_script(script)))
01472       return -1;
01473 
01474    /* Start an empty ADSI Session */
01475    if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
01476       return -1;
01477 
01478    /* Now begin the download attempt */
01479    if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
01480       /* User rejected us for some reason */
01481       ast_verb(3, "User rejected download attempt\n");
01482       ast_log(LOG_NOTICE, "User rejected download on channel %s\n", ast_channel_name(chan));
01483       ast_free(scr);
01484       return -1;
01485    }
01486 
01487    bytes = 0;
01488    /* Start with key definitions */
01489    for (x = 0; x < scr->numkeys; x++) {
01490       if (bytes + scr->keys[x].retstrlen > 253) {
01491          /* Send what we've collected so far */
01492          if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01493             ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01494             return -1;
01495          }
01496          bytes =0;
01497       }
01498       memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
01499       bytes += scr->keys[x].retstrlen;
01500 #ifdef DUMP_MESSAGES
01501       dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
01502 #endif
01503    }
01504    if (bytes) {
01505       if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01506          ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01507          return -1;
01508       }
01509    }
01510 
01511    bytes = 0;
01512    /* Continue with the display messages */
01513    for (x = 0; x < scr->numdisplays; x++) {
01514       if (bytes + scr->displays[x].datalen > 253) {
01515          /* Send what we've collected so far */
01516          if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01517             ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01518             return -1;
01519          }
01520          bytes =0;
01521       }
01522       memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
01523       bytes += scr->displays[x].datalen;
01524 #ifdef DUMP_MESSAGES
01525       dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
01526 #endif
01527    }
01528    if (bytes) {
01529       if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01530          ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01531          return -1;
01532       }
01533    }
01534 
01535    bytes = 0;
01536    /* Send subroutines */
01537    for (x = 0; x < scr->numsubs; x++) {
01538       if (bytes + scr->subs[x].datalen > 253) {
01539          /* Send what we've collected so far */
01540          if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01541             ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01542             return -1;
01543          }
01544          bytes =0;
01545       }
01546       memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
01547       bytes += scr->subs[x].datalen;
01548 #ifdef DUMP_MESSAGES
01549       dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
01550 #endif
01551    }
01552    if (bytes) {
01553       if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
01554          ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
01555          return -1;
01556       }
01557    }
01558 
01559    bytes = 0;
01560    bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
01561    bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
01562    if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
01563       return -1;
01564    if (ast_adsi_end_download(chan)) {
01565       /* Download failed for some reason */
01566       ast_verb(3, "Download attempt failed\n");
01567       ast_log(LOG_NOTICE, "Download failed on %s\n", ast_channel_name(chan));
01568       ast_free(scr);
01569       return -1;
01570    }
01571    ast_free(scr);
01572    ast_adsi_unload_session(chan);
01573    return 0;
01574 }
01575 
01576 static int adsi_exec(struct ast_channel *chan, const char *data)
01577 {
01578    int res = 0;
01579    
01580    if (ast_strlen_zero(data))
01581       data = "asterisk.adsi";
01582 
01583    if (!ast_adsi_available(chan)) {
01584       ast_verb(3, "ADSI Unavailable on CPE.  Not bothering to try.\n");
01585    } else {
01586       ast_verb(3, "ADSI Available on CPE.  Attempting Upload.\n");
01587       res = adsi_prog(chan, data);
01588    }
01589 
01590    return res;
01591 }
01592 
01593 static int unload_module(void)
01594 {
01595    return ast_unregister_application(app);
01596 }
01597 
01598 /*!
01599  * \brief Load the module
01600  *
01601  * Module loading including tests for configuration or dependencies.
01602  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
01603  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
01604  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
01605  * configuration file or other non-critical problem return 
01606  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
01607  */
01608 static int load_module(void)
01609 {
01610    if (ast_register_application_xml(app, adsi_exec))
01611       return AST_MODULE_LOAD_FAILURE;
01612    return AST_MODULE_LOAD_SUCCESS;
01613 }
01614 
01615 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk ADSI Programming Application",
01616       .load = load_module,
01617       .unload = unload_module,
01618       .nonoptreq = "res_adsi",
01619       );

Generated on Thu Oct 11 06:33:31 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6