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