1999-10-30 01:01:34 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
1999-10-30 01:01:34 +00:00
*
2005-01-21 07:06:25 +00:00
* Copyright ( C ) 1999 - 2005 , Digium , Inc .
1999-10-30 01:01:34 +00:00
*
2004-10-02 00:58:31 +00:00
* Mark Spencer < markster @ digium . com >
1999-10-30 01:01:34 +00:00
*
2005-09-14 20:46:50 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
1999-10-30 01:01:34 +00:00
* This program is free software , distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \ brief Provide a directory of extensions
2005-09-14 20:46:50 +00:00
*
2005-11-06 15:09:47 +00:00
* \ ingroup applications
1999-10-30 01:01:34 +00:00
*/
2005-06-06 22:39:32 +00:00
# include <string.h>
# include <ctype.h>
# include <stdlib.h>
# include <stdio.h>
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2005-04-21 06:02:45 +00:00
# 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"
1999-10-30 01:01:34 +00:00
static char * tdesc = " Extension Directory " ;
static char * app = " Directory " ;
2001-04-10 17:18:04 +00:00
static char * synopsis = " Provide directory of voicemail extensions " ;
static char * descrip =
2004-08-05 22:02:33 +00:00
" Directory(vm-context[|dial-context[|options]]): Presents the user with a directory \n "
2004-04-26 13:48:56 +00:00
" of extensions from which they may select by name. The list of names \n "
2005-11-07 22:01:22 +00:00
" and extensions are retrieved from voicemail.conf. The vm-context argument \n "
2004-04-26 13:48:56 +00:00
" is required, and specifies the context of voicemail.conf to use. The \n "
" dial-context is the context to use for dialing the users, and defaults to \n "
2004-08-05 22:02:33 +00:00
" the vm-context if unspecified. The 'f' option causes the directory to match \n "
" based on the first name in voicemail.conf instead of the last name. \n "
2005-11-07 22:01:22 +00:00
" The query should yield a contact unless the caller disconnects. \n "
" It also sets up the channel on exit to enter the extension the user selected. \n " " If the user enters '0' and there exists an extension 'o' in the current \n " " context, the directory will contact the extension associated with 'o' \n "
2004-12-21 22:43:25 +00:00
" and call control will resume at that extension. Entering '*' will exit similarly, \n "
" but to the 'a' extension, much like app_voicemail's behavior. \n " ;
2001-04-10 17:18:04 +00:00
1999-10-30 01:01:34 +00:00
/* For simplicity, I'm keeping the format compatible with the voicemail config,
but i ' m open to suggestions for isolating it */
2005-04-06 18:55:33 +00:00
# define VOICEMAIL_CONFIG "voicemail.conf"
1999-10-30 01:01:34 +00:00
/* How many digits to read in */
# define NUMDIGITS 3
STANDARD_LOCAL_USER ;
LOCAL_USER_DECL ;
static char * convert ( char * lastname )
{
char * tmp ;
int lcount = 0 ;
tmp = malloc ( NUMDIGITS + 1 ) ;
if ( tmp ) {
while ( ( * lastname > 32 ) & & lcount < NUMDIGITS ) {
switch ( toupper ( * lastname ) ) {
case ' 1 ' :
tmp [ lcount + + ] = ' 1 ' ;
break ;
case ' 2 ' :
case ' A ' :
case ' B ' :
case ' C ' :
tmp [ lcount + + ] = ' 2 ' ;
break ;
case ' 3 ' :
case ' D ' :
case ' E ' :
case ' F ' :
tmp [ lcount + + ] = ' 3 ' ;
break ;
case ' 4 ' :
case ' G ' :
case ' H ' :
case ' I ' :
tmp [ lcount + + ] = ' 4 ' ;
break ;
case ' 5 ' :
case ' J ' :
case ' K ' :
case ' L ' :
tmp [ lcount + + ] = ' 5 ' ;
break ;
case ' 6 ' :
case ' M ' :
case ' N ' :
case ' O ' :
tmp [ lcount + + ] = ' 6 ' ;
break ;
case ' 7 ' :
case ' P ' :
case ' Q ' :
case ' R ' :
case ' S ' :
tmp [ lcount + + ] = ' 7 ' ;
break ;
case ' 8 ' :
case ' T ' :
case ' U ' :
case ' V ' :
tmp [ lcount + + ] = ' 8 ' ;
break ;
case ' 9 ' :
case ' W ' :
case ' X ' :
case ' Y ' :
case ' Z ' :
tmp [ lcount + + ] = ' 9 ' ;
break ;
}
lastname + + ;
}
tmp [ lcount ] = ' \0 ' ;
}
return tmp ;
}
2004-06-22 03:43:41 +00:00
/* play name of mailbox owner.
* returns : - 1 for bad or missing extension
* ' 1 ' for selected entry from directory
* ' * ' for skipped entry from directory
*/
static int play_mailbox_owner ( struct ast_channel * chan , char * context , char * dialcontext , char * ext , char * name ) {
int res = 0 ;
2004-06-28 15:08:39 +00:00
int loop = 3 ;
2004-06-22 03:43:41 +00:00
char fn [ 256 ] ;
char fn2 [ 256 ] ;
/* Check for the VoiceMail2 greeting first */
snprintf ( fn , sizeof ( fn ) , " %s/voicemail/%s/%s/greet " ,
( char * ) ast_config_AST_SPOOL_DIR , context , ext ) ;
/* Otherwise, check for an old-style Voicemail greeting */
snprintf ( fn2 , sizeof ( fn2 ) , " %s/vm/%s/greet " ,
( char * ) ast_config_AST_SPOOL_DIR , ext ) ;
if ( ast_fileexists ( fn , NULL , chan - > language ) > 0 ) {
res = ast_streamfile ( chan , fn , chan - > language ) ;
if ( ! res ) {
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
}
ast_stopstream ( chan ) ;
} else if ( ast_fileexists ( fn2 , NULL , chan - > language ) > 0 ) {
res = ast_streamfile ( chan , fn2 , chan - > language ) ;
if ( ! res ) {
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
}
ast_stopstream ( chan ) ;
} else {
res = ast_say_character_str ( chan , ! ast_strlen_zero ( name ) ? name : ext ,
AST_DIGIT_ANY , chan - > language ) ;
}
while ( loop ) {
if ( ! res ) {
res = ast_streamfile ( chan , " dir-instr " , chan - > language ) ;
}
if ( ! res ) {
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
}
if ( ! res ) {
res = ast_waitfordigit ( chan , 3000 ) ;
}
ast_stopstream ( chan ) ;
if ( res > - 1 ) {
switch ( res ) {
case ' 1 ' :
2004-06-28 15:08:39 +00:00
/* Name selected */
2004-06-22 03:43:41 +00:00
loop = 0 ;
2005-09-15 01:31:49 +00:00
if ( ast_goto_if_exists ( chan , dialcontext , ext , 1 ) ) {
2004-06-22 03:43:41 +00:00
ast_log ( LOG_WARNING ,
" Can't find extension '%s' in context '%s'. "
" Did you pass the wrong context to Directory? \n " ,
2004-08-30 01:44:41 +00:00
ext , dialcontext ) ;
2004-06-22 03:43:41 +00:00
res = - 1 ;
}
break ;
case ' * ' :
2004-06-28 15:08:39 +00:00
/* Skip to next match in list */
2004-06-22 03:43:41 +00:00
loop = 0 ;
break ;
default :
2004-06-28 15:08:39 +00:00
/* Not '1', or '*', so decrement number of tries */
2004-06-22 03:43:41 +00:00
res = 0 ;
2004-06-28 15:08:39 +00:00
loop - - ;
2004-06-22 03:43:41 +00:00
break ;
} /* end switch */
} /* end if */
2004-06-28 15:08:39 +00:00
else {
/* User hungup, so jump out now */
loop = 0 ;
}
2004-06-22 03:43:41 +00:00
} /* end while */
return ( res ) ;
}
2005-03-03 05:31:43 +00:00
static struct ast_config * realtime_directory ( char * context )
{
2005-04-06 18:55:33 +00:00
struct ast_config * cfg ;
struct ast_config * rtdata ;
struct ast_category * cat ;
struct ast_variable * var ;
char * mailbox ;
char * fullname ;
char * hidefromdir ;
char tmp [ 100 ] ;
2005-03-03 05:31:43 +00:00
/* Load flat file config. */
2005-04-06 18:55:33 +00:00
cfg = ast_config_load ( VOICEMAIL_CONFIG ) ;
2005-03-03 05:31:43 +00:00
if ( ! cfg ) {
2005-04-06 18:55:33 +00:00
/* Loading config failed. */
ast_log ( LOG_WARNING , " Loading config failed. \n " ) ;
2005-03-03 05:31:43 +00:00
return NULL ;
}
2005-04-06 18:55:33 +00:00
/* Get realtime entries, categorized by their mailbox number
and present in the requested context */
rtdata = ast_load_realtime_multientry ( " voicemail " , " mailbox LIKE " , " % " , " context " , context , NULL ) ;
2005-03-03 05:31:43 +00:00
2005-04-06 18:55:33 +00:00
/* if there are no results, just return the entries from the config file */
if ( ! rtdata )
2005-03-03 05:31:43 +00:00
return cfg ;
2005-04-06 18:55:33 +00:00
/* Does the context exist within the config file? If not, make one */
cat = ast_category_get ( cfg , context ) ;
if ( ! cat ) {
2005-03-03 05:31:43 +00:00
cat = ast_category_new ( context ) ;
if ( ! cat ) {
2005-04-06 18:55:33 +00:00
ast_log ( LOG_WARNING , " Out of memory \n " ) ;
2005-03-03 05:31:43 +00:00
ast_config_destroy ( cfg ) ;
return NULL ;
}
ast_category_append ( cfg , cat ) ;
}
2005-04-06 18:55:33 +00:00
mailbox = ast_category_browse ( rtdata , NULL ) ;
while ( mailbox ) {
fullname = ast_variable_retrieve ( rtdata , mailbox , " fullname " ) ;
hidefromdir = ast_variable_retrieve ( rtdata , mailbox , " hidefromdir " ) ;
snprintf ( tmp , sizeof ( tmp ) , " no-password,%s,hidefromdir=%s " ,
fullname ? fullname : " " ,
hidefromdir ? hidefromdir : " no " ) ;
var = ast_variable_new ( mailbox , tmp ) ;
if ( var )
ast_variable_append ( cat , var ) ;
else
ast_log ( LOG_WARNING , " Out of memory adding mailbox '%s' \n " , mailbox ) ;
mailbox = ast_category_browse ( rtdata , mailbox ) ;
2005-03-03 05:31:43 +00:00
}
2005-04-06 18:55:33 +00:00
ast_config_destroy ( rtdata ) ;
2005-03-03 05:31:43 +00:00
return cfg ;
}
2004-08-05 22:02:33 +00:00
static int do_directory ( struct ast_channel * chan , struct ast_config * cfg , char * context , char * dialcontext , char digit , int last )
1999-10-30 01:01:34 +00:00
{
/* Read in the first three digits.. "digit" is the first digit, already read */
char ext [ NUMDIGITS + 1 ] ;
2004-05-03 04:36:46 +00:00
char name [ 80 ] = " " ;
1999-10-30 01:01:34 +00:00
struct ast_variable * v ;
int res ;
int found = 0 ;
2004-06-22 03:43:41 +00:00
int lastuserchoice = 0 ;
2003-01-30 15:03:20 +00:00
char * start , * pos , * conv , * stringp = NULL ;
2004-06-22 03:43:41 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( context ) ) {
2004-06-22 03:43:41 +00:00
ast_log ( LOG_WARNING ,
" Directory must be called with an argument "
" (context in which to interpret extensions) \n " ) ;
2003-03-26 21:09:56 +00:00
return - 1 ;
}
2004-12-08 21:24:00 +00:00
if ( digit = = ' 0 ' ) {
2005-09-15 01:31:49 +00:00
if ( ! ast_goto_if_exists ( chan , chan - > context , " o " , 1 ) | |
2005-09-15 00:56:09 +00:00
( ! ast_strlen_zero ( chan - > macrocontext ) & &
2005-09-15 01:31:49 +00:00
! ast_goto_if_exists ( chan , chan - > macrocontext , " o " , 1 ) ) ) {
2004-12-08 21:24:00 +00:00
return 0 ;
} else {
ast_log ( LOG_WARNING , " Can't find extension 'o' in current context. "
" Not Exiting the Directory! \n " ) ;
res = 0 ;
}
}
2004-12-21 22:43:25 +00:00
if ( digit = = ' * ' ) {
2005-09-15 01:31:49 +00:00
if ( ! ast_goto_if_exists ( chan , chan - > context , " a " , 1 ) | |
2005-09-15 00:56:09 +00:00
( ! ast_strlen_zero ( chan - > macrocontext ) & &
2005-09-15 01:31:49 +00:00
! ast_goto_if_exists ( chan , chan - > macrocontext , " a " , 1 ) ) ) {
2004-12-21 22:43:25 +00:00
return 0 ;
} else {
ast_log ( LOG_WARNING , " Can't find extension 'a' in current context. "
" Not Exiting the Directory! \n " ) ;
res = 0 ;
}
}
1999-10-30 01:01:34 +00:00
memset ( ext , 0 , sizeof ( ext ) ) ;
ext [ 0 ] = digit ;
2001-08-05 21:46:13 +00:00
res = 0 ;
2002-05-13 22:29:39 +00:00
if ( ast_readstring ( chan , ext + 1 , NUMDIGITS - 1 , 3000 , 3000 , " # " ) < 0 ) res = - 1 ;
1999-10-30 01:01:34 +00:00
if ( ! res ) {
/* Search for all names which start with those digits */
v = ast_variable_browse ( cfg , context ) ;
while ( v & & ! res ) {
/* Find all candidate extensions */
while ( v ) {
/* Find a candidate extension */
start = strdup ( v - > value ) ;
2005-07-15 22:06:15 +00:00
if ( start & & ! strcasestr ( start , " hidefromdir=yes " ) ) {
2003-01-30 15:03:20 +00:00
stringp = start ;
strsep ( & stringp , " , " ) ;
pos = strsep ( & stringp , " , " ) ;
1999-10-30 01:01:34 +00:00
if ( pos ) {
2005-07-10 23:12:25 +00:00
ast_copy_string ( name , pos , sizeof ( name ) ) ;
1999-10-30 01:01:34 +00:00
/* Grab the last name */
2004-08-05 22:02:33 +00:00
if ( last & & strrchr ( pos , ' ' ) )
1999-10-30 01:01:34 +00:00
pos = strrchr ( pos , ' ' ) + 1 ;
conv = convert ( pos ) ;
if ( conv ) {
if ( ! strcmp ( conv , ext ) ) {
/* Match! */
found + + ;
free ( conv ) ;
free ( start ) ;
break ;
}
free ( conv ) ;
}
}
free ( start ) ;
}
v = v - > next ;
}
2004-06-22 03:43:41 +00:00
1999-10-30 01:01:34 +00:00
if ( v ) {
/* We have a match -- play a greeting if they have it */
2004-06-22 03:43:41 +00:00
res = play_mailbox_owner ( chan , context , dialcontext , v - > name , name ) ;
switch ( res ) {
case - 1 :
2004-06-28 15:08:39 +00:00
/* user pressed '1' but extension does not exist, or
* user hungup
*/
2004-06-22 03:43:41 +00:00
lastuserchoice = 0 ;
1999-10-30 01:01:34 +00:00
break ;
2004-06-22 03:43:41 +00:00
case ' 1 ' :
2005-09-15 01:31:49 +00:00
/* user pressed '1' and extensions exists;
play_mailbox_owner will already have done
a goto ( ) on the channel
*/
2004-06-22 03:43:41 +00:00
lastuserchoice = res ;
break ;
case ' * ' :
/* user pressed '*' to skip something found */
lastuserchoice = res ;
1999-10-30 01:01:34 +00:00
res = 0 ;
2004-06-22 03:43:41 +00:00
break ;
default :
break ;
1999-10-30 01:01:34 +00:00
}
2004-06-22 03:43:41 +00:00
v = v - > next ;
1999-10-30 01:01:34 +00:00
}
}
2004-06-22 03:43:41 +00:00
if ( lastuserchoice ! = ' 1 ' ) {
if ( found )
res = ast_streamfile ( chan , " dir-nomore " , chan - > language ) ;
else
res = ast_streamfile ( chan , " dir-nomatch " , chan - > language ) ;
if ( ! res )
res = 1 ;
return res ;
}
2004-06-23 00:06:46 +00:00
return 0 ;
1999-10-30 01:01:34 +00:00
}
return res ;
}
static int directory_exec ( struct ast_channel * chan , void * data )
{
int res = 0 ;
struct localuser * u ;
struct ast_config * cfg ;
2004-08-05 22:02:33 +00:00
int last = 1 ;
char * context , * dialcontext , * dirintro , * options ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2005-10-04 22:44:15 +00:00
ast_log ( LOG_WARNING , " Directory requires an argument (context[,dialcontext]) \n " ) ;
1999-10-30 01:01:34 +00:00
return - 1 ;
}
2005-03-03 05:31:43 +00:00
2005-10-19 18:19:02 +00:00
LOCAL_USER_ADD ( u ) ;
2004-04-28 15:29:26 +00:00
context = ast_strdupa ( data ) ;
2004-04-26 13:48:56 +00:00
dialcontext = strchr ( context , ' | ' ) ;
if ( dialcontext ) {
* dialcontext = ' \0 ' ;
dialcontext + + ;
2004-08-05 22:02:33 +00:00
options = strchr ( dialcontext , ' | ' ) ;
if ( options ) {
* options = ' \0 ' ;
options + + ;
if ( strchr ( options , ' f ' ) )
last = 0 ;
}
} else
2004-04-26 13:48:56 +00:00
dialcontext = context ;
2005-03-03 05:31:43 +00:00
cfg = realtime_directory ( context ) ;
2005-10-19 18:19:02 +00:00
if ( ! cfg ) {
LOCAL_USER_REMOVE ( u ) ;
2005-03-03 05:31:43 +00:00
return - 1 ;
2005-10-19 18:19:02 +00:00
}
2005-03-03 05:31:43 +00:00
2004-04-29 13:53:38 +00:00
dirintro = ast_variable_retrieve ( cfg , context , " directoryintro " ) ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( dirintro ) )
2004-04-29 13:53:38 +00:00
dirintro = ast_variable_retrieve ( cfg , " general " , " directoryintro " ) ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( dirintro ) ) {
2004-08-05 22:02:33 +00:00
if ( last )
dirintro = " dir-intro " ;
else
dirintro = " dir-intro-fn " ;
}
2005-10-04 22:44:15 +00:00
2003-11-09 06:16:07 +00:00
if ( chan - > _state ! = AST_STATE_UP )
res = ast_answer ( chan ) ;
2005-10-04 22:44:15 +00:00
for ( ; ; ) {
if ( ! res )
res = ast_streamfile ( chan , dirintro , chan - > language ) ;
if ( ! res )
1999-10-30 01:01:34 +00:00
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
2005-10-04 22:44:15 +00:00
ast_stopstream ( chan ) ;
if ( ! res )
res = ast_waitfordigit ( chan , 5000 ) ;
if ( res > 0 ) {
res = do_directory ( chan , cfg , context , dialcontext , res , last ) ;
if ( res > 0 ) {
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
ast_stopstream ( chan ) ;
if ( res > = 0 ) {
continue ;
}
1999-10-30 01:01:34 +00:00
}
}
2005-10-04 22:44:15 +00:00
break ;
1999-10-30 01:01:34 +00:00
}
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg ) ;
1999-10-30 01:01:34 +00:00
LOCAL_USER_REMOVE ( u ) ;
return res ;
}
int unload_module ( void )
{
2005-10-18 22:52:21 +00:00
int res ;
res = ast_unregister_application ( app ) ;
1999-10-30 01:01:34 +00:00
STANDARD_HANGUP_LOCALUSERS ;
2005-10-18 22:52:21 +00:00
return res ;
1999-10-30 01:01:34 +00:00
}
int load_module ( void )
{
2001-04-10 17:18:04 +00:00
return ast_register_application ( app , directory_exec , synopsis , descrip ) ;
1999-10-30 01:01:34 +00:00
}
char * description ( void )
{
return tdesc ;
}
int usecount ( void )
{
int res ;
STANDARD_USECOUNT ( res ) ;
return res ;
}
2001-03-07 00:52:22 +00:00
char * key ( )
{
return ASTERISK_GPL_KEY ;
}