157 static const char app[] =
"Directory";
162 #define VOICEMAIL_CONFIG "voicemail.conf"
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),
176 OPT_ADSI = (1 << 10),
180 OPT_ARG_FIRSTNAME = 0,
181 OPT_ARG_LASTNAME = 1,
184 OPT_ARG_FILENAME = 4,
186 OPT_ARG_ARRAY_SIZE = 5,
213 static int adsi_search_input(
struct ast_channel *chan)
215 unsigned char buf[256];
217 unsigned char keys[6];
219 memset(keys, 0,
sizeof(keys));
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,
" ",
"");
230 ast_debug(3,
"Sending ADSI search input screen on %s\n", ast_channel_name(chan));
232 return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
235 static int adsi_confirm_match(
struct ast_channel *chan,
int seq,
int total,
const char *exten,
const char *name,
int showexten)
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};
241 unsigned char keys[8];
244 snprintf(matchbuf,
sizeof(matchbuf),
"%d of %d", seq + 1, total);
248 lines[2] = (
char*) name;
252 lines[3] = (
char*) exten;
256 for (x = 0; lines[x]; x++) {
257 bytes +=
ast_adsi_display(buf + bytes, ADSI_INFO_PAGE, x + 1, alignments[x], 0, lines[x],
"");
261 keys[3] = ADSI_KEY_APPS + 3;
262 keys[4] = ADSI_KEY_APPS + 4;
263 keys[5] = ADSI_KEY_APPS + 5;
271 ast_debug(3,
"Sending ADSI confirmation menu for %s\n", name);
273 return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
276 static int compare(
const char *text,
const char *
template)
280 if (ast_strlen_zero(text)) {
285 digit = toupper(*text++);
349 if (*
template++ != digit)
356 static int goto_exten(
struct ast_channel *chan,
const char *dialcontext,
char *ext)
361 ast_log(LOG_WARNING,
"Can't find extension '%s' in current context. "
362 "Not Exiting the Directory!\n", ext);
372 static int play_mailbox_owner(
struct ast_channel *chan,
const char *context,
373 const char *ext,
const char *name,
struct ast_flags *flags)
378 mailbox_id =
ast_alloca(strlen(ext) + strlen(context) + 2);
379 sprintf(mailbox_id,
"%s@%s", ext, context);
385 if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
391 if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
402 ast_debug(1,
"Selecting '%s' - %s@%s\n", item->name, item->exten,
S_OR(dialcontext, item->context));
404 if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
406 ast_channel_exten_set(chan, item->exten);
407 }
else if (ast_test_flag(flags, OPT_SKIP)) {
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));
424 int res = 0, opt_pause = 0;
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) {
443 res = select_item_pause(chan, flags, opts);
445 for (ptr = items, i = 0; i < count; i++, ptr++) {
448 if (ast_test_flag(flags, OPT_ADSI) && adsi_confirm_match(chan, i, count, item->exten, item->name, ast_test_flag(flags, OPT_SAYEXTENSION))) {
452 for (loop = 3 ; loop > 0; loop--) {
454 res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
462 goto_exten(chan, dialcontext,
"o");
465 }
else if (res ==
'1') {
466 return select_entry(chan, dialcontext, item, flags) ? -1 : 1;
467 }
else if (res ==
'*') {
470 }
else if (res ==
'#') {
491 int i, limit, res = 0;
495 select_item_pause(chan, flags, opts);
497 for (block = items; count; block += limit, count -= limit) {
502 for (i = 0; i < limit && !res; i++) {
505 snprintf(buf,
sizeof(buf),
"digits/%d", i + 1);
507 res =
ast_streamfile(chan,
"dir-multi1", ast_channel_language(chan));
515 res =
ast_streamfile(chan,
"dir-multi2", ast_channel_language(chan));
519 res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
527 if (!res && count > limit) {
528 res =
ast_streamfile(chan,
"dir-multi9", ast_channel_language(chan));
537 if (res && res >
'0' && res <
'1' + limit) {
539 return select_entry(chan, dialcontext, block[res -
'1'], flags) ? -1 : 1;
554 static struct ast_config *realtime_directory(
char *context,
const char *filename)
560 char *category = NULL;
561 const char *fullname;
562 const char *hidefromdir, *searchcontexts = NULL;
575 ast_log(LOG_WARNING,
"Loading config failed.\n");
577 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
578 ast_log(LOG_ERROR,
"Config file %s is in an invalid format. Aborting.\n", filename);
584 if (ast_strlen_zero(context) && (searchcontexts = ast_variable_retrieve(cfg,
"general",
"searchcontexts"))) {
592 }
else if (!ast_strlen_zero(context)) {
602 const char *mailbox = ast_variable_retrieve(rtdata, category,
"mailbox");
603 const char *ctx = ast_variable_retrieve(rtdata, category,
"context");
605 if (ast_strlen_zero(mailbox)) {
606 ast_debug(3,
"Skipping result with missing or empty mailbox\n");
610 fullname = ast_variable_retrieve(rtdata, category,
"fullname");
611 hidefromdir = ast_variable_retrieve(rtdata, category,
"hidefromdir");
619 if (ast_variable_retrieve(rtdata, category,
"alias")) {
621 for (alias = ast_variable_browse(rtdata, category); alias; alias = alias->
next) {
622 if (!strcasecmp(alias->
name,
"alias")) {
631 ast_log(LOG_WARNING,
"Out of memory\n");
642 ast_variable_append(cat, var);
644 ast_log(LOG_WARNING,
"Out of memory adding mailbox '%s'\n", mailbox);
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)
655 const char *key = NULL;
658 if (ast_strlen_zero(item_fullname)) {
664 key = strchr(item_fullname,
' ');
671 if (compare(key, pattern_ext))
674 ast_debug(1,
"Found match %s@%s\n", item_ext, item_context);
685 if (key != item_fullname) {
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);
716 for (v = ast_variable_browse(vmcfg, context); v; v = v->
next) {
721 strsep(&options,
",");
722 name = strsep(&options,
",");
723 strsep(&options,
",");
724 strsep(&options,
",");
727 if (options && strcasestr(options,
"hidefromdir=yes")) {
731 if (ast_strlen_zero(name)) {
737 if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
738 res = check_match(&item, context, name, v->
name, ext, 0 );
740 if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
741 res = check_match(&item, context, name, v->
name, ext, 1 );
743 if (!res && ast_test_flag(&flags, OPT_ALIAS)
744 && options && (alias = strcasestr(options,
"alias="))) {
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))) {
759 }
else if (res < 0) {
768 const char *position;
770 if (!strcasecmp(cat,
"general")) {
778 if (!(position = ast_variable_retrieve(ucfg, cat,
"fullname"))) {
783 if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
784 res = check_match(&item, context, position, cat, ext, 0 );
786 if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
787 res = check_match(&item, context, position, cat, ext, 1 );
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))) {
800 }
else if (res < 0) {
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)
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)) {
819 if (!strcmp(catg,
"general") || !strcmp(catg,
"zonemessages")) {
823 if ((res = search_directory_sub(catg, vmcfg, ucfg, ext, flags, alist))) {
829 ast_debug(1,
"Searching by category default\n");
830 return search_directory_sub(
"default", vmcfg, ucfg, ext, flags, alist);
834 ast_debug(1,
"Searching by category %s\n", context);
835 return search_directory_sub(context, vmcfg, ucfg, ext, flags, alist);
850 for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
851 if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
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[])
870 if (digit ==
'0' && !goto_exten(chan, dialcontext,
"o")) {
875 if (digit ==
'*' && !goto_exten(chan, dialcontext,
"a")) {
881 if (
ast_readstring(chan, ext + 1, digits - 1, 3000, 3000,
"#") < 0)
884 res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
895 res =
ast_streamfile(chan,
"dir-nomatch", ast_channel_language(chan));
909 sort_items(sorted, count);
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);
918 if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
920 res = select_item_menu(chan, sorted, count, dialcontext, flags, opts);
923 res = select_item_seq(chan, sorted, count, dialcontext, flags, opts);
927 res =
ast_streamfile(chan,
"dir-nomore", ast_channel_language(chan));
940 static int directory_exec(
struct ast_channel *chan,
const char *data)
942 int res = 0, digit = 3;
944 const char *dirintro;
945 char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { 0, };
948 enum { FIRST, LAST, BOTH } which = LAST;
949 char digits[9] =
"digits/3";
963 cfg = realtime_directory(args.vmcontext,
S_OR(opts[OPT_ARG_FILENAME], VOICEMAIL_CONFIG));
966 ast_log(LOG_ERROR,
"Unable to read the configuration data!\n");
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");
975 dirintro = ast_variable_retrieve(cfg, args.vmcontext,
"directoryintro");
976 if (ast_strlen_zero(dirintro))
977 dirintro = ast_variable_retrieve(cfg,
"general",
"directoryintro");
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]);
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]);
992 if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
993 digit = atoi(opts[OPT_ARG_LASTNAME]);
999 if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
1000 ast_set_flag(&flags, OPT_LISTBYLASTNAME);
1006 }
else if (digit < 1) {
1009 digits[7] = digit +
'0';
1011 if (ast_test_flag(&flags, OPT_ADSI)) {
1013 ast_log(LOG_WARNING,
"ADSI not available on %s\n", ast_channel_name(chan));
1014 ast_clear_flag(&flags, OPT_ADSI);
1024 if (!ast_test_flag(&flags, OPT_NOANSWER)) {
1030 if (ast_test_flag(&flags, OPT_ADSI) && adsi_search_input(chan)) {
1033 if (!ast_strlen_zero(dirintro) && !res) {
1046 which == FIRST ?
"dir-first" :
1047 which == LAST ?
"dir-last" :
1048 "dir-firstlast", AST_DIGIT_ANY);
1065 res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags, opts);
1082 }
else if (res < 0) {
1087 return res < 0 ? -1 : 0;
1090 static int unload_module(
void)
1097 static int load_module(
void)
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Main Channel structure associated with a channel.
int ast_adsi_voice_mode(unsigned char *buf, int when)
Puts CPE in voice mode.
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
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.
#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.
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.
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...
ast_channel_state
ast_channel states
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
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.
ADSI Support (built upon Caller*ID)
int ast_unregister_application(const char *app)
Unregister an application.
#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.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
#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.
Asterisk file paths, configured in asterisk.conf.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define AST_MAX_EXTENSION
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.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
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
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.
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
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".
Support for dynamic strings.
#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)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define ast_calloc(num, len)
A wrapper for calloc()
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.
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.
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
Structure used to handle boolean flags.
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
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.
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
int ast_answer(struct ast_channel *chan)
Answer a channel.
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
int ast_adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
Set input information.
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders)
Reads multiple digits.
#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.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
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.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
#define AST_APP_ARG(name)
Define an application argument.