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

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6