create the IAX2 processing threads as background threads so they will use smaller...
[asterisk-bristuff.git] / apps / app_adsiprog.c
blobc61f6bf3fb657736a0c78ae8a441c4f1403359d5
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 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <sys/types.h>
33 #include <netinet/in.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <errno.h>
42 #include "asterisk/file.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/adsi.h"
48 #include "asterisk/options.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/lock.h"
52 static char *app = "ADSIProg";
54 static char *synopsis = "Load Asterisk ADSI Scripts into phone";
56 /* #define DUMP_MESSAGES */
58 static char *descrip =
59 " ADSIProg(script): This application programs an ADSI Phone with the given\n"
60 "script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
62 struct adsi_event {
63 int id;
64 char *name;
67 static struct adsi_event events[] = {
68 { 1, "CALLERID" },
69 { 2, "VMWI" },
70 { 3, "NEARANSWER" },
71 { 4, "FARANSWER" },
72 { 5, "ENDOFRING" },
73 { 6, "IDLE" },
74 { 7, "OFFHOOK" },
75 { 8, "CIDCW" },
76 { 9, "BUSY" },
77 { 10, "FARRING" },
78 { 11, "DIALTONE" },
79 { 12, "RECALL" },
80 { 13, "MESSAGE" },
81 { 14, "REORDER" },
82 { 15, "DISTINCTIVERING" },
83 { 16, "RING" },
84 { 17, "REMINDERRING" },
85 { 18, "SPECIALRING" },
86 { 19, "CODEDRING" },
87 { 20, "TIMER" },
88 { 21, "INUSE" },
89 { 22, "EVENT22" },
90 { 23, "EVENT23" },
91 { 24, "CPEID" },
94 static struct adsi_event justify[] = {
95 { 0, "CENTER" },
96 { 1, "RIGHT" },
97 { 2, "LEFT" },
98 { 3, "INDENT" },
101 #define STATE_NORMAL 0
102 #define STATE_INKEY 1
103 #define STATE_INSUB 2
104 #define STATE_INIF 3
106 #define MAX_RET_CODE 20
107 #define MAX_SUB_LEN 255
108 #define MAX_MAIN_LEN 1600
110 #define ARG_STRING (1 << 0)
111 #define ARG_NUMBER (1 << 1)
113 struct adsi_soft_key {
114 char vname[40]; /* Which "variable" is associated with it */
115 int retstrlen; /* Length of return string */
116 int initlen; /* initial length */
117 int id;
118 int defined;
119 char retstr[80]; /* Return string data */
122 struct adsi_subscript {
123 char vname[40];
124 int id;
125 int defined;
126 int datalen;
127 int inscount;
128 int ifinscount;
129 char *ifdata;
130 char data[2048];
133 struct adsi_state {
134 char vname[40];
135 int id;
138 struct adsi_flag {
139 char vname[40];
140 int id;
143 struct adsi_display {
144 char vname[40];
145 int id;
146 char data[70];
147 int datalen;
150 struct adsi_script {
151 int state;
152 int numkeys;
153 int numsubs;
154 int numstates;
155 int numdisplays;
156 int numflags;
157 struct adsi_soft_key *key;
158 struct adsi_subscript *sub;
159 /* Pre-defined displays */
160 struct adsi_display displays[63];
161 /* ADSI States 1 (initial) - 254 */
162 struct adsi_state states[256];
163 /* Keys 2-63 */
164 struct adsi_soft_key keys[62];
165 /* Subscripts 0 (main) to 127 */
166 struct adsi_subscript subs[128];
167 /* Flags 1-7 */
168 struct adsi_flag flags[7];
170 /* Stuff from adsi script */
171 unsigned char sec[5];
172 char desc[19];
173 unsigned char fdn[5];
174 int ver;
178 static int process_token(void *out, char *src, int maxlen, int argtype)
180 if ((strlen(src) > 1) && src[0] == '\"') {
181 /* This is a quoted string */
182 if (!(argtype & ARG_STRING))
183 return -1;
184 src++;
185 /* Don't take more than what's there */
186 if (maxlen > strlen(src) - 1)
187 maxlen = strlen(src) - 1;
188 memcpy(out, src, maxlen);
189 ((char *)out)[maxlen] = '\0';
190 } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
191 if (!(argtype & ARG_NUMBER))
192 return -1;
193 /* Octal value */
194 if (sscanf(src, "%o", (int *)out) != 1)
195 return -1;
196 if (argtype & ARG_STRING) {
197 /* Convert */
198 *((unsigned int *)out) = htonl(*((unsigned int *)out));
200 } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
201 if (!(argtype & ARG_NUMBER))
202 return -1;
203 /* Hex value */
204 if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
205 return -1;
206 if (argtype & ARG_STRING) {
207 /* Convert */
208 *((unsigned int *)out) = htonl(*((unsigned int *)out));
210 } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
211 if (!(argtype & ARG_NUMBER))
212 return -1;
213 /* Hex value */
214 if (sscanf(src, "%d", (int *)out) != 1)
215 return -1;
216 if (argtype & ARG_STRING) {
217 /* Convert */
218 *((unsigned int *)out) = htonl(*((unsigned int *)out));
220 } else
221 return -1;
222 return 0;
225 static char *get_token(char **buf, char *script, int lineno)
227 char *tmp = *buf;
228 char *keyword;
229 int quoted = 0;
230 /* Advance past any white space */
231 while(*tmp && (*tmp < 33))
232 tmp++;
233 if (!*tmp)
234 return NULL;
235 keyword = tmp;
236 while(*tmp && ((*tmp > 32) || quoted)) {
237 if (*tmp == '\"') {
238 quoted = !quoted;
240 tmp++;
242 if (quoted) {
243 ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
244 return NULL;
246 *tmp = '\0';
247 tmp++;
248 while(*tmp && (*tmp < 33))
249 tmp++;
250 /* Note where we left off */
251 *buf = tmp;
252 return keyword;
255 static char *validdtmf = "123456789*0#ABCD";
257 static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
259 char dtmfstr[80];
260 char *a;
261 int bytes=0;
262 a = get_token(&args, script, lineno);
263 if (!a) {
264 ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
265 return 0;
267 if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
268 ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
269 return 0;
271 a = dtmfstr;
272 while(*a) {
273 if (strchr(validdtmf, *a)) {
274 *buf = *a;
275 buf++;
276 bytes++;
277 } else
278 ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
279 a++;
281 return bytes;
284 static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
286 char *page;
287 char *gline;
288 int line;
289 unsigned char cmd;
290 page = get_token(&args, script, lineno);
291 gline = get_token(&args, script, lineno);
292 if (!page || !gline) {
293 ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
294 return 0;
296 if (!strcasecmp(page, "INFO")) {
297 cmd = 0;
298 } else if (!strcasecmp(page, "COMM")) {
299 cmd = 0x80;
300 } else {
301 ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
302 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;
308 cmd |= line;
309 buf[0] = 0x8b;
310 buf[1] = cmd;
311 return 2;
314 static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
316 char *dir;
317 char *gline;
318 int line;
319 unsigned char cmd;
320 dir = get_token(&args, script, lineno);
321 gline = get_token(&args, script, lineno);
322 if (!dir || !gline) {
323 ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
324 return 0;
326 if (!strcasecmp(dir, "UP")) {
327 cmd = 0;
328 } else if (!strcasecmp(dir, "DOWN")) {
329 cmd = 0x20;
330 } else {
331 ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
332 return 0;
334 if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
335 ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
336 return 0;
338 cmd |= line;
339 buf[0] = 0x8c;
340 buf[1] = cmd;
341 return 2;
344 static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
346 char *gtime;
347 int ms;
348 gtime = get_token(&args, script, lineno);
349 if (!gtime) {
350 ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
351 return 0;
353 if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
354 ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
355 return 0;
357 buf[0] = 0x90;
358 if (id == 11)
359 buf[1] = ms / 100;
360 else
361 buf[1] = ms / 10;
362 return 2;
365 static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
367 char *gstate;
368 int state;
369 gstate = get_token(&args, script, lineno);
370 if (!gstate) {
371 ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
372 return 0;
374 if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
375 ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
376 return 0;
378 buf[0] = id;
379 buf[1] = state;
380 return 2;
383 static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
385 char *tok;
386 tok = get_token(&args, script, lineno);
387 if (tok)
388 ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
390 buf[0] = id;
391 /* For some reason the clear code is different slightly */
392 if (id == 7)
393 buf[1] = 0x10;
394 else
395 buf[1] = 0x00;
396 return 2;
399 static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
401 int x;
402 for (x=0;x<state->numflags;x++)
403 if (!strcasecmp(state->flags[x].vname, name))
404 return &state->flags[x];
405 /* Return now if we're not allowed to create */
406 if (!create)
407 return NULL;
408 if (state->numflags > 6) {
409 ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
410 return NULL;
412 ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
413 state->flags[state->numflags].id = state->numflags + 1;
414 state->numflags++;
415 return &state->flags[state->numflags-1];
418 static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
420 char *tok;
421 char sname[80];
422 struct adsi_flag *flag;
423 tok = get_token(&args, script, lineno);
424 if (!tok) {
425 ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
426 return 0;
428 if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
429 ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
430 return 0;
432 flag = getflagbyname(state, sname, script, lineno, 0);
433 if (!flag) {
434 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
435 return 0;
437 buf[0] = id;
438 buf[1] = ((flag->id & 0x7) << 4) | 1;
439 return 2;
442 static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
444 char *tok;
445 struct adsi_flag *flag;
446 char sname[80];
447 tok = get_token(&args, script, lineno);
448 if (!tok) {
449 ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
450 return 0;
452 if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
453 ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
454 return 0;
456 flag = getflagbyname(state, sname, script, lineno, 0);
457 if (!flag) {
458 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
459 return 0;
461 buf[0] = id;
462 buf[1] = ((flag->id & 0x7) << 4);
463 return 2;
466 static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
468 char *tok;
469 int secs;
470 tok = get_token(&args, script, lineno);
471 if (!tok) {
472 ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
473 return 0;
475 if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
476 ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
477 return 0;
479 buf[0] = id;
480 buf[1] = 0x1;
481 buf[2] = secs;
482 return 3;
485 static int geteventbyname(char *name)
487 int x;
488 for (x=0;x<sizeof(events) / sizeof(events[0]); x++) {
489 if (!strcasecmp(events[x].name, name))
490 return events[x].id;
492 return 0;
495 static int getjustifybyname(char *name)
497 int x;
498 for (x=0;x<sizeof(justify) / sizeof(justify[0]); x++) {
499 if (!strcasecmp(justify[x].name, name))
500 return justify[x].id;
502 return -1;
505 static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
507 int x;
508 for (x=0;x<state->numkeys;x++)
509 if (!strcasecmp(state->keys[x].vname, name))
510 return &state->keys[x];
511 if (state->numkeys > 61) {
512 ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
513 return NULL;
515 ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
516 state->keys[state->numkeys].id = state->numkeys + 2;
517 state->numkeys++;
518 return &state->keys[state->numkeys-1];
521 static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
523 int x;
524 for (x=0;x<state->numsubs;x++)
525 if (!strcasecmp(state->subs[x].vname, name))
526 return &state->subs[x];
527 if (state->numsubs > 127) {
528 ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
529 return NULL;
531 ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
532 state->subs[state->numsubs].id = state->numsubs;
533 state->numsubs++;
534 return &state->subs[state->numsubs-1];
537 static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
539 int x;
540 for (x=0;x<state->numstates;x++)
541 if (!strcasecmp(state->states[x].vname, name))
542 return &state->states[x];
543 /* Return now if we're not allowed to create */
544 if (!create)
545 return NULL;
546 if (state->numstates > 253) {
547 ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
548 return NULL;
550 ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
551 state->states[state->numstates].id = state->numstates + 1;
552 state->numstates++;
553 return &state->states[state->numstates-1];
556 static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
558 int x;
559 for (x=0;x<state->numdisplays;x++)
560 if (!strcasecmp(state->displays[x].vname, name))
561 return &state->displays[x];
562 /* Return now if we're not allowed to create */
563 if (!create)
564 return NULL;
565 if (state->numdisplays > 61) {
566 ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
567 return NULL;
569 ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
570 state->displays[state->numdisplays].id = state->numdisplays + 1;
571 state->numdisplays++;
572 return &state->displays[state->numdisplays-1];
575 static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
577 char *tok;
578 char newkey[80];
579 int bytes;
580 unsigned char keyid[6];
581 int x;
582 int flagid=0;
583 struct adsi_soft_key *key;
584 struct adsi_flag *flag;
586 for (x=0;x<7;x++) {
587 /* Up to 6 key arguments */
588 tok = get_token(&args, script, lineno);
589 if (!tok)
590 break;
591 if (!strcasecmp(tok, "UNLESS")) {
592 /* Check for trailing UNLESS flag */
593 tok = get_token(&args, script, lineno);
594 if (!tok) {
595 ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
596 } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
597 ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
598 } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) {
599 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
600 } else
601 flagid = flag->id;
602 if ((tok = get_token(&args, script, lineno)))
603 ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
604 break;
606 if (x > 5) {
607 ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
608 break;
610 if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
611 ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
612 continue;
615 key = getkeybyname(state, newkey, script, lineno);
616 if (!key)
617 break;
618 keyid[x] = key->id;
620 buf[0] = id;
621 buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
622 for (bytes=0;bytes<x;bytes++) {
623 buf[bytes + 2] = keyid[bytes];
625 return 2 + x;
628 static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
630 char *tok;
631 char dispname[80];
632 int line=0;
633 int flag=0;
634 int cmd = 3;
635 struct adsi_display *disp;
637 /* Get display */
638 tok = get_token(&args, script, lineno);
639 if (!tok || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
640 ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
641 return 0;
643 disp = getdisplaybyname(state, dispname, script, lineno, 0);
644 if (!disp) {
645 ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
646 return 0;
649 tok = get_token(&args, script, lineno);
650 if (!tok || strcasecmp(tok, "AT")) {
651 ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
652 return 0;
654 /* Get line number */
655 tok = get_token(&args, script, lineno);
656 if (!tok || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
657 ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
658 return 0;
660 tok = get_token(&args, script, lineno);
661 if (tok && !strcasecmp(tok, "NOUPDATE")) {
662 cmd = 1;
663 tok = get_token(&args, script, lineno);
665 if (tok && !strcasecmp(tok, "UNLESS")) {
666 /* Check for trailing UNLESS flag */
667 tok = get_token(&args, script, lineno);
668 if (!tok) {
669 ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
670 } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) {
671 ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
673 if ((tok = get_token(&args, script, lineno)))
674 ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
677 buf[0] = id;
678 buf[1] = (cmd << 6) | (disp->id & 0x3f);
679 buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
680 return 3;
683 static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
685 char *tok;
686 tok = get_token(&args, script, lineno);
687 if (tok)
688 ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
690 buf[0] = id;
691 buf[1] = 0x00;
692 return 2;
695 static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
697 char *tok;
698 tok = get_token(&args, script, lineno);
699 if (tok)
700 ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
702 buf[0] = id;
703 buf[1] = 0x7;
704 return 2;
707 static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
709 char *tok;
710 tok = get_token(&args, script, lineno);
711 if (tok)
712 ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
714 buf[0] = id;
715 buf[1] = 0;
716 return 2;
719 static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
721 char *tok;
722 tok = get_token(&args, script, lineno);
723 if (tok)
724 ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
726 buf[0] = id;
727 buf[1] = 0xf;
728 return 2;
731 static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
733 char *tok;
734 char subscript[80];
735 struct adsi_subscript *sub;
736 tok = get_token(&args, script, lineno);
737 if (!tok) {
738 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
739 return 0;
741 if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
742 ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
743 return 0;
745 sub = getsubbyname(state, subscript, script, lineno);
746 if (!sub)
747 return 0;
748 buf[0] = 0x9d;
749 buf[1] = sub->id;
750 return 2;
753 static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
755 char *tok;
756 char subscript[80];
757 char sname[80];
758 int sawin=0;
759 int event;
760 int snums[8];
761 int scnt = 0;
762 int x;
763 struct adsi_subscript *sub;
764 tok = get_token(&args, script, lineno);
765 if (!tok) {
766 ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
767 return 0;
769 event = geteventbyname(tok);
770 if (event < 1) {
771 ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
772 return 0;
774 tok = get_token(&args, script, lineno);
775 while ((!sawin && !strcasecmp(tok, "IN")) ||
776 (sawin && !strcasecmp(tok, "OR"))) {
777 sawin = 1;
778 if (scnt > 7) {
779 ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
780 return 0;
782 /* Process 'in' things */
783 tok = get_token(&args, script, lineno);
784 if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
785 ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
786 return 0;
788 if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
789 ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
790 return 0;
792 scnt++;
793 tok = get_token(&args, script, lineno);
794 if (!tok)
795 break;
797 if (!tok || strcasecmp(tok, "GOTO")) {
798 if (!tok)
799 tok = "<nothing>";
800 if (sawin)
801 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
802 else
803 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
805 tok = get_token(&args, script, lineno);
806 if (!tok) {
807 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
808 return 0;
810 if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
811 ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
812 return 0;
814 sub = getsubbyname(state, subscript, script, lineno);
815 if (!sub)
816 return 0;
817 buf[0] = 8;
818 buf[1] = event;
819 buf[2] = sub->id | 0x80;
820 for (x=0;x<scnt;x++)
821 buf[3 + x] = snums[x];
822 return 3 + scnt;
825 struct adsi_key_cmd {
826 char *name;
827 int id;
828 int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
831 static struct adsi_key_cmd kcmds[] = {
832 { "SENDDTMF", 0, send_dtmf },
833 /* Encoded DTMF would go here */
834 { "ONHOOK", 0x81 },
835 { "OFFHOOK", 0x82 },
836 { "FLASH", 0x83 },
837 { "WAITDIALTONE", 0x84 },
838 /* Send line number */
839 { "BLANK", 0x86 },
840 { "SENDCHARS", 0x87 },
841 { "CLEARCHARS", 0x88 },
842 { "BACKSPACE", 0x89 },
843 /* Tab column */
844 { "GOTOLINE", 0x8b, goto_line },
845 { "GOTOLINEREL", 0x8c, goto_line_rel },
846 { "PAGEUP", 0x8d },
847 { "PAGEDOWN", 0x8e },
848 /* Extended DTMF */
849 { "DELAY", 0x90, send_delay },
850 { "DIALPULSEONE", 0x91 },
851 { "DATAMODE", 0x92 },
852 { "VOICEMODE", 0x93 },
853 /* Display call buffer 'n' */
854 /* Clear call buffer 'n' */
855 { "CLEARCB1", 0x95, clearcbone },
856 { "DIGITCOLLECT", 0x96, digitcollect },
857 { "DIGITDIRECT", 0x96, digitdirect },
858 { "CLEAR", 0x97 },
859 { "SHOWDISPLAY", 0x98, showdisplay },
860 { "CLEARDISPLAY", 0x98, cleardisplay },
861 { "SHOWKEYS", 0x99, showkeys },
862 { "SETSTATE", 0x9a, set_state },
863 { "TIMERSTART", 0x9b, starttimer },
864 { "TIMERCLEAR", 0x9b, cleartimer },
865 { "SETFLAG", 0x9c, setflag },
866 { "CLEARFLAG", 0x9c, clearflag },
867 { "GOTO", 0x9d, subscript },
868 { "EVENT22", 0x9e },
869 { "EVENT23", 0x9f },
870 { "EXIT", 0xa0 },
873 static struct adsi_key_cmd opcmds[] = {
875 /* 1 - Branch on event -- handled specially */
876 { "SHOWKEYS", 2, showkeys },
877 /* Display Control */
878 { "SHOWDISPLAY", 3, showdisplay },
879 { "CLEARDISPLAY", 3, cleardisplay },
880 { "CLEAR", 5 },
881 { "SETSTATE", 6, set_state },
882 { "TIMERSTART", 7, starttimer },
883 { "TIMERCLEAR", 7, cleartimer },
884 { "ONEVENT", 8, onevent },
885 /* 9 - Subroutine label, treated specially */
886 { "SETFLAG", 10, setflag },
887 { "CLEARFLAG", 10, clearflag },
888 { "DELAY", 11, send_delay },
889 { "EXIT", 12 },
893 static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
895 int x;
896 char *unused;
897 int res;
898 for (x=0;x<sizeof(kcmds) / sizeof(kcmds[0]);x++) {
899 if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
900 if (kcmds[x].add_args) {
901 res = kcmds[x].add_args(key->retstr + key->retstrlen,
902 code, kcmds[x].id, args, state, script, lineno);
903 if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
904 key->retstrlen += res;
905 else
906 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);
907 } else {
908 if ((unused = get_token(&args, script, lineno)))
909 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
910 if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
911 key->retstr[key->retstrlen] = kcmds[x].id;
912 key->retstrlen++;
913 } else
914 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);
916 return 0;
919 return -1;
922 static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
924 int x;
925 char *unused;
926 int res;
927 int max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
928 for (x=0;x<sizeof(opcmds) / sizeof(opcmds[0]);x++) {
929 if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
930 if (opcmds[x].add_args) {
931 res = opcmds[x].add_args(sub->data + sub->datalen,
932 code, opcmds[x].id, args, state, script, lineno);
933 if ((sub->datalen + res + 1) <= max)
934 sub->datalen += res;
935 else {
936 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);
937 return -1;
939 } else {
940 if ((unused = get_token(&args, script, lineno)))
941 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
942 if ((sub->datalen + 2) <= max) {
943 sub->data[sub->datalen] = opcmds[x].id;
944 sub->datalen++;
945 } else {
946 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);
947 return -1;
950 /* Separate commands with 0xff */
951 sub->data[sub->datalen] = 0xff;
952 sub->datalen++;
953 sub->inscount++;
954 return 0;
957 return -1;
960 static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
962 char *keyword;
963 char *args;
964 char vname[256];
965 char tmp[80];
966 char tmp2[80];
967 int lrci;
968 int wi;
969 int event;
970 struct adsi_display *disp;
971 struct adsi_subscript *newsub;
972 /* Find the first keyword */
973 keyword = get_token(&buf, script, lineno);
974 if (!keyword)
975 return 0;
976 switch(state->state) {
977 case STATE_NORMAL:
978 if (!strcasecmp(keyword, "DESCRIPTION")) {
979 args = get_token(&buf, script, lineno);
980 if (args) {
981 if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
982 ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
983 } else
984 ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
985 } else if (!strcasecmp(keyword, "VERSION")) {
986 args = get_token(&buf, script, lineno);
987 if (args) {
988 if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
989 ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
990 } else
991 ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
992 } else if (!strcasecmp(keyword, "SECURITY")) {
993 args = get_token(&buf, script, lineno);
994 if (args) {
995 if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
996 ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
997 } else
998 ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
999 } else if (!strcasecmp(keyword, "FDN")) {
1000 args = get_token(&buf, script, lineno);
1001 if (args) {
1002 if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
1003 ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
1004 } else
1005 ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
1006 } else if (!strcasecmp(keyword, "KEY")) {
1007 args = get_token(&buf, script, lineno);
1008 if (!args) {
1009 ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
1010 break;
1012 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1013 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1014 break;
1016 state->key = getkeybyname(state, vname, script, lineno);
1017 if (!state->key) {
1018 ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
1019 break;
1021 if (state->key->defined) {
1022 ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
1023 break;
1025 args = get_token(&buf, script, lineno);
1026 if (!args || strcasecmp(args, "IS")) {
1027 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1028 break;
1030 args = get_token(&buf, script, lineno);
1031 if (!args) {
1032 ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
1033 break;
1035 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1036 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
1037 break;
1039 args = get_token(&buf, script, lineno);
1040 if (args) {
1041 if (strcasecmp(args, "OR")) {
1042 ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
1043 break;
1045 args = get_token(&buf, script, lineno);
1046 if (!args) {
1047 ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
1048 break;
1050 if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
1051 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
1052 break;
1054 } else {
1055 ast_copy_string(tmp2, tmp, sizeof(tmp2));
1057 if (strlen(tmp2) > 18) {
1058 ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
1059 tmp2[18] = '\0';
1061 if (strlen(tmp) > 7) {
1062 ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
1063 tmp[7] = '\0';
1065 /* Setup initial stuff */
1066 state->key->retstr[0] = 128;
1067 /* 1 has the length */
1068 state->key->retstr[2] = state->key->id;
1069 /* Put the Full name in */
1070 memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
1071 /* Update length */
1072 state->key->retstrlen = strlen(tmp2) + 3;
1073 /* Put trailing 0xff */
1074 state->key->retstr[state->key->retstrlen++] = 0xff;
1075 /* Put the short name */
1076 memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
1077 /* Update length */
1078 state->key->retstrlen += strlen(tmp);
1079 /* Put trailing 0xff */
1080 state->key->retstr[state->key->retstrlen++] = 0xff;
1081 /* Record initial length */
1082 state->key->initlen = state->key->retstrlen;
1083 state->state = STATE_INKEY;
1084 } else if (!strcasecmp(keyword, "SUB")) {
1085 args = get_token(&buf, script, lineno);
1086 if (!args) {
1087 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1088 break;
1090 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1091 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1092 break;
1094 state->sub = getsubbyname(state, vname, script, lineno);
1095 if (!state->sub) {
1096 ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
1097 break;
1099 if (state->sub->defined) {
1100 ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
1101 break;
1103 /* Setup sub */
1104 state->sub->data[0] = 130;
1105 /* 1 is the length */
1106 state->sub->data[2] = 0x0; /* Clear extensibility bit */
1107 state->sub->datalen = 3;
1108 if (state->sub->id) {
1109 /* If this isn't the main subroutine, make a subroutine label for it */
1110 state->sub->data[3] = 9;
1111 state->sub->data[4] = state->sub->id;
1112 /* 5 is length */
1113 state->sub->data[6] = 0xff;
1114 state->sub->datalen = 7;
1116 args = get_token(&buf, script, lineno);
1117 if (!args || strcasecmp(args, "IS")) {
1118 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1119 break;
1121 state->state = STATE_INSUB;
1122 } else if (!strcasecmp(keyword, "STATE")) {
1123 args = get_token(&buf, script, lineno);
1124 if (!args) {
1125 ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
1126 break;
1128 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1129 ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
1130 break;
1132 if (getstatebyname(state, vname, script, lineno, 0)) {
1133 ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
1134 break;
1136 getstatebyname(state, vname, script, lineno, 1);
1137 } else if (!strcasecmp(keyword, "FLAG")) {
1138 args = get_token(&buf, script, lineno);
1139 if (!args) {
1140 ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
1141 break;
1143 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1144 ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
1145 break;
1147 if (getflagbyname(state, vname, script, lineno, 0)) {
1148 ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
1149 break;
1151 getflagbyname(state, vname, script, lineno, 1);
1152 } else if (!strcasecmp(keyword, "DISPLAY")) {
1153 lrci = 0;
1154 wi = 0;
1155 args = get_token(&buf, script, lineno);
1156 if (!args) {
1157 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1158 break;
1160 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1161 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1162 break;
1164 if (getdisplaybyname(state, vname, script, lineno, 0)) {
1165 ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
1166 break;
1168 disp = getdisplaybyname(state, vname, script, lineno, 1);
1169 if (!disp)
1170 break;
1171 args = get_token(&buf, script, lineno);
1172 if (!args || strcasecmp(args, "IS")) {
1173 ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
1174 break;
1176 args = get_token(&buf, script, lineno);
1177 if (!args) {
1178 ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
1179 break;
1181 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1182 ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
1183 break;
1185 if (strlen(tmp) > 20) {
1186 ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
1187 tmp[20] = '\0';
1189 memcpy(disp->data + 5, tmp, strlen(tmp));
1190 disp->datalen = strlen(tmp) + 5;
1191 disp->data[disp->datalen++] = 0xff;
1193 args = get_token(&buf, script, lineno);
1194 if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1195 /* Got a column two */
1196 if (strlen(tmp) > 20) {
1197 ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
1198 tmp[20] = '\0';
1200 memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
1201 disp->datalen += strlen(tmp);
1202 args = get_token(&buf, script, lineno);
1204 while(args) {
1205 if (!strcasecmp(args, "JUSTIFY")) {
1206 args = get_token(&buf, script, lineno);
1207 if (!args) {
1208 ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
1209 break;
1211 lrci = getjustifybyname(args);
1212 if (lrci < 0) {
1213 ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
1214 break;
1216 } else if (!strcasecmp(args, "WRAP")) {
1217 wi = 0x80;
1218 } else {
1219 ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
1220 break;
1222 args = get_token(&buf, script, lineno);
1224 if (args) {
1225 /* Something bad happened */
1226 break;
1228 disp->data[0] = 129;
1229 disp->data[1] = disp->datalen - 2;
1230 disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
1231 disp->data[3] = wi;
1232 disp->data[4] = 0xff;
1233 } else {
1234 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
1236 break;
1237 case STATE_INKEY:
1238 if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
1239 if (!strcasecmp(keyword, "ENDKEY")) {
1240 /* Return to normal operation and increment current key */
1241 state->state = STATE_NORMAL;
1242 state->key->defined = 1;
1243 state->key->retstr[1] = state->key->retstrlen - 2;
1244 state->key = NULL;
1245 } else {
1246 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
1249 break;
1250 case STATE_INIF:
1251 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1252 if (!strcasecmp(keyword, "ENDIF")) {
1253 /* Return to normal SUB operation and increment current key */
1254 state->state = STATE_INSUB;
1255 state->sub->defined = 1;
1256 /* Store the proper number of instructions */
1257 state->sub->ifdata[2] = state->sub->ifinscount;
1258 } else if (!strcasecmp(keyword, "GOTO")) {
1259 args = get_token(&buf, script, lineno);
1260 if (!args) {
1261 ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
1262 break;
1264 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1265 ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
1266 break;
1268 newsub = getsubbyname(state, tmp, script, lineno);
1269 if (!newsub)
1270 break;
1271 /* Somehow you use GOTO to go to another place */
1272 state->sub->data[state->sub->datalen++] = 0x8;
1273 state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
1274 state->sub->data[state->sub->datalen++] = newsub->id;
1275 /* Terminate */
1276 state->sub->data[state->sub->datalen++] = 0xff;
1277 /* Increment counters */
1278 state->sub->inscount++;
1279 state->sub->ifinscount++;
1280 } else {
1281 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
1283 } else
1284 state->sub->ifinscount++;
1285 break;
1286 case STATE_INSUB:
1287 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1288 if (!strcasecmp(keyword, "ENDSUB")) {
1289 /* Return to normal operation and increment current key */
1290 state->state = STATE_NORMAL;
1291 state->sub->defined = 1;
1292 /* Store the proper length */
1293 state->sub->data[1] = state->sub->datalen - 2;
1294 if (state->sub->id) {
1295 /* if this isn't main, store number of instructions, too */
1296 state->sub->data[5] = state->sub->inscount;
1298 state->sub = NULL;
1299 } else if (!strcasecmp(keyword, "IFEVENT")) {
1300 args = get_token(&buf, script, lineno);
1301 if (!args) {
1302 ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
1303 break;
1305 event = geteventbyname(args);
1306 if (event < 1) {
1307 ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
1308 break;
1310 args = get_token(&buf, script, lineno);
1311 if (!args || strcasecmp(args, "THEN")) {
1312 ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
1313 break;
1315 state->sub->ifinscount = 0;
1316 state->sub->ifdata = state->sub->data +
1317 state->sub->datalen;
1318 /* Reserve header and insert op codes */
1319 state->sub->ifdata[0] = 0x1;
1320 state->sub->ifdata[1] = event;
1321 /* 2 is for the number of instructions */
1322 state->sub->ifdata[3] = 0xff;
1323 state->sub->datalen += 4;
1324 /* Update Subscript instruction count */
1325 state->sub->inscount++;
1326 state->state = STATE_INIF;
1327 } else {
1328 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
1331 break;
1332 default:
1333 ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
1335 return 0;
1338 static struct adsi_script *compile_script(char *script)
1340 FILE *f;
1341 char fn[256];
1342 char buf[256];
1343 char *c;
1344 int lineno=0;
1345 int x, err;
1346 struct adsi_script *scr;
1347 if (script[0] == '/')
1348 ast_copy_string(fn, script, sizeof(fn));
1349 else
1350 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script);
1351 f = fopen(fn, "r");
1352 if (!f) {
1353 ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
1354 return NULL;
1356 if (!(scr = ast_calloc(1, sizeof(*scr)))) {
1357 fclose(f);
1358 return NULL;
1360 /* Create "main" as first subroutine */
1361 getsubbyname(scr, "main", NULL, 0);
1362 while(!feof(f)) {
1363 fgets(buf, sizeof(buf), f);
1364 if (!feof(f)) {
1365 lineno++;
1366 /* Trim off trailing return */
1367 buf[strlen(buf) - 1] = '\0';
1368 c = strchr(buf, ';');
1369 /* Strip comments */
1370 if (c)
1371 *c = '\0';
1372 if (!ast_strlen_zero(buf))
1373 adsi_process(scr, buf, script, lineno);
1376 fclose(f);
1377 /* Make sure we're in the main routine again */
1378 switch(scr->state) {
1379 case STATE_NORMAL:
1380 break;
1381 case STATE_INSUB:
1382 ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
1383 free(scr);
1384 return NULL;
1385 case STATE_INKEY:
1386 ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
1387 free(scr);
1388 return NULL;
1390 err = 0;
1392 /* Resolve all keys and record their lengths */
1393 for (x=0;x<scr->numkeys;x++) {
1394 if (!scr->keys[x].defined) {
1395 ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
1396 err++;
1400 /* Resolve all subs */
1401 for (x=0;x<scr->numsubs;x++) {
1402 if (!scr->subs[x].defined) {
1403 ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
1404 err++;
1406 if (x == (scr->numsubs - 1)) {
1407 /* Clear out extension bit on last message */
1408 scr->subs[x].data[2] = 0x80;
1412 if (err) {
1413 free(scr);
1414 return NULL;
1416 return scr;
1419 #ifdef DUMP_MESSAGES
1420 static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
1422 int x;
1423 printf("%s %s: [ ", type, vname);
1424 for (x=0;x<buflen;x++)
1425 printf("%02x ", buf[x]);
1426 printf("]\n");
1428 #endif
1430 static int adsi_prog(struct ast_channel *chan, char *script)
1432 struct adsi_script *scr;
1433 int x;
1434 unsigned char buf[1024];
1435 int bytes;
1436 scr = compile_script(script);
1437 if (!scr)
1438 return -1;
1440 /* Start an empty ADSI Session */
1441 if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
1442 return -1;
1444 /* Now begin the download attempt */
1445 if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
1446 /* User rejected us for some reason */
1447 if (option_verbose > 2)
1448 ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n");
1449 ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
1450 free(scr);
1451 return -1;
1454 bytes = 0;
1455 /* Start with key definitions */
1456 for (x=0;x<scr->numkeys;x++) {
1457 if (bytes + scr->keys[x].retstrlen > 253) {
1458 /* Send what we've collected so far */
1459 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1460 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1461 return -1;
1463 bytes =0;
1465 memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
1466 bytes += scr->keys[x].retstrlen;
1467 #ifdef DUMP_MESSAGES
1468 dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
1469 #endif
1471 if (bytes) {
1472 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1473 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1474 return -1;
1478 bytes = 0;
1479 /* Continue with the display messages */
1480 for (x=0;x<scr->numdisplays;x++) {
1481 if (bytes + scr->displays[x].datalen > 253) {
1482 /* Send what we've collected so far */
1483 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1484 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1485 return -1;
1487 bytes =0;
1489 memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
1490 bytes += scr->displays[x].datalen;
1491 #ifdef DUMP_MESSAGES
1492 dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
1493 #endif
1495 if (bytes) {
1496 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1497 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1498 return -1;
1502 bytes = 0;
1503 /* Send subroutines */
1504 for (x=0;x<scr->numsubs;x++) {
1505 if (bytes + scr->subs[x].datalen > 253) {
1506 /* Send what we've collected so far */
1507 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1508 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1509 return -1;
1511 bytes =0;
1513 memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
1514 bytes += scr->subs[x].datalen;
1515 #ifdef DUMP_MESSAGES
1516 dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
1517 #endif
1519 if (bytes) {
1520 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1521 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1522 return -1;
1527 bytes = 0;
1528 bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
1529 bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
1530 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
1531 return -1;
1532 if (ast_adsi_end_download(chan)) {
1533 /* Download failed for some reason */
1534 if (option_verbose > 2)
1535 ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n");
1536 ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
1537 free(scr);
1538 return -1;
1540 free(scr);
1541 ast_adsi_unload_session(chan);
1542 return 0;
1545 static int adsi_exec(struct ast_channel *chan, void *data)
1547 int res=0;
1548 struct ast_module_user *u;
1550 u = ast_module_user_add(chan);
1552 if (ast_strlen_zero(data))
1553 data = "asterisk.adsi";
1555 if (!ast_adsi_available(chan)) {
1556 if (option_verbose > 2)
1557 ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE. Not bothering to try.\n");
1558 } else {
1559 if (option_verbose > 2)
1560 ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE. Attempting Upload.\n");
1561 res = adsi_prog(chan, data);
1564 ast_module_user_remove(u);
1566 return res;
1569 static int unload_module(void)
1571 int res;
1573 ast_module_user_hangup_all();
1575 res = ast_unregister_application(app);
1578 return res;
1581 static int load_module(void)
1583 return ast_register_application(app, adsi_exec, synopsis, descrip);
1586 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");