Wed Aug 18 22:33:41 2010

Asterisk developer's documentation


app_directory.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Provide a directory of extensions
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \ingroup applications
00026  */
00027 
00028 /*** MODULEINFO
00029    <depend>app_voicemail</depend>
00030  ***/
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 251819 $")
00034 
00035 #include <ctype.h>
00036 
00037 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
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 /* For simplicity, I'm keeping the format compatible with the voicemail config,
00089    but i'm open to suggestions for isolating it */
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    /* This *must* be the last value in this enum! */
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]; /* Text to order items. Either lastname+firstname or firstname+lastname */
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 /* play name of mailbox owner.
00211  * returns:  -1 for bad or missing extension
00212  *           '1' for selected entry from directory
00213  *           '*' for skipped entry from directory
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       /* If Option 'e' was specified, also read the extension number with the name */
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       /* We still want to set the exten though */
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    /* option p(n): cellphone pause option */
00276    /* allow early press of selection key */
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') { /* Name selected */
00293             return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
00294          } else if (res == '*') {
00295             /* Skip to next match in list */
00296             break;
00297          }
00298 
00299          if (res < 0)
00300             return -1;
00301 
00302          res = 0;
00303       }
00304       res = 0;
00305    }
00306 
00307    /* Nothing was selected */
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    /* option p(n): cellphone pause option */
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          /* Press <num> for <name>, [ extension <ext> ] */
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       /* Press "9" for more names. */
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    /* Nothing was selected */
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    /* Load flat file config. */
00387    cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
00388 
00389    if (!cfg) {
00390       /* Loading config failed. */
00391       ast_log(LOG_WARNING, "Loading config failed.\n");
00392       return NULL;
00393    }
00394 
00395    /* Get realtime entries, categorized by their mailbox number
00396       and present in the requested context */
00397    rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL);
00398 
00399    /* if there are no results, just return the entries from the config file */
00400    if (!rtdata)
00401       return cfg;
00402 
00403    /* Does the context exist within the config file? If not, make one */
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          /* Skip hidden */
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    /* Set key to last name or first name depending on search mode */
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    /* Match */
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       /* Key is the last name. Append first name to key in order to sort Last,First */
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       /* Ignore hidden */
00490       if (strcasestr(v->value, "hidefromdir=yes"))
00491          continue;
00492 
00493       ast_copy_string(buf, v->value, sizeof(buf));
00494       bufptr = buf;
00495 
00496       /* password,Full Name,email,pager,options */
00497       strsep(&bufptr, ",");
00498       pos = strsep(&bufptr, ",");
00499 
00500       /* No name to compare against */
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 /* use_first_name */);
00508       }
00509       if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00510          res = check_match(&item, pos, v->name, ext, 1 /* use_first_name */);
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          /* Find all candidate extensions */
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 /* use_first_name */);
00537          }
00538          if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00539             res = check_match(&item, position, cat, ext, 1 /* use_first_name */);
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    /* Bubble-sort items by the key */
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    /* Read in the first three digits..  "digit" is the first digit, already read */
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    /* Count items in the list */
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    /* Create plain array of pointers to items (for sorting) */
00633    sorted = ast_calloc(count, sizeof(*sorted));
00634 
00635    ptr = sorted;
00636    AST_LIST_TRAVERSE(&alist, item, entry) {
00637       *ptr++ = item;
00638    }
00639 
00640    /* Sort items */
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       /* Offer multiple entries at the same time */
00652       res = select_item_menu(chan, sorted, count, context, dialcontext, flags, opts);
00653    } else {
00654       /* Offer entries one by one */
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    /* If no options specified, search by last name */
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          /* Stop playing sounds as soon as we have a digit. */
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");

Generated on Wed Aug 18 22:33:41 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7