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