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