Asterisk - The Open Source Telephony Project GIT-master-7e7a603
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 ***/
157static 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
164enum {
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
179enum {
185 /* This *must* be the last value in this enum! */
187};
188
193 char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
194
196};
197
210 AST_APP_OPTION('d', OPT_ADSI), /* (Would've used 'a', but that was taken already) */
211});
212
213static 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
235static 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];
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
276static 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
356static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
357{
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 */
372static 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);
388 }
389 } else {
392 ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
394 }
395 }
396
397 return res;
398}
399
400static 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)) {
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
422static 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
436static 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
488static 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
554static 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
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");
633 if (rtdata) {
634 ast_config_destroy(rtdata);
635 }
636 return NULL;
637 }
638 ast_category_append(cfg, cat);
639 }
640
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
652static 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
698
699static 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);
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
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
805 }
806 }
807 return 0;
808}
809
810static 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");
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
839static 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
861static 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;
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;
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;
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
930exit:
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
940static 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);
954 );
955
956 parse = ast_strdupa(data);
957
959
960 if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
961 return -1;
962
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
982 if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
983 digit = atoi(opts[OPT_ARG_EITHER]);
984 }
985 which = BOTH;
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 */
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));
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) {
1025 /* Otherwise answer unless we're supposed to read while on-hook */
1026 res = ast_answer(chan);
1027 }
1028 }
1029 for (;;) {
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
1090static int unload_module(void)
1091{
1092 int res;
1094 return res;
1095}
1096
1097static int load_module(void)
1098{
1100}
1101
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:1639
static volatile unsigned int seq
Definition: app_sms.c:120
static char dialcontext[AST_MAX_CONTEXT]
#define var
Definition: ast_expr2f.c:605
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 int tmp()
Definition: bt_open.c:389
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:3175
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:6558
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
#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:1293
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
#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:1840
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:3056
char * strsep(char **str, const char *delims)
char * strcasestr(const char *, const char *)
#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:3622
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:783
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
#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:1111
#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:567
#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:626
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:8781
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:8271
@ 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]
Definition: search.h:40
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