Wed Oct 28 11:50:52 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: 219988 $")
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       /* No name to compare against */
00477       if (ast_strlen_zero(pos)) {
00478          continue;
00479       }
00480 
00481       res = 0;
00482       if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00483          res = check_match(&item, pos, v->name, ext, 0 /* use_first_name */);
00484       }
00485       if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00486          res = check_match(&item, pos, v->name, ext, 1 /* use_first_name */);
00487       }
00488 
00489       if (!res)
00490          continue;
00491       else if (res < 0)
00492          return -1;
00493 
00494       AST_LIST_INSERT_TAIL(alist, item, entry);
00495    }
00496 
00497    if (ucfg) {
00498       for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
00499          const char *position;
00500          if (!strcasecmp(cat, "general"))
00501             continue;
00502          if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
00503             continue;
00504 
00505          /* Find all candidate extensions */
00506          position = ast_variable_retrieve(ucfg, cat, "fullname");
00507          if (!position)
00508             continue;
00509 
00510          res = 0;
00511          if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00512             res = check_match(&item, position, cat, ext, 0 /* use_first_name */);
00513          }
00514          if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00515             res = check_match(&item, position, cat, ext, 1 /* use_first_name */);
00516          }
00517 
00518          if (!res)
00519             continue;
00520          else if (res < 0)
00521             return -1;
00522 
00523          AST_LIST_INSERT_TAIL(alist, item, entry);
00524       }
00525    }
00526    return 0;
00527 }
00528 
00529 static void sort_items(struct directory_item **sorted, int count)
00530 {
00531    int reordered, i;
00532    struct directory_item **ptr, *tmp;
00533 
00534    if (count < 2)
00535       return;
00536 
00537    /* Bubble-sort items by the key */
00538    do {
00539       reordered = 0;
00540       for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
00541          if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
00542             tmp = ptr[0];
00543             ptr[0] = ptr[1];
00544             ptr[1] = tmp;
00545             reordered++;
00546          }
00547       }
00548    } while (reordered);
00549 }
00550 
00551 static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
00552 {
00553    if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
00554       (!ast_strlen_zero(chan->macrocontext) &&
00555       !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
00556       return 0;
00557    } else {
00558       ast_log(LOG_WARNING, "Can't find extension '%s' in current context.  "
00559          "Not Exiting the Directory!\n", ext);
00560       return -1;
00561    }
00562 }
00563 
00564 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)
00565 {
00566    /* Read in the first three digits..  "digit" is the first digit, already read */
00567    int res = 0;
00568    itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00569    struct directory_item *item, **ptr, **sorted = NULL;
00570    int count, i;
00571    char ext[10] = "";
00572 
00573    if (ast_strlen_zero(context)) {
00574       ast_log(LOG_WARNING,
00575          "Directory must be called with an argument "
00576          "(context in which to interpret extensions)\n");
00577       return -1;
00578    }
00579 
00580    if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
00581       return 0;
00582    }
00583 
00584    if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
00585       return 0;
00586    }
00587 
00588    ext[0] = digit;
00589    if (ast_readstring(chan, ext + 1, digits - 1, 3000, 3000, "#") < 0)
00590       return -1;
00591 
00592    res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
00593    if (res)
00594       goto exit;
00595 
00596    /* Count items in the list */
00597    count = 0;
00598    AST_LIST_TRAVERSE(&alist, item, entry) {
00599       count++;
00600    }
00601 
00602    if (count < 1) {
00603       res = ast_streamfile(chan, "dir-nomatch", chan->language);
00604       goto exit;
00605    }
00606 
00607 
00608    /* Create plain array of pointers to items (for sorting) */
00609    sorted = ast_calloc(count, sizeof(*sorted));
00610 
00611    ptr = sorted;
00612    AST_LIST_TRAVERSE(&alist, item, entry) {
00613       *ptr++ = item;
00614    }
00615 
00616    /* Sort items */
00617    sort_items(sorted, count);
00618 
00619    if (option_debug) {
00620       ast_debug(2, "Listing matching entries:\n");
00621       for (ptr = sorted, i = 0; i < count; i++, ptr++) {
00622          ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
00623       }
00624    }
00625 
00626    if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
00627       /* Offer multiple entries at the same time */
00628       res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
00629    } else {
00630       /* Offer entries one by one */
00631       res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
00632    }
00633 
00634    if (!res) {
00635       res = ast_streamfile(chan, "dir-nomore", chan->language);
00636    }
00637 
00638 exit:
00639    if (sorted)
00640       ast_free(sorted);
00641 
00642    while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
00643       ast_free(item);
00644 
00645    return res;
00646 }
00647 
00648 static int directory_exec(struct ast_channel *chan, void *data)
00649 {
00650    int res = 0, digit = 3;
00651    struct ast_config *cfg, *ucfg;
00652    const char *dirintro;
00653    char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { "", };
00654    struct ast_flags flags = { 0 };
00655    struct ast_flags config_flags = { 0 };
00656    enum { FIRST, LAST, BOTH } which = LAST;
00657    char digits[9] = "digits/3";
00658    AST_DECLARE_APP_ARGS(args,
00659       AST_APP_ARG(vmcontext);
00660       AST_APP_ARG(dialcontext);
00661       AST_APP_ARG(options);
00662    );
00663 
00664    if (ast_strlen_zero(data)) {
00665       ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00666       return -1;
00667    }
00668 
00669    parse = ast_strdupa(data);
00670 
00671    AST_STANDARD_APP_ARGS(args, parse);
00672 
00673    if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
00674       return -1;
00675 
00676    if (ast_strlen_zero(args.dialcontext))
00677       args.dialcontext = args.vmcontext;
00678 
00679    cfg = realtime_directory(args.vmcontext);
00680    if (!cfg) {
00681       ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
00682       return -1;
00683    }
00684 
00685    ucfg = ast_config_load("users.conf", config_flags);
00686 
00687    dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
00688    if (ast_strlen_zero(dirintro))
00689       dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00690 
00691    if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00692       if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
00693          digit = atoi(opts[OPT_ARG_EITHER]);
00694       }
00695       which = BOTH;
00696    } else if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
00697       if (!ast_strlen_zero(opts[OPT_ARG_FIRSTNAME])) {
00698          digit = atoi(opts[OPT_ARG_FIRSTNAME]);
00699       }
00700       which = FIRST;
00701    } else {
00702       if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
00703          digit = atoi(opts[OPT_ARG_LASTNAME]);
00704       }
00705       which = LAST;
00706    }
00707 
00708    /* If no options specified, search by last name */
00709    if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
00710       ast_set_flag(&flags, OPT_LISTBYLASTNAME);
00711       which = LAST;
00712    }
00713 
00714    if (digit > 9) {
00715       digit = 9;
00716    } else if (digit < 1) {
00717       digit = 3;
00718    }
00719    digits[7] = digit + '0';
00720 
00721    if (chan->_state != AST_STATE_UP)
00722       res = ast_answer(chan);
00723 
00724    for (;;) {
00725       if (!ast_strlen_zero(dirintro) && !res) {
00726          res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
00727       } else if (!res) {
00728          /* Stop playing sounds as soon as we have a digit. */
00729          res = ast_stream_and_wait(chan, "dir-welcome", AST_DIGIT_ANY);
00730          if (!res) {
00731             res = ast_stream_and_wait(chan, "dir-pls-enter", AST_DIGIT_ANY);
00732          }
00733          if (!res) {
00734             res = ast_stream_and_wait(chan, digits, AST_DIGIT_ANY);
00735          }
00736          if (!res) {
00737             res = ast_stream_and_wait(chan, 
00738                which == FIRST ? "dir-first" :
00739                which == LAST ? "dir-last" :
00740                "dir-firstlast", AST_DIGIT_ANY);
00741          }
00742          if (!res) {
00743             ast_stream_and_wait(chan, "dir-usingkeypad", AST_DIGIT_ANY);
00744          }
00745       }
00746       ast_stopstream(chan);
00747       if (!res)
00748          res = ast_waitfordigit(chan, 5000);
00749 
00750       if (res <= 0)
00751          break;
00752 
00753       res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags);
00754       if (res)
00755          break;
00756 
00757       res = ast_waitstream(chan, AST_DIGIT_ANY);
00758       ast_stopstream(chan);
00759 
00760       if (res)
00761          break;
00762    }
00763 
00764    if (ucfg)
00765       ast_config_destroy(ucfg);
00766    ast_config_destroy(cfg);
00767 
00768    return res < 0 ? -1 : 0;
00769 }
00770 
00771 static int unload_module(void)
00772 {
00773    int res;
00774    res = ast_unregister_application(app);
00775    return res;
00776 }
00777 
00778 static int load_module(void)
00779 {
00780    return ast_register_application(app, directory_exec, synopsis, descrip);
00781 }
00782 
00783 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");

Generated on Wed Oct 28 11:50:52 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6