diff --git a/CHANGES b/CHANGES index 88e59fbe4c24a1d2cd78587f8add75759444175a..56769facdcc8439e8da511694f6fac349b7a9ec2 100644 --- a/CHANGES +++ b/CHANGES @@ -33,6 +33,9 @@ Applications using MeetMeAdmin. * app_authenticate now gives the ability to select a prompt other than the default. + * app_directory now pays attention to the searchcontexts setting in + voicemail.conf and will look through all contexts, if no context is + specified in the initial argument. Miscellaneous ------------- diff --git a/apps/app_directory.c b/apps/app_directory.c index 30b8f7f46639e9f65a14e24464c9bf80fca38615..b2a5741ed13e6ffbdd2b712e5b988a4c879ae3f9 100644 --- a/apps/app_directory.c +++ b/apps/app_directory.c @@ -46,7 +46,7 @@ static char *app = "Directory"; static char *synopsis = "Provide directory of voicemail extensions"; static char *descrip = -" Directory(vm-context[,dial-context[,options]]): This application will present\n" +" Directory([vm-context][,dial-context[,options]]): This application will present\n" "the calling channel with a directory of extensions from which they can search\n" "by name. The list of names and corresponding extensions is retrieved from the\n" "voicemail configuration file, voicemail.conf.\n" @@ -56,7 +56,9 @@ static char *descrip = " * - Jump to the 'a' extension, if it exists.\n\n" " Parameters:\n" " vm-context - This is the context within voicemail.conf to use for the\n" -" Directory.\n" +" Directory. If not specified and searchcontexts=no in\n" +" voicemail.conf, then \"default\" will be assumed.\n" +" Otherwise, in not specified, all contexts will be searched.\n" " dial-context - This is the dialplan context to use when looking for an\n" " extension that the user has selected, or when jumping to the\n" " 'o' or 'a' extension.\n\n" @@ -112,6 +114,7 @@ enum { struct directory_item { char exten[AST_MAX_EXTENSION + 1]; char name[AST_MAX_EXTENSION + 1]; + char context[AST_MAX_CONTEXT + 1]; char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */ AST_LIST_ENTRY(directory_item) entry; @@ -234,25 +237,25 @@ static int play_mailbox_owner(struct ast_channel *chan, const char *context, return res; } -static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags) +static int select_entry(struct ast_channel *chan, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags) { - ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext); + ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, S_OR(dialcontext, item->context)); if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) { /* We still want to set the exten though */ ast_copy_string(chan->exten, item->exten, sizeof(chan->exten)); - } else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) { + } else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) { ast_log(LOG_WARNING, "Can't find extension '%s' in context '%s'. " "Did you pass the wrong context to Directory?\n", - item->exten, dialcontext); + item->exten, S_OR(dialcontext, item->context)); return -1; } return 0; } -static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags) +static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags) { struct directory_item *item, **ptr; int i, res, loop; @@ -261,7 +264,7 @@ static int select_item_seq(struct ast_channel *chan, struct directory_item **ite item = *ptr; for (loop = 3 ; loop > 0; loop--) { - res = play_mailbox_owner(chan, context, item->exten, item->name, flags); + res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags); if (!res) res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY); @@ -270,7 +273,7 @@ static int select_item_seq(struct ast_channel *chan, struct directory_item **ite ast_stopstream(chan); if (res == '1') { /* Name selected */ - return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1; + return select_entry(chan, dialcontext, item, flags) ? -1 : 1; } else if (res == '*') { /* Skip to next match in list */ break; @@ -287,7 +290,7 @@ static int select_item_seq(struct ast_channel *chan, struct directory_item **ite return 0; } -static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags) +static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags) { struct directory_item **block, *item; int i, limit, res = 0; @@ -315,7 +318,7 @@ static int select_item_menu(struct ast_channel *chan, struct directory_item **it if (!res) res = ast_waitstream(chan, AST_DIGIT_ANY); if (!res) - res = play_mailbox_owner(chan, context, item->exten, item->name, flags); + res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags); if (!res) res = ast_waitstream(chan, AST_DIGIT_ANY); if (!res) @@ -334,7 +337,7 @@ static int select_item_menu(struct ast_channel *chan, struct directory_item **it } if (res && res > '0' && res < '1' + limit) { - return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1; + return select_entry(chan, dialcontext, block[res - '1'], flags) ? -1 : 1; } if (res < 0) @@ -355,7 +358,7 @@ static struct ast_config *realtime_directory(char *context) struct ast_variable *var; char *mailbox; const char *fullname; - const char *hidefromdir; + const char *hidefromdir, *searchcontexts = NULL; char tmp[100]; struct ast_flags config_flags = { 0 }; @@ -373,49 +376,64 @@ static struct ast_config *realtime_directory(char *context) /* Get realtime entries, categorized by their mailbox number and present in the requested context */ - rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL); + if (ast_strlen_zero(context) && (searchcontexts = ast_variable_retrieve(cfg, "general", "searchcontexts"))) { + if (ast_true(searchcontexts)) { + rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", SENTINEL); + context = NULL; + } else { + rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", "default", SENTINEL); + context = "default"; + } + } else { + rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL); + } /* if there are no results, just return the entries from the config file */ - if (!rtdata) + if (!rtdata) { return cfg; - - /* Does the context exist within the config file? If not, make one */ - cat = ast_category_get(cfg, context); - if (!cat) { - cat = ast_category_new(context, "", 99999); - if (!cat) { - ast_log(LOG_WARNING, "Out of memory\n"); - ast_config_destroy(cfg); - return NULL; - } - ast_category_append(cfg, cat); } mailbox = NULL; while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) { + const char *context = ast_variable_retrieve(rtdata, mailbox, "context"); + fullname = ast_variable_retrieve(rtdata, mailbox, "fullname"); if (ast_true((hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir")))) { /* Skip hidden */ continue; } snprintf(tmp, sizeof(tmp), "no-password,%s", S_OR(fullname, "")); - var = ast_variable_new(mailbox, tmp, ""); - if (var) + + /* Does the context exist within the config file? If not, make one */ + if (!(cat = ast_category_get(cfg, context))) { + if (!(cat = ast_category_new(context, "", 99999))) { + ast_log(LOG_WARNING, "Out of memory\n"); + ast_config_destroy(cfg); + return NULL; + } + ast_category_append(cfg, cat); + } + + if ((var = ast_variable_new(mailbox, tmp, ""))) { ast_variable_append(cat, var); - else + } else { ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox); + } } ast_config_destroy(rtdata); return cfg; } -static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name) +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) { struct directory_item *item; const char *key = NULL; int namelen; + if (ast_strlen_zero(item_fullname)) { + return 0; + } /* Set key to last name or first name depending on search mode */ if (!use_first_name) @@ -429,10 +447,13 @@ static int check_match(struct directory_item **result, const char *item_fullname if (compare(key, pattern_ext)) return 0; + ast_debug(1, "Found match %s@%s\n", item_ext, item_context); + /* Match */ item = ast_calloc(1, sizeof(*item)); if (!item) return -1; + ast_copy_string(item->context, item_context, sizeof(item->context)); ast_copy_string(item->name, item_fullname, sizeof(item->name)); ast_copy_string(item->exten, item_ext, sizeof(item->exten)); @@ -451,7 +472,7 @@ static int check_match(struct directory_item **result, const char *item_fullname typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist; -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 search_directory_sub(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist) { struct ast_variable *v; char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat; @@ -475,10 +496,10 @@ static int search_directory(const char *context, struct ast_config *vmcfg, struc res = 0; if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) { - res = check_match(&item, pos, v->name, ext, 0 /* use_first_name */); + res = check_match(&item, context, pos, v->name, ext, 0 /* use_first_name */); } if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) { - res = check_match(&item, pos, v->name, ext, 1 /* use_first_name */); + res = check_match(&item, context, pos, v->name, ext, 1 /* use_first_name */); } if (!res) @@ -504,10 +525,10 @@ static int search_directory(const char *context, struct ast_config *vmcfg, struc res = 0; if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) { - res = check_match(&item, position, cat, ext, 0 /* use_first_name */); + res = check_match(&item, context, position, cat, ext, 0 /* use_first_name */); } if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) { - res = check_match(&item, position, cat, ext, 1 /* use_first_name */); + res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */); } if (!res) @@ -521,6 +542,35 @@ static int search_directory(const char *context, struct ast_config *vmcfg, struc return 0; } +static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist) +{ + const char *searchcontexts = ast_variable_retrieve(vmcfg, "general", "searchcontexts"); + if (ast_strlen_zero(context)) { + if (!ast_strlen_zero(searchcontexts) && ast_true(searchcontexts)) { + /* Browse each context for a match */ + int res; + const char *catg; + for (catg = ast_category_browse(vmcfg, NULL); catg; catg = ast_category_browse(vmcfg, catg)) { + if (!strcmp(catg, "general") || !strcmp(catg, "zonemessages")) { + continue; + } + + if ((res = search_directory_sub(catg, vmcfg, ucfg, ext, flags, alist))) { + return res; + } + } + return 0; + } else { + ast_debug(1, "Searching by category default\n"); + return search_directory_sub("default", vmcfg, ucfg, ext, flags, alist); + } + } else { + /* Browse only the listed context for a match */ + ast_debug(1, "Searching by category %s\n", context); + return search_directory_sub(context, vmcfg, ucfg, ext, flags, alist); + } +} + static void sort_items(struct directory_item **sorted, int count) { int reordered, i; @@ -565,18 +615,11 @@ static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, stru int count, i; char ext[10] = ""; - if (ast_strlen_zero(context)) { - ast_log(LOG_WARNING, - "Directory must be called with an argument " - "(context in which to interpret extensions)\n"); - return -1; - } - - if (digit == '0' && !goto_exten(chan, dialcontext, "o")) { + if (digit == '0' && !goto_exten(chan, S_OR(dialcontext, "default"), "o")) { return 0; } - if (digit == '*' && !goto_exten(chan, dialcontext, "a")) { + if (digit == '*' && !goto_exten(chan, S_OR(dialcontext, "default"), "a")) { return 0; } @@ -614,16 +657,16 @@ static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, stru if (option_debug) { ast_debug(2, "Listing matching entries:\n"); for (ptr = sorted, i = 0; i < count; i++, ptr++) { - ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name); + ast_debug(2, "%s: %s\n", ptr[0]->exten, ptr[0]->name); } } if (ast_test_flag(flags, OPT_SELECTFROMMENU)) { /* Offer multiple entries at the same time */ - res = select_item_menu(chan, sorted, count, context, dialcontext, flags); + res = select_item_menu(chan, sorted, count, dialcontext, flags); } else { /* Offer entries one by one */ - res = select_item_seq(chan, sorted, count, context, dialcontext, flags); + res = select_item_seq(chan, sorted, count, dialcontext, flags); } if (!res) { @@ -645,7 +688,7 @@ static int directory_exec(struct ast_channel *chan, void *data) int res = 0, digit = 3; struct ast_config *cfg, *ucfg; const char *dirintro; - char *parse, *opts[OPT_ARG_ARRAY_SIZE]; + char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { 0, }; struct ast_flags flags = { 0 }; struct ast_flags config_flags = { 0 }; enum { FIRST, LAST, BOTH } which = LAST; @@ -656,11 +699,6 @@ static int directory_exec(struct ast_channel *chan, void *data) AST_APP_ARG(options); ); - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n"); - return -1; - } - parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); @@ -668,11 +706,7 @@ static int directory_exec(struct ast_channel *chan, void *data) if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options)) return -1; - if (ast_strlen_zero(args.dialcontext)) - args.dialcontext = args.vmcontext; - - cfg = realtime_directory(args.vmcontext); - if (!cfg) { + if (!(cfg = realtime_directory(args.vmcontext))) { ast_log(LOG_ERROR, "Unable to read the configuration data!\n"); return -1; }