00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 251819 $")
00034
00035 #include <ctype.h>
00036
00037 #include "asterisk/paths.h"
00038 #include "asterisk/file.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/module.h"
00041 #include "asterisk/say.h"
00042 #include "asterisk/app.h"
00043 #include "asterisk/utils.h"
00044
00045 static char *app = "Directory";
00046
00047 static char *synopsis = "Provide directory of voicemail extensions";
00048 static char *descrip =
00049 " Directory(vm-context[,dial-context[,options]]): This application will present\n"
00050 "the calling channel with a directory of extensions from which they can search\n"
00051 "by name. The list of names and corresponding extensions is retrieved from the\n"
00052 "voicemail configuration file, voicemail.conf.\n"
00053 " This application will immediately exit if one of the following DTMF digits are\n"
00054 "received and the extension to jump to exists:\n"
00055 " 0 - Jump to the 'o' extension, if it exists.\n"
00056 " * - Jump to the 'a' extension, if it exists.\n\n"
00057 " Parameters:\n"
00058 " vm-context - This is the context within voicemail.conf to use for the\n"
00059 " Directory.\n"
00060 " dial-context - This is the dialplan context to use when looking for an\n"
00061 " extension that the user has selected, or when jumping to the\n"
00062 " 'o' or 'a' extension.\n\n"
00063 " Options:\n"
00064 " e In addition to the name, also read the extension number to the\n"
00065 " caller before presenting dialing options.\n"
00066 " f[(<n>)] Allow the caller to enter the first name of a user in the\n"
00067 " directory instead of using the last name. If specified, the\n"
00068 " optional number argument will be used for the number of\n"
00069 " characters the user should enter.\n"
00070 " l[(<n>)] Allow the caller to enter the last name of a user in the\n"
00071 " directory. This is the default. If specified, the\n"
00072 " optional number argument will be used for the number of\n"
00073 " characters the user should enter.\n"
00074 " b[(<n>)] Allow the caller to enter either the first or the last name\n"
00075 " of a user in the directory. If specified, the optional number\n"
00076 " argument will be used for the number of characters the user\n"
00077 " should enter.\n"
00078 " m Instead of reading each name sequentially and asking for\n"
00079 " confirmation, create a menu of up to 8 names.\n"
00080 " p(<n>) Pause for n milliseconds after the digits are typed. This is\n"
00081 " helpful for people with cellphones, who are not holding the\n"
00082 " receiver to their ear while entering DTMF.\n"
00083 "\n"
00084 " Only one of the f, l, or b options may be specified. If more than one is\n"
00085 " specified, then Directory will act as if 'b' was specified. The number\n"
00086 " of characters for the user to type defaults to 3.\n";
00087
00088
00089
00090
00091 #define VOICEMAIL_CONFIG "voicemail.conf"
00092
00093 enum {
00094 OPT_LISTBYFIRSTNAME = (1 << 0),
00095 OPT_SAYEXTENSION = (1 << 1),
00096 OPT_FROMVOICEMAIL = (1 << 2),
00097 OPT_SELECTFROMMENU = (1 << 3),
00098 OPT_LISTBYLASTNAME = (1 << 4),
00099 OPT_LISTBYEITHER = OPT_LISTBYFIRSTNAME | OPT_LISTBYLASTNAME,
00100 OPT_PAUSE = (1 << 5),
00101 } directory_option_flags;
00102
00103 enum {
00104 OPT_ARG_FIRSTNAME = 0,
00105 OPT_ARG_LASTNAME = 1,
00106 OPT_ARG_EITHER = 2,
00107 OPT_ARG_PAUSE = 3,
00108
00109 OPT_ARG_ARRAY_SIZE = 4,
00110 };
00111
00112 struct directory_item {
00113 char exten[AST_MAX_EXTENSION + 1];
00114 char name[AST_MAX_EXTENSION + 1];
00115 char key[50];
00116
00117 AST_LIST_ENTRY(directory_item) entry;
00118 };
00119
00120 AST_APP_OPTIONS(directory_app_options, {
00121 AST_APP_OPTION_ARG('f', OPT_LISTBYFIRSTNAME, OPT_ARG_FIRSTNAME),
00122 AST_APP_OPTION_ARG('l', OPT_LISTBYLASTNAME, OPT_ARG_LASTNAME),
00123 AST_APP_OPTION_ARG('b', OPT_LISTBYEITHER, OPT_ARG_EITHER),
00124 AST_APP_OPTION_ARG('p', OPT_PAUSE, OPT_ARG_PAUSE),
00125 AST_APP_OPTION('e', OPT_SAYEXTENSION),
00126 AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
00127 AST_APP_OPTION('m', OPT_SELECTFROMMENU),
00128 });
00129
00130 static int compare(const char *text, const char *template)
00131 {
00132 char digit;
00133
00134 if (ast_strlen_zero(text)) {
00135 return -1;
00136 }
00137
00138 while (*template) {
00139 digit = toupper(*text++);
00140 switch (digit) {
00141 case 0:
00142 return -1;
00143 case '1':
00144 digit = '1';
00145 break;
00146 case '2':
00147 case 'A':
00148 case 'B':
00149 case 'C':
00150 digit = '2';
00151 break;
00152 case '3':
00153 case 'D':
00154 case 'E':
00155 case 'F':
00156 digit = '3';
00157 break;
00158 case '4':
00159 case 'G':
00160 case 'H':
00161 case 'I':
00162 digit = '4';
00163 break;
00164 case '5':
00165 case 'J':
00166 case 'K':
00167 case 'L':
00168 digit = '5';
00169 break;
00170 case '6':
00171 case 'M':
00172 case 'N':
00173 case 'O':
00174 digit = '6';
00175 break;
00176 case '7':
00177 case 'P':
00178 case 'Q':
00179 case 'R':
00180 case 'S':
00181 digit = '7';
00182 break;
00183 case '8':
00184 case 'T':
00185 case 'U':
00186 case 'V':
00187 digit = '8';
00188 break;
00189 case '9':
00190 case 'W':
00191 case 'X':
00192 case 'Y':
00193 case 'Z':
00194 digit = '9';
00195 break;
00196
00197 default:
00198 if (digit > ' ')
00199 return -1;
00200 continue;
00201 }
00202
00203 if (*template++ != digit)
00204 return -1;
00205 }
00206
00207 return 0;
00208 }
00209
00210
00211
00212
00213
00214
00215 static int play_mailbox_owner(struct ast_channel *chan, const char *context,
00216 const char *ext, const char *name, struct ast_flags *flags)
00217 {
00218 int res = 0;
00219 if ((res = ast_app_sayname(chan, ext, context)) >= 0) {
00220 ast_stopstream(chan);
00221
00222 if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
00223 ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
00224 res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
00225 }
00226 } else {
00227 res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
00228 if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
00229 ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
00230 res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
00231 }
00232 }
00233
00234 return res;
00235 }
00236
00237 static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
00238 {
00239 ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext);
00240
00241 if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
00242
00243 ast_copy_string(chan->exten, item->exten, sizeof(chan->exten));
00244 } else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) {
00245 ast_log(LOG_WARNING,
00246 "Can't find extension '%s' in context '%s'. "
00247 "Did you pass the wrong context to Directory?\n",
00248 item->exten, dialcontext);
00249 return -1;
00250 }
00251
00252 return 0;
00253 }
00254
00255 static int select_item_pause(struct ast_channel *chan, struct ast_flags *flags, char *opts[])
00256 {
00257 int res = 0, opt_pause = 0;
00258
00259 if (ast_test_flag(flags, OPT_PAUSE) && !ast_strlen_zero(opts[OPT_ARG_PAUSE])) {
00260 opt_pause = atoi(opts[OPT_ARG_PAUSE]);
00261 if (opt_pause > 3000) {
00262 opt_pause = 3000;
00263 }
00264 res = ast_waitfordigit(chan, opt_pause);
00265 }
00266 return res;
00267 }
00268
00269 static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count,
00270 const char *context, const char *dialcontext, struct ast_flags *flags, char *opts[])
00271 {
00272 struct directory_item *item, **ptr;
00273 int i, res, loop;
00274
00275
00276
00277 res = select_item_pause(chan, flags, opts);
00278
00279 for (ptr = items, i = 0; i < count; i++, ptr++) {
00280 item = *ptr;
00281
00282 for (loop = 3 ; loop > 0; loop--) {
00283
00284 if (!res)
00285 res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
00286 if (!res)
00287 res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
00288 if (!res)
00289 res = ast_waitfordigit(chan, 3000);
00290 ast_stopstream(chan);
00291
00292 if (res == '1') {
00293 return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
00294 } else if (res == '*') {
00295
00296 break;
00297 }
00298
00299 if (res < 0)
00300 return -1;
00301
00302 res = 0;
00303 }
00304 res = 0;
00305 }
00306
00307
00308 return 0;
00309 }
00310
00311 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, char *opts[])
00312 {
00313 struct directory_item **block, *item;
00314 int i, limit, res = 0;
00315 char buf[9];
00316
00317
00318 select_item_pause(chan, flags, opts);
00319
00320 for (block = items; count; block += limit, count -= limit) {
00321 limit = count;
00322 if (limit > 8)
00323 limit = 8;
00324
00325 for (i = 0; i < limit && !res; i++) {
00326 item = block[i];
00327
00328 snprintf(buf, sizeof(buf), "digits/%d", i + 1);
00329
00330 res = ast_streamfile(chan, "dir-multi1", chan->language);
00331 if (!res)
00332 res = ast_waitstream(chan, AST_DIGIT_ANY);
00333 if (!res)
00334 res = ast_streamfile(chan, buf, chan->language);
00335 if (!res)
00336 res = ast_waitstream(chan, AST_DIGIT_ANY);
00337 if (!res)
00338 res = ast_streamfile(chan, "dir-multi2", chan->language);
00339 if (!res)
00340 res = ast_waitstream(chan, AST_DIGIT_ANY);
00341 if (!res)
00342 res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
00343 if (!res)
00344 res = ast_waitstream(chan, AST_DIGIT_ANY);
00345 if (!res)
00346 res = ast_waitfordigit(chan, 800);
00347 }
00348
00349
00350 if (!res && count > limit) {
00351 res = ast_streamfile(chan, "dir-multi9", chan->language);
00352 if (!res)
00353 res = ast_waitstream(chan, AST_DIGIT_ANY);
00354 }
00355
00356 if (!res) {
00357 res = ast_waitfordigit(chan, 3000);
00358 }
00359
00360 if (res && res > '0' && res < '1' + limit) {
00361 return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
00362 }
00363
00364 if (res < 0)
00365 return -1;
00366
00367 res = 0;
00368 }
00369
00370
00371 return 0;
00372 }
00373
00374 static struct ast_config *realtime_directory(char *context)
00375 {
00376 struct ast_config *cfg;
00377 struct ast_config *rtdata;
00378 struct ast_category *cat;
00379 struct ast_variable *var;
00380 char *mailbox;
00381 const char *fullname;
00382 const char *hidefromdir;
00383 char tmp[100];
00384 struct ast_flags config_flags = { 0 };
00385
00386
00387 cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
00388
00389 if (!cfg) {
00390
00391 ast_log(LOG_WARNING, "Loading config failed.\n");
00392 return NULL;
00393 }
00394
00395
00396
00397 rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL);
00398
00399
00400 if (!rtdata)
00401 return cfg;
00402
00403
00404 cat = ast_category_get(cfg, context);
00405 if (!cat) {
00406 cat = ast_category_new(context, "", 99999);
00407 if (!cat) {
00408 ast_log(LOG_WARNING, "Out of memory\n");
00409 ast_config_destroy(cfg);
00410 if (rtdata) {
00411 ast_config_destroy(rtdata);
00412 }
00413 return NULL;
00414 }
00415 ast_category_append(cfg, cat);
00416 }
00417
00418 mailbox = NULL;
00419 while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
00420 fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00421 if (ast_true((hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir")))) {
00422
00423 continue;
00424 }
00425 snprintf(tmp, sizeof(tmp), "no-password,%s", S_OR(fullname, ""));
00426 var = ast_variable_new(mailbox, tmp, "");
00427 if (var)
00428 ast_variable_append(cat, var);
00429 else
00430 ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00431 }
00432 ast_config_destroy(rtdata);
00433
00434 return cfg;
00435 }
00436
00437 static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
00438 {
00439 struct directory_item *item;
00440 const char *key = NULL;
00441 int namelen;
00442
00443
00444
00445 if (!use_first_name)
00446 key = strchr(item_fullname, ' ');
00447
00448 if (key)
00449 key++;
00450 else
00451 key = item_fullname;
00452
00453 if (compare(key, pattern_ext))
00454 return 0;
00455
00456
00457 item = ast_calloc(1, sizeof(*item));
00458 if (!item)
00459 return -1;
00460 ast_copy_string(item->name, item_fullname, sizeof(item->name));
00461 ast_copy_string(item->exten, item_ext, sizeof(item->exten));
00462
00463 ast_copy_string(item->key, key, sizeof(item->key));
00464 if (key != item_fullname) {
00465
00466 namelen = key - item_fullname - 1;
00467 if (namelen > sizeof(item->key) - strlen(item->key) - 1)
00468 namelen = sizeof(item->key) - strlen(item->key) - 1;
00469 strncat(item->key, item_fullname, namelen);
00470 }
00471
00472 *result = item;
00473 return 1;
00474 }
00475
00476 typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
00477
00478 static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
00479 {
00480 struct ast_variable *v;
00481 char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
00482 struct directory_item *item;
00483 int res;
00484
00485 ast_debug(2, "Pattern: %s\n", ext);
00486
00487 for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
00488
00489
00490 if (strcasestr(v->value, "hidefromdir=yes"))
00491 continue;
00492
00493 ast_copy_string(buf, v->value, sizeof(buf));
00494 bufptr = buf;
00495
00496
00497 strsep(&bufptr, ",");
00498 pos = strsep(&bufptr, ",");
00499
00500
00501 if (ast_strlen_zero(pos)) {
00502 continue;
00503 }
00504
00505 res = 0;
00506 if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00507 res = check_match(&item, pos, v->name, ext, 0 );
00508 }
00509 if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00510 res = check_match(&item, pos, v->name, ext, 1 );
00511 }
00512
00513 if (!res)
00514 continue;
00515 else if (res < 0)
00516 return -1;
00517
00518 AST_LIST_INSERT_TAIL(alist, item, entry);
00519 }
00520
00521 if (ucfg) {
00522 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
00523 const char *position;
00524 if (!strcasecmp(cat, "general"))
00525 continue;
00526 if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
00527 continue;
00528
00529
00530 position = ast_variable_retrieve(ucfg, cat, "fullname");
00531 if (!position)
00532 continue;
00533
00534 res = 0;
00535 if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00536 res = check_match(&item, position, cat, ext, 0 );
00537 }
00538 if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00539 res = check_match(&item, position, cat, ext, 1 );
00540 }
00541
00542 if (!res)
00543 continue;
00544 else if (res < 0)
00545 return -1;
00546
00547 AST_LIST_INSERT_TAIL(alist, item, entry);
00548 }
00549 }
00550 return 0;
00551 }
00552
00553 static void sort_items(struct directory_item **sorted, int count)
00554 {
00555 int reordered, i;
00556 struct directory_item **ptr, *tmp;
00557
00558 if (count < 2)
00559 return;
00560
00561
00562 do {
00563 reordered = 0;
00564 for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
00565 if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
00566 tmp = ptr[0];
00567 ptr[0] = ptr[1];
00568 ptr[1] = tmp;
00569 reordered++;
00570 }
00571 }
00572 } while (reordered);
00573 }
00574
00575 static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
00576 {
00577 if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
00578 (!ast_strlen_zero(chan->macrocontext) &&
00579 !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
00580 return 0;
00581 } else {
00582 ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
00583 "Not Exiting the Directory!\n", ext);
00584 return -1;
00585 }
00586 }
00587
00588 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[])
00589 {
00590
00591 int res = 0;
00592 itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00593 struct directory_item *item, **ptr, **sorted = NULL;
00594 int count, i;
00595 char ext[10] = "";
00596
00597 if (ast_strlen_zero(context)) {
00598 ast_log(LOG_WARNING,
00599 "Directory must be called with an argument "
00600 "(context in which to interpret extensions)\n");
00601 return -1;
00602 }
00603
00604 if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
00605 return digit;
00606 }
00607
00608 if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
00609 return digit;
00610 }
00611
00612 ext[0] = digit;
00613 if (ast_readstring(chan, ext + 1, digits - 1, 3000, 3000, "#") < 0)
00614 return -1;
00615
00616 res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
00617 if (res)
00618 goto exit;
00619
00620
00621 count = 0;
00622 AST_LIST_TRAVERSE(&alist, item, entry) {
00623 count++;
00624 }
00625
00626 if (count < 1) {
00627 res = ast_streamfile(chan, "dir-nomatch", chan->language);
00628 goto exit;
00629 }
00630
00631
00632
00633 sorted = ast_calloc(count, sizeof(*sorted));
00634
00635 ptr = sorted;
00636 AST_LIST_TRAVERSE(&alist, item, entry) {
00637 *ptr++ = item;
00638 }
00639
00640
00641 sort_items(sorted, count);
00642
00643 if (option_debug) {
00644 ast_debug(2, "Listing matching entries:\n");
00645 for (ptr = sorted, i = 0; i < count; i++, ptr++) {
00646 ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
00647 }
00648 }
00649
00650 if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
00651
00652 res = select_item_menu(chan, sorted, count, context, dialcontext, flags, opts);
00653 } else {
00654
00655 res = select_item_seq(chan, sorted, count, context, dialcontext, flags, opts);
00656 }
00657
00658 if (!res) {
00659 res = ast_streamfile(chan, "dir-nomore", chan->language);
00660 }
00661
00662 exit:
00663 if (sorted)
00664 ast_free(sorted);
00665
00666 while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
00667 ast_free(item);
00668
00669 return res;
00670 }
00671
00672 static int directory_exec(struct ast_channel *chan, void *data)
00673 {
00674 int res = 0, digit = 3;
00675 struct ast_config *cfg, *ucfg;
00676 const char *dirintro;
00677 char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { "", };
00678 struct ast_flags flags = { 0 };
00679 struct ast_flags config_flags = { 0 };
00680 enum { FIRST, LAST, BOTH } which = LAST;
00681 char digits[9] = "digits/3";
00682 AST_DECLARE_APP_ARGS(args,
00683 AST_APP_ARG(vmcontext);
00684 AST_APP_ARG(dialcontext);
00685 AST_APP_ARG(options);
00686 );
00687
00688 if (ast_strlen_zero(data)) {
00689 ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00690 return -1;
00691 }
00692
00693 parse = ast_strdupa(data);
00694
00695 AST_STANDARD_APP_ARGS(args, parse);
00696
00697 if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
00698 return -1;
00699
00700 if (ast_strlen_zero(args.dialcontext))
00701 args.dialcontext = args.vmcontext;
00702
00703 cfg = realtime_directory(args.vmcontext);
00704 if (!cfg) {
00705 ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
00706 return -1;
00707 }
00708
00709 ucfg = ast_config_load("users.conf", config_flags);
00710
00711 dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
00712 if (ast_strlen_zero(dirintro))
00713 dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00714
00715 if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00716 if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
00717 digit = atoi(opts[OPT_ARG_EITHER]);
00718 }
00719 which = BOTH;
00720 } else if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00721 if (!ast_strlen_zero(opts[OPT_ARG_FIRSTNAME])) {
00722 digit = atoi(opts[OPT_ARG_FIRSTNAME]);
00723 }
00724 which = FIRST;
00725 } else {
00726 if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
00727 digit = atoi(opts[OPT_ARG_LASTNAME]);
00728 }
00729 which = LAST;
00730 }
00731
00732
00733 if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00734 ast_set_flag(&flags, OPT_LISTBYLASTNAME);
00735 which = LAST;
00736 }
00737
00738 if (digit > 9) {
00739 digit = 9;
00740 } else if (digit < 1) {
00741 digit = 3;
00742 }
00743 digits[7] = digit + '0';
00744
00745 if (chan->_state != AST_STATE_UP)
00746 res = ast_answer(chan);
00747
00748 for (;;) {
00749 if (!ast_strlen_zero(dirintro) && !res) {
00750 res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
00751 } else if (!res) {
00752
00753 res = ast_stream_and_wait(chan, "dir-welcome", AST_DIGIT_ANY);
00754 if (!res) {
00755 res = ast_stream_and_wait(chan, "dir-pls-enter", AST_DIGIT_ANY);
00756 }
00757 if (!res) {
00758 res = ast_stream_and_wait(chan, digits, AST_DIGIT_ANY);
00759 }
00760 if (!res) {
00761 res = ast_stream_and_wait(chan,
00762 which == FIRST ? "dir-first" :
00763 which == LAST ? "dir-last" :
00764 "dir-firstlast", AST_DIGIT_ANY);
00765 }
00766 if (!res) {
00767 res = ast_stream_and_wait(chan, "dir-usingkeypad", AST_DIGIT_ANY);
00768 }
00769 }
00770 ast_stopstream(chan);
00771 if (!res)
00772 res = ast_waitfordigit(chan, 5000);
00773
00774 if (res <= 0)
00775 break;
00776
00777 res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags, opts);
00778 if (res)
00779 break;
00780
00781 res = ast_waitstream(chan, AST_DIGIT_ANY);
00782 ast_stopstream(chan);
00783
00784 if (res)
00785 break;
00786 }
00787
00788 if (ucfg)
00789 ast_config_destroy(ucfg);
00790 ast_config_destroy(cfg);
00791
00792 return res < 0 ? -1 : 0;
00793 }
00794
00795 static int unload_module(void)
00796 {
00797 int res;
00798 res = ast_unregister_application(app);
00799 return res;
00800 }
00801
00802 static int load_module(void)
00803 {
00804 return ast_register_application(app, directory_exec, synopsis, descrip);
00805 }
00806
00807 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");