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

Generated on Thu Oct 11 06:47:02 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6