Thu Dec 17 13:33:40 2009

Asterisk developer's documentation


app_directory.c File Reference

Provide a directory of extensions. More...

#include "asterisk.h"
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/say.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"

Go to the source code of this file.

Defines

#define NUMDIGITS   3
#define VOICEMAIL_CONFIG   "voicemail.conf"

Functions

static void __reg_module (void)
static void __unreg_module (void)
static char * convert (const char *lastname)
static int directory_exec (struct ast_channel *chan, void *data)
static int do_directory (struct ast_channel *chan, struct ast_config *cfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, int last, int readext, int fromappvm)
static int load_module (void)
static int play_mailbox_owner (struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name, int readext, int fromappvm)
static struct ast_configrealtime_directory (char *context)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Extension Directory" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, }
static char * app = "Directory"
static const struct ast_module_infoast_module_info = &__mod_info
static char * descrip
static char * synopsis = "Provide directory of voicemail extensions"


Detailed Description

Provide a directory of extensions.

Author:
Mark Spencer <markster@digium.com>

Definition in file app_directory.c.


Define Documentation

#define NUMDIGITS   3

Definition at line 88 of file app_directory.c.

Referenced by convert(), and do_directory().

#define VOICEMAIL_CONFIG   "voicemail.conf"

Definition at line 85 of file app_directory.c.

Referenced by load_config(), load_module(), realtime_directory(), and vm_change_password().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 707 of file app_directory.c.

static void __unreg_module ( void   )  [static]

Definition at line 707 of file app_directory.c.

static char* convert ( const char *  lastname  )  [static]

Definition at line 206 of file app_directory.c.

References ast_malloc, and NUMDIGITS.

Referenced by do_directory().

00207 {
00208    char *tmp;
00209    int lcount = 0;
00210    tmp = ast_malloc(NUMDIGITS + 1);
00211    if (tmp) {
00212       while((*lastname > 32) && lcount < NUMDIGITS) {
00213          switch(toupper(*lastname)) {
00214          case '1':
00215             tmp[lcount++] = '1';
00216             break;
00217          case '2':
00218          case 'A':
00219          case 'B':
00220          case 'C':
00221             tmp[lcount++] = '2';
00222             break;
00223          case '3':
00224          case 'D':
00225          case 'E':
00226          case 'F':
00227             tmp[lcount++] = '3';
00228             break;
00229          case '4':
00230          case 'G':
00231          case 'H':
00232          case 'I':
00233             tmp[lcount++] = '4';
00234             break;
00235          case '5':
00236          case 'J':
00237          case 'K':
00238          case 'L':
00239             tmp[lcount++] = '5';
00240             break;
00241          case '6':
00242          case 'M':
00243          case 'N':
00244          case 'O':
00245             tmp[lcount++] = '6';
00246             break;
00247          case '7':
00248          case 'P':
00249          case 'Q':
00250          case 'R':
00251          case 'S':
00252             tmp[lcount++] = '7';
00253             break;
00254          case '8':
00255          case 'T':
00256          case 'U':
00257          case 'V':
00258             tmp[lcount++] = '8';
00259             break;
00260          case '9':
00261          case 'W':
00262          case 'X':
00263          case 'Y':
00264          case 'Z':
00265             tmp[lcount++] = '9';
00266             break;
00267          }
00268          lastname++;
00269       }
00270       tmp[lcount] = '\0';
00271    }
00272    return tmp;
00273 }

static int directory_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 595 of file app_directory.c.

References ast_channel::_state, ast_answer(), AST_APP_ARG, ast_config_destroy(), ast_config_load(), AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, ast_log(), ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_variable_retrieve(), ast_waitfordigit(), ast_waitstream(), dialcontext, do_directory(), ast_channel::language, last, LOG_ERROR, LOG_WARNING, parse(), and realtime_directory().

Referenced by load_module().

