Asterisk - The Open Source Telephony Project  21.4.1
app_directory.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 Provide a directory of extensions
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \ingroup applications
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 #include "asterisk.h"
32 
33 #include <ctype.h>
34 
35 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
36 #include "asterisk/file.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/module.h"
39 #include "asterisk/say.h"
40 #include "asterisk/app.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/adsi.h"
43 
44 /*** DOCUMENTATION
45  <application name="Directory" language="en_US">
46  <synopsis>
47  Provide directory of voicemail extensions.
48  </synopsis>
49  <syntax>
50  <parameter name="vm-context">
51  <para>This is the context within voicemail.conf to use for the Directory. If not
52  specified and <literal>searchcontexts=no</literal> in
53  <filename>voicemail.conf</filename>, then <literal>default</literal>
54  will be assumed.</para>
55  </parameter>
56  <parameter name="dial-context" required="false">
57  <para>This is the dialplan context to use when looking for an
58  extension that the user has selected, or when jumping to the
59  <literal>o</literal> or <literal>a</literal> extension. If not
60  specified, the current context will be used.</para>
61  </parameter>
62  <parameter name="options" required="false">
63  <optionlist>
64  <option name="e">
65  <para>In addition to the name, also read the extension number to the
66  caller before presenting dialing options.</para>
67  </option>
68  <option name="f">
69  <para>Allow the caller to enter the first name of a user in the
70  directory instead of using the last name. If specified, the
71  optional number argument will be used for the number of
72  characters the user should enter.</para>
73  <argument name="n" required="true" />
74  </option>
75  <option name="l">
76  <para>Allow the caller to enter the last name of a user in the
77  directory. This is the default. If specified, the
78  optional number argument will be used for the number of
79  characters the user should enter.</para>
80  <argument name="n" required="true" />
81  </option>
82  <option name="b">
83  <para> Allow the caller to enter either the first or the last name
84  of a user in the directory. If specified, the optional number
85  argument will be used for the number of characters the user should enter.</para>
86  <argument name="n" required="true" />
87  </option>
88  <option name="a">
89  <para>Allow the caller to additionally enter an alias for a user in the
90  directory. This option must be specified in addition to the
91  <literal>f</literal>, <literal>l</literal>, or <literal>b</literal>
92  option.</para>
93  </option>
94  <option name="m">
95  <para>Instead of reading each name sequentially and asking for
96  confirmation, create a menu of up to 8 names.</para>
97  </option>
98  <option name="n">
99  <para>Read digits even if the channel is not answered.</para>
100  </option>
101  <option name="p">
102  <para>Pause for n milliseconds after the digits are typed. This is
103  helpful for people with cellphones, who are not holding the
104  receiver to their ear while entering DTMF.</para>
105  <argument name="n" required="true" />
106  </option>
107  <option name="c">
108  <para>Load the specified config file instead of voicemail.conf</para>
109  <argument name="filename" required="true" />
110  </option>
111  <option name="s">
112  <para>Skip calling the extension, instead set it in the <variable>DIRECTORY_EXTEN</variable>
113  channel variable.</para>
114  </option>
115  <option name="d">
116  <para>Enable ADSI support for screen phone searching and retrieval
117  of directory results.</para>
118  <para>Additionally, the channel must be ADSI-enabled and you must
119  have an ADSI-compatible (Type III) CPE for this to work.</para>
120  </option>
121  </optionlist>
122  <note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
123  options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as
124  if <replaceable>b</replaceable> was specified. The number
125  of characters for the user to type defaults to <literal>3</literal>.</para></note>
126  </parameter>
127  </syntax>
128  <description>
129  <para>This application will present the calling channel with a directory of extensions from which they can search
130  by name. The list of names and corresponding extensions is retrieved from the
131  voicemail configuration file, <filename>voicemail.conf</filename>, or from the specified filename.</para>
132  <para>This application will immediately exit if one of the following DTMF digits are
133  received and the extension to jump to exists:</para>
134  <para><literal>0</literal> - Jump to the 'o' extension, if it exists.</para>
135  <para><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
136  <para>This application will set the following channel variables before completion:</para>
137  <variablelist>
138  <variable name="DIRECTORY_RESULT">
139  <para>Reason Directory application exited.</para>
140  <value name="OPERATOR">User requested operator</value>
141  <value name="ASSISTANT">User requested assistant</value>
142  <value name="TIMEOUT">User allowed DTMF wait duration to pass without sending DTMF</value>
143  <value name="HANGUP">The channel hung up before the application finished</value>
144  <value name="SELECTED">User selected a user to call from the directory</value>
145  <value name="USEREXIT">User exited with '#' during selection</value>
146  <value name="FAILED">The application failed</value>
147  </variable>
148  <variable name="DIRECTORY_EXTEN">
149  <para>If the skip calling option is set this will be set to the selected extension
150  provided one is selected.</para>
151  </variable>
152  </variablelist>
153  </description>
154  </application>
155 
156  ***/
157 static const char app[] = "Directory";
158 
159 /* For simplicity, I'm keeping the format compatible with the voicemail config,
160  but i'm open to suggestions for isolating it */
161 
162 #define VOICEMAIL_CONFIG "voicemail.conf"
163 
164 enum {
165  OPT_LISTBYFIRSTNAME = (1 << 0),
166  OPT_SAYEXTENSION = (1 << 1),
167  OPT_FROMVOICEMAIL = (1 << 2),
168  OPT_SELECTFROMMENU = (1 << 3),
169  OPT_LISTBYLASTNAME = (1 << 4),
170  OPT_LISTBYEITHER = OPT_LISTBYFIRSTNAME | OPT_LISTBYLASTNAME,
171  OPT_PAUSE = (1 << 5),
172  OPT_NOANSWER = (1 << 6),
173  OPT_ALIAS = (1 << 7),
174  OPT_CONFIG_FILE = (1 << 8),
175  OPT_SKIP = (1 << 9),
176  OPT_ADSI = (1 << 10),
177 };
178 
179 enum {
180  OPT_ARG_FIRSTNAME = 0,
181  OPT_ARG_LASTNAME = 1,
182  OPT_ARG_EITHER = 2,
183  OPT_ARG_PAUSE = 3,
184  OPT_ARG_FILENAME = 4,
185  /* This *must* be the last value in this enum! */
186  OPT_ARG_ARRAY_SIZE = 5,
187 };
188 
190  char exten[AST_MAX_EXTENSION + 1];
191  char name[AST_MAX_EXTENSION + 1];
192  char context[AST_MAX_CONTEXT + 1];
193  char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
194 
196 };
197 
198 AST_APP_OPTIONS(directory_app_options, {
199  AST_APP_OPTION_ARG('f', OPT_LISTBYFIRSTNAME, OPT_ARG_FIRSTNAME),
200  AST_APP_OPTION_ARG('l', OPT_LISTBYLASTNAME, OPT_ARG_LASTNAME),
201  AST_APP_OPTION_ARG('b', OPT_LISTBYEITHER, OPT_ARG_EITHER),
202  AST_APP_OPTION_ARG('p', OPT_PAUSE, OPT_ARG_PAUSE),
203  AST_APP_OPTION('e', OPT_SAYEXTENSION),
204  AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
205  AST_APP_OPTION('m', OPT_SELECTFROMMENU),
206  AST_APP_OPTION('n', OPT_NOANSWER),
207  AST_APP_OPTION('a', OPT_ALIAS),
208  AST_APP_OPTION_ARG('c', OPT_CONFIG_FILE, OPT_ARG_FILENAME),
209  AST_APP_OPTION('s', OPT_SKIP),
210  AST_APP_OPTION('d', OPT_ADSI), /* (Would've used 'a', but that was taken already) */
211 });
212 
213 static int adsi_search_input(struct ast_channel *chan)
214 {
215  unsigned char buf[256];
216  int bytes = 0;
217  unsigned char keys[6];
218 
219  memset(keys, 0, sizeof(keys));
220 
221  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
222  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
223  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
224  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Query: ***", "");
225  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
226  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Search", "Search", "#", 1);
227  bytes += ast_adsi_set_keys(buf + bytes, keys);
228  bytes += ast_adsi_voice_mode(buf + bytes, 0);
229 
230  ast_debug(3, "Sending ADSI search input screen on %s\n", ast_channel_name(chan));
231 
232  return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
233 }
234 
235 static int adsi_confirm_match(struct ast_channel *chan, int seq, int total, const char *exten, const char *name, int showexten)
236 {
237  unsigned char buf[4096];
238  int alignments[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
239  char *lines[5] = {NULL, NULL, NULL, NULL, NULL};
240  int x, bytes = 0;
241  unsigned char keys[8];
242  char matchbuf[32];
243 
244  snprintf(matchbuf, sizeof(matchbuf), "%d of %d", seq + 1, total); /* Make it 1-indexed for user consumption */
245 
246  lines[0] = " "; /* Leave the first line empty so the following lines stand out more */
247  lines[1] = matchbuf;
248  lines[2] = (char*) name;
249 
250  if (showexten) {
251  /* If say extension option is set, show it for ADSI as well */
252  lines[3] = (char*) exten;
253  }
254 
255  /* Don't use ast_adsi_print here, this way we can send it all at once instead of in 2 transmissions */
256  for (x = 0; lines[x]; x++) {
257  bytes += ast_adsi_display(buf + bytes, ADSI_INFO_PAGE, x + 1, alignments[x], 0, lines[x], "");
258  }
259  bytes += ast_adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
260 
261  keys[3] = ADSI_KEY_APPS + 3;
262  keys[4] = ADSI_KEY_APPS + 4;
263  keys[5] = ADSI_KEY_APPS + 5;
264  /* You might think we only need to set the keys up the first time, but nope, we've got to do it each time. */
265  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Dial", "Dial", "1", 0);
266  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Next", "Next", "*", 0);
267  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 0);
268  bytes += ast_adsi_set_keys(buf + bytes, keys);
269  bytes += ast_adsi_voice_mode(buf + bytes, 0);
270 
271  ast_debug(3, "Sending ADSI confirmation menu for %s\n", name);
272 
273  return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
274 }
275 
276 static int compare(const char *text, const char *template)
277 {
278  char digit;
279 
280  if (ast_strlen_zero(text)) {
281  return -1;
282  }
283 
284  while (*template) {
285  digit = toupper(*text++);
286  switch (digit) {
287  case 0:
288  return -1;
289  case '1':
290  digit = '1';
291  break;
292  case '2':
293  case 'A':
294  case 'B':
295  case 'C':
296  digit = '2';
297  break;
298  case '3':
299  case 'D':
300  case 'E':
301  case 'F':
302  digit = '3';
303  break;
304  case '4':
305  case 'G':
306  case 'H':
307  case 'I':
308  digit = '4';
309  break;
310  case '5':
311  case 'J':
312  case 'K':
313  case 'L':
314  digit = '5';
315  break;
316  case '6':
317  case 'M':
318  case 'N':
319  case 'O':
320  digit = '6';
321  break;
322  case '7':
323  case 'P':
324  case 'Q':
325  case 'R':
326  case 'S':
327  digit = '7';
328  break;
329  case '8':
330  case 'T':
331  case 'U':
332  case 'V':
333  digit = '8';
334  break;
335  case '9':
336  case 'W':
337  case 'X':
338  case 'Y':
339  case 'Z':
340  digit = '9';
341  break;
342 
343  default:
344  if (digit > ' ')
345  return -1;
346  continue;
347  }
348 
349  if (*template++ != digit)
350  return -1;
351  }
352 
353  return 0;
354 }
355 
356 static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
357 {
358  if (!ast_goto_if_exists(chan, S_OR(dialcontext, ast_channel_context(chan)), ext, 1)) {
359  return 0;
360  } else {
361  ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
362  "Not Exiting the Directory!\n", ext);
363  return -1;
364  }
365 }
366 
367 /* play name of mailbox owner.
368  * returns: -1 for bad or missing extension
369  * '1' for selected entry from directory
370  * '*' for skipped entry from directory
371  */
372 static int play_mailbox_owner(struct ast_channel *chan, const char *context,
373  const char *ext, const char *name, struct ast_flags *flags)
374 {
375  int res = 0;
376  char *mailbox_id;
377 
378  mailbox_id = ast_alloca(strlen(ext) + strlen(context) + 2);
379  sprintf(mailbox_id, "%s@%s", ext, context); /* Safe */
380 
381  res = ast_app_sayname(chan, mailbox_id);
382  if (res >= 0) {
383  ast_stopstream(chan);
384  /* If Option 'e' was specified, also read the extension number with the name */
385  if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
386  ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
387  res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
388  }
389  } else {
390  res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
391  if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
392  ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
393  res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
394  }
395  }
396 
397  return res;
398 }
399 
400 static int select_entry(struct ast_channel *chan, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
401 {
402  ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, S_OR(dialcontext, item->context));
403 
404  if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
405  /* We still want to set the exten though */
406  ast_channel_exten_set(chan, item->exten);
407  } else if (ast_test_flag(flags, OPT_SKIP)) {
408  /* Skip calling the extension, only set it in the channel variable. */
409  pbx_builtin_setvar_helper(chan, "DIRECTORY_EXTEN", item->exten);
410  } else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
411  ast_log(LOG_WARNING,
412  "Can't find extension '%s' in context '%s'. "
413  "Did you pass the wrong context to Directory?\n",
414  item->exten, S_OR(dialcontext, item->context));
415  return -1;
416  }
417 
418  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "SELECTED");
419  return 0;
420 }
421 
422 static int select_item_pause(struct ast_channel *chan, struct ast_flags *flags, char *opts[])
423 {
424  int res = 0, opt_pause = 0;
425 
426  if (ast_test_flag(flags, OPT_PAUSE) && !ast_strlen_zero(opts[OPT_ARG_PAUSE])) {
427  opt_pause = atoi(opts[OPT_ARG_PAUSE]);
428  if (opt_pause > 3000) {
429  opt_pause = 3000;
430  }
431  res = ast_waitfordigit(chan, opt_pause);
432  }
433  return res;
434 }
435 
436 static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags, char *opts[])
437 {
438  struct directory_item *item, **ptr;
439  int i, res, loop;
440 
441  /* option p(n): cellphone pause option */
442  /* allow early press of selection key */
443  res = select_item_pause(chan, flags, opts);
444 
445  for (ptr = items, i = 0; i < count; i++, ptr++) {
446  item = *ptr;
447 
448  if (ast_test_flag(flags, OPT_ADSI) && adsi_confirm_match(chan, i, count, item->exten, item->name, ast_test_flag(flags, OPT_SAYEXTENSION))) {
449  return -1;
450  }
451 
452  for (loop = 3 ; loop > 0; loop--) {
453  if (!res)
454  res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
455  if (!res)
456  res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
457  if (!res)
458  res = ast_waitfordigit(chan, 3000);
459  ast_stopstream(chan);
460 
461  if (res == '0') { /* operator selected */
462  goto_exten(chan, dialcontext, "o");
463  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "OPERATOR");
464  return '0';
465  } else if (res == '1') { /* Name selected */
466  return select_entry(chan, dialcontext, item, flags) ? -1 : 1;
467  } else if (res == '*') {
468  /* Skip to next match in list */
469  break;
470  } else if (res == '#') {
471  /* Exit reading, continue in dialplan */
472  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "USEREXIT");
473  return res;
474  }
475 
476  if (res < 0)
477  return -1;
478 
479  res = 0;
480  }
481  res = 0;
482  }
483 
484  /* Nothing was selected */
485  return 0;
486 }
487 
488 static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags, char *opts[])
489 {
490  struct directory_item **block, *item;
491  int i, limit, res = 0;
492  char buf[7+12]; /* INT_MIN has a length of 12 chars */
493 
494  /* option p(n): cellphone pause option */
495  select_item_pause(chan, flags, opts);
496 
497  for (block = items; count; block += limit, count -= limit) {
498  limit = count;
499  if (limit > 8)
500  limit = 8;
501 
502  for (i = 0; i < limit && !res; i++) {
503  item = block[i];
504 
505  snprintf(buf, sizeof(buf), "digits/%d", i + 1);
506  /* Press <num> for <name>, [ extension <ext> ] */
507  res = ast_streamfile(chan, "dir-multi1", ast_channel_language(chan));
508  if (!res)
509  res = ast_waitstream(chan, AST_DIGIT_ANY);
510  if (!res)
511  res = ast_streamfile(chan, buf, ast_channel_language(chan));
512  if (!res)
513  res = ast_waitstream(chan, AST_DIGIT_ANY);
514  if (!res)
515  res = ast_streamfile(chan, "dir-multi2", ast_channel_language(chan));
516  if (!res)
517  res = ast_waitstream(chan, AST_DIGIT_ANY);
518  if (!res)
519  res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
520  if (!res)
521  res = ast_waitstream(chan, AST_DIGIT_ANY);
522  if (!res)
523  res = ast_waitfordigit(chan, 800);
524  }
525 
526  /* Press "9" for more names. */
527  if (!res && count > limit) {
528  res = ast_streamfile(chan, "dir-multi9", ast_channel_language(chan));
529  if (!res)
530  res = ast_waitstream(chan, AST_DIGIT_ANY);
531  }
532 
533  if (!res) {
534  res = ast_waitfordigit(chan, 3000);
535  }
536 
537  if (res && res > '0' && res < '1' + limit) {
538  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "SELECTED");
539  return select_entry(chan, dialcontext, block[res - '1'], flags) ? -1 : 1;
540  }
541 
542  if (res < 0)
543  return -1;
544 
545  res = 0;
546  }
547 
548  /* Nothing was selected */
549  return 0;
550 }
551 
553 
554 static struct ast_config *realtime_directory(char *context, const char *filename)
555 {
556  struct ast_config *cfg;
557  struct ast_config *rtdata = NULL;
558  struct ast_category *cat;
559  struct ast_variable *var;
560  char *category = NULL;
561  const char *fullname;
562  const char *hidefromdir, *searchcontexts = NULL;
563  struct ast_flags config_flags = { 0 };
564  struct ast_str *tmp = ast_str_thread_get(&commonbuf, 100);
565 
566  if (!tmp) {
567  return NULL;
568  }
569 
570  /* Load flat file config. */
571  cfg = ast_config_load(filename, config_flags);
572 
573  if (!cfg) {
574  /* Loading config failed. */
575  ast_log(LOG_WARNING, "Loading config failed.\n");
576  return NULL;
577  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
578  ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", filename);
579  return NULL;
580  }
581 
582  /* Get realtime entries, categorized by their mailbox number
583  and present in the requested context */
584  if (ast_strlen_zero(context) && (searchcontexts = ast_variable_retrieve(cfg, "general", "searchcontexts"))) {
585  if (ast_true(searchcontexts)) {
586  rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", SENTINEL);
587  context = NULL;
588  } else {
589  rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", "default", SENTINEL);
590  context = "default";
591  }
592  } else if (!ast_strlen_zero(context)) {
593  rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL);
594  }
595 
596  /* if there are no results, just return the entries from the config file */
597  if (!rtdata) {
598  return cfg;
599  }
600 
601  while ((category = ast_category_browse(rtdata, category))) {
602  const char *mailbox = ast_variable_retrieve(rtdata, category, "mailbox");
603  const char *ctx = ast_variable_retrieve(rtdata, category, "context");
604 
605  if (ast_strlen_zero(mailbox)) {
606  ast_debug(3, "Skipping result with missing or empty mailbox\n");
607  continue;
608  }
609 
610  fullname = ast_variable_retrieve(rtdata, category, "fullname");
611  hidefromdir = ast_variable_retrieve(rtdata, category, "hidefromdir");
612  if (ast_true(hidefromdir)) {
613  /* Skip hidden */
614  continue;
615  }
616 
617  /* password,Full Name,email,pager,options */
618  ast_str_set(&tmp, 0, "no-password,%s,,,", S_OR(fullname, ""));
619  if (ast_variable_retrieve(rtdata, category, "alias")) {
620  struct ast_variable *alias;
621  for (alias = ast_variable_browse(rtdata, category); alias; alias = alias->next) {
622  if (!strcasecmp(alias->name, "alias")) {
623  ast_str_append(&tmp, 0, "|alias=%s", alias->value);
624  }
625  }
626  }
627 
628  /* Does the context exist within the config file? If not, make one */
629  if (!(cat = ast_category_get(cfg, ctx, NULL))) {
630  if (!(cat = ast_category_new_dynamic(ctx))) {
631  ast_log(LOG_WARNING, "Out of memory\n");
632  ast_config_destroy(cfg);
633  if (rtdata) {
634  ast_config_destroy(rtdata);
635  }
636  return NULL;
637  }
638  ast_category_append(cfg, cat);
639  }
640 
641  if ((var = ast_variable_new(mailbox, ast_str_buffer(tmp), ""))) {
642  ast_variable_append(cat, var);
643  } else {
644  ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
645  }
646  }
647  ast_config_destroy(rtdata);
648 
649  return cfg;
650 }
651 
652 static int check_match(struct directory_item **result, const char *item_context, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
653 {
654  struct directory_item *item;
655  const char *key = NULL;
656  int namelen;
657 
658  if (ast_strlen_zero(item_fullname)) {
659  return 0;
660  }
661 
662  /* Set key to last name or first name depending on search mode */
663  if (!use_first_name)
664  key = strchr(item_fullname, ' ');
665 
666  if (key)
667  key++;
668  else
669  key = item_fullname;
670 
671  if (compare(key, pattern_ext))
672  return 0;
673 
674  ast_debug(1, "Found match %s@%s\n", item_ext, item_context);
675 
676  /* Match */
677  item = ast_calloc(1, sizeof(*item));
678  if (!item)
679  return -1;
680  ast_copy_string(item->context, item_context, sizeof(item->context));
681  ast_copy_string(item->name, item_fullname, sizeof(item->name));
682  ast_copy_string(item->exten, item_ext, sizeof(item->exten));
683 
684  ast_copy_string(item->key, key, sizeof(item->key));
685  if (key != item_fullname) {
686  /* Key is the last name. Append first name to key in order to sort Last,First */
687  namelen = key - item_fullname - 1;
688  if (namelen > sizeof(item->key) - strlen(item->key) - 1)
689  namelen = sizeof(item->key) - strlen(item->key) - 1;
690  strncat(item->key, item_fullname, namelen);
691  }
692 
693  *result = item;
694  return 1;
695 }
696 
697 typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
698 
699 static int search_directory_sub(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
700 {
701  struct ast_variable *v;
702  struct ast_str *buf = ast_str_thread_get(&commonbuf, 100);
703  char *name;
704  char *options;
705  char *alias;
706  char *cat;
707  struct directory_item *item;
708  int res;
709 
710  if (!buf) {
711  return -1;
712  }
713 
714  ast_debug(2, "Pattern: %s\n", ext);
715 
716  for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
717  ast_str_set(&buf, 0, "%s", v->value);
718  options = ast_str_buffer(buf);
719 
720  /* password,Full Name,email,pager,options */
721  strsep(&options, ","); /* Skip password */
722  name = strsep(&options, ","); /* Save full name */
723  strsep(&options, ","); /* Skip email */
724  strsep(&options, ","); /* Skip pager */
725  /* options is now the options field if it exists. */
726 
727  if (options && strcasestr(options, "hidefromdir=yes")) {
728  /* Ignore hidden */
729  continue;
730  }
731  if (ast_strlen_zero(name)) {
732  /* No name to compare against */
733  continue;
734  }
735 
736  res = 0;
737  if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
738  res = check_match(&item, context, name, v->name, ext, 0 /* use_first_name */);
739  }
740  if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
741  res = check_match(&item, context, name, v->name, ext, 1 /* use_first_name */);
742  }
743  if (!res && ast_test_flag(&flags, OPT_ALIAS)
744  && options && (alias = strcasestr(options, "alias="))) {
745  char *a;
746 
747  ast_debug(1, "Found alias: %s\n", alias);
748  while ((a = strsep(&alias, "|"))) {
749  if (!strncasecmp(a, "alias=", 6)) {
750  if ((res = check_match(&item, context, a + 6, v->name, ext, 1))) {
751  break;
752  }
753  }
754  }
755  }
756 
757  if (!res) {
758  continue;
759  } else if (res < 0) {
760  return -1;
761  }
762 
763  AST_LIST_INSERT_TAIL(alist, item, entry);
764  }
765 
766  if (ucfg) {
767  for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
768  const char *position;
769 
770  if (!strcasecmp(cat, "general")) {
771  continue;
772  }
773  if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory"))) {
774  continue;
775  }
776 
777  /* Find all candidate extensions */
778  if (!(position = ast_variable_retrieve(ucfg, cat, "fullname"))) {
779  continue;
780  }
781 
782  res = 0;
783  if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
784  res = check_match(&item, context, position, cat, ext, 0 /* use_first_name */);
785  }
786  if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
787  res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */);
788  }
789  if (!res && ast_test_flag(&flags, OPT_ALIAS)) {
790  for (v = ast_variable_browse(ucfg, cat); v; v = v->next) {
791  if (!strcasecmp(v->name, "alias")
792  && (res = check_match(&item, context, v->value, cat, ext, 1))) {
793  break;
794  }
795  }
796  }
797 
798  if (!res) {
799  continue;
800  } else if (res < 0) {
801  return -1;
802  }
803 
804  AST_LIST_INSERT_TAIL(alist, item, entry);
805  }
806  }
807  return 0;
808 }
809 
810 static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
811 {
812  const char *searchcontexts = ast_variable_retrieve(vmcfg, "general", "searchcontexts");
813  if (ast_strlen_zero(context)) {
814  if (!ast_strlen_zero(searchcontexts) && ast_true(searchcontexts)) {
815  /* Browse each context for a match */
816  int res;
817  const char *catg;
818  for (catg = ast_category_browse(vmcfg, NULL); catg; catg = ast_category_browse(vmcfg, catg)) {
819  if (!strcmp(catg, "general") || !strcmp(catg, "zonemessages")) {
820  continue;
821  }
822 
823  if ((res = search_directory_sub(catg, vmcfg, ucfg, ext, flags, alist))) {
824  return res;
825  }
826  }
827  return 0;
828  } else {
829  ast_debug(1, "Searching by category default\n");
830  return search_directory_sub("default", vmcfg, ucfg, ext, flags, alist);
831  }
832  } else {
833  /* Browse only the listed context for a match */
834  ast_debug(1, "Searching by category %s\n", context);
835  return search_directory_sub(context, vmcfg, ucfg, ext, flags, alist);
836  }
837 }
838 
839 static void sort_items(struct directory_item **sorted, int count)
840 {
841  int reordered, i;
842  struct directory_item **ptr, *tmp;
843 
844  if (count < 2)
845  return;
846 
847  /* Bubble-sort items by the key */
848  do {
849  reordered = 0;
850  for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
851  if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
852  tmp = ptr[0];
853  ptr[0] = ptr[1];
854  ptr[1] = tmp;
855  reordered++;
856  }
857  }
858  } while (reordered);
859 }
860 
861 static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, int digits, struct ast_flags *flags, char *opts[])
862 {
863  /* Read in the first three digits.. "digit" is the first digit, already read */
864  int res = 0;
865  itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
866  struct directory_item *item, **ptr, **sorted = NULL;
867  int count, i;
868  char ext[10] = "";
869 
870  if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
871  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "OPERATOR");
872  return digit;
873  }
874 
875  if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
876  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "ASSISTANT");
877  return digit;
878  }
879 
880  ext[0] = digit;
881  if (ast_readstring(chan, ext + 1, digits - 1, 3000, 3000, "#") < 0)
882  return -1;
883 
884  res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
885  if (res)
886  goto exit;
887 
888  /* Count items in the list */
889  count = 0;
890  AST_LIST_TRAVERSE(&alist, item, entry) {
891  count++;
892  }
893 
894  if (count < 1) {
895  res = ast_streamfile(chan, "dir-nomatch", ast_channel_language(chan));
896  goto exit;
897  }
898 
899 
900  /* Create plain array of pointers to items (for sorting) */
901  sorted = ast_calloc(count, sizeof(*sorted));
902 
903  ptr = sorted;
904  AST_LIST_TRAVERSE(&alist, item, entry) {
905  *ptr++ = item;
906  }
907 
908  /* Sort items */
909  sort_items(sorted, count);
910 
911  if (DEBUG_ATLEAST(2)) {
912  ast_log(LOG_DEBUG, "Listing matching entries:\n");
913  for (ptr = sorted, i = 0; i < count; i++, ptr++) {
914  ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
915  }
916  }
917 
918  if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
919  /* Offer multiple entries at the same time */
920  res = select_item_menu(chan, sorted, count, dialcontext, flags, opts);
921  } else {
922  /* Offer entries one by one */
923  res = select_item_seq(chan, sorted, count, dialcontext, flags, opts);
924  }
925 
926  if (!res) {
927  res = ast_streamfile(chan, "dir-nomore", ast_channel_language(chan));
928  }
929 
930 exit:
931  if (sorted)
932  ast_free(sorted);
933 
934  while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
935  ast_free(item);
936 
937  return res;
938 }
939 
940 static int directory_exec(struct ast_channel *chan, const char *data)
941 {
942  int res = 0, digit = 3;
943  struct ast_config *cfg, *ucfg;
944  const char *dirintro;
945  char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { 0, };
946  struct ast_flags flags = { 0 };
947  struct ast_flags config_flags = { 0 };
948  enum { FIRST, LAST, BOTH } which = LAST;
949  char digits[9] = "digits/3";
951  AST_APP_ARG(vmcontext);
952  AST_APP_ARG(dialcontext);
953  AST_APP_ARG(options);
954  );
955 
956  parse = ast_strdupa(data);
957 
958  AST_STANDARD_APP_ARGS(args, parse);
959 
960  if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
961  return -1;
962 
963  cfg = realtime_directory(args.vmcontext, S_OR(opts[OPT_ARG_FILENAME], VOICEMAIL_CONFIG));
964 
965  if (!cfg) {
966  ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
967  return -1;
968  }
969 
970  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
971  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Aborting.\n");
972  ucfg = NULL;
973  }
974 
975  dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
976  if (ast_strlen_zero(dirintro))
977  dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
978  /* the above prompts probably should be modified to include 0 for dialing operator
979  and # for exiting (continues in dialplan) */
980 
981  if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
982  if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
983  digit = atoi(opts[OPT_ARG_EITHER]);
984  }
985  which = BOTH;
986  } else if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
987  if (!ast_strlen_zero(opts[OPT_ARG_FIRSTNAME])) {
988  digit = atoi(opts[OPT_ARG_FIRSTNAME]);
989  }
990  which = FIRST;
991  } else {
992  if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
993  digit = atoi(opts[OPT_ARG_LASTNAME]);
994  }
995  which = LAST;
996  }
997 
998  /* If no options specified, search by last name */
999  if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
1000  ast_set_flag(&flags, OPT_LISTBYLASTNAME);
1001  which = LAST;
1002  }
1003 
1004  if (digit > 9) {
1005  digit = 9;
1006  } else if (digit < 1) {
1007  digit = 3;
1008  }
1009  digits[7] = digit + '0';
1010 
1011  if (ast_test_flag(&flags, OPT_ADSI)) {
1012  if (!ast_adsi_available(chan)) {
1013  ast_log(LOG_WARNING, "ADSI not available on %s\n", ast_channel_name(chan));
1014  ast_clear_flag(&flags, OPT_ADSI);
1015  } else {
1016  res = ast_adsi_load_session(chan, NULL, 0, 1);
1017  if (res < 0) {
1018  return res;
1019  }
1020  }
1021  }
1022 
1023  if (ast_channel_state(chan) != AST_STATE_UP) {
1024  if (!ast_test_flag(&flags, OPT_NOANSWER)) {
1025  /* Otherwise answer unless we're supposed to read while on-hook */
1026  res = ast_answer(chan);
1027  }
1028  }
1029  for (;;) {
1030  if (ast_test_flag(&flags, OPT_ADSI) && adsi_search_input(chan)) {
1031  return -1;
1032  }
1033  if (!ast_strlen_zero(dirintro) && !res) {
1034  res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
1035  } else if (!res) {
1036  /* Stop playing sounds as soon as we have a digit. */
1037  res = ast_stream_and_wait(chan, "dir-welcome", AST_DIGIT_ANY);
1038  if (!res) {
1039  res = ast_stream_and_wait(chan, "dir-pls-enter", AST_DIGIT_ANY);
1040  }
1041  if (!res) {
1042  res = ast_stream_and_wait(chan, digits, AST_DIGIT_ANY);
1043  }
1044  if (!res) {
1045  res = ast_stream_and_wait(chan,
1046  which == FIRST ? "dir-first" :
1047  which == LAST ? "dir-last" :
1048  "dir-firstlast", AST_DIGIT_ANY);
1049  }
1050  if (!res) {
1051  res = ast_stream_and_wait(chan, "dir-usingkeypad", AST_DIGIT_ANY);
1052  }
1053  }
1054  ast_stopstream(chan);
1055  if (!res)
1056  res = ast_waitfordigit(chan, 5000);
1057 
1058  if (res <= 0) {
1059  if (res == 0) {
1060  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "TIMEOUT");
1061  }
1062  break;
1063  }
1064 
1065  res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags, opts);
1066  if (res)
1067  break;
1068 
1069  res = ast_waitstream(chan, AST_DIGIT_ANY);
1070  ast_stopstream(chan);
1071  if (res < 0) {
1072  break;
1073  }
1074  }
1075 
1076  if (ucfg)
1077  ast_config_destroy(ucfg);
1078  ast_config_destroy(cfg);
1079 
1080  if (ast_check_hangup(chan)) {
1081  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "HANGUP");
1082  } else if (res < 0) {
1083  /* If the res < 0 and we didn't hangup, an unaccounted for error must have happened. */
1084  pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "FAILED");
1085  }
1086 
1087  return res < 0 ? -1 : 0;
1088 }
1089 
1090 static int unload_module(void)
1091 {
1092  int res;
1093  res = ast_unregister_application(app);
1094  return res;
1095 }
1096 
1097 static int load_module(void)
1098 {
1099  return ast_register_application_xml(app, directory_exec);
1100 }
1101 
1102 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Main Channel structure associated with a channel.
int ast_adsi_voice_mode(unsigned char *buf, int when)
Puts CPE in voice mode.
Definition: adsi.c:252
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
Asterisk main include file. File version handling, generic pbx functions.
int ast_app_sayname(struct ast_channel *chan, const char *mailbox_id)
Play a recorded user name for the mailbox to the specified channel.
Definition: main/app.c:637
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
Structure for variables, used for configurations and for channel variables.
int ast_adsi_set_keys(unsigned char *buf, unsigned char *keys)
Set which soft keys should be displayed.
Definition: adsi.c:307
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
ast_channel_state
ast_channel states
Definition: channelstate.h:35
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
ADSI Support (built upon Caller*ID)
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
Utility functions.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2833
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
#define ast_config_load(filename, flags)
Load a config file.
int ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
Set input format.
Definition: adsi.c:329
Asterisk file paths, configured in asterisk.conf.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define AST_MAX_EXTENSION
Definition: channel.h:134
Definition: term.c:57
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 ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
int ast_say_character_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)
function to pronounce character and phonetic strings
Definition: channel.c:8271
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Definition: f2c.h:128
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
Support for dynamic strings.
Definition: strings.h:623
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8781
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_MAX_CONTEXT
Definition: channel.h:135
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1878
int ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
Creates "load soft key" parameters.
Definition: adsi.c:296
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
Structure used to handle boolean flags.
Definition: utils.h:199
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
Definition: linkedlists.h:252
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3175
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#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
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:773
Definition: search.h:40
int ast_adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
Set input information.
Definition: adsi.c:318
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders)
Reads multiple digits.
Definition: channel.c:6558
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1111
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.