Mon Mar 20 08:20:06 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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  * \ingroup applications
00024  */
00025  
00026 #include <string.h>
00027 #include <ctype.h>
00028 #include <stdlib.h>
00029 #include <stdio.h>
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
00034 
00035 #include "asterisk/lock.h"
00036 #include "asterisk/file.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/module.h"
00041 #include "asterisk/config.h"
00042 #include "asterisk/say.h"
00043 #include "asterisk/utils.h"
00044 
00045 static char *tdesc = "Extension Directory";
00046 static char *app = "Directory";
00047 
00048 static char *synopsis = "Provide directory of voicemail extensions";
00049 static char *descrip =
00050 "  Directory(vm-context[|dial-context[|options]]): This application will present\n"
00051 "the calling channel with a directory of extensions from which they can search\n"
00052 "by name. The list of names and corresponding extensions is retrieved from the\n"
00053 "voicemail configuration file, voicemail.conf.\n"
00054 "  This applicaiton will immediate exit if one of the following DTMF digits are\n"
00055 "received and the extension to jump to exists:\n"
00056 "    0 - Jump to the 'o' extension, if it exists.\n"
00057 "    * - Jump to the 'a' extension, if it exists.\n\n"
00058 "  Parameters:\n"
00059 "    vm-context   - This is the context within voicemail.conf to use for the\n"
00060 "                   Directory.\n"
00061 "    dial-context - This is the dialplan context to use when looking for an\n"
00062 "                   extension that the user has selected, or when jumping to the\n"
00063 "                   'o' or 'a' extension.\n\n"
00064 "  Options:\n"
00065 "    f - Allow the caller to enter the first name of a user in the directory\n"
00066 "        instead of using the last name.\n"; 
00067 
00068 /* For simplicity, I'm keeping the format compatible with the voicemail config,
00069    but i'm open to suggestions for isolating it */
00070 
00071 #define VOICEMAIL_CONFIG "voicemail.conf"
00072 
00073 /* How many digits to read in */
00074 #define NUMDIGITS 3
00075 
00076 STANDARD_LOCAL_USER;
00077 
00078 LOCAL_USER_DECL;
00079 
00080 static char *convert(char *lastname)
00081 {
00082    char *tmp;
00083    int lcount = 0;
00084    tmp = malloc(NUMDIGITS + 1);
00085    if (tmp) {
00086       while((*lastname > 32) && lcount < NUMDIGITS) {
00087          switch(toupper(*lastname)) {
00088          case '1':
00089             tmp[lcount++] = '1';
00090             break;
00091          case '2':
00092          case 'A':
00093          case 'B':
00094          case 'C':
00095             tmp[lcount++] = '2';
00096             break;
00097          case '3':
00098          case 'D':
00099          case 'E':
00100          case 'F':
00101             tmp[lcount++] = '3';
00102             break;
00103          case '4':
00104          case 'G':
00105          case 'H':
00106          case 'I':
00107             tmp[lcount++] = '4';
00108             break;
00109          case '5':
00110          case 'J':
00111          case 'K':
00112          case 'L':
00113             tmp[lcount++] = '5';
00114             break;
00115          case '6':
00116          case 'M':
00117          case 'N':
00118          case 'O':
00119             tmp[lcount++] = '6';
00120             break;
00121          case '7':
00122          case 'P':
00123          case 'Q':
00124          case 'R':
00125          case 'S':
00126             tmp[lcount++] = '7';
00127             break;
00128          case '8':
00129          case 'T':
00130          case 'U':
00131          case 'V':
00132             tmp[lcount++] = '8';
00133             break;
00134          case '9':
00135          case 'W':
00136          case 'X':
00137          case 'Y':
00138          case 'Z':
00139             tmp[lcount++] = '9';
00140             break;
00141          }
00142          lastname++;
00143       }
00144       tmp[lcount] = '\0';
00145    }
00146    return tmp;
00147 }
00148 
00149 /* play name of mailbox owner.
00150  * returns:  -1 for bad or missing extension
00151  *           '1' for selected entry from directory
00152  *           '*' for skipped entry from directory
00153  */
00154 static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
00155    int res = 0;
00156    int loop = 3;
00157    char fn[256];
00158    char fn2[256];
00159 
00160    /* Check for the VoiceMail2 greeting first */
00161    snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
00162       (char *)ast_config_AST_SPOOL_DIR, context, ext);
00163 
00164    /* Otherwise, check for an old-style Voicemail greeting */
00165    snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
00166       (char *)ast_config_AST_SPOOL_DIR, ext);
00167 
00168    if (ast_fileexists(fn, NULL, chan->language) > 0) {
00169       res = ast_streamfile(chan, fn, chan->language);
00170       if (!res) {
00171          res = ast_waitstream(chan, AST_DIGIT_ANY);
00172       }
00173       ast_stopstream(chan);
00174    } else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
00175       res = ast_streamfile(chan, fn2, chan->language);
00176       if (!res) {
00177          res = ast_waitstream(chan, AST_DIGIT_ANY);
00178       }
00179       ast_stopstream(chan);
00180    } else {
00181       res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
00182                AST_DIGIT_ANY, chan->language);
00183    }
00184 
00185    while (loop) {
00186       if (!res) {
00187          res = ast_streamfile(chan, "dir-instr", chan->language);
00188       }
00189       if (!res) {
00190          res = ast_waitstream(chan, AST_DIGIT_ANY);
00191       }
00192       if (!res) {
00193          res = ast_waitfordigit(chan, 3000);
00194       }
00195       ast_stopstream(chan);
00196    
00197       if (res > -1) {
00198          switch (res) {
00199             case '1':
00200                /* Name selected */
00201                loop = 0;
00202                if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
00203                   ast_log(LOG_WARNING,
00204                      "Can't find extension '%s' in context '%s'.  "
00205                      "Did you pass the wrong context to Directory?\n",
00206                      ext, dialcontext);
00207                   res = -1;
00208                }
00209                break;
00210    
00211             case '*':   
00212                /* Skip to next match in list */
00213                loop = 0;
00214                break;
00215    
00216             default:
00217                /* Not '1', or '*', so decrement number of tries */
00218                res = 0;
00219                loop--;
00220                break;
00221          } /* end switch */
00222       } /* end if */
00223       else {
00224          /* User hungup, so jump out now */
00225          loop = 0;
00226       }
00227    } /* end while */
00228 
00229    return(res);
00230 }
00231 
00232 static struct ast_config *realtime_directory(char *context)
00233 {
00234    struct ast_config *cfg;
00235    struct ast_config *rtdata;
00236    struct ast_category *cat;
00237    struct ast_variable *var;
00238    char *mailbox;
00239    char *fullname;
00240    char *hidefromdir;
00241    char tmp[100];
00242 
00243    /* Load flat file config. */
00244    cfg = ast_config_load(VOICEMAIL_CONFIG);
00245 
00246    if (!cfg) {
00247       /* Loading config failed. */
00248       ast_log(LOG_WARNING, "Loading config failed.\n");
00249       return NULL;
00250    }
00251 
00252    /* Get realtime entries, categorized by their mailbox number
00253       and present in the requested context */
00254    rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
00255 
00256    /* if there are no results, just return the entries from the config file */
00257    if (!rtdata)
00258       return cfg;
00259 
00260    /* Does the context exist within the config file? If not, make one */
00261    cat = ast_category_get(cfg, context);
00262    if (!cat) {
00263       cat = ast_category_new(context);
00264       if (!cat) {
00265          ast_log(LOG_WARNING, "Out of memory\n");
00266          ast_config_destroy(cfg);
00267          return NULL;
00268       }
00269       ast_category_append(cfg, cat);
00270    }
00271 
00272    mailbox = ast_category_browse(rtdata, NULL);
00273    while (mailbox) {
00274       fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00275       hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
00276       snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
00277           fullname ? fullname : "",
00278           hidefromdir ? hidefromdir : "no");
00279       var = ast_variable_new(mailbox, tmp);
00280       if (var)
00281          ast_variable_append(cat, var);
00282       else
00283          ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00284       mailbox = ast_category_browse(rtdata, mailbox);
00285    }
00286    ast_config_destroy(rtdata);
00287 
00288    return cfg;
00289 }
00290 
00291 static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit, int last)
00292 {
00293    /* Read in the first three digits..  "digit" is the first digit, already read */
00294    char ext[NUMDIGITS + 1];
00295    char name[80] = "";
00296    struct ast_variable *v;
00297    int res;
00298    int found=0;
00299    int lastuserchoice = 0;
00300    char *start, *pos, *conv,*stringp=NULL;
00301 
00302    if (ast_strlen_zero(context)) {
00303       ast_log(LOG_WARNING,
00304          "Directory must be called with an argument "
00305          "(context in which to interpret extensions)\n");
00306       return -1;
00307    }
00308    if (digit == '0') {
00309       if (!ast_goto_if_exists(chan, chan->context, "o", 1) ||
00310           (!ast_strlen_zero(chan->macrocontext) &&
00311            !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
00312          return 0;
00313       } else {
00314          ast_log(LOG_WARNING, "Can't find extension 'o' in current context.  "
00315             "Not Exiting the Directory!\n");
00316          res = 0;
00317       }
00318    }  
00319    if (digit == '*') {
00320       if (!ast_goto_if_exists(chan, chan->context, "a", 1) ||
00321           (!ast_strlen_zero(chan->macrocontext) &&
00322            !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
00323          return 0;
00324       } else {
00325          ast_log(LOG_WARNING, "Can't find extension 'a' in current context.  "
00326             "Not Exiting the Directory!\n");
00327          res = 0;
00328       }
00329    }  
00330    memset(ext, 0, sizeof(ext));
00331    ext[0] = digit;
00332    res = 0;
00333    if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
00334    if (!res) {
00335       /* Search for all names which start with those digits */
00336       v = ast_variable_browse(cfg, context);
00337       while(v && !res) {
00338          /* Find all candidate extensions */
00339          while(v) {
00340             /* Find a candidate extension */
00341             start = strdup(v->value);
00342             if (start && !strcasestr(start, "hidefromdir=yes")) {
00343                stringp=start;
00344                strsep(&stringp, ",");
00345                pos = strsep(&stringp, ",");
00346                if (pos) {
00347                   ast_copy_string(name, pos, sizeof(name));
00348                   /* Grab the last name */
00349                   if (last && strrchr(pos,' '))
00350                      pos = strrchr(pos, ' ') + 1;
00351                   conv = convert(pos);
00352                   if (conv) {
00353                      if (!strcmp(conv, ext)) {
00354                         /* Match! */
00355                         found++;
00356                         free(conv);
00357                         free(start);
00358                         break;
00359                      }
00360                      free(conv);
00361                   }
00362                }
00363                free(start);
00364             }
00365             v = v->next;
00366          }
00367 
00368          if (v) {
00369             /* We have a match -- play a greeting if they have it */
00370             res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
00371             switch (res) {
00372                case -1:
00373                   /* user pressed '1' but extension does not exist, or
00374                    * user hungup
00375                    */
00376                   lastuserchoice = 0;
00377                   break;
00378                case '1':
00379                   /* user pressed '1' and extensions exists;
00380                      play_mailbox_owner will already have done
00381                      a goto() on the channel
00382                    */
00383                   lastuserchoice = res;
00384                   break;
00385                case '*':
00386                   /* user pressed '*' to skip something found */
00387                   lastuserchoice = res;
00388                   res = 0;
00389                   break;
00390                default:
00391                   break;
00392             }
00393             v = v->next;
00394          }
00395       }
00396 
00397       if (lastuserchoice != '1') {
00398          if (found) 
00399             res = ast_streamfile(chan, "dir-nomore", chan->language);
00400          else
00401             res = ast_streamfile(chan, "dir-nomatch", chan->language);
00402          if (!res)
00403             res = 1;
00404          return res;
00405       }
00406       return 0;
00407    }
00408    return res;
00409 }
00410 
00411 static int directory_exec(struct ast_channel *chan, void *data)
00412 {
00413    int res = 0;
00414    struct localuser *u;
00415    struct ast_config *cfg;
00416    int last = 1;
00417    char *context, *dialcontext, *dirintro, *options;
00418 
00419    if (ast_strlen_zero(data)) {
00420       ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00421       return -1;
00422    }
00423 
00424    LOCAL_USER_ADD(u);
00425 
00426    context = ast_strdupa(data);
00427    dialcontext = strchr(context, '|');
00428    if (dialcontext) {
00429       *dialcontext = '\0';
00430       dialcontext++;
00431       options = strchr(dialcontext, '|');
00432       if (options) {
00433          *options = '\0';
00434          options++; 
00435          if (strchr(options, 'f'))
00436             last = 0;
00437       }
00438    } else   
00439       dialcontext = context;
00440 
00441    cfg = realtime_directory(context);
00442    if (!cfg) {
00443       LOCAL_USER_REMOVE(u);
00444       return -1;
00445    }
00446 
00447    dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
00448    if (ast_strlen_zero(dirintro))
00449       dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00450    if (ast_strlen_zero(dirintro)) {
00451       if (last)
00452          dirintro = "dir-intro"; 
00453       else
00454          dirintro = "dir-intro-fn";
00455    }
00456 
00457    if (chan->_state != AST_STATE_UP) 
00458       res = ast_answer(chan);
00459 
00460    for (;;) {
00461       if (!res)
00462          res = ast_streamfile(chan, dirintro, chan->language);
00463       if (!res)
00464          res = ast_waitstream(chan, AST_DIGIT_ANY);
00465       ast_stopstream(chan);
00466       if (!res)
00467          res = ast_waitfordigit(chan, 5000);
00468       if (res > 0) {
00469          res = do_directory(chan, cfg, context, dialcontext, res, last);
00470          if (res > 0) {
00471             res = ast_waitstream(chan, AST_DIGIT_ANY);
00472             ast_stopstream(chan);
00473             if (res >= 0) {
00474                continue;
00475             }
00476          }
00477       }
00478       break;
00479    }
00480    ast_config_destroy(cfg);
00481    LOCAL_USER_REMOVE(u);
00482    return res;
00483 }
00484 
00485 int unload_module(void)
00486 {
00487    int res;
00488 
00489    res = ast_unregister_application(app);
00490 
00491    STANDARD_HANGUP_LOCALUSERS;
00492 
00493    return res;
00494 }
00495 
00496 int load_module(void)
00497 {
00498    return ast_register_application(app, directory_exec, synopsis, descrip);
00499 }
00500 
00501 char *description(void)
00502 {
00503    return tdesc;
00504 }
00505 
00506 int usecount(void)
00507 {
00508    int res;
00509    STANDARD_USECOUNT(res);
00510    return res;
00511 }
00512 
00513 char *key()
00514 {
00515    return ASTERISK_GPL_KEY;
00516 }

Generated on Mon Mar 20 08:20:06 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1