00596 {
00597    int res = 0;
00598    struct ast_module_user *u;
00599    struct ast_config *cfg, *ucfg;
00600    int last = 1;
00601    int readext = 0;
00602    int fromappvm = 0;
00603    const char *dirintro;
00604    char *parse;
00605    AST_DECLARE_APP_ARGS(args,
00606       AST_APP_ARG(vmcontext);
00607       AST_APP_ARG(dialcontext);
00608       AST_APP_ARG(options);
00609    );
00610 
00611    if (ast_strlen_zero(data)) {
00612       ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00613       return -1;
00614    }
00615 
00616    u = ast_module_user_add(chan);
00617 
00618    parse = ast_strdupa(data);
00619 
00620    AST_STANDARD_APP_ARGS(args, parse);
00621       
00622    if (args.options) {
00623       if (strchr(args.options, 'f'))
00624          last = 0;
00625       if (strchr(args.options, 'e'))
00626          readext = 1;
00627       if (strchr(args.options, 'v'))
00628          fromappvm = 1;
00629    }
00630 
00631    if (ast_strlen_zero(args.dialcontext)) 
00632       args.dialcontext = args.vmcontext;
00633 
00634    cfg = realtime_directory(args.vmcontext);
00635    if (!cfg) {
00636       ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
00637       ast_module_user_remove(u);
00638       return -1;
00639    }
00640    
00641    ucfg = ast_config_load("users.conf");
00642 
00643    dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
00644    if (ast_strlen_zero(dirintro))
00645       dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00646    if (ast_strlen_zero(dirintro))
00647       dirintro = last ? "dir-intro" : "dir-intro-fn";
00648 
00649    if (chan->_state != AST_STATE_UP) 
00650       res = ast_answer(chan);
00651 
00652    for (;;) {
00653       if (!res)
00654          res = ast_stream_and_wait(chan, dirintro, chan->language, AST_DIGIT_ANY);
00655       ast_stopstream(chan);
00656       if (!res)
00657          res = ast_waitfordigit(chan, 5000);
00658       if (res > 0) {
00659          res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, last, readext, fromappvm);
00660          if (res > 0) {
00661             res = ast_waitstream(chan, AST_DIGIT_ANY);
00662             ast_stopstream(chan);
00663             if (res >= 0)
00664                continue;
00665          }
00666       }
00667       break;
00668    }
00669    if (ucfg)
00670       ast_config_destroy(ucfg);
00671    ast_config_destroy(cfg);
00672    ast_module_user_remove(u);
00673    return res;
00674 }

static int do_directory ( struct ast_channel chan,
struct ast_config cfg,
struct ast_config ucfg,
char *  context,
char *  dialcontext,
char  digit,
int  last,
int  readext,
int  fromappvm 
) [static]

Definition at line 418 of file app_directory.c.

References ast_category_browse(), ast_config_option(), ast_copy_string(), ast_goto_if_exists(), ast_log(), ast_readstring(), ast_streamfile(), ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), convert(), ext, free, ast_channel::language, LOG_WARNING, ast_channel::macrocontext, ast_channel::name, name, NUMDIGITS, play_mailbox_owner(), and strdup.

Referenced by directory_exec().

