Wed Aug 18 22:33:40 2010

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

Generated on Wed Aug 18 22:33:40 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7