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