00419 {
00420    /* Read in the first three digits..  "digit" is the first digit, already read */
00421    char ext[NUMDIGITS + 1], *cat;
00422    char name[80] = "";
00423    struct ast_variable *v;
00424    int res;
00425    int found=0;
00426    int lastuserchoice = 0;
00427    char *start, *conv, *stringp = NULL;
00428    const char *pos;
00429    int breakout = 0;
00430 
00431    if (ast_strlen_zero(context)) {
00432       ast_log(LOG_WARNING,
00433          "Directory must be called with an argument "
00434          "(context in which to interpret extensions)\n");
00435       return -1;
00436    }
00437    if (digit == '0') {
00438       if (!ast_goto_if_exists(chan, dialcontext, "o", 1) ||
00439           (!ast_strlen_zero(chan->macrocontext) &&
00440            !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
00441          return 0;
00442       } else {
00443          ast_log(LOG_WARNING, "Can't find extension 'o' in current context.  "
00444             "Not Exiting the Directory!\n");
00445          res = 0;
00446       }
00447    }  
00448    if (digit == '*') {
00449       if (!ast_goto_if_exists(chan, dialcontext, "a", 1) ||
00450           (!ast_strlen_zero(chan->macrocontext) &&
00451            !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
00452          return 0;
00453       } else {
00454          ast_log(LOG_WARNING, "Can't find extension 'a' in current context.  "
00455             "Not Exiting the Directory!\n");
00456          res = 0;
00457       }
00458    }  
00459    memset(ext, 0, sizeof(ext));
00460    ext[0] = digit;
00461    res = 0;
00462    if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
00463    if (!res) {
00464       /* Search for all names which start with those digits */
00465       v = ast_variable_browse(cfg, context);
00466       while(v && !res) {
00467          /* Find all candidate extensions */
00468          while(v) {
00469             /* Find a candidate extension */
00470             start = strdup(v->value);
00471             if (start && !strcasestr(start, "hidefromdir=yes")) {
00472                stringp=start;
00473                strsep(&stringp, ",");
00474                pos = strsep(&stringp, ",");
00475                if (pos) {
00476                   ast_copy_string(name, pos, sizeof(name));
00477                   /* Grab the last name */
00478                   if (last && strrchr(pos,' '))
00479                      pos = strrchr(pos, ' ') + 1;
00480                   conv = convert(pos);
00481                   if (conv) {
00482                      if (!strncmp(conv, ext, strlen(ext))) {
00483                         /* Match! */
00484                         found++;
00485                         free(conv);
00486                         free(start);
00487                         break;
00488                      }
00489                      free(conv);
00490                   }
00491                }
00492                free(start);
00493             }
00494             v = v->next;
00495          }
00496 
00497          if (v) {
00498             /* We have a match -- play a greeting if they have it */
00499             res = play_mailbox_owner(chan, context, dialcontext, v->name, name, readext, fromappvm);
00500             switch (res) {
00501                case -1:
00502                   /* user pressed '1' but extension does not exist, or
00503                    * user hungup
00504                    */
00505                   lastuserchoice = 0;
00506                   break;
00507                case '1':
00508                   /* user pressed '1' and extensions exists;
00509                      play_mailbox_owner will already have done
00510                      a goto() on the channel
00511                    */
00512                   lastuserchoice = res;
00513                   break;
00514                case '*':
00515                   /* user pressed '*' to skip something found */
00516                   lastuserchoice = res;
00517                   res = 0;
00518                   break;
00519                default:
00520                   break;
00521             }
00522             v = v->next;
00523          }
00524       }
00525 
00526       if (!res && ucfg) {
00527          /* Search users.conf for all names which start with those digits */
00528          for (cat = ast_category_browse(ucfg, NULL); cat && !res ; cat = ast_category_browse(ucfg, cat)) {
00529             if (!strcasecmp(cat, "general"))
00530                continue;
00531             if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
00532                continue;
00533             
00534             /* Find all candidate extensions */
00535             if ((pos = ast_variable_retrieve(ucfg, cat, "fullname"))) {
00536                ast_copy_string(name, pos, sizeof(name));
00537                /* Grab the last name */
00538                if (last && strrchr(pos,' '))
00539                   pos = strrchr(pos, ' ') + 1;
00540                conv = convert(pos);
00541                if (conv) {
00542                   if (!strcmp(conv, ext)) {
00543                      /* Match! */
00544                      found++;
00545                      /* We have a match -- play a greeting if they have it */
00546                      res = play_mailbox_owner(chan, context, dialcontext, cat, name, readext, fromappvm);
00547                      switch (res) {
00548                      case -1:
00549                         /* user pressed '1' but extension does not exist, or
00550                          * user hungup
00551                          */
00552                         lastuserchoice = 0;
00553                         breakout = 1;
00554                         break;
00555                      case '1':
00556                         /* user pressed '1' and extensions exists;
00557                            play_mailbox_owner will already have done
00558                            a goto() on the channel
00559                          */
00560                         lastuserchoice = res;
00561                         breakout = 1;
00562                         break;
00563                      case '*':
00564                         /* user pressed '*' to skip something found */
00565                         lastuserchoice = res;
00566                         breakout = 0;
00567                         res = 0;
00568                         break;
00569                      default:
00570                         breakout = 1;
00571                         break;
00572                      }
00573                      free(conv);
00574                      if (breakout)
00575                         break;
00576                   }
00577                   else
00578                      free(conv);
00579                }
00580             }
00581          }
00582       }
00583          
00584       if (lastuserchoice != '1') {
00585          res = ast_streamfile(chan, found ? "dir-nomore" : "dir-nomatch", chan->language);
00586          if (!res)
00587             res = 1;
00588          return res;
00589       }
00590       return 0;
00591    }
00592    return res;
00593 }

static int load_module ( void   )  [static]

Definition at line 683 of file app_directory.c.

References ast_config_destroy(), ast_config_load(), ast_copy_string(), ast_log(), ast_register_application(), ast_variable_retrieve(), directory_exec(), LOG_WARNING, and VOICEMAIL_CONFIG.

00684 {
00685 #ifdef ODBC_STORAGE
00686    struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG);
00687    const char *tmp;
00688 
00689    if (cfg) {
00690       if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
00691          ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
00692       }
00693       if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
00694          ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
00695       }
00696       if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
00697          ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
00698       }
00699       ast_config_destroy(cfg);
00700    } else
00701       ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
00702 #endif
00703 
00704    return ast_register_application(app, directory_exec, synopsis, descrip);
00705 }

