Sat Aug 6 00:39:19 2011

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

Generated on Sat Aug 6 00:39:19 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7