Sat Aug 6 00:39:34 2011

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 722 of file app_directory.c.

static void __unreg_module ( void   )  [static]

Definition at line 722 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 608 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().

00609 {
00610    int res = 0;
00611    struct ast_module_user *u;
00612    struct ast_config *cfg, *ucfg;
00613    int last = 1;
00614    int readext = 0;
00615    int fromappvm = 0;
00616    const char *dirintro;
00617    char *parse;
00618    AST_DECLARE_APP_ARGS(args,
00619       AST_APP_ARG(vmcontext);
00620       AST_APP_ARG(dialcontext);
00621       AST_APP_ARG(options);
00622    );
00623 
00624    if (ast_strlen_zero(data)) {
00625       ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00626       return -1;
00627    }
00628 
00629    u = ast_module_user_add(chan);
00630 
00631    parse = ast_strdupa(data);
00632 
00633    AST_STANDARD_APP_ARGS(args, parse);
00634       
00635    if (args.options) {
00636       if (strchr(args.options, 'f'))
00637          last = 0;
00638       if (strchr(args.options, 'e'))
00639          readext = 1;
00640       if (strchr(args.options, 'v'))
00641          fromappvm = 1;
00642    }
00643 
00644    if (ast_strlen_zero(args.dialcontext)) 
00645       args.dialcontext = args.vmcontext;
00646 
00647    cfg = realtime_directory(args.vmcontext);
00648    if (!cfg) {
00649       ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
00650       ast_module_user_remove(u);
00651       return -1;
00652    }
00653    
00654    ucfg = ast_config_load("users.conf");
00655 
00656    dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
00657    if (ast_strlen_zero(dirintro))
00658       dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00659    if (ast_strlen_zero(dirintro))
00660       dirintro = last ? "dir-intro" : "dir-intro-fn";
00661    /* the above prompts probably should be modified to include 0 for dialing operator
00662       and # for exiting (continues in dialplan) */
00663 
00664    if (chan->_state != AST_STATE_UP) 
00665       res = ast_answer(chan);
00666 
00667    for (;;) {
00668       if (!res)
00669          res = ast_stream_and_wait(chan, dirintro, chan->language, AST_DIGIT_ANY);
00670       ast_stopstream(chan);
00671       if (!res)
00672          res = ast_waitfordigit(chan, 5000);
00673       if (res > 0) {
00674          res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, last, readext, fromappvm);
00675          if (res > 0) {
00676             res = ast_waitstream(chan, AST_DIGIT_ANY);
00677             ast_stopstream(chan);
00678             if (res >= 0)
00679                continue;
00680          }
00681       }
00682       break;
00683    }
00684    if (ucfg)
00685       ast_config_destroy(ucfg);
00686    ast_config_destroy(cfg);
00687    ast_module_user_remove(u);
00688    return res;
00689 }

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 428 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().

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

static int load_module ( void   )  [static]

Definition at line 698 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.

00699 {
00700 #ifdef ODBC_STORAGE
00701    struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG);
00702    const char *tmp;
00703 
00704    if (cfg) {
00705       if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
00706          ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
00707       }
00708       if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
00709          ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
00710       }
00711       if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
00712          ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
00713       }
00714       ast_config_destroy(cfg);
00715    } else
00716       ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
00717 #endif
00718 
00719    return ast_register_application(app, directory_exec, synopsis, descrip);
00720 }

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, ast_channel::macrocontext, 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 == '0') {
00333       if (!ast_goto_if_exists(chan, dialcontext, "o", 1) ||
00334           (!ast_strlen_zero(chan->macrocontext) &&
00335            !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
00336          /* return 1 to indicate goto has been performed */
00337          return '1';
00338          }
00339       }
00340       if (res == '1') { /* Name selected */
00341          if (fromappvm) {
00342             /* We still want to set the exten though */
00343             ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00344          } else {
00345             if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
00346                ast_log(LOG_WARNING,
00347                   "Can't find extension '%s' in context '%s'.  "
00348                   "Did you pass the wrong context to Directory?\n",
00349                   ext, dialcontext);
00350                res = -1;
00351             }
00352          }
00353          break;
00354       }
00355       if (res == '*') /* Skip to next match in list */
00356          break;
00357       if (res == '#')
00358          break;
00359 
00360       /* Not '1', or '*', so decrement number of tries */
00361       res = 0;
00362    }
00363 
00364    return(res);
00365 }

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

Definition at line 367 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().

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

static int unload_module ( void   )  [static]

Definition at line 691 of file app_directory.c.

References ast_unregister_application().

00692 {
00693    int res;
00694    res = ast_unregister_application(app);
00695    return res;
00696 }


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 722 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 722 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 Sat Aug 6 00:39:34 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7