Wed Jan 8 2020 09:49:39

Asterisk developer's documentation


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