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