Thu Sep 7 01:02:50 2017

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

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1