fallback to standard English prompts properly when using new prompt directory layout
[asterisk-bristuff.git] / apps / app_adsiprog.c
blob40330152b03d79078657cb5cb2800ef310001b93
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 <sys/types.h>
37 #include <netinet/in.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <ctype.h>
43 #include <stdio.h>
44 #include <errno.h>
46 #include "asterisk/file.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/module.h"
51 #include "asterisk/adsi.h"
52 #include "asterisk/options.h"
53 #include "asterisk/utils.h"
54 #include "asterisk/lock.h"
56 static char *app = "ADSIProg";
58 static char *synopsis = "Load Asterisk ADSI Scripts into phone";
60 /* #define DUMP_MESSAGES */
62 static char *descrip =
63 " ADSIProg(script): This application programs an ADSI Phone with the given\n"
64 "script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
66 struct adsi_event {
67 int id;
68 char *name;
71 static struct adsi_event events[] = {
72 { 1, "CALLERID" },
73 { 2, "VMWI" },
74 { 3, "NEARANSWER" },
75 { 4, "FARANSWER" },
76 { 5, "ENDOFRING" },
77 { 6, "IDLE" },
78 { 7, "OFFHOOK" },
79 { 8, "CIDCW" },
80 { 9, "BUSY" },
81 { 10, "FARRING" },
82 { 11, "DIALTONE" },
83 { 12, "RECALL" },
84 { 13, "MESSAGE" },
85 { 14, "REORDER" },
86 { 15, "DISTINCTIVERING" },
87 { 16, "RING" },
88 { 17, "REMINDERRING" },
89 { 18, "SPECIALRING" },
90 { 19, "CODEDRING" },
91 { 20, "TIMER" },
92 { 21, "INUSE" },
93 { 22, "EVENT22" },
94 { 23, "EVENT23" },
95 { 24, "CPEID" },
98 static struct adsi_event justify[] = {
99 { 0, "CENTER" },
100 { 1, "RIGHT" },
101 { 2, "LEFT" },
102 { 3, "INDENT" },
105 #define STATE_NORMAL 0
106 #define STATE_INKEY 1
107 #define STATE_INSUB 2
108 #define STATE_INIF 3
110 #define MAX_RET_CODE 20
111 #define MAX_SUB_LEN 255
112 #define MAX_MAIN_LEN 1600
114 #define ARG_STRING (1 << 0)
115 #define ARG_NUMBER (1 << 1)
117 struct adsi_soft_key {
118 char vname[40]; /* Which "variable" is associated with it */
119 int retstrlen; /* Length of return string */
120 int initlen; /* initial length */
121 int id;
122 int defined;
123 char retstr[80]; /* Return string data */
126 struct adsi_subscript {
127 char vname[40];
128 int id;
129 int defined;
130 int datalen;
131 int inscount;
132 int ifinscount;
133 char *ifdata;
134 char data[2048];
137 struct adsi_state {
138 char vname[40];
139 int id;
142 struct adsi_flag {
143 char vname[40];
144 int id;
147 struct adsi_display {
148 char vname[40];
149 int id;
150 char data[70];
151 int datalen;
154 struct adsi_script {
155 int state;
156 int numkeys;
157 int numsubs;
158 int numstates;
159 int numdisplays;
160 int numflags;
161 struct adsi_soft_key *key;
162 struct adsi_subscript *sub;
163 /* Pre-defined displays */
164 struct adsi_display displays[63];
165 /* ADSI States 1 (initial) - 254 */
166 struct adsi_state states[256];
167 /* Keys 2-63 */
168 struct adsi_soft_key keys[62];
169 /* Subscripts 0 (main) to 127 */
170 struct adsi_subscript subs[128];
171 /* Flags 1-7 */
172 struct adsi_flag flags[7];
174 /* Stuff from adsi script */
175 unsigned char sec[5];
176 char desc[19];
177 unsigned char fdn[5];
178 int ver;
182 static int process_token(void *out, char *src, int maxlen, int argtype)
184 if ((strlen(src) > 1) && src[0] == '\"') {
185 /* This is a quoted string */
186 if (!(argtype & ARG_STRING))
187 return -1;
188 src++;
189 /* Don't take more than what's there */
190 if (maxlen > strlen(src) - 1)
191 maxlen = strlen(src) - 1;
192 memcpy(out, src, maxlen);
193 ((char *)out)[maxlen] = '\0';
194 } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
195 if (!(argtype & ARG_NUMBER))
196 return -1;
197 /* Octal value */
198 if (sscanf(src, "%o", (int *)out) != 1)
199 return -1;
200 if (argtype & ARG_STRING) {
201 /* Convert */
202 *((unsigned int *)out) = htonl(*((unsigned int *)out));
204 } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
205 if (!(argtype & ARG_NUMBER))
206 return -1;
207 /* Hex value */
208 if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
209 return -1;
210 if (argtype & ARG_STRING) {
211 /* Convert */
212 *((unsigned int *)out) = htonl(*((unsigned int *)out));
214 } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
215 if (!(argtype & ARG_NUMBER))
216 return -1;
217 /* Hex value */
218 if (sscanf(src, "%d", (int *)out) != 1)
219 return -1;
220 if (argtype & ARG_STRING) {
221 /* Convert */
222 *((unsigned int *)out) = htonl(*((unsigned int *)out));
224 } else
225 return -1;
226 return 0;
229 static char *get_token(char **buf, char *script, int lineno)
231 char *tmp = *buf;
232 char *keyword;
233 int quoted = 0;
234 /* Advance past any white space */
235 while(*tmp && (*tmp < 33))
236 tmp++;
237 if (!*tmp)
238 return NULL;
239 keyword = tmp;
240 while(*tmp && ((*tmp > 32) || quoted)) {
241 if (*tmp == '\"') {
242 quoted = !quoted;
244 tmp++;
246 if (quoted) {
247 ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
248 return NULL;
250 *tmp = '\0';
251 tmp++;
252 while(*tmp && (*tmp < 33))
253 tmp++;
254 /* Note where we left off */
255 *buf = tmp;
256 return keyword;
259 static char *validdtmf = "123456789*0#ABCD";
261 static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
263 char dtmfstr[80];
264 char *a;
265 int bytes=0;
266 a = get_token(&args, script, lineno);
267 if (!a) {
268 ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
269 return 0;
271 if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
272 ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
273 return 0;
275 a = dtmfstr;
276 while(*a) {
277 if (strchr(validdtmf, *a)) {
278 *buf = *a;
279 buf++;
280 bytes++;
281 } else
282 ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
283 a++;
285 return bytes;
288 static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
290 char *page;
291 char *gline;
292 int line;
293 unsigned char cmd;
294 page = get_token(&args, script, lineno);
295 gline = get_token(&args, script, lineno);
296 if (!page || !gline) {
297 ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
298 return 0;
300 if (!strcasecmp(page, "INFO")) {
301 cmd = 0;
302 } else if (!strcasecmp(page, "COMM")) {
303 cmd = 0x80;
304 } else {
305 ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
306 return 0;
308 if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
309 ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
310 return 0;
312 cmd |= line;
313 buf[0] = 0x8b;
314 buf[1] = cmd;
315 return 2;
318 static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
320 char *dir;
321 char *gline;
322 int line;
323 unsigned char cmd;
324 dir = get_token(&args, script, lineno);
325 gline = get_token(&args, script, lineno);
326 if (!dir || !gline) {
327 ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
328 return 0;
330 if (!strcasecmp(dir, "UP")) {
331 cmd = 0;
332 } else if (!strcasecmp(dir, "DOWN")) {
333 cmd = 0x20;
334 } else {
335 ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
336 return 0;
338 if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
339 ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
340 return 0;
342 cmd |= line;
343 buf[0] = 0x8c;
344 buf[1] = cmd;
345 return 2;
348 static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
350 char *gtime;
351 int ms;
352 gtime = get_token(&args, script, lineno);
353 if (!gtime) {
354 ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
355 return 0;
357 if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
358 ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
359 return 0;
361 buf[0] = 0x90;
362 if (id == 11)
363 buf[1] = ms / 100;
364 else
365 buf[1] = ms / 10;
366 return 2;
369 static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
371 char *gstate;
372 int state;
373 gstate = get_token(&args, script, lineno);
374 if (!gstate) {
375 ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
376 return 0;
378 if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
379 ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
380 return 0;
382 buf[0] = id;
383 buf[1] = state;
384 return 2;
387 static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
389 char *tok;
390 tok = get_token(&args, script, lineno);
391 if (tok)
392 ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
394 buf[0] = id;
395 /* For some reason the clear code is different slightly */
396 if (id == 7)
397 buf[1] = 0x10;
398 else
399 buf[1] = 0x00;
400 return 2;
403 static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
405 int x;
406 for (x=0;x<state->numflags;x++)
407 if (!strcasecmp(state->flags[x].vname, name))
408 return &state->flags[x];
409 /* Return now if we're not allowed to create */
410 if (!create)
411 return NULL;
412 if (state->numflags > 6) {
413 ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
414 return NULL;
416 ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
417 state->flags[state->numflags].id = state->numflags + 1;
418 state->numflags++;
419 return &state->flags[state->numflags-1];
422 static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
424 char *tok;
425 char sname[80];
426 struct adsi_flag *flag;
427 tok = get_token(&args, script, lineno);
428 if (!tok) {
429 ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
430 return 0;
432 if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
433 ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
434 return 0;
436 flag = getflagbyname(state, sname, script, lineno, 0);
437 if (!flag) {
438 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
439 return 0;
441 buf[0] = id;
442 buf[1] = ((flag->id & 0x7) << 4) | 1;
443 return 2;
446 static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
448 char *tok;
449 struct adsi_flag *flag;
450 char sname[80];
451 tok = get_token(&args, script, lineno);
452 if (!tok) {
453 ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
454 return 0;
456 if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
457 ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
458 return 0;
460 flag = getflagbyname(state, sname, script, lineno, 0);
461 if (!flag) {
462 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
463 return 0;
465 buf[0] = id;
466 buf[1] = ((flag->id & 0x7) << 4);
467 return 2;
470 static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
472 char *tok;
473 int secs;
474 tok = get_token(&args, script, lineno);
475 if (!tok) {
476 ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
477 return 0;
479 if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
480 ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
481 return 0;
483 buf[0] = id;
484 buf[1] = 0x1;
485 buf[2] = secs;
486 return 3;
489 static int geteventbyname(char *name)
491 int x;
492 for (x=0;x<sizeof(events) / sizeof(events[0]); x++) {
493 if (!strcasecmp(events[x].name, name))
494 return events[x].id;
496 return 0;
499 static int getjustifybyname(char *name)
501 int x;
502 for (x=0;x<sizeof(justify) / sizeof(justify[0]); x++) {
503 if (!strcasecmp(justify[x].name, name))
504 return justify[x].id;
506 return -1;
509 static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
511 int x;
512 for (x=0;x<state->numkeys;x++)
513 if (!strcasecmp(state->keys[x].vname, name))
514 return &state->keys[x];
515 if (state->numkeys > 61) {
516 ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
517 return NULL;
519 ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
520 state->keys[state->numkeys].id = state->numkeys + 2;
521 state->numkeys++;
522 return &state->keys[state->numkeys-1];
525 static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
527 int x;
528 for (x=0;x<state->numsubs;x++)
529 if (!strcasecmp(state->subs[x].vname, name))
530 return &state->subs[x];
531 if (state->numsubs > 127) {
532 ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
533 return NULL;
535 ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
536 state->subs[state->numsubs].id = state->numsubs;
537 state->numsubs++;
538 return &state->subs[state->numsubs-1];
541 static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
543 int x;
544 for (x=0;x<state->numstates;x++)
545 if (!strcasecmp(state->states[x].vname, name))
546 return &state->states[x];
547 /* Return now if we're not allowed to create */
548 if (!create)
549 return NULL;
550 if (state->numstates > 253) {
551 ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
552 return NULL;
554 ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
555 state->states[state->numstates].id = state->numstates + 1;
556 state->numstates++;
557 return &state->states[state->numstates-1];
560 static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
562 int x;
563 for (x=0;x<state->numdisplays;x++)
564 if (!strcasecmp(state->displays[x].vname, name))
565 return &state->displays[x];
566 /* Return now if we're not allowed to create */
567 if (!create)
568 return NULL;
569 if (state->numdisplays > 61) {
570 ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
571 return NULL;
573 ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
574 state->displays[state->numdisplays].id = state->numdisplays + 1;
575 state->numdisplays++;
576 return &state->displays[state->numdisplays-1];
579 static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
581 char *tok;
582 char newkey[80];
583 int bytes;
584 unsigned char keyid[6];
585 int x;
586 int flagid=0;
587 struct adsi_soft_key *key;
588 struct adsi_flag *flag;
590 for (x=0;x<7;x++) {
591 /* Up to 6 key arguments */
592 tok = get_token(&args, script, lineno);
593 if (!tok)
594 break;
595 if (!strcasecmp(tok, "UNLESS")) {
596 /* Check for trailing UNLESS flag */
597 tok = get_token(&args, script, lineno);
598 if (!tok) {
599 ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
600 } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
601 ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
602 } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) {
603 ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
604 } else
605 flagid = flag->id;
606 if ((tok = get_token(&args, script, lineno)))
607 ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
608 break;
610 if (x > 5) {
611 ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
612 break;
614 if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
615 ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
616 continue;
619 key = getkeybyname(state, newkey, script, lineno);
620 if (!key)
621 break;
622 keyid[x] = key->id;
624 buf[0] = id;
625 buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
626 for (bytes=0;bytes<x;bytes++) {
627 buf[bytes + 2] = keyid[bytes];
629 return 2 + x;
632 static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
634 char *tok;
635 char dispname[80];
636 int line=0;
637 int flag=0;
638 int cmd = 3;
639 struct adsi_display *disp;
641 /* Get display */
642 tok = get_token(&args, script, lineno);
643 if (!tok || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
644 ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
645 return 0;
647 disp = getdisplaybyname(state, dispname, script, lineno, 0);
648 if (!disp) {
649 ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
650 return 0;
653 tok = get_token(&args, script, lineno);
654 if (!tok || strcasecmp(tok, "AT")) {
655 ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
656 return 0;
658 /* Get line number */
659 tok = get_token(&args, script, lineno);
660 if (!tok || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
661 ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
662 return 0;
664 tok = get_token(&args, script, lineno);
665 if (tok && !strcasecmp(tok, "NOUPDATE")) {
666 cmd = 1;
667 tok = get_token(&args, script, lineno);
669 if (tok && !strcasecmp(tok, "UNLESS")) {
670 /* Check for trailing UNLESS flag */
671 tok = get_token(&args, script, lineno);
672 if (!tok) {
673 ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
674 } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) {
675 ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
677 if ((tok = get_token(&args, script, lineno)))
678 ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
681 buf[0] = id;
682 buf[1] = (cmd << 6) | (disp->id & 0x3f);
683 buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
684 return 3;
687 static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
689 char *tok;
690 tok = get_token(&args, script, lineno);
691 if (tok)
692 ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
694 buf[0] = id;
695 buf[1] = 0x00;
696 return 2;
699 static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
701 char *tok;
702 tok = get_token(&args, script, lineno);
703 if (tok)
704 ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
706 buf[0] = id;
707 buf[1] = 0x7;
708 return 2;
711 static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
713 char *tok;
714 tok = get_token(&args, script, lineno);
715 if (tok)
716 ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
718 buf[0] = id;
719 buf[1] = 0;
720 return 2;
723 static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
725 char *tok;
726 tok = get_token(&args, script, lineno);
727 if (tok)
728 ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
730 buf[0] = id;
731 buf[1] = 0xf;
732 return 2;
735 static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
737 char *tok;
738 char subscript[80];
739 struct adsi_subscript *sub;
740 tok = get_token(&args, script, lineno);
741 if (!tok) {
742 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
743 return 0;
745 if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
746 ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
747 return 0;
749 sub = getsubbyname(state, subscript, script, lineno);
750 if (!sub)
751 return 0;
752 buf[0] = 0x9d;
753 buf[1] = sub->id;
754 return 2;
757 static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
759 char *tok;
760 char subscript[80];
761 char sname[80];
762 int sawin=0;
763 int event;
764 int snums[8];
765 int scnt = 0;
766 int x;
767 struct adsi_subscript *sub;
768 tok = get_token(&args, script, lineno);
769 if (!tok) {
770 ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
771 return 0;
773 event = geteventbyname(tok);
774 if (event < 1) {
775 ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
776 return 0;
778 tok = get_token(&args, script, lineno);
779 while ((!sawin && !strcasecmp(tok, "IN")) ||
780 (sawin && !strcasecmp(tok, "OR"))) {
781 sawin = 1;
782 if (scnt > 7) {
783 ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
784 return 0;
786 /* Process 'in' things */
787 tok = get_token(&args, script, lineno);
788 if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
789 ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
790 return 0;
792 if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
793 ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
794 return 0;
796 scnt++;
797 tok = get_token(&args, script, lineno);
798 if (!tok)
799 break;
801 if (!tok || strcasecmp(tok, "GOTO")) {
802 if (!tok)
803 tok = "<nothing>";
804 if (sawin)
805 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
806 else
807 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
809 tok = get_token(&args, script, lineno);
810 if (!tok) {
811 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
812 return 0;
814 if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
815 ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
816 return 0;
818 sub = getsubbyname(state, subscript, script, lineno);
819 if (!sub)
820 return 0;
821 buf[0] = 8;
822 buf[1] = event;
823 buf[2] = sub->id | 0x80;
824 for (x=0;x<scnt;x++)
825 buf[3 + x] = snums[x];
826 return 3 + scnt;
829 struct adsi_key_cmd {
830 char *name;
831 int id;
832 int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
835 static struct adsi_key_cmd kcmds[] = {
836 { "SENDDTMF", 0, send_dtmf },
837 /* Encoded DTMF would go here */
838 { "ONHOOK", 0x81 },
839 { "OFFHOOK", 0x82 },
840 { "FLASH", 0x83 },
841 { "WAITDIALTONE", 0x84 },
842 /* Send line number */
843 { "BLANK", 0x86 },
844 { "SENDCHARS", 0x87 },
845 { "CLEARCHARS", 0x88 },
846 { "BACKSPACE", 0x89 },
847 /* Tab column */
848 { "GOTOLINE", 0x8b, goto_line },
849 { "GOTOLINEREL", 0x8c, goto_line_rel },
850 { "PAGEUP", 0x8d },
851 { "PAGEDOWN", 0x8e },
852 /* Extended DTMF */
853 { "DELAY", 0x90, send_delay },
854 { "DIALPULSEONE", 0x91 },
855 { "DATAMODE", 0x92 },
856 { "VOICEMODE", 0x93 },
857 /* Display call buffer 'n' */
858 /* Clear call buffer 'n' */
859 { "CLEARCB1", 0x95, clearcbone },
860 { "DIGITCOLLECT", 0x96, digitcollect },
861 { "DIGITDIRECT", 0x96, digitdirect },
862 { "CLEAR", 0x97 },
863 { "SHOWDISPLAY", 0x98, showdisplay },
864 { "CLEARDISPLAY", 0x98, cleardisplay },
865 { "SHOWKEYS", 0x99, showkeys },
866 { "SETSTATE", 0x9a, set_state },
867 { "TIMERSTART", 0x9b, starttimer },
868 { "TIMERCLEAR", 0x9b, cleartimer },
869 { "SETFLAG", 0x9c, setflag },
870 { "CLEARFLAG", 0x9c, clearflag },
871 { "GOTO", 0x9d, subscript },
872 { "EVENT22", 0x9e },
873 { "EVENT23", 0x9f },
874 { "EXIT", 0xa0 },
877 static struct adsi_key_cmd opcmds[] = {
879 /* 1 - Branch on event -- handled specially */
880 { "SHOWKEYS", 2, showkeys },
881 /* Display Control */
882 { "SHOWDISPLAY", 3, showdisplay },
883 { "CLEARDISPLAY", 3, cleardisplay },
884 { "CLEAR", 5 },
885 { "SETSTATE", 6, set_state },
886 { "TIMERSTART", 7, starttimer },
887 { "TIMERCLEAR", 7, cleartimer },
888 { "ONEVENT", 8, onevent },
889 /* 9 - Subroutine label, treated specially */
890 { "SETFLAG", 10, setflag },
891 { "CLEARFLAG", 10, clearflag },
892 { "DELAY", 11, send_delay },
893 { "EXIT", 12 },
897 static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
899 int x;
900 char *unused;
901 int res;
902 for (x=0;x<sizeof(kcmds) / sizeof(kcmds[0]);x++) {
903 if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
904 if (kcmds[x].add_args) {
905 res = kcmds[x].add_args(key->retstr + key->retstrlen,
906 code, kcmds[x].id, args, state, script, lineno);
907 if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
908 key->retstrlen += res;
909 else
910 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);
911 } else {
912 if ((unused = get_token(&args, script, lineno)))
913 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
914 if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
915 key->retstr[key->retstrlen] = kcmds[x].id;
916 key->retstrlen++;
917 } else
918 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);
920 return 0;
923 return -1;
926 static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
928 int x;
929 char *unused;
930 int res;
931 int max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
932 for (x=0;x<sizeof(opcmds) / sizeof(opcmds[0]);x++) {
933 if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
934 if (opcmds[x].add_args) {
935 res = opcmds[x].add_args(sub->data + sub->datalen,
936 code, opcmds[x].id, args, state, script, lineno);
937 if ((sub->datalen + res + 1) <= max)
938 sub->datalen += res;
939 else {
940 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);
941 return -1;
943 } else {
944 if ((unused = get_token(&args, script, lineno)))
945 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
946 if ((sub->datalen + 2) <= max) {
947 sub->data[sub->datalen] = opcmds[x].id;
948 sub->datalen++;
949 } else {
950 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);
951 return -1;
954 /* Separate commands with 0xff */
955 sub->data[sub->datalen] = 0xff;
956 sub->datalen++;
957 sub->inscount++;
958 return 0;
961 return -1;
964 static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
966 char *keyword;
967 char *args;
968 char vname[256];
969 char tmp[80];
970 char tmp2[80];
971 int lrci;
972 int wi;
973 int event;
974 struct adsi_display *disp;
975 struct adsi_subscript *newsub;
976 /* Find the first keyword */
977 keyword = get_token(&buf, script, lineno);
978 if (!keyword)
979 return 0;
980 switch(state->state) {
981 case STATE_NORMAL:
982 if (!strcasecmp(keyword, "DESCRIPTION")) {
983 args = get_token(&buf, script, lineno);
984 if (args) {
985 if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
986 ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
987 } else
988 ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
989 } else if (!strcasecmp(keyword, "VERSION")) {
990 args = get_token(&buf, script, lineno);
991 if (args) {
992 if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
993 ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
994 } else
995 ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
996 } else if (!strcasecmp(keyword, "SECURITY")) {
997 args = get_token(&buf, script, lineno);
998 if (args) {
999 if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
1000 ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
1001 } else
1002 ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
1003 } else if (!strcasecmp(keyword, "FDN")) {
1004 args = get_token(&buf, script, lineno);
1005 if (args) {
1006 if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
1007 ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
1008 } else
1009 ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
1010 } else if (!strcasecmp(keyword, "KEY")) {
1011 args = get_token(&buf, script, lineno);
1012 if (!args) {
1013 ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
1014 break;
1016 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1017 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1018 break;
1020 state->key = getkeybyname(state, vname, script, lineno);
1021 if (!state->key) {
1022 ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
1023 break;
1025 if (state->key->defined) {
1026 ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
1027 break;
1029 args = get_token(&buf, script, lineno);
1030 if (!args || strcasecmp(args, "IS")) {
1031 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1032 break;
1034 args = get_token(&buf, script, lineno);
1035 if (!args) {
1036 ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
1037 break;
1039 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1040 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
1041 break;
1043 args = get_token(&buf, script, lineno);
1044 if (args) {
1045 if (strcasecmp(args, "OR")) {
1046 ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
1047 break;
1049 args = get_token(&buf, script, lineno);
1050 if (!args) {
1051 ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
1052 break;
1054 if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
1055 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
1056 break;
1058 } else {
1059 ast_copy_string(tmp2, tmp, sizeof(tmp2));
1061 if (strlen(tmp2) > 18) {
1062 ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
1063 tmp2[18] = '\0';
1065 if (strlen(tmp) > 7) {
1066 ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
1067 tmp[7] = '\0';
1069 /* Setup initial stuff */
1070 state->key->retstr[0] = 128;
1071 /* 1 has the length */
1072 state->key->retstr[2] = state->key->id;
1073 /* Put the Full name in */
1074 memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
1075 /* Update length */
1076 state->key->retstrlen = strlen(tmp2) + 3;
1077 /* Put trailing 0xff */
1078 state->key->retstr[state->key->retstrlen++] = 0xff;
1079 /* Put the short name */
1080 memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
1081 /* Update length */
1082 state->key->retstrlen += strlen(tmp);
1083 /* Put trailing 0xff */
1084 state->key->retstr[state->key->retstrlen++] = 0xff;
1085 /* Record initial length */
1086 state->key->initlen = state->key->retstrlen;
1087 state->state = STATE_INKEY;
1088 } else if (!strcasecmp(keyword, "SUB")) {
1089 args = get_token(&buf, script, lineno);
1090 if (!args) {
1091 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1092 break;
1094 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1095 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1096 break;
1098 state->sub = getsubbyname(state, vname, script, lineno);
1099 if (!state->sub) {
1100 ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
1101 break;
1103 if (state->sub->defined) {
1104 ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
1105 break;
1107 /* Setup sub */
1108 state->sub->data[0] = 130;
1109 /* 1 is the length */
1110 state->sub->data[2] = 0x0; /* Clear extensibility bit */
1111 state->sub->datalen = 3;
1112 if (state->sub->id) {
1113 /* If this isn't the main subroutine, make a subroutine label for it */
1114 state->sub->data[3] = 9;
1115 state->sub->data[4] = state->sub->id;
1116 /* 5 is length */
1117 state->sub->data[6] = 0xff;
1118 state->sub->datalen = 7;
1120 args = get_token(&buf, script, lineno);
1121 if (!args || strcasecmp(args, "IS")) {
1122 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1123 break;
1125 state->state = STATE_INSUB;
1126 } else if (!strcasecmp(keyword, "STATE")) {
1127 args = get_token(&buf, script, lineno);
1128 if (!args) {
1129 ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
1130 break;
1132 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1133 ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
1134 break;
1136 if (getstatebyname(state, vname, script, lineno, 0)) {
1137 ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
1138 break;
1140 getstatebyname(state, vname, script, lineno, 1);
1141 } else if (!strcasecmp(keyword, "FLAG")) {
1142 args = get_token(&buf, script, lineno);
1143 if (!args) {
1144 ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
1145 break;
1147 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1148 ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
1149 break;
1151 if (getflagbyname(state, vname, script, lineno, 0)) {
1152 ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
1153 break;
1155 getflagbyname(state, vname, script, lineno, 1);
1156 } else if (!strcasecmp(keyword, "DISPLAY")) {
1157 lrci = 0;
1158 wi = 0;
1159 args = get_token(&buf, script, lineno);
1160 if (!args) {
1161 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1162 break;
1164 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1165 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1166 break;
1168 if (getdisplaybyname(state, vname, script, lineno, 0)) {
1169 ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
1170 break;
1172 disp = getdisplaybyname(state, vname, script, lineno, 1);
1173 if (!disp)
1174 break;
1175 args = get_token(&buf, script, lineno);
1176 if (!args || strcasecmp(args, "IS")) {
1177 ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
1178 break;
1180 args = get_token(&buf, script, lineno);
1181 if (!args) {
1182 ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
1183 break;
1185 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1186 ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
1187 break;
1189 if (strlen(tmp) > 20) {
1190 ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
1191 tmp[20] = '\0';
1193 memcpy(disp->data + 5, tmp, strlen(tmp));
1194 disp->datalen = strlen(tmp) + 5;
1195 disp->data[disp->datalen++] = 0xff;
1197 args = get_token(&buf, script, lineno);
1198 if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1199 /* Got a column two */
1200 if (strlen(tmp) > 20) {
1201 ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
1202 tmp[20] = '\0';
1204 memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
1205 disp->datalen += strlen(tmp);
1206 args = get_token(&buf, script, lineno);
1208 while(args) {
1209 if (!strcasecmp(args, "JUSTIFY")) {
1210 args = get_token(&buf, script, lineno);
1211 if (!args) {
1212 ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
1213 break;
1215 lrci = getjustifybyname(args);
1216 if (lrci < 0) {
1217 ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
1218 break;
1220 } else if (!strcasecmp(args, "WRAP")) {
1221 wi = 0x80;
1222 } else {
1223 ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
1224 break;
1226 args = get_token(&buf, script, lineno);
1228 if (args) {
1229 /* Something bad happened */
1230 break;
1232 disp->data[0] = 129;
1233 disp->data[1] = disp->datalen - 2;
1234 disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
1235 disp->data[3] = wi;
1236 disp->data[4] = 0xff;
1237 } else {
1238 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
1240 break;
1241 case STATE_INKEY:
1242 if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
1243 if (!strcasecmp(keyword, "ENDKEY")) {
1244 /* Return to normal operation and increment current key */
1245 state->state = STATE_NORMAL;
1246 state->key->defined = 1;
1247 state->key->retstr[1] = state->key->retstrlen - 2;
1248 state->key = NULL;
1249 } else {
1250 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
1253 break;
1254 case STATE_INIF:
1255 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1256 if (!strcasecmp(keyword, "ENDIF")) {
1257 /* Return to normal SUB operation and increment current key */
1258 state->state = STATE_INSUB;
1259 state->sub->defined = 1;
1260 /* Store the proper number of instructions */
1261 state->sub->ifdata[2] = state->sub->ifinscount;
1262 } else if (!strcasecmp(keyword, "GOTO")) {
1263 args = get_token(&buf, script, lineno);
1264 if (!args) {
1265 ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
1266 break;
1268 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1269 ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
1270 break;
1272 newsub = getsubbyname(state, tmp, script, lineno);
1273 if (!newsub)
1274 break;
1275 /* Somehow you use GOTO to go to another place */
1276 state->sub->data[state->sub->datalen++] = 0x8;
1277 state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
1278 state->sub->data[state->sub->datalen++] = newsub->id;
1279 /* Terminate */
1280 state->sub->data[state->sub->datalen++] = 0xff;
1281 /* Increment counters */
1282 state->sub->inscount++;
1283 state->sub->ifinscount++;
1284 } else {
1285 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
1287 } else
1288 state->sub->ifinscount++;
1289 break;
1290 case STATE_INSUB:
1291 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1292 if (!strcasecmp(keyword, "ENDSUB")) {
1293 /* Return to normal operation and increment current key */
1294 state->state = STATE_NORMAL;
1295 state->sub->defined = 1;
1296 /* Store the proper length */
1297 state->sub->data[1] = state->sub->datalen - 2;
1298 if (state->sub->id) {
1299 /* if this isn't main, store number of instructions, too */
1300 state->sub->data[5] = state->sub->inscount;
1302 state->sub = NULL;
1303 } else if (!strcasecmp(keyword, "IFEVENT")) {
1304 args = get_token(&buf, script, lineno);
1305 if (!args) {
1306 ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
1307 break;
1309 event = geteventbyname(args);
1310 if (event < 1) {
1311 ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
1312 break;
1314 args = get_token(&buf, script, lineno);
1315 if (!args || strcasecmp(args, "THEN")) {
1316 ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
1317 break;
1319 state->sub->ifinscount = 0;
1320 state->sub->ifdata = state->sub->data +
1321 state->sub->datalen;
1322 /* Reserve header and insert op codes */
1323 state->sub->ifdata[0] = 0x1;
1324 state->sub->ifdata[1] = event;
1325 /* 2 is for the number of instructions */
1326 state->sub->ifdata[3] = 0xff;
1327 state->sub->datalen += 4;
1328 /* Update Subscript instruction count */
1329 state->sub->inscount++;
1330 state->state = STATE_INIF;
1331 } else {
1332 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
1335 break;
1336 default:
1337 ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
1339 return 0;
1342 static struct adsi_script *compile_script(char *script)
1344 FILE *f;
1345 char fn[256];
1346 char buf[256];
1347 char *c;
1348 int lineno=0;
1349 int x, err;
1350 struct adsi_script *scr;
1351 if (script[0] == '/')
1352 ast_copy_string(fn, script, sizeof(fn));
1353 else
1354 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script);
1355 f = fopen(fn, "r");
1356 if (!f) {
1357 ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
1358 return NULL;
1360 if (!(scr = ast_calloc(1, sizeof(*scr)))) {
1361 fclose(f);
1362 return NULL;
1364 /* Create "main" as first subroutine */
1365 getsubbyname(scr, "main", NULL, 0);
1366 while(!feof(f)) {
1367 fgets(buf, sizeof(buf), f);
1368 if (!feof(f)) {
1369 lineno++;
1370 /* Trim off trailing return */
1371 buf[strlen(buf) - 1] = '\0';
1372 c = strchr(buf, ';');
1373 /* Strip comments */
1374 if (c)
1375 *c = '\0';
1376 if (!ast_strlen_zero(buf))
1377 adsi_process(scr, buf, script, lineno);
1380 fclose(f);
1381 /* Make sure we're in the main routine again */
1382 switch(scr->state) {
1383 case STATE_NORMAL:
1384 break;
1385 case STATE_INSUB:
1386 ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
1387 free(scr);
1388 return NULL;
1389 case STATE_INKEY:
1390 ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
1391 free(scr);
1392 return NULL;
1394 err = 0;
1396 /* Resolve all keys and record their lengths */
1397 for (x=0;x<scr->numkeys;x++) {
1398 if (!scr->keys[x].defined) {
1399 ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
1400 err++;
1404 /* Resolve all subs */
1405 for (x=0;x<scr->numsubs;x++) {
1406 if (!scr->subs[x].defined) {
1407 ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
1408 err++;
1410 if (x == (scr->numsubs - 1)) {
1411 /* Clear out extension bit on last message */
1412 scr->subs[x].data[2] = 0x80;
1416 if (err) {
1417 free(scr);
1418 return NULL;
1420 return scr;
1423 #ifdef DUMP_MESSAGES
1424 static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
1426 int x;
1427 printf("%s %s: [ ", type, vname);
1428 for (x=0;x<buflen;x++)
1429 printf("%02x ", buf[x]);
1430 printf("]\n");
1432 #endif
1434 static int adsi_prog(struct ast_channel *chan, char *script)
1436 struct adsi_script *scr;
1437 int x;
1438 unsigned char buf[1024];
1439 int bytes;
1440 scr = compile_script(script);
1441 if (!scr)
1442 return -1;
1444 /* Start an empty ADSI Session */
1445 if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
1446 return -1;
1448 /* Now begin the download attempt */
1449 if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
1450 /* User rejected us for some reason */
1451 if (option_verbose > 2)
1452 ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n");
1453 ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
1454 free(scr);
1455 return -1;
1458 bytes = 0;
1459 /* Start with key definitions */
1460 for (x=0;x<scr->numkeys;x++) {
1461 if (bytes + scr->keys[x].retstrlen > 253) {
1462 /* Send what we've collected so far */
1463 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1464 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1465 return -1;
1467 bytes =0;
1469 memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
1470 bytes += scr->keys[x].retstrlen;
1471 #ifdef DUMP_MESSAGES
1472 dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
1473 #endif
1475 if (bytes) {
1476 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1477 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1478 return -1;
1482 bytes = 0;
1483 /* Continue with the display messages */
1484 for (x=0;x<scr->numdisplays;x++) {
1485 if (bytes + scr->displays[x].datalen > 253) {
1486 /* Send what we've collected so far */
1487 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1488 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1489 return -1;
1491 bytes =0;
1493 memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
1494 bytes += scr->displays[x].datalen;
1495 #ifdef DUMP_MESSAGES
1496 dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
1497 #endif
1499 if (bytes) {
1500 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1501 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1502 return -1;
1506 bytes = 0;
1507 /* Send subroutines */
1508 for (x=0;x<scr->numsubs;x++) {
1509 if (bytes + scr->subs[x].datalen > 253) {
1510 /* Send what we've collected so far */
1511 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1512 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1513 return -1;
1515 bytes =0;
1517 memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
1518 bytes += scr->subs[x].datalen;
1519 #ifdef DUMP_MESSAGES
1520 dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
1521 #endif
1523 if (bytes) {
1524 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1525 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1526 return -1;
1531 bytes = 0;
1532 bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
1533 bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
1534 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
1535 return -1;
1536 if (ast_adsi_end_download(chan)) {
1537 /* Download failed for some reason */
1538 if (option_verbose > 2)
1539 ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n");
1540 ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
1541 free(scr);
1542 return -1;
1544 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;
1552 struct ast_module_user *u;
1554 u = ast_module_user_add(chan);
1556 if (ast_strlen_zero(data))
1557 data = "asterisk.adsi";
1559 if (!ast_adsi_available(chan)) {
1560 if (option_verbose > 2)
1561 ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE. Not bothering to try.\n");
1562 } else {
1563 if (option_verbose > 2)
1564 ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE. Attempting Upload.\n");
1565 res = adsi_prog(chan, data);
1568 ast_module_user_remove(u);
1570 return res;
1573 static int unload_module(void)
1575 int res;
1577 ast_module_user_hangup_all();
1579 res = ast_unregister_application(app);
1582 return res;
1585 static int load_module(void)
1587 return ast_register_application(app, adsi_exec, synopsis, descrip);
1590 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");