Creating tag for the release of asterisk-1.6.1-beta1
[asterisk-bristuff.git] / apps / app_adsiprog.c
blob8ee3a0410d134fd469dd10fbc1cc67c0a3fbc196
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Program Asterisk ADSI Scripts into phone
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
28 /*** MODULEINFO
29 <depend>res_adsi</depend>
30 ***/
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <netinet/in.h>
37 #include <ctype.h>
39 #include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/adsi.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/lock.h"
48 static char *app = "ADSIProg";
50 static char *synopsis = "Load Asterisk ADSI Scripts into phone";
52 /* #define DUMP_MESSAGES */
54 static char *descrip =
55 " ADSIProg(script): This application programs an ADSI Phone with the given\n"
56 "script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
58 struct adsi_event {
59 int id;
60 char *name;
63 static struct adsi_event events[] = {
64 { 1, "CALLERID" },
65 { 2, "VMWI" },
66 { 3, "NEARANSWER" },
67 { 4, "FARANSWER" },
68 { 5, "ENDOFRING" },
69 { 6, "IDLE" },
70 { 7, "OFFHOOK" },
71 { 8, "CIDCW" },
72 { 9, "BUSY" },
73 { 10, "FARRING" },
74 { 11, "DIALTONE" },
75 { 12, "RECALL" },
76 { 13, "MESSAGE" },
77 { 14, "REORDER" },
78 { 15, "DISTINCTIVERING" },
79 { 16, "RING" },
80 { 17, "REMINDERRING" },
81 { 18, "SPECIALRING" },
82 { 19, "CODEDRING" },
83 { 20, "TIMER" },
84 { 21, "INUSE" },
85 { 22, "EVENT22" },
86 { 23, "EVENT23" },
87 { 24, "CPEID" },
90 static struct adsi_event justify[] = {
91 { 0, "CENTER" },
92 { 1, "RIGHT" },
93 { 2, "LEFT" },
94 { 3, "INDENT" },
97 #define STATE_NORMAL 0
98 #define STATE_INKEY 1
99 #define STATE_INSUB 2
100 #define STATE_INIF 3
102 #define MAX_RET_CODE 20
103 #define MAX_SUB_LEN 255
104 #define MAX_MAIN_LEN 1600
106 #define ARG_STRING (1 << 0)
107 #define ARG_NUMBER (1 << 1)
109 struct adsi_soft_key {
110 char vname[40]; /* Which "variable" is associated with it */
111 int retstrlen; /* Length of return string */
112 int initlen; /* initial length */
113 int id;
114 int defined;
115 char retstr[80]; /* Return string data */
118 struct adsi_subscript {
119 char vname[40];
120 int id;
121 int defined;
122 int datalen;
123 int inscount;
124 int ifinscount;
125 char *ifdata;
126 char data[2048];
129 struct adsi_state {
130 char vname[40];
131 int id;
134 struct adsi_flag {
135 char vname[40];
136 int id;
139 struct adsi_display {
140 char vname[40];
141 int id;
142 char data[70];
143 int datalen;
146 struct adsi_script {
147 int state;
148 int numkeys;
149 int numsubs;
150 int numstates;
151 int numdisplays;
152 int numflags;
153 struct adsi_soft_key *key;
154 struct adsi_subscript *sub;
155 /* Pre-defined displays */
156 struct adsi_display displays[63];
157 /* ADSI States 1 (initial) - 254 */
158 struct adsi_state states[256];
159 /* Keys 2-63 */
160 struct adsi_soft_key keys[62];
161 /* Subscripts 0 (main) to 127 */
162 struct adsi_subscript subs[128];
163 /* Flags 1-7 */
164 struct adsi_flag flags[7];
166 /* Stuff from adsi script */
167 unsigned char sec[5];
168 char desc[19];
169 unsigned char fdn[5];
170 int ver;
174 static int process_token(void *out, char *src, int maxlen, int argtype)
176 if ((strlen(src) > 1) && src[0] == '\"') {
177 /* This is a quoted string */
178 if (!(argtype & ARG_STRING))
179 return -1;
180 src++;
181 /* Don't take more than what's there */
182 if (maxlen > strlen(src) - 1)
183 maxlen = strlen(src) - 1;
184 memcpy(out, src, maxlen);
185 ((char *)out)[maxlen] = '\0';
186 } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
187 if (!(argtype & ARG_NUMBER))
188 return -1;
189 /* Octal value */
190 if (sscanf(src, "%o", (int *)out) != 1)
191 return -1;
192 if (argtype & ARG_STRING) {
193 /* Convert */
194 *((unsigned int *)out) = htonl(*((unsigned int *)out));
196 } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
197 if (!(argtype & ARG_NUMBER))
198 return -1;
199 /* Hex value */
200 if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
201 return -1;
202 if (argtype & ARG_STRING) {
203 /* Convert */
204 *((unsigned int *)out) = htonl(*((unsigned int *)out));
206 } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
207 if (!(argtype & ARG_NUMBER))
208 return -1;
209 /* Hex value */
210 if (sscanf(src, "%d", (int *)out) != 1)
211 return -1;
212 if (argtype & ARG_STRING) {
213 /* Convert */
214 *((unsigned int *)out) = htonl(*((unsigned int *)out));
216 } else
217 return -1;
218 return 0;
221 static char *get_token(char **buf, char *script, int lineno)
223 char *tmp = *buf, *keyword;
224 int quoted = 0;
226 /* Advance past any white space */
227 while(*tmp && (*tmp < 33))
228 tmp++;
229 if (!*tmp)
230 return NULL;
231 keyword = tmp;
232 while(*tmp && ((*tmp > 32) || quoted)) {
233 if (*tmp == '\"') {
234 quoted = !quoted;
236 tmp++;
238 if (quoted) {
239 ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
240 return NULL;
242 *tmp = '\0';
243 tmp++;
244 while(*tmp && (*tmp < 33))
245 tmp++;
246 /* Note where we left off */
247 *buf = tmp;
248 return keyword;
251 static char *validdtmf = "123456789*0#ABCD";
253 static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
255 char dtmfstr[80], *a;
256 int bytes = 0;
258 if (!(a = get_token(&args, script, lineno))) {
259 ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
260 return 0;
263 if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
264 ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
265 return 0;
268 a = dtmfstr;
270 while (*a) {
271 if (strchr(validdtmf, *a)) {
272 *buf = *a;
273 buf++;
274 bytes++;
275 } else
276 ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
277 a++;
280 return bytes;
283 static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
285 char *page = get_token(&args, script, lineno);
286 char *gline = get_token(&args, script, lineno);
287 int line;
288 unsigned char cmd;
290 if (!page || !gline) {
291 ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
292 return 0;
295 if (!strcasecmp(page, "INFO"))
296 cmd = 0;
297 else if (!strcasecmp(page, "COMM"))
298 cmd = 0x80;
299 else {
300 ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
301 return 0;
304 if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
305 ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
306 return 0;
309 cmd |= line;
310 buf[0] = 0x8b;
311 buf[1] = cmd;
313 return 2;
316 static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
318 char *dir = get_token(&args, script, lineno);
319 char *gline = get_token(&args, script, lineno);
320 int line;
321 unsigned char cmd;
323 if (!dir || !gline) {
324 ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
325 return 0;
328 if (!strcasecmp(dir, "UP"))
329 cmd = 0;
330 else if (!strcasecmp(dir, "DOWN"))
331 cmd = 0x20;
332 else {
333 ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
334 return 0;
337 if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
338 ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
339 return 0;
342 cmd |= line;
343 buf[0] = 0x8c;
344 buf[1] = cmd;
346 return 2;
349 static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
351 char *gtime = get_token(&args, script, lineno);
352 int ms;
354 if (!gtime) {
355 ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
356 return 0;
359 if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
360 ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
361 return 0;
364 buf[0] = 0x90;
366 if (id == 11)
367 buf[1] = ms / 100;
368 else
369 buf[1] = ms / 10;
371 return 2;
374 static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
376 char *gstate = get_token(&args, script, lineno);
377 int state;
379 if (!gstate) {
380 ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
381 return 0;
384 if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
385 ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
386 return 0;
389 buf[0] = id;
390 buf[1] = state;
392 return 2;
395 static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
397 char *tok = get_token(&args, script, lineno);
399 if (tok)
400 ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
402 buf[0] = id;
404 /* For some reason the clear code is different slightly */
405 if (id == 7)
406 buf[1] = 0x10;
407 else
408 buf[1] = 0x00;
410 return 2;
413 static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
415 int x;
417 for (x = 0; x < state->numflags; x++) {
418 if (!strcasecmp(state->flags[x].vname, name))
419 return &state->flags[x];
422 /* Return now if we're not allowed to create */
423 if (!create)
424 return NULL;
426 if (state->numflags > 6) {
427 ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
428 return NULL;
431 ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
432 state->flags[state->numflags].id = state->numflags + 1;
433 state->numflags++;
435 return &state->flags[state->numflags-1];
438 static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
440 char *tok = get_token(&args, script, lineno);
441 char sname[80];
442 struct adsi_flag *flag;
444 if (!tok) {
445 ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
446 return 0;
449 if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
450 ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
451 return 0;
454 if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
455 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
456 return 0;
459 buf[0] = id;
460 buf[1] = ((flag->id & 0x7) << 4) | 1;
462 return 2;
465 static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
467 char *tok = get_token(&args, script, lineno);
468 struct adsi_flag *flag;
469 char sname[80];
471 if (!tok) {
472 ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
473 return 0;
476 if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
477 ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
478 return 0;
481 if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
482 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
483 return 0;
486 buf[0] = id;
487 buf[1] = ((flag->id & 0x7) << 4);
489 return 2;
492 static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
494 char *tok = get_token(&args, script, lineno);
495 int secs;
497 if (!tok) {
498 ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
499 return 0;
502 if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
503 ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
504 return 0;
507 buf[0] = id;
508 buf[1] = 0x1;
509 buf[2] = secs;
511 return 3;
514 static int geteventbyname(char *name)
516 int x;
518 for (x = 0; x < ARRAY_LEN(events); x++) {
519 if (!strcasecmp(events[x].name, name))
520 return events[x].id;
523 return 0;
526 static int getjustifybyname(char *name)
528 int x;
530 for (x = 0; x < ARRAY_LEN(justify); x++) {
531 if (!strcasecmp(justify[x].name, name))
532 return justify[x].id;
535 return -1;
538 static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
540 int x;
542 for (x = 0; x < state->numkeys; x++) {
543 if (!strcasecmp(state->keys[x].vname, name))
544 return &state->keys[x];
547 if (state->numkeys > 61) {
548 ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
549 return NULL;
552 ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
553 state->keys[state->numkeys].id = state->numkeys + 2;
554 state->numkeys++;
556 return &state->keys[state->numkeys-1];
559 static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
561 int x;
563 for (x = 0; x < state->numsubs; x++) {
564 if (!strcasecmp(state->subs[x].vname, name))
565 return &state->subs[x];
568 if (state->numsubs > 127) {
569 ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
570 return NULL;
573 ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
574 state->subs[state->numsubs].id = state->numsubs;
575 state->numsubs++;
577 return &state->subs[state->numsubs-1];
580 static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
582 int x;
584 for (x = 0; x <state->numstates; x++) {
585 if (!strcasecmp(state->states[x].vname, name))
586 return &state->states[x];
589 /* Return now if we're not allowed to create */
590 if (!create)
591 return NULL;
593 if (state->numstates > 253) {
594 ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
595 return NULL;
598 ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
599 state->states[state->numstates].id = state->numstates + 1;
600 state->numstates++;
602 return &state->states[state->numstates-1];
605 static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
607 int x;
609 for (x = 0; x < state->numdisplays; x++) {
610 if (!strcasecmp(state->displays[x].vname, name))
611 return &state->displays[x];
614 /* Return now if we're not allowed to create */
615 if (!create)
616 return NULL;
618 if (state->numdisplays > 61) {
619 ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
620 return NULL;
623 ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
624 state->displays[state->numdisplays].id = state->numdisplays + 1;
625 state->numdisplays++;
627 return &state->displays[state->numdisplays-1];
630 static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
632 char *tok, newkey[80];
633 int bytes, x, flagid = 0;
634 unsigned char keyid[6];
635 struct adsi_soft_key *key;
636 struct adsi_flag *flag;
638 for (x = 0; x < 7; x++) {
639 /* Up to 6 key arguments */
640 if (!(tok = get_token(&args, script, lineno)))
641 break;
642 if (!strcasecmp(tok, "UNLESS")) {
643 /* Check for trailing UNLESS flag */
644 if (!(tok = get_token(&args, script, lineno)))
645 ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
646 else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING))
647 ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
648 else if (!(flag = getflagbyname(state, newkey, script, lineno, 0)))
649 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
650 else
651 flagid = flag->id;
652 if ((tok = get_token(&args, script, lineno)))
653 ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
654 break;
656 if (x > 5) {
657 ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
658 break;
660 if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
661 ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
662 continue;
665 if (!(key = getkeybyname(state, newkey, script, lineno)))
666 break;
667 keyid[x] = key->id;
669 buf[0] = id;
670 buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
671 for (bytes = 0; bytes < x; bytes++)
672 buf[bytes + 2] = keyid[bytes];
674 return 2 + x;
677 static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
679 char *tok, dispname[80];
680 int line = 0, flag = 0, cmd = 3;
681 struct adsi_display *disp;
683 /* Get display */
684 if (!(tok = get_token(&args, script, lineno)) || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
685 ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
686 return 0;
689 if (!(disp = getdisplaybyname(state, dispname, script, lineno, 0))) {
690 ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
691 return 0;
694 if (!(tok = get_token(&args, script, lineno)) || strcasecmp(tok, "AT")) {
695 ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
696 return 0;
699 /* Get line number */
700 if (!(tok = get_token(&args, script, lineno)) || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
701 ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
702 return 0;
705 if ((tok = get_token(&args, script, lineno)) && !strcasecmp(tok, "NOUPDATE")) {
706 cmd = 1;
707 tok = get_token(&args, script, lineno);
710 if (tok && !strcasecmp(tok, "UNLESS")) {
711 /* Check for trailing UNLESS flag */
712 if (!(tok = get_token(&args, script, lineno)))
713 ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
714 else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER))
715 ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
717 if ((tok = get_token(&args, script, lineno)))
718 ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
721 buf[0] = id;
722 buf[1] = (cmd << 6) | (disp->id & 0x3f);
723 buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
725 return 3;
728 static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
730 char *tok = get_token(&args, script, lineno);
732 if (tok)
733 ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
735 buf[0] = id;
736 buf[1] = 0x00;
737 return 2;
740 static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
742 char *tok = get_token(&args, script, lineno);
744 if (tok)
745 ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
747 buf[0] = id;
748 buf[1] = 0x7;
749 return 2;
752 static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
754 char *tok = get_token(&args, script, lineno);
756 if (tok)
757 ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
759 buf[0] = id;
760 buf[1] = 0;
761 return 2;
764 static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
766 char *tok = get_token(&args, script, lineno);
768 if (tok)
769 ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
771 buf[0] = id;
772 buf[1] = 0xf;
773 return 2;
776 static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
778 char *tok = get_token(&args, script, lineno);
779 char subscr[80];
780 struct adsi_subscript *sub;
782 if (!tok) {
783 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
784 return 0;
787 if (process_token(subscr, tok, sizeof(subscr) - 1, ARG_STRING)) {
788 ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
789 return 0;
792 if (!(sub = getsubbyname(state, subscr, script, lineno)))
793 return 0;
795 buf[0] = 0x9d;
796 buf[1] = sub->id;
798 return 2;
801 static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
803 char *tok = get_token(&args, script, lineno);
804 char subscr[80], sname[80];
805 int sawin = 0, event, snums[8], scnt = 0, x;
806 struct adsi_subscript *sub;
808 if (!tok) {
809 ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
810 return 0;
813 if ((event = geteventbyname(tok)) < 1) {
814 ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
815 return 0;
818 tok = get_token(&args, script, lineno);
819 while ((!sawin && !strcasecmp(tok, "IN")) || (sawin && !strcasecmp(tok, "OR"))) {
820 sawin = 1;
821 if (scnt > 7) {
822 ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
823 return 0;
825 /* Process 'in' things */
826 tok = get_token(&args, script, lineno);
827 if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
828 ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
829 return 0;
831 if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
832 ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
833 return 0;
835 scnt++;
836 if (!(tok = get_token(&args, script, lineno)))
837 break;
839 if (!tok || strcasecmp(tok, "GOTO")) {
840 if (!tok)
841 tok = "<nothing>";
842 if (sawin)
843 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
844 else
845 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
847 if (!(tok = get_token(&args, script, lineno))) {
848 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
849 return 0;
851 if (process_token(subscr, tok, sizeof(subscr) - 1, ARG_STRING)) {
852 ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
853 return 0;
855 if (!(sub = getsubbyname(state, subscr, script, lineno)))
856 return 0;
857 buf[0] = 8;
858 buf[1] = event;
859 buf[2] = sub->id | 0x80;
860 for (x = 0; x < scnt; x++)
861 buf[3 + x] = snums[x];
862 return 3 + scnt;
865 struct adsi_key_cmd {
866 char *name;
867 int id;
868 int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
871 static struct adsi_key_cmd kcmds[] = {
872 { "SENDDTMF", 0, send_dtmf },
873 /* Encoded DTMF would go here */
874 { "ONHOOK", 0x81 },
875 { "OFFHOOK", 0x82 },
876 { "FLASH", 0x83 },
877 { "WAITDIALTONE", 0x84 },
878 /* Send line number */
879 { "BLANK", 0x86 },
880 { "SENDCHARS", 0x87 },
881 { "CLEARCHARS", 0x88 },
882 { "BACKSPACE", 0x89 },
883 /* Tab column */
884 { "GOTOLINE", 0x8b, goto_line },
885 { "GOTOLINEREL", 0x8c, goto_line_rel },
886 { "PAGEUP", 0x8d },
887 { "PAGEDOWN", 0x8e },
888 /* Extended DTMF */
889 { "DELAY", 0x90, send_delay },
890 { "DIALPULSEONE", 0x91 },
891 { "DATAMODE", 0x92 },
892 { "VOICEMODE", 0x93 },
893 /* Display call buffer 'n' */
894 /* Clear call buffer 'n' */
895 { "CLEARCB1", 0x95, clearcbone },
896 { "DIGITCOLLECT", 0x96, digitcollect },
897 { "DIGITDIRECT", 0x96, digitdirect },
898 { "CLEAR", 0x97 },
899 { "SHOWDISPLAY", 0x98, showdisplay },
900 { "CLEARDISPLAY", 0x98, cleardisplay },
901 { "SHOWKEYS", 0x99, showkeys },
902 { "SETSTATE", 0x9a, set_state },
903 { "TIMERSTART", 0x9b, starttimer },
904 { "TIMERCLEAR", 0x9b, cleartimer },
905 { "SETFLAG", 0x9c, setflag },
906 { "CLEARFLAG", 0x9c, clearflag },
907 { "GOTO", 0x9d, subscript },
908 { "EVENT22", 0x9e },
909 { "EVENT23", 0x9f },
910 { "EXIT", 0xa0 },
913 static struct adsi_key_cmd opcmds[] = {
915 /* 1 - Branch on event -- handled specially */
916 { "SHOWKEYS", 2, showkeys },
917 /* Display Control */
918 { "SHOWDISPLAY", 3, showdisplay },
919 { "CLEARDISPLAY", 3, cleardisplay },
920 { "CLEAR", 5 },
921 { "SETSTATE", 6, set_state },
922 { "TIMERSTART", 7, starttimer },
923 { "TIMERCLEAR", 7, cleartimer },
924 { "ONEVENT", 8, onevent },
925 /* 9 - Subroutine label, treated specially */
926 { "SETFLAG", 10, setflag },
927 { "CLEARFLAG", 10, clearflag },
928 { "DELAY", 11, send_delay },
929 { "EXIT", 12 },
933 static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
935 int x, res;
936 char *unused;
938 for (x = 0; x < ARRAY_LEN(kcmds); x++) {
939 if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
940 if (kcmds[x].add_args) {
941 res = kcmds[x].add_args(key->retstr + key->retstrlen,
942 code, kcmds[x].id, args, state, script, lineno);
943 if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
944 key->retstrlen += res;
945 else
946 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);
947 } else {
948 if ((unused = get_token(&args, script, lineno)))
949 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
950 if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
951 key->retstr[key->retstrlen] = kcmds[x].id;
952 key->retstrlen++;
953 } else
954 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);
956 return 0;
959 return -1;
962 static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
964 int x, res, max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
965 char *unused;
967 for (x = 0; x < ARRAY_LEN(opcmds); x++) {
968 if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
969 if (opcmds[x].add_args) {
970 res = opcmds[x].add_args(sub->data + sub->datalen,
971 code, opcmds[x].id, args, state, script, lineno);
972 if ((sub->datalen + res + 1) <= max)
973 sub->datalen += res;
974 else {
975 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);
976 return -1;
978 } else {
979 if ((unused = get_token(&args, script, lineno)))
980 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
981 if ((sub->datalen + 2) <= max) {
982 sub->data[sub->datalen] = opcmds[x].id;
983 sub->datalen++;
984 } else {
985 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);
986 return -1;
989 /* Separate commands with 0xff */
990 sub->data[sub->datalen] = 0xff;
991 sub->datalen++;
992 sub->inscount++;
993 return 0;
996 return -1;
999 static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
1001 char *keyword = get_token(&buf, script, lineno);
1002 char *args, vname[256], tmp[80], tmp2[80];
1003 int lrci, wi, event;
1004 struct adsi_display *disp;
1005 struct adsi_subscript *newsub;
1007 if (!keyword)
1008 return 0;
1010 switch(state->state) {
1011 case STATE_NORMAL:
1012 if (!strcasecmp(keyword, "DESCRIPTION")) {
1013 if ((args = get_token(&buf, script, lineno))) {
1014 if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
1015 ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
1016 } else
1017 ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
1018 } else if (!strcasecmp(keyword, "VERSION")) {
1019 if ((args = get_token(&buf, script, lineno))) {
1020 if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
1021 ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
1022 } else
1023 ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
1024 } else if (!strcasecmp(keyword, "SECURITY")) {
1025 if ((args = get_token(&buf, script, lineno))) {
1026 if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
1027 ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
1028 } else
1029 ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
1030 } else if (!strcasecmp(keyword, "FDN")) {
1031 if ((args = get_token(&buf, script, lineno))) {
1032 if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
1033 ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
1034 } else
1035 ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
1036 } else if (!strcasecmp(keyword, "KEY")) {
1037 if (!(args = get_token(&buf, script, lineno))) {
1038 ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
1039 break;
1041 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1042 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1043 break;
1045 if (!(state->key = getkeybyname(state, vname, script, lineno))) {
1046 ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
1047 break;
1049 if (state->key->defined) {
1050 ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
1051 break;
1053 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
1054 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1055 break;
1057 if (!(args = get_token(&buf, script, lineno))) {
1058 ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
1059 break;
1061 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1062 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
1063 break;
1065 if ((args = get_token(&buf, script, lineno))) {
1066 if (strcasecmp(args, "OR")) {
1067 ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
1068 break;
1070 if (!(args = get_token(&buf, script, lineno))) {
1071 ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
1072 break;
1074 if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
1075 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
1076 break;
1078 } else {
1079 ast_copy_string(tmp2, tmp, sizeof(tmp2));
1081 if (strlen(tmp2) > 18) {
1082 ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
1083 tmp2[18] = '\0';
1085 if (strlen(tmp) > 7) {
1086 ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
1087 tmp[7] = '\0';
1089 /* Setup initial stuff */
1090 state->key->retstr[0] = 128;
1091 /* 1 has the length */
1092 state->key->retstr[2] = state->key->id;
1093 /* Put the Full name in */
1094 memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
1095 /* Update length */
1096 state->key->retstrlen = strlen(tmp2) + 3;
1097 /* Put trailing 0xff */
1098 state->key->retstr[state->key->retstrlen++] = 0xff;
1099 /* Put the short name */
1100 memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
1101 /* Update length */
1102 state->key->retstrlen += strlen(tmp);
1103 /* Put trailing 0xff */
1104 state->key->retstr[state->key->retstrlen++] = 0xff;
1105 /* Record initial length */
1106 state->key->initlen = state->key->retstrlen;
1107 state->state = STATE_INKEY;
1108 } else if (!strcasecmp(keyword, "SUB")) {
1109 if (!(args = get_token(&buf, script, lineno))) {
1110 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1111 break;
1113 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1114 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1115 break;
1117 if (!(state->sub = getsubbyname(state, vname, script, lineno))) {
1118 ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
1119 break;
1121 if (state->sub->defined) {
1122 ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
1123 break;
1125 /* Setup sub */
1126 state->sub->data[0] = 130;
1127 /* 1 is the length */
1128 state->sub->data[2] = 0x0; /* Clear extensibility bit */
1129 state->sub->datalen = 3;
1130 if (state->sub->id) {
1131 /* If this isn't the main subroutine, make a subroutine label for it */
1132 state->sub->data[3] = 9;
1133 state->sub->data[4] = state->sub->id;
1134 /* 5 is length */
1135 state->sub->data[6] = 0xff;
1136 state->sub->datalen = 7;
1138 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
1139 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1140 break;
1142 state->state = STATE_INSUB;
1143 } else if (!strcasecmp(keyword, "STATE")) {
1144 if (!(args = get_token(&buf, script, lineno))) {
1145 ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
1146 break;
1148 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1149 ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
1150 break;
1152 if (getstatebyname(state, vname, script, lineno, 0)) {
1153 ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
1154 break;
1156 getstatebyname(state, vname, script, lineno, 1);
1157 } else if (!strcasecmp(keyword, "FLAG")) {
1158 if (!(args = get_token(&buf, script, lineno))) {
1159 ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
1160 break;
1162 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1163 ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
1164 break;
1166 if (getflagbyname(state, vname, script, lineno, 0)) {
1167 ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
1168 break;
1170 getflagbyname(state, vname, script, lineno, 1);
1171 } else if (!strcasecmp(keyword, "DISPLAY")) {
1172 lrci = 0;
1173 wi = 0;
1174 if (!(args = get_token(&buf, script, lineno))) {
1175 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1176 break;
1178 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1179 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1180 break;
1182 if (getdisplaybyname(state, vname, script, lineno, 0)) {
1183 ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
1184 break;
1186 if (!(disp = getdisplaybyname(state, vname, script, lineno, 1)))
1187 break;
1188 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
1189 ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
1190 break;
1192 if (!(args = get_token(&buf, script, lineno))) {
1193 ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
1194 break;
1196 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1197 ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
1198 break;
1200 if (strlen(tmp) > 20) {
1201 ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
1202 tmp[20] = '\0';
1204 memcpy(disp->data + 5, tmp, strlen(tmp));
1205 disp->datalen = strlen(tmp) + 5;
1206 disp->data[disp->datalen++] = 0xff;
1208 args = get_token(&buf, script, lineno);
1209 if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1210 /* Got a column two */
1211 if (strlen(tmp) > 20) {
1212 ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
1213 tmp[20] = '\0';
1215 memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
1216 disp->datalen += strlen(tmp);
1217 args = get_token(&buf, script, lineno);
1219 while (args) {
1220 if (!strcasecmp(args, "JUSTIFY")) {
1221 args = get_token(&buf, script, lineno);
1222 if (!args) {
1223 ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
1224 break;
1226 lrci = getjustifybyname(args);
1227 if (lrci < 0) {
1228 ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
1229 break;
1231 } else if (!strcasecmp(args, "WRAP")) {
1232 wi = 0x80;
1233 } else {
1234 ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
1235 break;
1237 args = get_token(&buf, script, lineno);
1239 if (args) {
1240 /* Something bad happened */
1241 break;
1243 disp->data[0] = 129;
1244 disp->data[1] = disp->datalen - 2;
1245 disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
1246 disp->data[3] = wi;
1247 disp->data[4] = 0xff;
1248 } else {
1249 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
1251 break;
1252 case STATE_INKEY:
1253 if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
1254 if (!strcasecmp(keyword, "ENDKEY")) {
1255 /* Return to normal operation and increment current key */
1256 state->state = STATE_NORMAL;
1257 state->key->defined = 1;
1258 state->key->retstr[1] = state->key->retstrlen - 2;
1259 state->key = NULL;
1260 } else {
1261 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
1264 break;
1265 case STATE_INIF:
1266 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1267 if (!strcasecmp(keyword, "ENDIF")) {
1268 /* Return to normal SUB operation and increment current key */
1269 state->state = STATE_INSUB;
1270 state->sub->defined = 1;
1271 /* Store the proper number of instructions */
1272 state->sub->ifdata[2] = state->sub->ifinscount;
1273 } else if (!strcasecmp(keyword, "GOTO")) {
1274 if (!(args = get_token(&buf, script, lineno))) {
1275 ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
1276 break;
1278 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1279 ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
1280 break;
1282 if (!(newsub = getsubbyname(state, tmp, script, lineno)))
1283 break;
1284 /* Somehow you use GOTO to go to another place */
1285 state->sub->data[state->sub->datalen++] = 0x8;
1286 state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
1287 state->sub->data[state->sub->datalen++] = newsub->id;
1288 /* Terminate */
1289 state->sub->data[state->sub->datalen++] = 0xff;
1290 /* Increment counters */
1291 state->sub->inscount++;
1292 state->sub->ifinscount++;
1293 } else {
1294 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
1296 } else
1297 state->sub->ifinscount++;
1298 break;
1299 case STATE_INSUB:
1300 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1301 if (!strcasecmp(keyword, "ENDSUB")) {
1302 /* Return to normal operation and increment current key */
1303 state->state = STATE_NORMAL;
1304 state->sub->defined = 1;
1305 /* Store the proper length */
1306 state->sub->data[1] = state->sub->datalen - 2;
1307 if (state->sub->id) {
1308 /* if this isn't main, store number of instructions, too */
1309 state->sub->data[5] = state->sub->inscount;
1311 state->sub = NULL;
1312 } else if (!strcasecmp(keyword, "IFEVENT")) {
1313 if (!(args = get_token(&buf, script, lineno))) {
1314 ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
1315 break;
1317 if ((event = geteventbyname(args)) < 1) {
1318 ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
1319 break;
1321 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "THEN")) {
1322 ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
1323 break;
1325 state->sub->ifinscount = 0;
1326 state->sub->ifdata = state->sub->data + state->sub->datalen;
1327 /* Reserve header and insert op codes */
1328 state->sub->ifdata[0] = 0x1;
1329 state->sub->ifdata[1] = event;
1330 /* 2 is for the number of instructions */
1331 state->sub->ifdata[3] = 0xff;
1332 state->sub->datalen += 4;
1333 /* Update Subscript instruction count */
1334 state->sub->inscount++;
1335 state->state = STATE_INIF;
1336 } else {
1337 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
1340 break;
1341 default:
1342 ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
1344 return 0;
1347 static struct adsi_script *compile_script(char *script)
1349 FILE *f;
1350 char fn[256], buf[256], *c;
1351 int lineno = 0, x, err;
1352 struct adsi_script *scr;
1354 if (script[0] == '/')
1355 ast_copy_string(fn, script, sizeof(fn));
1356 else
1357 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, script);
1359 if (!(f = fopen(fn, "r"))) {
1360 ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
1361 return NULL;
1364 if (!(scr = ast_calloc(1, sizeof(*scr)))) {
1365 fclose(f);
1366 return NULL;
1369 /* Create "main" as first subroutine */
1370 getsubbyname(scr, "main", NULL, 0);
1371 while (!feof(f)) {
1372 fgets(buf, sizeof(buf), f);
1373 if (!feof(f)) {
1374 lineno++;
1375 /* Trim off trailing return */
1376 buf[strlen(buf) - 1] = '\0';
1377 /* Strip comments */
1378 if ((c = strchr(buf, ';')))
1379 *c = '\0';
1380 if (!ast_strlen_zero(buf))
1381 adsi_process(scr, buf, script, lineno);
1384 fclose(f);
1385 /* Make sure we're in the main routine again */
1386 switch(scr->state) {
1387 case STATE_NORMAL:
1388 break;
1389 case STATE_INSUB:
1390 ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
1391 ast_free(scr);
1392 return NULL;
1393 case STATE_INKEY:
1394 ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
1395 ast_free(scr);
1396 return NULL;
1398 err = 0;
1400 /* Resolve all keys and record their lengths */
1401 for (x = 0; x < scr->numkeys; x++) {
1402 if (!scr->keys[x].defined) {
1403 ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
1404 err++;
1408 /* Resolve all subs */
1409 for (x = 0; x < scr->numsubs; x++) {
1410 if (!scr->subs[x].defined) {
1411 ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
1412 err++;
1414 if (x == (scr->numsubs - 1)) {
1415 /* Clear out extension bit on last message */
1416 scr->subs[x].data[2] = 0x80;
1420 if (err) {
1421 ast_free(scr);
1422 return NULL;
1424 return scr;
1427 #ifdef DUMP_MESSAGES
1428 static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
1430 int x;
1431 printf("%s %s: [ ", type, vname);
1432 for (x = 0; x < buflen; x++)
1433 printf("%02x ", buf[x]);
1434 printf("]\n");
1436 #endif
1438 static int adsi_prog(struct ast_channel *chan, char *script)
1440 struct adsi_script *scr;
1441 int x, bytes;
1442 unsigned char buf[1024];
1444 if (!(scr = compile_script(script)))
1445 return -1;
1447 /* Start an empty ADSI Session */
1448 if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
1449 return -1;
1451 /* Now begin the download attempt */
1452 if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
1453 /* User rejected us for some reason */
1454 ast_verb(3, "User rejected download attempt\n");
1455 ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
1456 ast_free(scr);
1457 return -1;
1460 bytes = 0;
1461 /* Start with key definitions */
1462 for (x = 0; x < scr->numkeys; x++) {
1463 if (bytes + scr->keys[x].retstrlen > 253) {
1464 /* Send what we've collected so far */
1465 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1466 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1467 return -1;
1469 bytes =0;
1471 memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
1472 bytes += scr->keys[x].retstrlen;
1473 #ifdef DUMP_MESSAGES
1474 dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
1475 #endif
1477 if (bytes) {
1478 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1479 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1480 return -1;
1484 bytes = 0;
1485 /* Continue with the display messages */
1486 for (x = 0; x < scr->numdisplays; x++) {
1487 if (bytes + scr->displays[x].datalen > 253) {
1488 /* Send what we've collected so far */
1489 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1490 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1491 return -1;
1493 bytes =0;
1495 memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
1496 bytes += scr->displays[x].datalen;
1497 #ifdef DUMP_MESSAGES
1498 dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
1499 #endif
1501 if (bytes) {
1502 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1503 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1504 return -1;
1508 bytes = 0;
1509 /* Send subroutines */
1510 for (x = 0; x < scr->numsubs; x++) {
1511 if (bytes + scr->subs[x].datalen > 253) {
1512 /* Send what we've collected so far */
1513 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1514 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1515 return -1;
1517 bytes =0;
1519 memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
1520 bytes += scr->subs[x].datalen;
1521 #ifdef DUMP_MESSAGES
1522 dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
1523 #endif
1525 if (bytes) {
1526 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1527 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1528 return -1;
1532 bytes = 0;
1533 bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
1534 bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
1535 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
1536 return -1;
1537 if (ast_adsi_end_download(chan)) {
1538 /* Download failed for some reason */
1539 ast_verb(3, "Download attempt failed\n");
1540 ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
1541 ast_free(scr);
1542 return -1;
1544 ast_free(scr);
1545 ast_adsi_unload_session(chan);
1546 return 0;
1549 static int adsi_exec(struct ast_channel *chan, void *data)
1551 int res = 0;
1553 if (ast_strlen_zero(data))
1554 data = "asterisk.adsi";
1556 if (!ast_adsi_available(chan)) {
1557 ast_verb(3, "ADSI Unavailable on CPE. Not bothering to try.\n");
1558 } else {
1559 ast_verb(3, "ADSI Available on CPE. Attempting Upload.\n");
1560 res = adsi_prog(chan, data);
1563 return res;
1566 static int unload_module(void)
1568 return ast_unregister_application(app);
1571 static int load_module(void)
1573 if (ast_register_application(app, adsi_exec, synopsis, descrip))
1574 return AST_MODULE_LOAD_FAILURE;
1575 return AST_MODULE_LOAD_SUCCESS;
1578 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");