static int play_mailbox_owner ( struct ast_channel chan,
char *  context,
char *  dialcontext,
char *  ext,
char *  name,
int  readext,
int  fromappvm 
) [static]

Definition at line 280 of file app_directory.c.

References ast_config_AST_SPOOL_DIR, ast_copy_string(), AST_DIGIT_ANY, ast_filedelete(), ast_fileexists(), ast_goto_if_exists(), ast_log(), ast_say_character_str(), ast_stopstream(), ast_stream_and_wait(), ast_strlen_zero(), ast_waitfordigit(), ast_channel::exten, ast_channel::language, LOG_WARNING, and S_OR.

Referenced by do_directory().

00283 {
00284    int res = 0;
00285    int loop;
00286    char fn[256];
00287 
00288    /* Check for the VoiceMail2 greeting first */
00289    snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
00290       ast_config_AST_SPOOL_DIR, context, ext);
00291 #ifdef ODBC_STORAGE
00292    retrieve_file(fn);
00293 #endif
00294 
00295    if (ast_fileexists(fn, NULL, chan->language) <= 0) {
00296       /* no file, check for an old-style Voicemail greeting */
00297       snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
00298          ast_config_AST_SPOOL_DIR, ext);
00299    }
00300 #ifdef ODBC_STORAGE
00301    retrieve_file(fn);
00302 #endif
00303 
00304    if (ast_fileexists(fn, NULL, chan->language) > 0) {
00305       res = ast_stream_and_wait(chan, fn, chan->language, AST_DIGIT_ANY);
00306       ast_stopstream(chan);
00307       /* If Option 'e' was specified, also read the extension number with the name */
00308       if (readext) {
00309          ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
00310          res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
00311       }
00312    } else {
00313       res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
00314       if (!ast_strlen_zero(name) && readext) {
00315          ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
00316          res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
00317       }
00318    }
00319 #ifdef ODBC_STORAGE
00320    ast_filedelete(fn, NULL);  
00321 #endif
00322 
00323    for (loop = 3 ; loop > 0; loop--) {
00324       if (!res)
00325          res = ast_stream_and_wait(chan, "dir-instr", chan->language, AST_DIGIT_ANY);
00326       if (!res)
00327          res = ast_waitfordigit(chan, 3000);
00328       ast_stopstream(chan);
00329    
00330       if (res < 0) /* User hungup, so jump out now */
00331          break;
00332       if (res == '1') { /* Name selected */
00333          if (fromappvm) {
00334             /* We still want to set the exten though */
00335             ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00336          } else {
00337             if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
00338                ast_log(LOG_WARNING,
00339                   "Can't find extension '%s' in context '%s'.  "
00340                   "Did you pass the wrong context to Directory?\n",
00341                   ext, dialcontext);
00342                res = -1;
00343             }
00344          }
00345          break;
00346       }
00347       if (res == '*') /* Skip to next match in list */
00348          break;
00349 
00350       /* Not '1', or '*', so decrement number of tries */
00351       res = 0;
00352    }
00353 
00354    return(res);
00355 }

