Fri Jul 24 00:40:38 2009

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: 165324 $")
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_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
00256 {
00257    struct directory_item *item, **ptr;
00258    int i, res, loop;
00259 
00260    for (ptr = items, i = 0; i < count; i++, ptr++) {
00261       item = *ptr;
00262 
00263       for (loop = 3 ; loop > 0; loop--) {
00264          res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
00265 
00266          if (!res)
00267             res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
00268          if (!res)
00269             res = ast_waitfordigit(chan, 3000);
00270          ast_stopstream(chan);
00271    
00272          if (res == '1') { /* Name selected */
00273             return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
00274          } else if (res == '*') {
00275             /* Skip to next match in list */
00276             break;
00277          }
00278 
00279          if (res < 0)
00280             return -1;
00281 
00282          res = 0;
00283       }
00284    }
00285 
00286    /* Nothing was selected */
00287    return 0;
00288 }
00289 
00290 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)
00291 {
00292    struct directory_item **block, *item;
00293    int i, limit, res = 0;
00294    char buf[9];
00295 
00296    for (block = items; count; block += limit, count -= limit) {
00297       limit = count;
00298       if (limit > 8)
00299          limit = 8;
00300 
00301       for (i = 0; i < limit && !res; i++) {
00302          item = block[i];
00303 
00304          snprintf(buf, sizeof(buf), "digits/%d", i + 1);
00305          /* Press <num> for <name>, [ extension <ext> ] */
00306          res = ast_streamfile(chan, "dir-multi1", chan->language);
00307          if (!res)
00308             res = ast_waitstream(chan, AST_DIGIT_ANY);
00309          if (!res)
00310             res = ast_streamfile(chan, buf, chan->language);
00311          if (!res)
00312             res = ast_waitstream(chan, AST_DIGIT_ANY);
00313          if (!res)
00314             res = ast_streamfile(chan, "dir-multi2", chan->language);
00315          if (!res)
00316             res = ast_waitstream(chan, AST_DIGIT_ANY);
00317          if (!res)
00318             res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
00319          if (!res)
00320             res = ast_waitstream(chan, AST_DIGIT_ANY);
00321          if (!res)
00322             res = ast_waitfordigit(chan, 800);
00323       }
00324 
00325       /* Press "9" for more names. */
00326       if (!res && count > limit) {
00327          res = ast_streamfile(chan, "dir-multi9", chan->language);
00328          if (!res)
00329             res = ast_waitstream(chan, AST_DIGIT_ANY);
00330       }
00331 
00332       if (!res) {
00333          res = ast_waitfordigit(chan, 3000);
00334       }
00335 
00336       if (res && res > '0' && res < '1' + limit) {
00337          return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
00338       }
00339 
00340       if (res < 0)
00341          return -1;
00342 
00343       res = 0;
00344    }
00345 
00346    /* Nothing was selected */
00347    return 0;
00348 }
00349 
00350 static struct ast_config *realtime_directory(char *context)
00351 {
00352    struct ast_config *cfg;
00353    struct ast_config *rtdata;
00354    struct ast_category *cat;
00355    struct ast_variable *var;
00356    char *mailbox;
00357    const char *fullname;
00358    const char *hidefromdir;
00359    char tmp[100];
00360    struct ast_flags config_flags = { 0 };
00361 
00362    /* Load flat file config. */
00363    cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
00364 
00365    if (!cfg) {
00366       /* Loading config failed. */
00367       ast_log(LOG_WARNING, "Loading config failed.\n");
00368       return NULL;
00369    }
00370 
00371    /* Get realtime entries, categorized by their mailbox number
00372       and present in the requested context */
00373    rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL);
00374 
00375    /* if there are no results, just return the entries from the config file */
00376    if (!rtdata)
00377       return cfg;
00378 
00379    /* Does the context exist within the config file? If not, make one */
00380    cat = ast_category_get(cfg, context);
00381    if (!cat) {
00382       cat = ast_category_new(context, "", 99999);
00383       if (!cat) {
00384          ast_log(LOG_WARNING, "Out of memory\n");
00385          ast_config_destroy(cfg);
00386          if (rtdata) {
00387             ast_config_destroy(rtdata);
00388          }
00389          return NULL;
00390       }
00391       ast_category_append(cfg, cat);
00392    }
00393 
00394    mailbox = NULL;
00395    while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
00396       fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00397       if (ast_true((hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir")))) {
00398          /* Skip hidden */
00399          continue;
00400       }
00401       snprintf(tmp, sizeof(tmp), "no-password,%s", S_OR(fullname, ""));
00402       var = ast_variable_new(mailbox, tmp, "");
00403       if (var)
00404          ast_variable_append(cat, var);
00405       else
00406          ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00407    }
00408    ast_config_destroy(rtdata);
00409 
00410    return cfg;
00411 }
00412 
00413 static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
00414 {
00415    struct directory_item *item;
00416    const char *key = NULL;
00417    int namelen;
00418 
00419 
00420    /* Set key to last name or first name depending on search mode */
00421    if (!use_first_name)
00422       key = strchr(item_fullname, ' ');
00423 
00424    if (key)
00425       key++;
00426    else
00427       key = item_fullname;
00428 
00429    if (compare(key, pattern_ext))
00430       return 0;
00431 
00432    /* Match */
00433    item = ast_calloc(1, sizeof(*item));
00434    if (!item)
00435       return -1;
00436    ast_copy_string(item->name, item_fullname, sizeof(item->name));
00437    ast_copy_string(item->exten, item_ext, sizeof(item->exten));
00438 
00439    ast_copy_string(item->key, key, sizeof(item->key));
00440    if (key != item_fullname) {
00441       /* Key is the last name. Append first name to key in order to sort Last,First */
00442       namelen = key - item_fullname - 1;
00443       if (namelen > sizeof(item->key) - strlen(item->key) - 1)
00444          namelen = sizeof(item->key) - strlen(item->key) - 1;
00445       strncat(item->key, item_fullname, namelen);
00446    }
00447 
00448    *result = item;
00449    return 1;
00450 }
00451 
00452 typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
00453 
00454 static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
00455 {
00456    struct ast_variable *v;
00457    char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
00458    struct directory_item *item;
00459    int res;
00460 
00461    ast_debug(2, "Pattern: %s\n", ext);
00462 
00463    for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
00464 
00465       /* Ignore hidden */
00466       if (strcasestr(v->value, "hidefromdir=yes"))
00467          continue;
00468 
00469       ast_copy_string(buf, v->value, sizeof(buf));
00470       bufptr = buf;
00471 
00472       /* password,Full Name,email,pager,options */
00473       strsep(&bufptr, ",");
00474       pos = strsep(&bufptr, ",");
00475 
00476       res = 0;
00477       if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00478          res = check_match(&item, pos, v->name, ext, 0 /* use_first_name */);
00479       }
00480       if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00481          res = check_match(&item, pos, v->name, ext, 1 /* use_first_name */);
00482       }
00483 
00484       if (!res)
00485          continue;
00486       else if (res < 0)
00487          return -1;
00488 
00489       AST_LIST_INSERT_TAIL(alist, item, entry);
00490    }
00491 
00492    if (ucfg) {
00493       for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
00494          const char *position;
00495          if (!strcasecmp(cat, "general"))
00496             continue;
00497          if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
00498             continue;
00499 
00500          /* Find all candidate extensions */
00501          position = ast_variable_retrieve(ucfg, cat, "fullname");
00502          if (!position)
00503             continue;
00504 
00505          res = 0;
00506          if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00507             res = check_match(&item, position, cat, ext, 0 /* use_first_name */);
00508          }
00509          if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00510             res = check_match(&item, position, cat, 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    return 0;
00522 }
00523 
00524 static void sort_items(struct directory_item **sorted, int count)
00525 {
00526    int reordered, i;
00527    struct directory_item **ptr, *tmp;
00528 
00529    if (count < 2)
00530       return;
00531 
00532    /* Bubble-sort items by the key */
00533    do {
00534       reordered = 0;
00535       for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
00536          if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
00537             tmp = ptr[0];
00538             ptr[0] = ptr[1];
00539             ptr[1] = tmp;
00540             reordered++;
00541          }
00542       }
00543    } while (reordered);
00544 }
00545 
00546 static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
00547 {
00548    if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
00549       (!ast_strlen_zero(chan->macrocontext) &&
00550       !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
00551       return 0;
00552    } else {
00553       ast_log(LOG_WARNING, "Can't find extension '%s' in current context.  "
00554          "Not Exiting the Directory!\n", ext);
00555       return -1;
00556    }
00557 }
00558 
00559 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)
00560 {
00561    /* Read in the first three digits..  "digit" is the first digit, already read */
00562    int res = 0;
00563    itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00564    struct directory_item *item, **ptr, **sorted = NULL;
00565    int count, i;
00566    char ext[10] = "";
00567 
00568    if (ast_strlen_zero(context)) {
00569       ast_log(LOG_WARNING,
00570          "Directory must be called with an argument "
00571          "(context in which to interpret extensions)\n");
00572       return -1;
00573    }
00574 
00575    if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
00576       return 0;
00577    }
00578 
00579    if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
00580       return 0;
00581    }
00582 
00583    ext[0] = digit;
00584    if (ast_readstring(chan, ext + 1, digits - 1, 3000, 3000, "#") < 0)
00585       return -1;
00586 
00587    res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
00588    if (res)
00589       goto exit;
00590 
00591    /* Count items in the list */
00592    count = 0;
00593    AST_LIST_TRAVERSE(&alist, item, entry) {
00594       count++;
00595    }
00596 
00597    if (count < 1) {
00598       res = ast_streamfile(chan, "dir-nomatch", chan->language);
00599       goto exit;
00600    }
00601 
00602 
00603    /* Create plain array of pointers to items (for sorting) */
00604    sorted = ast_calloc(count, sizeof(*sorted));
00605 
00606    ptr = sorted;
00607    AST_LIST_TRAVERSE(&alist, item, entry) {
00608       *ptr++ = item;
00609    }
00610 
00611    /* Sort items */
00612    sort_items(sorted, count);
00613 
00614    if (option_debug) {
00615       ast_debug(2, "Listing matching entries:\n");
00616       for (ptr = sorted, i = 0; i < count; i++, ptr++) {
00617          ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
00618       }
00619    }
00620 
00621    if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
00622       /* Offer multiple entries at the same time */
00623       res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
00624    } else {
00625       /* Offer entries one by one */
00626       res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
00627    }
00628 
00629    if (!res) {
00630       res = ast_streamfile(chan, "dir-nomore", chan->language);
00631    }
00632 
00633 exit:
00634    if (sorted)
00635       ast_free(sorted);
00636 
00637    while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
00638       ast_free(item);
00639 
00640    return res;
00641 }
00642 
00643 static int directory_exec(struct ast_channel *chan, void *data)
00644 {
00645    int res = 0, digit = 3;
00646    struct ast_config *cfg, *ucfg;
00647    const char *dirintro;
00648    char *parse, *opts[OPT_ARG_ARRAY_SIZE];
00649    struct ast_flags flags = { 0 };
00650    struct ast_flags config_flags = { 0 };
00651    enum { FIRST, LAST, BOTH } which = LAST;
00652    char digits[9] = "digits/3";
00653    AST_DECLARE_APP_ARGS(args,
00654       AST_APP_ARG(vmcontext);
00655       AST_APP_ARG(dialcontext);
00656       AST_APP_ARG(options);
00657    );
00658 
00659    if (ast_strlen_zero(data)) {
00660       ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00661       return -1;
00662    }
00663 
00664    parse = ast_strdupa(data);
00665 
00666    AST_STANDARD_APP_ARGS(args, parse);
00667 
00668    if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
00669       return -1;
00670 
00671    if (ast_strlen_zero(args.dialcontext))
00672       args.dialcontext = args.vmcontext;
00673 
00674    cfg = realtime_directory(args.vmcontext);
00675    if (!cfg) {
00676       ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
00677       return -1;
00678    }
00679 
00680    ucfg = ast_config_load("users.conf", config_flags);
00681 
00682    dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
00683    if (ast_strlen_zero(dirintro))
00684       dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00685 
00686    if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00687       if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
00688          digit = atoi(opts[OPT_ARG_EITHER]);
00689       }
00690       which = BOTH;
00691    } else if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00692       if (!ast_strlen_zero(opts[OPT_ARG_FIRSTNAME])) {
00693          digit = atoi(opts[OPT_ARG_FIRSTNAME]);
00694       }
00695       which = FIRST;
00696    } else {
00697       if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
00698          digit = atoi(opts[OPT_ARG_LASTNAME]);
00699       }
00700       which = LAST;
00701    }
00702 
00703    /* If no options specified, search by last name */
00704    if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00705       ast_set_flag(&flags, OPT_LISTBYLASTNAME);
00706       which = LAST;
00707    }
00708 
00709    if (digit > 9) {
00710       digit = 9;
00711    } else if (digit < 1) {
00712       digit = 3;
00713    }
00714    digits[7] = digit + '0';
00715 
00716    if (chan->_state != AST_STATE_UP)
00717       res = ast_answer(chan);
00718 
00719    for (;;) {
00720       if (!ast_strlen_zero(dirintro) && !res) {
00721          res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
00722       } else if (!res) {
00723          /* Stop playing sounds as soon as we have a digit. */
00724          res = ast_stream_and_wait(chan, "dir-welcome", AST_DIGIT_ANY);
00725          if (!res) {
00726             res = ast_stream_and_wait(chan, "dir-pls-enter", AST_DIGIT_ANY);
00727          }
00728          if (!res) {
00729             res = ast_stream_and_wait(chan, digits, AST_DIGIT_ANY);
00730          }
00731          if (!res) {
00732             res = ast_stream_and_wait(chan, 
00733                which == FIRST ? "dir-first" :
00734                which == LAST ? "dir-last" :
00735                "dir-firstlast", AST_DIGIT_ANY);
00736          }
00737          if (!res) {
00738             ast_stream_and_wait(chan, "dir-usingkeypad", AST_DIGIT_ANY);
00739          }
00740       }
00741       ast_stopstream(chan);
00742       if (!res)
00743          res = ast_waitfordigit(chan, 5000);
00744 
00745       if (res <= 0)
00746          break;
00747 
00748       res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags);
00749       if (res)
00750          break;
00751 
00752       res = ast_waitstream(chan, AST_DIGIT_ANY);
00753       ast_stopstream(chan);
00754 
00755       if (res)
00756          break;
00757    }
00758 
00759    if (ucfg)
00760       ast_config_destroy(ucfg);
00761    ast_config_destroy(cfg);
00762 
00763    return res < 0 ? -1 : 0;
00764 }
00765 
00766 static int unload_module(void)
00767 {
00768    int res;
00769    res = ast_unregister_application(app);
00770    return res;
00771 }
00772 
00773 static int load_module(void)
00774 {
00775    return ast_register_application(app, directory_exec, synopsis, descrip);
00776 }
00777 
00778 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");

Generated on Fri Jul 24 00:40:38 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7