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