static struct ast_config* realtime_directory ( char *  context  )  [static]

Definition at line 357 of file app_directory.c.

References ast_category_append(), ast_category_browse(), ast_category_get(), ast_category_new(), ast_config_destroy(), ast_config_load(), ast_load_realtime_multientry(), ast_log(), ast_variable_append(), ast_variable_new(), ast_variable_retrieve(), LOG_WARNING, mailbox, var, and VOICEMAIL_CONFIG.

Referenced by directory_exec().

00358 {
00359    struct ast_config *cfg;
00360    struct ast_config *rtdata;
00361    struct ast_category *cat;
00362    struct ast_variable *var;
00363    char *mailbox;
00364    const char *fullname;
00365    const char *hidefromdir;
00366    char tmp[100];
00367 
00368    /* Load flat file config. */
00369    cfg = ast_config_load(VOICEMAIL_CONFIG);
00370 
00371    if (!cfg) {
00372       /* Loading config failed. */
00373       ast_log(LOG_WARNING, "Loading config failed.\n");
00374       return NULL;
00375    }
00376 
00377    /* Get realtime entries, categorized by their mailbox number
00378       and present in the requested context */
00379    rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
00380 
00381    /* if there are no results, just return the entries from the config file */
00382    if (!rtdata)
00383       return cfg;
00384 
00385    /* Does the context exist within the config file? If not, make one */
00386    cat = ast_category_get(cfg, context);
00387    if (!cat) {
00388       cat = ast_category_new(context);
00389       if (!cat) {
00390          ast_log(LOG_WARNING, "Out of memory\n");
00391          ast_config_destroy(cfg);
00392          if (rtdata) {
00393             ast_config_destroy(rtdata);
00394          }
00395          return NULL;
00396       }
00397       ast_category_append(cfg, cat);
00398    }
00399 
00400    mailbox = NULL;
00401    while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
00402       fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00403       hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
00404       snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
00405           fullname ? fullname : "",
00406           hidefromdir ? hidefromdir : "no");
00407       var = ast_variable_new(mailbox, tmp);
00408       if (var)
00409          ast_variable_append(cat, var);
00410       else
00411          ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00412    }
00413    ast_config_destroy(rtdata);
00414 
00415    return cfg;
00416 }

static int unload_module ( void   )  [static]

Definition at line 676 of file app_directory.c.

References ast_unregister_application().

00677 {
00678    int res;
00679    res = ast_unregister_application(app);
00680    return res;
00681 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Extension Directory" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 707 of file app_directory.c.

char* app = "Directory" [static]

Definition at line 58 of file app_directory.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 707 of file app_directory.c.

char* descrip [static]

Definition at line 61 of file app_directory.c.

char* synopsis = "Provide directory of voicemail extensions" [static]

Definition at line 60 of file app_directory.c.


Generated on Thu Dec 17 13:33:40 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7