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