Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 tok = get_token(&args, script, lineno);
852 if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
853 ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
854 return 0;
855 }
856 if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) == NULL)) {
857 ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
858 return 0;
859 }
860 scnt++;
861 if (!(tok = get_token(&args, script, lineno)))
862 break;
863 }
864 if (!tok || strcasecmp(tok, "GOTO")) {
865 if (!tok)
866 tok = "<nothing>";
867 if (sawin)
868 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
869 else
870 ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
871 }
872 if (!(tok = get_token(&args, script, lineno))) {
873 ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
874 return 0;
875 }
876 if (process_token(subscr, tok, sizeof(subscr) - 1, ARG_STRING)) {
877 ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
878 return 0;
879 }
880 if (!(sub = getsubbyname(state, subscr, script, lineno)))
881 return 0;
882 buf[0] = 8;
883 buf[1] = event;
884 buf[2] = sub->id | 0x80;
885 for (x = 0; x < scnt; x++)
886 buf[3 + x] = snums[x];
887 return 3 + scnt;
888}
889
891 char *name;
892 int id;
893 int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno);
894};
895
896static const struct adsi_key_cmd kcmds[] = {
897 { "SENDDTMF", 0, send_dtmf },
898 /* Encoded DTMF would go here */
899 { "ONHOOK", 0x81 },
900 { "OFFHOOK", 0x82 },
901 { "FLASH", 0x83 },
902 { "WAITDIALTONE", 0x84 },
903 /* Send line number */
904 { "BLANK", 0x86 },
905 { "SENDCHARS", 0x87 },
906 { "CLEARCHARS", 0x88 },
907 { "BACKSPACE", 0x89 },
908 /* Tab column */
909 { "GOTOLINE", 0x8b, goto_line },
910 { "GOTOLINEREL", 0x8c, goto_line_rel },
911 { "PAGEUP", 0x8d },
912 { "PAGEDOWN", 0x8e },
913 /* Extended DTMF */
914 { "DELAY", 0x90, send_delay },
915 { "DIALPULSEONE", 0x91 },
916 { "DATAMODE", 0x92 },
917 { "VOICEMODE", 0x93 },
918 /* Display call buffer 'n' */
919 /* Clear call buffer 'n' */
920 { "CLEARCB1", 0x95, clearcbone },
921 { "DIGITCOLLECT", 0x96, digitcollect },
922 { "DIGITDIRECT", 0x96, digitdirect },
923 { "CLEAR", 0x97 },
924 { "SHOWDISPLAY", 0x98, showdisplay },
925 { "CLEARDISPLAY", 0x98, cleardisplay },
926 { "SHOWKEYS", 0x99, showkeys },
927 { "SETSTATE", 0x9a, set_state },
928 { "TIMERSTART", 0x9b, starttimer },
929 { "TIMERCLEAR", 0x9b, cleartimer },
930 { "SETFLAG", 0x9c, setflag },
931 { "CLEARFLAG", 0x9c, clearflag },
932 { "GOTO", 0x9d, subscript },
933 { "EVENT22", 0x9e },
934 { "EVENT23", 0x9f },
935 { "EXIT", 0xa0 },
936};
937
938static const struct adsi_key_cmd opcmds[] = {
939
940 /* 1 - Branch on event -- handled specially */
941 { "SHOWKEYS", 2, showkeys },
942 /* Display Control */
943 { "SHOWDISPLAY", 3, showdisplay },
944 { "CLEARDISPLAY", 3, cleardisplay },
945 { "CLEAR", 5 },
946 { "SETSTATE", 6, set_state },
947 { "TIMERSTART", 7, starttimer },
948 { "TIMERCLEAR", 7, cleartimer },
949 { "ONEVENT", 8, onevent },
950 /* 9 - Subroutine label, treated specially */
951 { "SETFLAG", 10, setflag },
952 { "CLEARFLAG", 10, clearflag },
953 { "DELAY", 11, send_delay },
954 { "EXIT", 12 },
955};
956
957
958static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, const char *script, int lineno)
959{
960 int x, res;
961 char *unused;
962
963 for (x = 0; x < ARRAY_LEN(kcmds); x++) {
964 if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
965 if (kcmds[x].add_args) {
966 res = kcmds[x].add_args(key->retstr + key->retstrlen,
967 code, kcmds[x].id, args, state, script, lineno);
968 if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
969 key->retstrlen += res;
970 else
971 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);
972 } else {
973 if ((unused = get_token(&args, script, lineno)))
974 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
975 if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
976 key->retstr[key->retstrlen] = kcmds[x].id;
977 key->retstrlen++;
978 } else
979 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);
980 }
981 return 0;
982 }
983 }
984 return -1;
985}
986
987static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, const char *script, int lineno)
988{
989 int x, res, max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
990 char *unused;
991
992 for (x = 0; x < ARRAY_LEN(opcmds); x++) {
993 if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
994 if (opcmds[x].add_args) {
995 res = opcmds[x].add_args(sub->data + sub->datalen,
996 code, opcmds[x].id, args, state, script, lineno);
997 if ((sub->datalen + res + 1) <= max)
998 sub->datalen += res;
999 else {
1000 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);
1001 return -1;
1002 }
1003 } else {
1004 if ((unused = get_token(&args, script, lineno)))
1005 ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
1006 if ((sub->datalen + 2) <= max) {
1007 sub->data[sub->datalen] = opcmds[x].id;
1008 sub->datalen++;
1009 } else {
1010 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);
1011 return -1;
1012 }
1013 }
1014 /* Separate commands with 0xff */
1015 sub->data[sub->datalen] = 0xff;
1016 sub->datalen++;
1017 sub->inscount++;
1018 return 0;
1019 }
1020 }
1021 return -1;
1022}
1023
1024static int adsi_process(struct adsi_script *state, char *buf, const char *script, int lineno)
1025{
1026 char *keyword = get_token(&buf, script, lineno);
1027 char *args, vname[256], tmp[80], tmp2[80];
1028 int lrci, wi, event;
1029 struct adsi_display *disp;
1030 struct adsi_subscript *newsub;
1031
1032 if (!keyword)
1033 return 0;
1034
1035 switch(state->state) {
1036 case STATE_NORMAL:
1037 if (!strcasecmp(keyword, "DESCRIPTION")) {
1038 if ((args = get_token(&buf, script, lineno))) {
1039 if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
1040 ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
1041 } else
1042 ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
1043 } else if (!strcasecmp(keyword, "VERSION")) {
1044 if ((args = get_token(&buf, script, lineno))) {
1045 if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
1046 ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
1047 } else
1048 ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
1049 } else if (!strcasecmp(keyword, "SECURITY")) {
1050 if ((args = get_token(&buf, script, lineno))) {
1051 if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
1052 ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
1053 } else
1054 ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
1055 } else if (!strcasecmp(keyword, "FDN")) {
1056 if ((args = get_token(&buf, script, lineno))) {
1057 if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
1058 ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
1059 } else
1060 ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
1061 } else if (!strcasecmp(keyword, "KEY")) {
1062 if (!(args = get_token(&buf, script, lineno))) {
1063 ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
1064 break;
1065 }
1066 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1067 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1068 break;
1069 }
1070 if (!(state->key = getkeybyname(state, vname, script, lineno))) {
1071 ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
1072 break;
1073 }
1074 if (state->key->defined) {
1075 ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
1076 break;
1077 }
1078 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
1079 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1080 break;
1081 }
1082 if (!(args = get_token(&buf, script, lineno))) {
1083 ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
1084 break;
1085 }
1086 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1087 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
1088 break;
1089 }
1090 if ((args = get_token(&buf, script, lineno))) {
1091 if (strcasecmp(args, "OR")) {
1092 ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
1093 break;
1094 }
1095 if (!(args = get_token(&buf, script, lineno))) {
1096 ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
1097 break;
1098 }
1099 if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
1100 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
1101 break;
1102 }
1103 } else {
1104 ast_copy_string(tmp2, tmp, sizeof(tmp2));
1105 }
1106 if (strlen(tmp2) > 18) {
1107 ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
1108 tmp2[18] = '\0';
1109 }
1110 if (strlen(tmp) > 7) {
1111 ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
1112 tmp[7] = '\0';
1113 }
1114 /* Setup initial stuff */
1115 state->key->retstr[0] = 0x80;
1116 /* 1 has the length */
1117 state->key->retstr[2] = state->key->id;
1118 /* Put the Full name in */
1119 memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
1120 /* Update length */
1121 state->key->retstrlen = strlen(tmp2) + 3;
1122 /* Put trailing 0xff */
1123 state->key->retstr[state->key->retstrlen++] = 0xff;
1124 /* Put the short name */
1125 memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
1126 /* Update length */
1127 state->key->retstrlen += strlen(tmp);
1128 /* Put trailing 0xff */
1129 state->key->retstr[state->key->retstrlen++] = 0xff;
1130 /* Record initial length */
1131 state->key->initlen = state->key->retstrlen;
1132 state->state = STATE_INKEY;
1133 } else if (!strcasecmp(keyword, "SUB")) {
1134 if (!(args = get_token(&buf, script, lineno))) {
1135 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1136 break;
1137 }
1138 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1139 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1140 break;
1141 }
1142 if (!(state->sub = getsubbyname(state, vname, script, lineno))) {
1143 ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
1144 break;
1145 }
1146 if (state->sub->defined) {
1147 ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
1148 break;
1149 }
1150 /* Setup sub */
1151 state->sub->data[0] = 0x82;
1152 /* 1 is the length */
1153 state->sub->data[2] = 0x0; /* Clear extensibility bit */
1154 state->sub->datalen = 3;
1155 if (state->sub->id) {
1156 /* If this isn't the main subroutine, make a subroutine label for it */
1157 state->sub->data[3] = 9;
1158 state->sub->data[4] = state->sub->id;
1159 /* 5 is length */
1160 state->sub->data[6] = 0xff;
1161 state->sub->datalen = 7;
1162 }
1163 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
1164 ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
1165 break;
1166 }
1167 state->state = STATE_INSUB;
1168 } else if (!strcasecmp(keyword, "STATE")) {
1169 if (!(args = get_token(&buf, script, lineno))) {
1170 ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
1171 break;
1172 }
1173 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1174 ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
1175 break;
1176 }
1177 if (getstatebyname(state, vname, script, lineno, 0)) {
1178 ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
1179 break;
1180 }
1181 getstatebyname(state, vname, script, lineno, 1);
1182 } else if (!strcasecmp(keyword, "FLAG")) {
1183 if (!(args = get_token(&buf, script, lineno))) {
1184 ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
1185 break;
1186 }
1187 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1188 ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
1189 break;
1190 }
1191 if (getflagbyname(state, vname, script, lineno, 0)) {
1192 ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
1193 break;
1194 }
1195 getflagbyname(state, vname, script, lineno, 1);
1196 } else if (!strcasecmp(keyword, "DISPLAY")) {
1197 lrci = 0;
1198 wi = 0;
1199 if (!(args = get_token(&buf, script, lineno))) {
1200 ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
1201 break;
1202 }
1203 if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
1204 ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
1205 break;
1206 }
1207 if (getdisplaybyname(state, vname, script, lineno, 0)) {
1208 ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
1209 break;
1210 }
1211 if (!(disp = getdisplaybyname(state, vname, script, lineno, 1)))
1212 break;
1213 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
1214 ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
1215 break;
1216 }
1217 if (!(args = get_token(&buf, script, lineno))) {
1218 ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
1219 break;
1220 }
1221 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1222 ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
1223 break;
1224 }
1225 if (strlen(tmp) > 20) {
1226 ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
1227 tmp[20] = '\0';
1228 }
1229 memcpy(disp->data + 5, tmp, strlen(tmp));
1230 disp->datalen = strlen(tmp) + 5;
1231 disp->data[disp->datalen++] = 0xff;
1232
1233 args = get_token(&buf, script, lineno);
1234 if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1235 /* Got a column two */
1236 if (strlen(tmp) > 20) {
1237 ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
1238 tmp[20] = '\0';
1239 }
1240 memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
1241 disp->datalen += strlen(tmp);
1242 args = get_token(&buf, script, lineno);
1243 }
1244 while (args) {
1245 if (!strcasecmp(args, "JUSTIFY")) {
1246 args = get_token(&buf, script, lineno);
1247 if (!args) {
1248 ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
1249 break;
1250 }
1251 lrci = getjustifybyname(args);
1252 if (lrci < 0) {
1253 ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
1254 break;
1255 }
1256 } else if (!strcasecmp(args, "WRAP")) {
1257 wi = 0x80;
1258 } else {
1259 ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
1260 break;
1261 }
1262 args = get_token(&buf, script, lineno);
1263 }
1264 if (args) {
1265 /* Something bad happened */
1266 break;
1267 }
1268 disp->data[0] = 0x81;
1269 disp->data[1] = disp->datalen - 2;
1270 disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
1271 disp->data[3] = wi;
1272 disp->data[4] = 0xff;
1273 } else {
1274 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
1275 }
1276 break;
1277 case STATE_INKEY:
1278 if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
1279 if (!strcasecmp(keyword, "ENDKEY")) {
1280 /* Return to normal operation and increment current key */
1281 state->state = STATE_NORMAL;
1282 state->key->defined = 1;
1283 state->key->retstr[1] = state->key->retstrlen - 2;
1284 state->key = NULL;
1285 } else {
1286 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
1287 }
1288 }
1289 break;
1290 case STATE_INIF:
1291 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1292 if (!strcasecmp(keyword, "ENDIF")) {
1293 /* Return to normal SUB operation and increment current key */
1294 state->state = STATE_INSUB;
1295 state->sub->defined = 1;
1296 /* Store the proper number of instructions */
1297 state->sub->ifdata[2] = state->sub->ifinscount;
1298 } else if (!strcasecmp(keyword, "GOTO")) {
1299 if (!(args = get_token(&buf, script, lineno))) {
1300 ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
1301 break;
1302 }
1303 if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
1304 ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
1305 break;
1306 }
1307 if (!(newsub = getsubbyname(state, tmp, script, lineno)))
1308 break;
1309 /* Somehow you use GOTO to go to another place */
1310 state->sub->data[state->sub->datalen++] = 0x8;
1311 state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
1312 state->sub->data[state->sub->datalen++] = newsub->id;
1313 /* Terminate */
1314 state->sub->data[state->sub->datalen++] = 0xff;
1315 /* Increment counters */
1316 state->sub->inscount++;
1317 state->sub->ifinscount++;
1318 } else {
1319 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
1320 }
1321 } else
1322 state->sub->ifinscount++;
1323 break;
1324 case STATE_INSUB:
1325 if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
1326 if (!strcasecmp(keyword, "ENDSUB")) {
1327 /* Return to normal operation and increment current key */
1328 state->state = STATE_NORMAL;
1329 state->sub->defined = 1;
1330 /* Store the proper length */
1331 state->sub->data[1] = state->sub->datalen - 2;
1332 if (state->sub->id) {
1333 /* if this isn't main, store number of instructions, too */
1334 state->sub->data[5] = state->sub->inscount;
1335 }
1336 state->sub = NULL;
1337 } else if (!strcasecmp(keyword, "IFEVENT")) {
1338 if (!(args = get_token(&buf, script, lineno))) {
1339 ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
1340 break;
1341 }
1342 if ((event = geteventbyname(args)) < 1) {
1343 ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
1344 break;
1345 }
1346 if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "THEN")) {
1347 ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
1348 break;
1349 }
1350 state->sub->ifinscount = 0;
1351 state->sub->ifdata = state->sub->data + state->sub->datalen;
1352 /* Reserve header and insert op codes */
1353 state->sub->ifdata[0] = 0x1;
1354 state->sub->ifdata[1] = event;
1355 /* 2 is for the number of instructions */
1356 state->sub->ifdata[3] = 0xff;
1357 state->sub->datalen += 4;
1358 /* Update Subscript instruction count */
1359 state->sub->inscount++;
1360 state->state = STATE_INIF;
1361 } else {
1362 ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
1363 }
1364 }
1365 break;
1366 default:
1367 ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
1368 }
1369 return 0;
1370}
1371
1372static struct adsi_script *compile_script(const char *script)
1373{
1374 FILE *f;
1375 char fn[256], buf[256], *c;
1376 int lineno = 0, x, err;
1377 struct adsi_script *scr;
1378
1379 if (script[0] == '/')
1380 ast_copy_string(fn, script, sizeof(fn));
1381 else
1382 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, script);
1383
1384 if (!(f = fopen(fn, "r"))) {
1385 ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
1386 return NULL;
1387 }
1388
1389 if (!(scr = ast_calloc(1, sizeof(*scr)))) {
1390 fclose(f);
1391 return NULL;
1392 }
1393
1394 /* Create "main" as first subroutine */
1395 getsubbyname(scr, "main", NULL, 0);
1396 while (!feof(f)) {
1397 if (!fgets(buf, sizeof(buf), f)) {
1398 continue;
1399 }
1400 if (!feof(f)) {
1401 lineno++;
1402 /* Trim off trailing return */
1403 buf[strlen(buf) - 1] = '\0';
1404 /* Strip comments */
1405 if ((c = strchr(buf, ';')))
1406 *c = '\0';
1407 if (!ast_strlen_zero(buf))
1408 adsi_process(scr, buf, script, lineno);
1409 }
1410 }
1411 fclose(f);
1412 /* Make sure we're in the main routine again */
1413 switch(scr->state) {
1414 case STATE_NORMAL:
1415 break;
1416 case STATE_INSUB:
1417 ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
1418 ast_free(scr);
1419 return NULL;
1420 case STATE_INKEY:
1421 ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
1422 ast_free(scr);
1423 return NULL;
1424 }
1425 err = 0;
1426
1427 /* Resolve all keys and record their lengths */
1428 for (x = 0; x < scr->numkeys; x++) {
1429 if (!scr->keys[x].defined) {
1430 ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
1431 err++;
1432 }
1433 }
1434
1435 /* Resolve all subs */
1436 for (x = 0; x < scr->numsubs; x++) {
1437 if (!scr->subs[x].defined) {
1438 ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
1439 err++;
1440 }
1441 if (x == (scr->numsubs - 1)) {
1442 /* Clear out extension bit on last message */
1443 scr->subs[x].data[2] = 0x80;
1444 }
1445 }
1446
1447 if (err) {
1448 ast_free(scr);
1449 return NULL;
1450 }
1451 return scr;
1452}
1453
1454#ifdef DUMP_MESSAGES
1455static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
1456{
1457 int x;
1458 printf("%s %s: [ ", type, vname);
1459 for (x = 0; x < buflen; x++)
1460 printf("%02hhx ", buf[x]);
1461 printf("]\n");
1462}
1463#endif
1464
1465static int adsi_prog(struct ast_channel *chan, const char *script)
1466{
1467 struct adsi_script *scr;
1468 int x, bytes;
1469 unsigned char buf[1024];
1470
1471 if (!(scr = compile_script(script)))
1472 return -1;
1473
1474 /* Start an empty ADSI Session */
1475 if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
1476 return -1;
1477
1478 /* Now begin the download attempt */
1479 if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
1480 /* User rejected us for some reason */
1481 ast_verb(3, "User rejected download attempt\n");
1482 ast_log(LOG_NOTICE, "User rejected download on channel %s\n", ast_channel_name(chan));
1483 ast_free(scr);
1484 return -1;
1485 }
1486
1487 bytes = 0;
1488 /* Start with key definitions */
1489 for (x = 0; x < scr->numkeys; x++) {
1490 if (bytes + scr->keys[x].retstrlen > 253) {
1491 /* Send what we've collected so far */
1492 if (ast_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->keys[x].retstr, scr->keys[x].retstrlen);
1499 bytes += scr->keys[x].retstrlen;
1500#ifdef DUMP_MESSAGES
1501 dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
1502#endif
1503 }
1504 if (bytes) {
1505 if (ast_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 /* Continue with the display messages */
1513 for (x = 0; x < scr->numdisplays; x++) {
1514 if (bytes + scr->displays[x].datalen > 253) {
1515 /* Send what we've collected so far */
1516 if (ast_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->displays[x].data, scr->displays[x].datalen);
1523 bytes += scr->displays[x].datalen;
1524#ifdef DUMP_MESSAGES
1525 dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
1526#endif
1527 }
1528 if (bytes) {
1529 if (ast_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 bytes = 0;
1536 /* Send subroutines */
1537 for (x = 0; x < scr->numsubs; x++) {
1538 if (bytes + scr->subs[x].datalen > 253) {
1539 /* Send what we've collected so far */
1540 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1541 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1542 return -1;
1543 }
1544 bytes =0;
1545 }
1546 memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
1547 bytes += scr->subs[x].datalen;
1548#ifdef DUMP_MESSAGES
1549 dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
1550#endif
1551 }
1552 if (bytes) {
1553 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
1554 ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
1555 return -1;
1556 }
1557 }
1558
1559 bytes = 0;
1560 bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
1561 bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
1562 if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
1563 return -1;
1564 if (ast_adsi_end_download(chan)) {
1565 /* Download failed for some reason */
1566 ast_verb(3, "Download attempt failed\n");
1567 ast_log(LOG_NOTICE, "Download failed on %s\n", ast_channel_name(chan));
1568 ast_free(scr);
1569 return -1;
1570 }
1571 ast_free(scr);
1573 return 0;
1574}
1575
1576static int adsi_exec(struct ast_channel *chan, const char *data)
1577{
1578 int res = 0;
1579
1580 if (ast_strlen_zero(data))
1581 data = "asterisk.adsi";
1582
1583 if (!ast_adsi_available(chan)) {
1584 ast_verb(3, "ADSI Unavailable on CPE. Not bothering to try.\n");
1585 } else {
1586 ast_verb(3, "ADSI Available on CPE. Attempting Upload.\n");
1587 res = adsi_prog(chan, data);
1588 }
1589
1590 return res;
1591}
1592
1593static int unload_module(void)
1594{
1596}
1597
1598/*!
1599 * \brief Load the module
1600 *
1601 * Module loading including tests for configuration or dependencies.
1602 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
1603 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
1604 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
1605 * configuration file or other non-critical problem return
1606 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
1607 */
1608static int load_module(void)
1609{
1613}
1614
1615AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk ADSI Programming Application",
1616 .support_level = AST_MODULE_SUPPORT_DEPRECATED,
1617 .load = load_module,
1618 .unload = unload_module,
1619 .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:958
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:987
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:896
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:938
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:1808
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:151
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:893
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:666