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