Fri Aug 24 02:22:10 2007

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

Generated on Fri Aug 24 02:22:10 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1