2001-12-27 11:07:33 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2001-12-27 11:07:33 +00:00
*
2006-01-05 09:12:33 +00:00
* Copyright ( C ) 1999 - 2006 , Digium , Inc .
2001-12-27 11:07:33 +00:00
*
2004-08-31 13:32:11 +00:00
* Mark Spencer < markster @ digium . com >
2001-12-27 11:07:33 +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 .
*
2001-12-27 11:07:33 +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
*
2006-01-19 22:09:18 +00:00
* \ brief Routines implementing call features as call pickup , parking and transfer
2005-12-30 21:18:06 +00:00
*
* \ author Mark Spencer < markster @ digium . com >
2001-12-27 11:07:33 +00:00
*/
2005-06-06 22:12:19 +00:00
# include <pthread.h>
# include <stdlib.h>
# include <errno.h>
# include <unistd.h>
# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <sys/time.h>
# include <sys/signal.h>
# include <netinet/in.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/options.h"
2005-06-23 22:12:01 +00:00
# include "asterisk/causes.h"
2005-04-21 06:02:45 +00:00
# include "asterisk/module.h"
# include "asterisk/translate.h"
# include "asterisk/app.h"
# include "asterisk/say.h"
# include "asterisk/features.h"
# include "asterisk/musiconhold.h"
# include "asterisk/config.h"
# include "asterisk/cli.h"
# include "asterisk/manager.h"
# include "asterisk/utils.h"
# include "asterisk/adsi.h"
2005-11-08 04:02:35 +00:00
# include "asterisk/monitor.h"
2001-12-27 11:07:33 +00:00
2005-05-09 14:28:53 +00:00
# ifdef __AST_DEBUG_MALLOC
static void FREE ( void * ptr )
{
free ( ptr ) ;
}
# else
# define FREE free
# endif
2003-02-02 19:37:23 +00:00
# define DEFAULT_PARK_TIME 45000
2004-08-01 01:38:15 +00:00
# define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
2005-01-04 04:01:40 +00:00
# define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
2006-05-23 18:23:05 +00:00
# define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
2003-02-02 19:37:23 +00:00
2005-06-23 22:12:01 +00:00
# define AST_MAX_WATCHERS 256
2001-12-27 11:07:33 +00:00
static char * parkedcall = " ParkedCall " ;
2006-01-09 09:07:58 +00:00
static int parkingtime = DEFAULT_PARK_TIME ; /*!< No more than 45 seconds parked before you do something with them */
static char parking_con [ AST_MAX_EXTENSION ] ; /*!< Context for which parking is made accessible */
static char parking_con_dial [ AST_MAX_EXTENSION ] ; /*!< Context for dialback for parking (KLUDGE) */
static char parking_ext [ AST_MAX_EXTENSION ] ; /*!< Extension you type to park the call */
static char pickup_ext [ AST_MAX_EXTENSION ] ; /*!< Call pickup extension */
static int parking_start ; /*!< First available extension for parking */
static int parking_stop ; /*!< Last available extension for parking */
static char courtesytone [ 256 ] ; /*!< Courtesy tone */
static int parkedplay = 0 ; /*!< Who to play the courtesy tone to */
static char xfersound [ 256 ] ; /*!< Call transfer sound */
static char xferfailsound [ 256 ] ; /*!< Call transfer failure sound */
2001-12-27 11:07:33 +00:00
2005-10-13 23:58:33 +00:00
static int parking_offset ;
static int parkfindnext ;
2005-04-27 03:58:40 +00:00
2005-10-13 23:58:33 +00:00
static int adsipark ;
2004-09-15 19:49:33 +00:00
2005-10-13 23:58:33 +00:00
static int transferdigittimeout ;
static int featuredigittimeout ;
2004-08-01 01:38:15 +00:00
2006-05-23 18:23:05 +00:00
static int atxfernoanswertimeout ;
2006-01-09 09:07:58 +00:00
static char * registrar = " res_features " ; /*!< Registrar for operations */
2001-12-27 11:07:33 +00:00
2006-01-09 09:07:58 +00:00
/* module and CLI command definitions */
2001-12-27 11:07:33 +00:00
static char * synopsis = " Answer a parked call " ;
static char * descrip = " ParkedCall(exten): "
2004-12-28 23:49:46 +00:00
" Used to connect to a parked call. This application is always \n "
2001-12-27 11:07:33 +00:00
" registered internally and does not need to be explicitly added \n "
" into the dialplan, although you should include the 'parkedcalls' \n "
" context. \n " ;
2004-08-03 06:31:20 +00:00
static char * parkcall = " Park " ;
static char * synopsis2 = " Park yourself " ;
2006-04-10 17:32:29 +00:00
static char * descrip2 = " Park(): "
2004-08-03 06:31:20 +00:00
" Used to park yourself (typically in combination with a supervised \n "
2004-12-28 23:49:46 +00:00
" transfer to know the parking space). This application is always \n "
2004-08-03 06:31:20 +00:00
" registered internally and does not need to be explicitly added \n "
" into the dialplan, although you should include the 'parkedcalls' \n "
" context. \n " ;
2006-01-09 09:07:58 +00:00
static struct ast_app * monitor_app = NULL ;
static int monitor_ok = 1 ;
2004-09-17 03:49:57 +00:00
2001-12-27 11:07:33 +00:00
struct parkeduser {
struct ast_channel * chan ;
struct timeval start ;
int parkingnum ;
/* Where to go if our parking time expires */
2005-07-10 23:58:33 +00:00
char context [ AST_MAX_CONTEXT ] ;
2001-12-27 11:07:33 +00:00
char exten [ AST_MAX_EXTENSION ] ;
int priority ;
2003-02-02 19:37:23 +00:00
int parkingtime ;
2004-08-03 06:31:20 +00:00
int notquiteyet ;
2004-12-09 22:39:14 +00:00
char peername [ 1024 ] ;
2005-03-17 21:52:57 +00:00
unsigned char moh_trys ;
2001-12-27 11:07:33 +00:00
struct parkeduser * next ;
} ;
static struct parkeduser * parkinglot ;
2006-04-17 04:31:21 +00:00
AST_MUTEX_DEFINE_STATIC ( parking_lock ) ; /*!< protects all static variables above */
2001-12-27 11:07:33 +00:00
static pthread_t parking_thread ;
char * ast_parking_ext ( void )
{
return parking_ext ;
}
2003-04-09 04:00:43 +00:00
char * ast_pickup_ext ( void )
{
return pickup_ext ;
}
2005-01-05 19:56:47 +00:00
struct ast_bridge_thread_obj
{
struct ast_bridge_config bconfig ;
struct ast_channel * chan ;
struct ast_channel * peer ;
} ;
2006-04-17 04:31:21 +00:00
/*! \brief store context, priority and extension */
static void set_c_e_p ( struct ast_channel * chan , const char * context , const char * ext , int pri )
2006-04-16 18:49:46 +00:00
{
2006-04-17 04:31:21 +00:00
ast_copy_string ( chan - > context , context , sizeof ( chan - > context ) ) ;
2006-04-16 18:49:46 +00:00
ast_copy_string ( chan - > exten , ext , sizeof ( chan - > exten ) ) ;
chan - > priority = pri ;
}
2005-04-22 02:55:14 +00:00
static void check_goto_on_transfer ( struct ast_channel * chan )
{
struct ast_channel * xferchan ;
2005-12-03 19:25:33 +00:00
const char * val = pbx_builtin_getvar_helper ( chan , " GOTO_ON_BLINDXFR " ) ;
char * x , * goto_on_transfer ;
struct ast_frame * f ;
2005-04-22 02:55:14 +00:00
2006-05-10 13:22:15 +00:00
if ( ast_strlen_zero ( val ) )
return ;
goto_on_transfer = ast_strdupa ( val ) ;
if ( ! ( xferchan = ast_channel_alloc ( 0 ) ) )
return ;
for ( x = goto_on_transfer ; x & & * x ; x + + ) {
if ( * x = = ' ^ ' )
* x = ' | ' ;
}
ast_string_field_set ( xferchan , name , chan - > name ) ;
/* Make formats okay */
xferchan - > readformat = chan - > readformat ;
xferchan - > writeformat = chan - > writeformat ;
ast_channel_masquerade ( xferchan , chan ) ;
ast_parseable_goto ( xferchan , goto_on_transfer ) ;
xferchan - > _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL ) ;
xferchan - > _softhangup = 0 ;
if ( ( f = ast_read ( xferchan ) ) ) {
ast_frfree ( f ) ;
f = NULL ;
ast_pbx_start ( xferchan ) ;
} else {
ast_hangup ( xferchan ) ;
2005-04-22 02:55:14 +00:00
}
}
2005-06-23 22:12:01 +00:00
static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel * caller , const char * type , int format , void * data , int timeout , int * outstate , const char * cid_num , const char * cid_name ) ;
2005-01-05 19:56:47 +00:00
static void * ast_bridge_call_thread ( void * data )
{
struct ast_bridge_thread_obj * tobj = data ;
2005-11-06 21:00:35 +00:00
2005-01-05 19:56:47 +00:00
tobj - > chan - > appl = " Transferred Call " ;
2006-04-14 23:20:29 +00:00
tobj - > chan - > data = tobj - > peer - > name ;
2005-01-05 19:56:47 +00:00
tobj - > peer - > appl = " Transferred Call " ;
2006-04-14 23:20:29 +00:00
tobj - > peer - > data = tobj - > chan - > name ;
2005-01-05 19:56:47 +00:00
if ( tobj - > chan - > cdr ) {
2005-11-06 21:00:35 +00:00
ast_cdr_reset ( tobj - > chan - > cdr , NULL ) ;
2005-01-05 19:56:47 +00:00
ast_cdr_setdestchan ( tobj - > chan - > cdr , tobj - > peer - > name ) ;
}
if ( tobj - > peer - > cdr ) {
2005-11-06 21:00:35 +00:00
ast_cdr_reset ( tobj - > peer - > cdr , NULL ) ;
2005-01-05 19:56:47 +00:00
ast_cdr_setdestchan ( tobj - > peer - > cdr , tobj - > chan - > name ) ;
}
ast_bridge_call ( tobj - > peer , tobj - > chan , & tobj - > bconfig ) ;
ast_hangup ( tobj - > chan ) ;
ast_hangup ( tobj - > peer ) ;
2006-04-17 04:31:21 +00:00
bzero ( tobj , sizeof ( * tobj ) ) ; /*! \todo XXX for safety */
2005-01-05 19:56:47 +00:00
free ( tobj ) ;
return NULL ;
}
static void ast_bridge_call_thread_launch ( void * data )
{
pthread_t thread ;
pthread_attr_t attr ;
2005-11-09 00:16:08 +00:00
struct sched_param sched ;
2005-01-05 19:56:47 +00:00
2005-11-09 00:16:08 +00:00
pthread_attr_init ( & attr ) ;
2005-01-05 19:56:47 +00:00
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED ) ;
2005-11-09 00:16:08 +00:00
ast_pthread_create ( & thread , & attr , ast_bridge_call_thread , data ) ;
pthread_attr_destroy ( & attr ) ;
memset ( & sched , 0 , sizeof ( sched ) ) ;
pthread_setschedparam ( thread , SCHED_RR , & sched ) ;
2005-01-05 19:56:47 +00:00
}
2004-09-15 19:49:33 +00:00
static int adsi_announce_park ( struct ast_channel * chan , int parkingnum )
{
int res ;
int justify [ 5 ] = { ADSI_JUST_CENT , ADSI_JUST_CENT , ADSI_JUST_CENT , ADSI_JUST_CENT } ;
2005-09-07 21:01:31 +00:00
char tmp [ 256 ] ;
2005-09-07 18:55:03 +00:00
char * message [ 5 ] = { NULL , NULL , NULL , NULL , NULL } ;
2004-09-15 19:49:33 +00:00
snprintf ( tmp , sizeof ( tmp ) , " Parked on %d " , parkingnum ) ;
2005-09-07 18:55:03 +00:00
message [ 0 ] = tmp ;
2004-09-15 19:49:33 +00:00
res = adsi_load_session ( chan , NULL , 0 , 1 ) ;
2006-04-14 23:20:29 +00:00
if ( res = = - 1 )
2004-09-15 19:49:33 +00:00
return res ;
return adsi_print ( chan , message , justify , 1 ) ;
}
2006-01-19 22:09:18 +00:00
/*! \brief Park a call
We put the user in the parking list , then wake up the parking thread to be sure it looks
after these channels too */
2003-02-02 19:37:23 +00:00
int ast_park_call ( struct ast_channel * chan , struct ast_channel * peer , int timeout , int * extout )
2001-12-27 11:07:33 +00:00
{
struct parkeduser * pu , * cur ;
2005-04-27 03:58:40 +00:00
int i , x , parking_range ;
2004-08-03 06:31:20 +00:00
char exten [ AST_MAX_EXTENSION ] ;
struct ast_context * con ;
2006-01-21 22:09:06 +00:00
if ( ! ( pu = ast_calloc ( 1 , sizeof ( * pu ) ) ) ) {
2005-07-25 17:31:53 +00:00
return - 1 ;
}
ast_mutex_lock ( & parking_lock ) ;
parking_range = parking_stop - parking_start + 1 ;
for ( i = 0 ; i < parking_range ; i + + ) {
x = ( i + parking_offset ) % parking_range + parking_start ;
cur = parkinglot ;
while ( cur ) {
if ( cur - > parkingnum = = x )
2001-12-27 11:07:33 +00:00
break ;
2005-07-25 17:31:53 +00:00
cur = cur - > next ;
2001-12-27 11:07:33 +00:00
}
2005-07-25 17:31:53 +00:00
if ( ! cur )
break ;
}
2005-04-27 03:58:40 +00:00
2005-07-25 17:31:53 +00:00
if ( ! ( i < parking_range ) ) {
ast_log ( LOG_WARNING , " No more parking spaces \n " ) ;
free ( pu ) ;
ast_mutex_unlock ( & parking_lock ) ;
return - 1 ;
}
if ( parkfindnext )
parking_offset = x - parking_start + 1 ;
chan - > appl = " Parked Call " ;
chan - > data = NULL ;
pu - > chan = chan ;
/* Start music on hold */
if ( chan ! = peer ) {
ast_indicate ( pu - > chan , AST_CONTROL_HOLD ) ;
ast_moh_start ( pu - > chan , NULL ) ;
}
pu - > start = ast_tvnow ( ) ;
pu - > parkingnum = x ;
2006-04-16 18:37:01 +00:00
pu - > parkingtime = ( timeout > 0 ) ? timeout : parkingtime ;
2005-07-25 17:31:53 +00:00
if ( extout )
* extout = x ;
if ( peer )
ast_copy_string ( pu - > peername , peer - > name , sizeof ( pu - > peername ) ) ;
/* Remember what had been dialed, so that if the parking
expires , we try to come back to the same place */
2006-04-14 23:20:29 +00:00
ast_copy_string ( pu - > context , S_OR ( chan - > macrocontext , chan - > context ) , sizeof ( pu - > context ) ) ;
ast_copy_string ( pu - > exten , S_OR ( chan - > macroexten , chan - > exten ) , sizeof ( pu - > exten ) ) ;
pu - > priority = chan - > macropriority ? chan - > macropriority : chan - > priority ;
2005-07-25 17:31:53 +00:00
pu - > next = parkinglot ;
parkinglot = pu ;
/* If parking a channel directly, don't quiet yet get parking running on it */
if ( peer = = chan )
pu - > notquiteyet = 1 ;
ast_mutex_unlock ( & parking_lock ) ;
/* Wake up the (presumably select()ing) thread */
pthread_kill ( parking_thread , SIGURG ) ;
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Parked %s on %d. Will timeout back to extension [%s] %s, %d in %d seconds \n " , pu - > chan - > name , pu - > parkingnum , pu - > context , pu - > exten , pu - > priority , ( pu - > parkingtime / 1000 ) ) ;
manager_event ( EVENT_FLAG_CALL , " ParkedCall " ,
" Exten: %d \r \n "
" Channel: %s \r \n "
" From: %s \r \n "
" Timeout: %ld \r \n "
" CallerID: %s \r \n "
2006-04-21 10:47:07 +00:00
" CallerIDName: %s \r \n " ,
pu - > parkingnum , pu - > chan - > name , peer ? peer - > name : " " ,
( long ) pu - > start . tv_sec + ( long ) ( pu - > parkingtime / 1000 ) - ( long ) time ( NULL ) ,
S_OR ( pu - > chan - > cid . cid_num , " <unknown> " ) ,
S_OR ( pu - > chan - > cid . cid_name , " <unknown> " )
2005-07-25 17:31:53 +00:00
) ;
2003-07-02 14:06:12 +00:00
2005-07-25 17:31:53 +00:00
if ( peer ) {
2006-04-14 23:20:29 +00:00
if ( adsipark & & adsi_available ( peer ) )
2005-07-25 17:31:53 +00:00
adsi_announce_park ( peer , pu - > parkingnum ) ;
2006-04-14 23:20:29 +00:00
if ( adsipark & & adsi_available ( peer ) )
2005-07-25 17:31:53 +00:00
adsi_unload_session ( peer ) ;
}
con = ast_context_find ( parking_con ) ;
if ( ! con ) {
con = ast_context_create ( NULL , parking_con , registrar ) ;
2006-04-14 23:20:29 +00:00
if ( ! con )
2005-07-25 17:31:53 +00:00
ast_log ( LOG_ERROR , " Parking context '%s' does not exist and unable to create \n " , parking_con ) ;
}
if ( con ) {
snprintf ( exten , sizeof ( exten ) , " %d " , x ) ;
ast_add_extension2 ( con , 1 , exten , 1 , NULL , NULL , parkedcall , strdup ( exten ) , FREE , registrar ) ;
}
if ( peer )
ast_say_digits ( peer , pu - > parkingnum , " " , peer - > language ) ;
if ( pu - > notquiteyet ) {
/* Wake up parking thread if we're really done */
ast_moh_start ( pu - > chan , NULL ) ;
pu - > notquiteyet = 0 ;
pthread_kill ( parking_thread , SIGURG ) ;
2001-12-27 11:07:33 +00:00
}
return 0 ;
}
2003-02-02 19:37:23 +00:00
int ast_masq_park_call ( struct ast_channel * rchan , struct ast_channel * peer , int timeout , int * extout )
2001-12-27 11:07:33 +00:00
{
struct ast_channel * chan ;
struct ast_frame * f ;
2005-07-25 17:31:53 +00:00
2001-12-27 11:07:33 +00:00
/* Make a new, fake channel that we'll use to masquerade in the real one */
2006-01-21 22:09:06 +00:00
if ( ( chan = ast_channel_alloc ( 0 ) ) ) {
2001-12-27 11:07:33 +00:00
/* Let us keep track of the channel name */
2006-02-01 23:05:28 +00:00
ast_string_field_build ( chan , name , " Parked/%s " , rchan - > name ) ;
2005-07-25 17:31:53 +00:00
2001-12-27 11:07:33 +00:00
/* Make formats okay */
chan - > readformat = rchan - > readformat ;
chan - > writeformat = rchan - > writeformat ;
ast_channel_masquerade ( chan , rchan ) ;
2005-07-25 17:31:53 +00:00
2001-12-27 11:07:33 +00:00
/* Setup the extensions and such */
2006-04-16 18:49:46 +00:00
set_c_e_p ( chan , rchan - > context , rchan - > exten , rchan - > priority ) ;
2005-07-25 17:31:53 +00:00
2001-12-27 11:07:33 +00:00
/* Make the masq execute */
f = ast_read ( chan ) ;
if ( f )
ast_frfree ( f ) ;
2003-02-02 19:37:23 +00:00
ast_park_call ( chan , peer , timeout , extout ) ;
2001-12-27 11:07:33 +00:00
} else {
ast_log ( LOG_WARNING , " Unable to create parked channel \n " ) ;
return - 1 ;
}
return 0 ;
}
2005-01-04 04:01:40 +00:00
# define FEATURE_RETURN_HANGUP -1
# define FEATURE_RETURN_SUCCESSBREAK 0
# define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
# define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
# define FEATURE_RETURN_PASSDIGITS 21
# define FEATURE_RETURN_STOREDIGITS 22
# define FEATURE_RETURN_SUCCESS 23
# define FEATURE_SENSE_CHAN (1 << 0)
# define FEATURE_SENSE_PEER (1 << 1)
2005-08-23 02:22:33 +00:00
2006-04-16 19:26:57 +00:00
/*
* set caller and callee according to the direction
*/
static void set_peers ( struct ast_channel * * caller , struct ast_channel * * callee ,
struct ast_channel * peer , struct ast_channel * chan , int sense )
{
if ( sense = = FEATURE_SENSE_PEER ) {
* caller = peer ;
* callee = chan ;
} else {
* callee = peer ;
* caller = chan ;
}
}
2005-01-04 04:01:40 +00:00
2006-05-22 16:43:43 +00:00
static int builtin_parkcall ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
struct ast_channel * parker ;
struct ast_channel * parkee ;
int res = 0 ;
struct localuser * u ;
LOCAL_USER_ADD ( u ) ;
set_peers ( & parker , & parkee , peer , chan , sense ) ;
/* Setup the exten/priority to be s/1 since we don't know
where this call should return */
strcpy ( chan - > exten , " s " ) ;
chan - > priority = 1 ;
if ( chan - > _state ! = AST_STATE_UP )
res = ast_answer ( chan ) ;
if ( ! res )
res = ast_safe_sleep ( chan , 1000 ) ;
if ( ! res )
res = ast_park_call ( parkee , parker , 0 , NULL ) ;
LOCAL_USER_REMOVE ( u ) ;
if ( ! res ) {
if ( sense = = FEATURE_SENSE_CHAN )
res = AST_PBX_NO_HANGUP_PEER ;
else
res = AST_PBX_KEEPALIVE ;
}
return res ;
}
2005-01-05 19:56:47 +00:00
static int builtin_automonitor ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
2005-12-08 18:18:59 +00:00
char * caller_chan_id = NULL , * callee_chan_id = NULL , * args = NULL , * touch_filename = NULL ;
2005-01-10 04:03:30 +00:00
int x = 0 ;
size_t len ;
2006-04-18 13:05:48 +00:00
struct ast_channel * caller_chan , * callee_chan ;
2005-01-11 18:24:27 +00:00
2005-01-10 04:03:30 +00:00
if ( ! monitor_ok ) {
ast_log ( LOG_ERROR , " Cannot record the call. The monitor application is disabled. \n " ) ;
return - 1 ;
}
2005-01-05 19:56:47 +00:00
2006-04-16 18:37:01 +00:00
if ( ! monitor_app & & ! ( monitor_app = pbx_findapp ( " Monitor " ) ) ) {
monitor_ok = 0 ;
ast_log ( LOG_ERROR , " Cannot record the call. The monitor application is disabled. \n " ) ;
return - 1 ;
2005-01-10 04:03:30 +00:00
}
2006-04-16 19:26:57 +00:00
set_peers ( & caller_chan , & callee_chan , peer , chan , sense ) ;
2005-01-10 04:03:30 +00:00
if ( ! ast_strlen_zero ( courtesytone ) ) {
2005-01-11 18:24:27 +00:00
if ( ast_autoservice_start ( callee_chan ) )
2005-01-10 04:03:30 +00:00
return - 1 ;
2006-04-21 20:28:32 +00:00
if ( ast_stream_and_wait ( caller_chan , courtesytone , caller_chan - > language , " " ) ) {
2006-04-16 22:46:00 +00:00
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
ast_autoservice_stop ( callee_chan ) ;
return - 1 ;
2005-01-05 22:38:07 +00:00
}
2005-01-11 18:24:27 +00:00
if ( ast_autoservice_stop ( callee_chan ) )
2005-01-10 04:03:30 +00:00
return - 1 ;
}
2005-01-11 18:24:27 +00:00
if ( callee_chan - > monitor ) {
2005-01-10 04:03:30 +00:00
if ( option_verbose > 3 )
ast_verbose ( VERBOSE_PREFIX_3 " User hit '%s' to stop recording call. \n " , code ) ;
2005-01-11 18:24:27 +00:00
ast_monitor_stop ( callee_chan , 1 ) ;
2005-01-05 19:56:47 +00:00
return FEATURE_RETURN_SUCCESS ;
}
2005-01-11 18:24:27 +00:00
if ( caller_chan & & callee_chan ) {
2005-12-03 19:25:33 +00:00
const char * touch_format = pbx_builtin_getvar_helper ( caller_chan , " TOUCH_MONITOR_FORMAT " ) ;
const char * touch_monitor = pbx_builtin_getvar_helper ( caller_chan , " TOUCH_MONITOR " ) ;
2005-05-16 00:43:16 +00:00
if ( ! touch_format )
touch_format = pbx_builtin_getvar_helper ( callee_chan , " TOUCH_MONITOR_FORMAT " ) ;
2005-01-10 04:03:30 +00:00
if ( ! touch_monitor )
2005-01-11 18:24:27 +00:00
touch_monitor = pbx_builtin_getvar_helper ( callee_chan , " TOUCH_MONITOR " ) ;
2005-12-08 18:18:59 +00:00
2005-01-10 04:03:30 +00:00
if ( touch_monitor ) {
len = strlen ( touch_monitor ) + 50 ;
args = alloca ( len ) ;
2005-12-08 18:18:59 +00:00
touch_filename = alloca ( len ) ;
2006-02-22 22:53:49 +00:00
snprintf ( touch_filename , len , " auto-%ld-%s " , ( long ) time ( NULL ) , touch_monitor ) ;
2005-12-08 18:18:59 +00:00
snprintf ( args , len , " %s|%s|m " , ( touch_format ) ? touch_format : " wav " , touch_filename ) ;
2005-01-10 04:03:30 +00:00
} else {
2006-04-21 10:47:07 +00:00
caller_chan_id = ast_strdupa ( S_OR ( caller_chan - > cid . cid_num , caller_chan - > name ) ) ;
callee_chan_id = ast_strdupa ( S_OR ( callee_chan - > cid . cid_num , callee_chan - > name ) ) ;
2005-01-11 18:24:27 +00:00
len = strlen ( caller_chan_id ) + strlen ( callee_chan_id ) + 50 ;
2005-01-10 04:03:30 +00:00
args = alloca ( len ) ;
2005-12-08 18:18:59 +00:00
touch_filename = alloca ( len ) ;
2006-02-22 22:53:49 +00:00
snprintf ( touch_filename , len , " auto-%ld-%s-%s " , ( long ) time ( NULL ) , caller_chan_id , callee_chan_id ) ;
2006-04-21 10:47:07 +00:00
snprintf ( args , len , " %s|%s|m " , S_OR ( touch_format , " wav " ) , touch_filename ) ;
2005-01-10 04:03:30 +00:00
}
2006-04-14 23:20:29 +00:00
for ( x = 0 ; x < strlen ( args ) ; x + + ) {
2005-01-10 04:03:30 +00:00
if ( args [ x ] = = ' / ' )
args [ x ] = ' - ' ;
2006-04-14 23:20:29 +00:00
}
2005-01-10 04:03:30 +00:00
if ( option_verbose > 3 )
ast_verbose ( VERBOSE_PREFIX_3 " User hit '%s' to record call. filename: %s \n " , code , args ) ;
2006-03-30 21:29:39 +00:00
pbx_exec ( callee_chan , monitor_app , args ) ;
2005-12-08 18:18:59 +00:00
pbx_builtin_setvar_helper ( callee_chan , " TOUCH_MONITOR_OUTPUT " , touch_filename ) ;
pbx_builtin_setvar_helper ( caller_chan , " TOUCH_MONITOR_OUTPUT " , touch_filename ) ;
2005-01-10 04:03:30 +00:00
return FEATURE_RETURN_SUCCESS ;
}
ast_log ( LOG_NOTICE , " Cannot record the call. One or both channels have gone away. \n " ) ;
2005-01-05 19:56:47 +00:00
return - 1 ;
}
2005-01-04 04:01:40 +00:00
static int builtin_disconnect ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
if ( option_verbose > 3 )
ast_verbose ( VERBOSE_PREFIX_3 " User hit '%s' to disconnect call. \n " , code ) ;
return FEATURE_RETURN_HANGUP ;
}
2006-04-16 19:26:57 +00:00
static int finishup ( struct ast_channel * chan )
{
int res ;
ast_moh_stop ( chan ) ;
res = ast_autoservice_stop ( chan ) ;
ast_indicate ( chan , AST_CONTROL_UNHOLD ) ;
return res ;
}
static const char * real_ctx ( struct ast_channel * transferer , struct ast_channel * transferee )
{
2006-05-26 17:59:29 +00:00
const char * s = pbx_builtin_getvar_helper ( transferer , " TRANSFER_CONTEXT " ) ;
2006-04-16 19:26:57 +00:00
if ( ast_strlen_zero ( s ) )
2006-05-26 17:59:29 +00:00
s = pbx_builtin_getvar_helper ( transferee , " TRANSFER_CONTEXT " ) ;
2006-04-16 19:26:57 +00:00
if ( ast_strlen_zero ( s ) ) /* Use the non-macro context to transfer the call XXX ? */
s = transferer - > macrocontext ;
if ( ast_strlen_zero ( s ) )
s = transferer - > context ;
return s ;
}
2005-01-04 04:01:40 +00:00
static int builtin_blindtransfer ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
struct ast_channel * transferer ;
struct ast_channel * transferee ;
2005-12-03 19:25:33 +00:00
const char * transferer_real_context ;
2006-04-16 19:56:08 +00:00
char xferto [ 256 ] ;
2005-01-04 04:01:40 +00:00
int res ;
2006-04-16 19:26:57 +00:00
set_peers ( & transferer , & transferee , peer , chan , sense ) ;
transferer_real_context = real_ctx ( transferer , transferee ) ;
2006-04-16 19:41:12 +00:00
/* Start autoservice on chan while we talk to the originator */
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_HOLD ) ;
2005-01-04 04:01:40 +00:00
ast_autoservice_start ( transferee ) ;
ast_moh_start ( transferee , NULL ) ;
2006-04-16 19:56:08 +00:00
memset ( xferto , 0 , sizeof ( xferto ) ) ;
2005-02-17 20:04:10 +00:00
2005-01-04 04:01:40 +00:00
/* Transfer */
2006-04-21 20:28:32 +00:00
res = ast_stream_and_wait ( transferer , " pbx-transfer " , transferer - > language , AST_DIGIT_ANY ) ;
2006-04-16 21:41:06 +00:00
if ( res < 0 ) {
finishup ( transferee ) ;
return - 1 ; /* error ? */
2005-01-04 04:01:40 +00:00
}
2006-04-18 13:05:48 +00:00
if ( res > 0 ) /* If they've typed a digit already, handle it */
xferto [ 0 ] = ( char ) res ;
2005-01-04 04:01:40 +00:00
2005-02-17 20:04:10 +00:00
ast_stopstream ( transferer ) ;
2006-04-16 19:56:08 +00:00
res = ast_app_dtget ( transferer , transferer_real_context , xferto , sizeof ( xferto ) , 100 , transferdigittimeout ) ;
2006-04-16 21:41:06 +00:00
if ( res < 0 ) { /* hangup, would be 0 for invalid and 1 for valid */
finishup ( transferee ) ;
2005-01-04 04:01:40 +00:00
return res ;
}
2006-04-16 19:56:08 +00:00
if ( ! strcmp ( xferto , ast_parking_ext ( ) ) ) {
2006-04-16 21:41:06 +00:00
res = finishup ( transferee ) ;
2005-01-17 12:37:55 +00:00
if ( res )
2005-01-04 04:01:40 +00:00
res = - 1 ;
2006-04-16 21:41:06 +00:00
else if ( ! ast_park_call ( transferee , transferer , 0 , NULL ) ) { /* success */
2005-01-04 04:01:40 +00:00
/* We return non-zero, but tell the PBX not to hang the channel when
the thread dies - - We have to be careful now though . We are responsible for
hanging up the channel , else it will never be hung up ! */
2006-04-14 23:20:29 +00:00
return ( transferer = = peer ) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER ;
2005-01-04 04:01:40 +00:00
} else {
ast_log ( LOG_WARNING , " Unable to park call %s \n " , transferee - > name ) ;
}
2006-04-17 04:31:21 +00:00
/*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
2006-04-16 19:56:08 +00:00
} else if ( ast_exists_extension ( transferee , transferer_real_context , xferto , 1 , transferer - > cid . cid_num ) ) {
2005-01-04 04:01:40 +00:00
pbx_builtin_setvar_helper ( peer , " BLINDTRANSFER " , chan - > name ) ;
pbx_builtin_setvar_helper ( chan , " BLINDTRANSFER " , peer - > name ) ;
2006-04-16 21:41:06 +00:00
res = finishup ( transferee ) ;
2005-01-04 04:01:40 +00:00
if ( ! transferee - > pbx ) {
/* Doh! Use our handy async_goto functions */
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Transferring %s to '%s' (context %s) priority 1 \n "
2006-04-16 19:56:08 +00:00
, transferee - > name , xferto , transferer_real_context ) ;
if ( ast_async_goto ( transferee , transferer_real_context , xferto , 1 ) )
2005-01-04 04:01:40 +00:00
ast_log ( LOG_WARNING , " Async goto failed :-( \n " ) ;
res = - 1 ;
} else {
/* Set the channel's new extension, since it exists, using transferer context */
2006-04-16 19:56:08 +00:00
set_c_e_p ( transferee , transferer_real_context , xferto , 0 ) ;
2005-01-04 04:01:40 +00:00
}
2005-04-22 02:55:14 +00:00
check_goto_on_transfer ( transferer ) ;
2005-01-04 04:01:40 +00:00
return res ;
} else {
if ( option_verbose > 2 )
2006-04-16 19:56:08 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Unable to find extension '%s' in context '%s' \n " , xferto , transferer_real_context ) ;
2005-01-04 04:01:40 +00:00
}
2006-05-22 20:19:00 +00:00
if ( ast_stream_and_wait ( transferer , xferfailsound , transferer - > language , AST_DIGIT_ANY ) < 0 ) {
2006-04-16 21:41:06 +00:00
finishup ( transferee ) ;
return - 1 ;
2005-01-04 04:01:40 +00:00
}
ast_stopstream ( transferer ) ;
2006-04-16 21:41:06 +00:00
res = finishup ( transferee ) ;
2005-01-04 04:01:40 +00:00
if ( res ) {
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Hungup during autoservice stop on '%s' \n " , transferee - > name ) ;
return res ;
}
return FEATURE_RETURN_SUCCESS ;
}
2006-04-18 13:05:48 +00:00
static int check_compat ( struct ast_channel * c , struct ast_channel * newchan )
{
if ( ast_channel_make_compatible ( c , newchan ) < 0 ) {
ast_log ( LOG_WARNING , " Had to drop call because I couldn't make %s compatible with %s \n " ,
c - > name , newchan - > name ) ;
ast_hangup ( newchan ) ;
return - 1 ;
}
return 0 ;
}
2005-01-05 19:56:47 +00:00
static int builtin_atxfer ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
struct ast_channel * transferer ;
struct ast_channel * transferee ;
2005-12-03 19:25:33 +00:00
const char * transferer_real_context ;
2006-04-18 13:05:48 +00:00
char xferto [ 256 ] ;
2005-01-05 19:56:47 +00:00
int res ;
2006-04-18 13:05:48 +00:00
int outstate = 0 ;
struct ast_channel * newchan ;
struct ast_channel * xferchan ;
2005-01-05 19:56:47 +00:00
struct ast_bridge_thread_obj * tobj ;
2006-04-18 13:05:48 +00:00
struct ast_bridge_config bconfig ;
struct ast_frame * f ;
int l ;
2005-01-05 19:56:47 +00:00
2006-04-17 04:31:21 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Executing Attended Transfer %s, %s (sense=%d) \n " , chan - > name , peer - > name , sense ) ;
2006-04-16 19:26:57 +00:00
set_peers ( & transferer , & transferee , peer , chan , sense ) ;
transferer_real_context = real_ctx ( transferer , transferee ) ;
2006-04-16 19:41:12 +00:00
/* Start autoservice on chan while we talk to the originator */
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_HOLD ) ;
2005-01-05 19:56:47 +00:00
ast_autoservice_start ( transferee ) ;
ast_moh_start ( transferee , NULL ) ;
2005-02-17 20:04:10 +00:00
memset ( xferto , 0 , sizeof ( xferto ) ) ;
2005-01-05 19:56:47 +00:00
/* Transfer */
2006-04-21 20:28:32 +00:00
res = ast_stream_and_wait ( transferer , " pbx-transfer " , transferer - > language , AST_DIGIT_ANY ) ;
2006-04-16 22:22:53 +00:00
if ( res < 0 ) {
finishup ( transferee ) ;
return res ;
2006-04-18 13:05:48 +00:00
}
if ( res > 0 ) /* If they've typed a digit already, handle it */
2006-04-16 22:22:53 +00:00
xferto [ 0 ] = ( char ) res ;
2006-04-16 19:26:57 +00:00
2006-04-16 22:22:53 +00:00
/* this is specific of atxfer */
res = ast_app_dtget ( transferer , transferer_real_context , xferto , sizeof ( xferto ) , 100 , transferdigittimeout ) ;
2006-04-16 23:05:42 +00:00
if ( res < 0 ) { /* hangup, would be 0 for invalid and 1 for valid */
finishup ( transferee ) ;
return res ;
}
if ( res = = 0 ) {
2006-04-16 22:22:53 +00:00
ast_log ( LOG_WARNING , " Did not read data. \n " ) ;
2006-04-16 23:05:42 +00:00
finishup ( transferee ) ;
2006-04-21 20:28:32 +00:00
if ( ast_stream_and_wait ( transferer , " beeperr " , transferer - > language , " " ) )
2006-04-16 22:22:53 +00:00
return - 1 ;
2006-04-16 23:05:42 +00:00
return FEATURE_RETURN_SUCCESS ;
}
2006-04-18 13:05:48 +00:00
2006-04-16 23:05:42 +00:00
/* valid extension, res == 1 */
2006-04-18 13:05:48 +00:00
if ( ! ast_exists_extension ( transferer , transferer_real_context , xferto , 1 , transferer - > cid . cid_num ) ) {
ast_log ( LOG_WARNING , " Extension %s does not exist in context %s \n " , xferto , transferer_real_context ) ;
finishup ( transferee ) ;
2006-04-21 20:28:32 +00:00
if ( ast_stream_and_wait ( transferer , " beeperr " , transferer - > language , " " ) )
2006-04-18 13:05:48 +00:00
return - 1 ;
return FEATURE_RETURN_SUCCESS ;
}
l = strlen ( xferto ) ;
snprintf ( xferto + l , sizeof ( xferto ) - l , " @%s/n " , transferer_real_context ) ; /* append context */
newchan = ast_feature_request_and_dial ( transferer , " Local " , ast_best_codec ( transferer - > nativeformats ) ,
2006-05-23 18:23:05 +00:00
xferto , atxfernoanswertimeout , & outstate , transferer - > cid . cid_num , transferer - > cid . cid_name ) ;
2006-04-18 13:05:48 +00:00
ast_indicate ( transferer , - 1 ) ;
if ( ! newchan ) {
finishup ( transferee ) ;
/* any reason besides user requested cancel and busy triggers the failed sound */
if ( outstate ! = AST_CONTROL_UNHOLD & & outstate ! = AST_CONTROL_BUSY & &
2006-04-21 20:28:32 +00:00
ast_stream_and_wait ( transferer , xferfailsound , transferer - > language , " " ) )
2006-04-18 13:05:48 +00:00
return - 1 ;
return FEATURE_RETURN_SUCCESS ;
}
if ( check_compat ( transferer , newchan ) )
return - 1 ;
memset ( & bconfig , 0 , sizeof ( struct ast_bridge_config ) ) ;
ast_set_flag ( & ( bconfig . features_caller ) , AST_FEATURE_DISCONNECT ) ;
ast_set_flag ( & ( bconfig . features_callee ) , AST_FEATURE_DISCONNECT ) ;
res = ast_bridge_call ( transferer , newchan , & bconfig ) ;
if ( newchan - > _softhangup | | newchan - > _state ! = AST_STATE_UP | | ! transferer - > _softhangup ) {
ast_hangup ( newchan ) ;
2006-04-21 20:28:32 +00:00
if ( ast_stream_and_wait ( transferer , xfersound , transferer - > language , " " ) )
2006-05-08 11:22:00 +00:00
ast_log ( LOG_WARNING , " Failed to play transfer sound! \n " ) ;
2006-04-18 13:05:48 +00:00
finishup ( transferee ) ;
transferer - > _softhangup = 0 ;
return FEATURE_RETURN_SUCCESS ;
}
2005-01-05 19:56:47 +00:00
2006-04-18 13:05:48 +00:00
if ( check_compat ( transferee , newchan ) )
return - 1 ;
ast_moh_stop ( transferee ) ;
if ( ( ast_autoservice_stop ( transferee ) < 0 )
| | ( ast_waitfordigit ( transferee , 100 ) < 0 )
| | ( ast_waitfordigit ( newchan , 100 ) < 0 )
| | ast_check_hangup ( transferee )
| | ast_check_hangup ( newchan ) ) {
ast_hangup ( newchan ) ;
return - 1 ;
2005-01-05 19:56:47 +00:00
}
2006-04-18 13:05:48 +00:00
xferchan = ast_channel_alloc ( 0 ) ;
if ( ! xferchan ) {
ast_hangup ( newchan ) ;
return - 1 ;
}
ast_string_field_build ( xferchan , name , " Transfered/%s " , transferee - > name ) ;
/* Make formats okay */
xferchan - > readformat = transferee - > readformat ;
xferchan - > writeformat = transferee - > writeformat ;
ast_channel_masquerade ( xferchan , transferee ) ;
ast_explicit_goto ( xferchan , transferee - > context , transferee - > exten , transferee - > priority ) ;
xferchan - > _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL ) ;
xferchan - > _softhangup = 0 ;
if ( ( f = ast_read ( xferchan ) ) )
ast_frfree ( f ) ;
newchan - > _state = AST_STATE_UP ;
ast_clear_flag ( newchan , AST_FLAGS_ALL ) ;
newchan - > _softhangup = 0 ;
tobj = ast_calloc ( 1 , sizeof ( struct ast_bridge_thread_obj ) ) ;
if ( ! tobj ) {
ast_hangup ( xferchan ) ;
ast_hangup ( newchan ) ;
return - 1 ;
}
tobj - > chan = xferchan ;
tobj - > peer = newchan ;
tobj - > bconfig = * config ;
2006-04-21 20:28:32 +00:00
if ( ast_stream_and_wait ( newchan , xfersound , newchan - > language , " " ) )
2006-05-08 11:22:00 +00:00
ast_log ( LOG_WARNING , " Failed to play transfer sound! \n " ) ;
2006-04-18 13:05:48 +00:00
ast_bridge_call_thread_launch ( tobj ) ;
return - 1 ; /* XXX meaning the channel is bridged ? */
2005-01-05 19:56:47 +00:00
}
2005-01-04 04:01:40 +00:00
2005-01-05 19:56:47 +00:00
/* add atxfer and automon as undefined so you can only use em if you configure them */
2005-01-04 04:01:40 +00:00
# define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
2006-01-19 22:09:18 +00:00
2005-01-04 04:01:40 +00:00
struct ast_call_feature builtin_features [ ] =
2005-08-23 02:22:33 +00:00
{
2005-01-04 04:01:40 +00:00
{ AST_FEATURE_REDIRECT , " Blind Transfer " , " blindxfer " , " # " , " # " , builtin_blindtransfer , AST_FEATURE_FLAG_NEEDSDTMF } ,
2005-01-05 19:56:47 +00:00
{ AST_FEATURE_REDIRECT , " Attended Transfer " , " atxfer " , " " , " " , builtin_atxfer , AST_FEATURE_FLAG_NEEDSDTMF } ,
{ AST_FEATURE_AUTOMON , " One Touch Monitor " , " automon " , " " , " " , builtin_automonitor , AST_FEATURE_FLAG_NEEDSDTMF } ,
2005-01-04 04:01:40 +00:00
{ AST_FEATURE_DISCONNECT , " Disconnect Call " , " disconnect " , " * " , " * " , builtin_disconnect , AST_FEATURE_FLAG_NEEDSDTMF } ,
2006-05-22 16:43:43 +00:00
{ AST_FEATURE_PARKCALL , " Park Call " , " parkcall " , " " , " " , builtin_parkcall , AST_FEATURE_FLAG_NEEDSDTMF } ,
2005-01-04 04:01:40 +00:00
} ;
2005-08-23 02:22:33 +00:00
2006-05-11 20:07:44 +00:00
static AST_LIST_HEAD_STATIC ( feature_list , ast_call_feature ) ;
2005-08-23 02:22:33 +00:00
2006-01-19 22:09:18 +00:00
/*! \brief register new feature into feature_list*/
2005-08-23 02:22:33 +00:00
void ast_register_feature ( struct ast_call_feature * feature )
{
if ( ! feature ) {
ast_log ( LOG_NOTICE , " You didn't pass a feature! \n " ) ;
return ;
}
AST_LIST_LOCK ( & feature_list ) ;
AST_LIST_INSERT_HEAD ( & feature_list , feature , feature_entry ) ;
AST_LIST_UNLOCK ( & feature_list ) ;
if ( option_verbose > = 2 )
ast_verbose ( VERBOSE_PREFIX_2 " Registered Feature '%s' \n " , feature - > sname ) ;
}
2006-01-19 22:09:18 +00:00
/*! \brief unregister feature from feature_list */
2005-08-23 02:22:33 +00:00
void ast_unregister_feature ( struct ast_call_feature * feature )
{
2006-04-16 18:37:01 +00:00
if ( ! feature )
return ;
2005-08-23 02:22:33 +00:00
AST_LIST_LOCK ( & feature_list ) ;
AST_LIST_REMOVE ( & feature_list , feature , feature_entry ) ;
AST_LIST_UNLOCK ( & feature_list ) ;
free ( feature ) ;
}
2005-08-23 14:40:03 +00:00
static void ast_unregister_features ( void )
{
struct ast_call_feature * feature ;
AST_LIST_LOCK ( & feature_list ) ;
while ( ( feature = AST_LIST_REMOVE_HEAD ( & feature_list , feature_entry ) ) )
free ( feature ) ;
AST_LIST_UNLOCK ( & feature_list ) ;
}
2005-08-23 02:22:33 +00:00
2006-01-19 22:09:18 +00:00
/*! \brief find a feature by name */
2005-08-23 02:22:33 +00:00
static struct ast_call_feature * find_feature ( char * name )
{
struct ast_call_feature * tmp ;
AST_LIST_LOCK ( & feature_list ) ;
2005-09-07 21:36:30 +00:00
AST_LIST_TRAVERSE ( & feature_list , tmp , feature_entry ) {
if ( ! strcasecmp ( tmp - > sname , name ) )
break ;
2005-08-23 02:22:33 +00:00
}
AST_LIST_UNLOCK ( & feature_list ) ;
return tmp ;
}
2006-01-19 22:09:18 +00:00
/*! \brief exec an app by feature */
2005-08-23 02:22:33 +00:00
static int feature_exec_app ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
struct ast_app * app ;
struct ast_call_feature * feature ;
int res ;
AST_LIST_LOCK ( & feature_list ) ;
AST_LIST_TRAVERSE ( & feature_list , feature , feature_entry ) {
2006-04-14 23:20:29 +00:00
if ( ! strcasecmp ( feature - > exten , code ) )
break ;
2005-08-23 02:22:33 +00:00
}
AST_LIST_UNLOCK ( & feature_list ) ;
if ( ! feature ) { /* shouldn't ever happen! */
ast_log ( LOG_NOTICE , " Found feature before, but at execing we've lost it?? \n " ) ;
return - 1 ;
}
app = pbx_findapp ( feature - > app ) ;
if ( app ) {
2006-04-16 18:37:01 +00:00
struct ast_channel * work = ast_test_flag ( feature , AST_FEATURE_FLAG_CALLEE ) ? peer : chan ;
2006-03-30 21:29:39 +00:00
res = pbx_exec ( work , app , feature - > app_args ) ;
2006-05-21 15:25:31 +00:00
if ( res = = AST_PBX_KEEPALIVE )
return FEATURE_RETURN_PBX_KEEPALIVE ;
else if ( res = = AST_PBX_NO_HANGUP_PEER )
return FEATURE_RETURN_NO_HANGUP_PEER ;
else if ( res )
return FEATURE_RETURN_SUCCESSBREAK ;
2005-08-23 02:22:33 +00:00
} else {
ast_log ( LOG_WARNING , " Could not find application (%s) \n " , feature - > app ) ;
2006-01-05 23:08:55 +00:00
return - 2 ;
2005-08-23 02:22:33 +00:00
}
2006-04-17 04:31:21 +00:00
return FEATURE_RETURN_SUCCESS ; /*! \todo XXX should probably return res */
2005-08-23 02:22:33 +00:00
}
2005-01-04 04:01:40 +00:00
static void unmap_features ( void )
{
int x ;
2005-07-25 17:31:53 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x + + )
2005-01-04 04:01:40 +00:00
strcpy ( builtin_features [ x ] . exten , builtin_features [ x ] . default_exten ) ;
}
static int remap_feature ( const char * name , const char * value )
{
int x ;
int res = - 1 ;
2005-07-25 17:31:53 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
2005-01-04 04:01:40 +00:00
if ( ! strcasecmp ( name , builtin_features [ x ] . sname ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( builtin_features [ x ] . exten , value , sizeof ( builtin_features [ x ] . exten ) ) ;
2005-01-04 04:01:40 +00:00
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Remapping feature %s (%s) to sequence '%s' \n " , builtin_features [ x ] . fname , builtin_features [ x ] . sname , builtin_features [ x ] . exten ) ;
res = 0 ;
} else if ( ! strcmp ( value , builtin_features [ x ] . exten ) )
ast_log ( LOG_WARNING , " Sequence '%s' already mapped to function %s (%s) while assigning to %s \n " , value , builtin_features [ x ] . fname , builtin_features [ x ] . sname , name ) ;
}
return res ;
}
static int ast_feature_interpret ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
int x ;
2005-01-10 14:46:59 +00:00
struct ast_flags features ;
2005-01-04 04:01:40 +00:00
int res = FEATURE_RETURN_PASSDIGITS ;
2005-08-23 02:22:33 +00:00
struct ast_call_feature * feature ;
2005-12-03 19:25:33 +00:00
const char * dynamic_features = pbx_builtin_getvar_helper ( chan , " DYNAMIC_FEATURES " ) ;
2005-01-04 04:01:40 +00:00
if ( sense = = FEATURE_SENSE_CHAN )
2005-01-10 14:46:59 +00:00
ast_copy_flags ( & features , & ( config - > features_caller ) , AST_FLAGS_ALL ) ;
2005-01-04 04:01:40 +00:00
else
2005-01-10 14:46:59 +00:00
ast_copy_flags ( & features , & ( config - > features_callee ) , AST_FLAGS_ALL ) ;
2006-04-17 04:31:21 +00:00
if ( option_debug > 2 )
ast_log ( LOG_DEBUG , " Feature interpret: chan=%s, peer=%s, sense=%d, features=%d \n " , chan - > name , peer - > name , sense , features . flags ) ;
2005-09-07 21:36:30 +00:00
2005-08-23 15:33:27 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
2005-01-10 14:46:59 +00:00
if ( ( ast_test_flag ( & features , builtin_features [ x ] . feature_mask ) ) & &
2005-01-04 04:01:40 +00:00
! ast_strlen_zero ( builtin_features [ x ] . exten ) ) {
/* Feature is up for consideration */
if ( ! strcmp ( builtin_features [ x ] . exten , code ) ) {
res = builtin_features [ x ] . operation ( chan , peer , config , code , sense ) ;
break ;
} else if ( ! strncmp ( builtin_features [ x ] . exten , code , strlen ( code ) ) ) {
if ( res = = FEATURE_RETURN_PASSDIGITS )
2005-09-07 21:36:30 +00:00
res = FEATURE_RETURN_STOREDIGITS ;
2005-08-23 02:22:33 +00:00
}
}
}
2005-11-08 01:55:31 +00:00
if ( ! ast_strlen_zero ( dynamic_features ) ) {
2005-09-07 21:36:30 +00:00
char * tmp = ast_strdupa ( dynamic_features ) ;
2005-08-23 02:22:33 +00:00
char * tok ;
2005-09-07 21:36:30 +00:00
while ( ( tok = strsep ( & tmp , " # " ) ) ! = NULL ) {
feature = find_feature ( tok ) ;
2005-08-23 02:22:33 +00:00
2005-09-07 21:36:30 +00:00
if ( feature ) {
2005-08-23 02:22:33 +00:00
/* Feature is up for consideration */
if ( ! strcmp ( feature - > exten , code ) ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Feature Found: %s exten: %s \n " , feature - > sname , tok ) ;
2006-05-21 15:25:31 +00:00
if ( sense = = FEATURE_SENSE_CHAN )
res = feature - > operation ( chan , peer , config , code , sense ) ;
else
res = feature - > operation ( peer , chan , config , code , sense ) ;
2005-08-23 02:22:33 +00:00
break ;
} else if ( ! strncmp ( feature - > exten , code , strlen ( code ) ) ) {
2005-01-04 04:01:40 +00:00
res = FEATURE_RETURN_STOREDIGITS ;
2005-08-23 02:22:33 +00:00
}
2005-01-04 04:01:40 +00:00
}
}
}
2005-08-23 02:22:33 +00:00
2005-01-04 04:01:40 +00:00
return res ;
}
2005-09-07 21:36:30 +00:00
static void set_config_flags ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config )
2005-01-04 04:01:40 +00:00
{
int x ;
2005-09-07 21:36:30 +00:00
2005-01-10 14:46:59 +00:00
ast_clear_flag ( config , AST_FLAGS_ALL ) ;
2005-08-23 15:33:27 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
2005-09-07 21:36:30 +00:00
if ( ast_test_flag ( builtin_features + x , AST_FEATURE_FLAG_NEEDSDTMF ) ) {
if ( ast_test_flag ( & ( config - > features_caller ) , builtin_features [ x ] . feature_mask ) )
2005-01-04 04:01:40 +00:00
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 ) ;
2005-09-07 21:36:30 +00:00
if ( ast_test_flag ( & ( config - > features_callee ) , builtin_features [ x ] . feature_mask ) )
2005-01-04 04:01:40 +00:00
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 ) ;
}
}
2005-09-07 21:36:30 +00:00
if ( chan & & peer & & ! ( ast_test_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 ) & & ast_test_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 ) ) ) {
2005-12-03 19:25:33 +00:00
const char * dynamic_features = pbx_builtin_getvar_helper ( chan , " DYNAMIC_FEATURES " ) ;
2005-09-07 21:36:30 +00:00
if ( dynamic_features ) {
char * tmp = ast_strdupa ( dynamic_features ) ;
char * tok ;
struct ast_call_feature * feature ;
/* while we have a feature */
2006-05-10 13:22:15 +00:00
while ( ( tok = strsep ( & tmp , " # " ) ) ) {
2006-04-16 18:37:01 +00:00
if ( ( feature = find_feature ( tok ) ) & & ast_test_flag ( feature , AST_FEATURE_FLAG_NEEDSDTMF ) ) {
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_CALLER ) )
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 ) ;
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_CALLEE ) )
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 ) ;
2005-09-07 21:36:30 +00:00
}
}
}
}
2005-01-04 04:01:40 +00:00
}
2006-04-17 04:31:21 +00:00
/*! \todo XXX Check - this is very similar to the code in channel.c */
2005-06-23 22:12:01 +00:00
static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel * caller , const char * type , int format , void * data , int timeout , int * outstate , const char * cid_num , const char * cid_name )
{
int state = 0 ;
int cause = 0 ;
int to ;
struct ast_channel * chan ;
struct ast_channel * monitor_chans [ 2 ] ;
struct ast_channel * active_channel ;
int res = 0 , ready = 0 ;
if ( ( chan = ast_request ( type , format , data , & cause ) ) ) {
ast_set_callerid ( chan , cid_num , cid_name , cid_num ) ;
2005-11-01 20:29:36 +00:00
ast_channel_inherit_variables ( caller , chan ) ;
2006-05-26 17:59:29 +00:00
pbx_builtin_setvar_helper ( chan , " TRANSFERERNAME " , caller - > name ) ;
2005-06-23 22:12:01 +00:00
if ( ! ast_call ( chan , data , timeout ) ) {
2005-07-15 23:00:47 +00:00
struct timeval started ;
2005-06-23 22:12:01 +00:00
int x , len = 0 ;
char * disconnect_code = NULL , * dialed_code = NULL ;
ast_indicate ( caller , AST_CONTROL_RINGING ) ;
/* support dialing of the featuremap disconnect code while performing an attended tranfer */
2005-07-25 17:31:53 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
2005-06-23 22:12:01 +00:00
if ( strcasecmp ( builtin_features [ x ] . sname , " disconnect " ) )
continue ;
disconnect_code = builtin_features [ x ] . exten ;
len = strlen ( disconnect_code ) + 1 ;
dialed_code = alloca ( len ) ;
memset ( dialed_code , 0 , len ) ;
break ;
}
x = 0 ;
2005-07-15 23:00:47 +00:00
started = ast_tvnow ( ) ;
2005-06-23 22:12:01 +00:00
to = timeout ;
while ( ! ast_check_hangup ( caller ) & & timeout & & ( chan - > _state ! = AST_STATE_UP ) ) {
2006-04-16 20:09:01 +00:00
struct ast_frame * f = NULL ;
2005-06-23 22:12:01 +00:00
monitor_chans [ 0 ] = caller ;
monitor_chans [ 1 ] = chan ;
active_channel = ast_waitfor_n ( monitor_chans , 2 , & to ) ;
/* see if the timeout has been violated */
2005-07-15 23:00:47 +00:00
if ( ast_tvdiff_ms ( ast_tvnow ( ) , started ) > timeout ) {
2005-06-23 22:12:01 +00:00
state = AST_CONTROL_UNHOLD ;
ast_log ( LOG_NOTICE , " We exceeded our AT-timeout \n " ) ;
break ; /*doh! timeout*/
}
2006-04-16 20:09:01 +00:00
if ( ! active_channel )
2005-06-23 22:12:01 +00:00
continue ;
if ( chan & & ( chan = = active_channel ) ) {
f = ast_read ( chan ) ;
if ( f = = NULL ) { /*doh! where'd he go?*/
state = AST_CONTROL_HANGUP ;
res = 0 ;
break ;
}
if ( f - > frametype = = AST_FRAME_CONTROL | | f - > frametype = = AST_FRAME_DTMF | | f - > frametype = = AST_FRAME_TEXT ) {
if ( f - > subclass = = AST_CONTROL_RINGING ) {
state = f - > subclass ;
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is ringing \n " , chan - > name ) ;
ast_indicate ( caller , AST_CONTROL_RINGING ) ;
} else if ( ( f - > subclass = = AST_CONTROL_BUSY ) | | ( f - > subclass = = AST_CONTROL_CONGESTION ) ) {
state = f - > subclass ;
2006-03-23 22:00:11 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is busy \n " , chan - > name ) ;
ast_indicate ( caller , AST_CONTROL_BUSY ) ;
2005-06-23 22:12:01 +00:00
ast_frfree ( f ) ;
f = NULL ;
break ;
} else if ( f - > subclass = = AST_CONTROL_ANSWER ) {
/* This is what we are hoping for */
state = f - > subclass ;
ast_frfree ( f ) ;
f = NULL ;
ready = 1 ;
break ;
} else {
ast_log ( LOG_NOTICE , " Don't know what to do about control frame: %d \n " , f - > subclass ) ;
}
/* else who cares */
}
} else if ( caller & & ( active_channel = = caller ) ) {
f = ast_read ( caller ) ;
if ( f = = NULL ) { /*doh! where'd he go?*/
if ( caller - > _softhangup & & ! chan - > _softhangup ) {
/* make this a blind transfer */
ready = 1 ;
break ;
}
state = AST_CONTROL_HANGUP ;
res = 0 ;
break ;
}
if ( f - > frametype = = AST_FRAME_DTMF ) {
dialed_code [ x + + ] = f - > subclass ;
dialed_code [ x ] = ' \0 ' ;
if ( strlen ( dialed_code ) = = len ) {
x = 0 ;
} else if ( x & & strncmp ( dialed_code , disconnect_code , x ) ) {
x = 0 ;
dialed_code [ x ] = ' \0 ' ;
}
if ( * dialed_code & & ! strcmp ( dialed_code , disconnect_code ) ) {
/* Caller Canceled the call */
state = AST_CONTROL_UNHOLD ;
ast_frfree ( f ) ;
f = NULL ;
break ;
}
}
}
2006-04-16 20:09:01 +00:00
if ( f )
2005-06-23 22:12:01 +00:00
ast_frfree ( f ) ;
2006-04-16 20:09:01 +00:00
} /* end while */
2005-06-23 22:12:01 +00:00
} else
ast_log ( LOG_NOTICE , " Unable to call channel %s/%s \n " , type , ( char * ) data ) ;
} else {
ast_log ( LOG_NOTICE , " Unable to request channel %s/%s \n " , type , ( char * ) data ) ;
switch ( cause ) {
case AST_CAUSE_BUSY :
state = AST_CONTROL_BUSY ;
break ;
case AST_CAUSE_CONGESTION :
state = AST_CONTROL_CONGESTION ;
break ;
}
}
ast_indicate ( caller , - 1 ) ;
if ( chan & & ready ) {
if ( chan - > _state = = AST_STATE_UP )
state = AST_CONTROL_ANSWER ;
res = 0 ;
} else if ( chan ) {
res = - 1 ;
ast_hangup ( chan ) ;
chan = NULL ;
} else {
res = - 1 ;
}
if ( outstate )
* outstate = state ;
if ( chan & & res < = 0 ) {
2006-01-21 22:09:06 +00:00
if ( chan - > cdr | | ( chan - > cdr = ast_cdr_alloc ( ) ) ) {
2005-06-23 22:12:01 +00:00
char tmp [ 256 ] ;
ast_cdr_init ( chan - > cdr , chan ) ;
snprintf ( tmp , 256 , " %s/%s " , type , ( char * ) data ) ;
ast_cdr_setapp ( chan - > cdr , " Dial " , tmp ) ;
ast_cdr_update ( chan ) ;
ast_cdr_start ( chan - > cdr ) ;
ast_cdr_end ( chan - > cdr ) ;
/* If the cause wasn't handled properly */
if ( ast_cdr_disposition ( chan - > cdr , chan - > hangupcause ) )
ast_cdr_failed ( chan - > cdr ) ;
} else {
ast_log ( LOG_WARNING , " Unable to create Call Detail Record \n " ) ;
}
}
return chan ;
}
2004-04-26 23:22:34 +00:00
int ast_bridge_call ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config )
2001-12-27 11:07:33 +00:00
{
/* Copy voice back and forth between the two channels. Give the peer
the ability to transfer calls with ' # < extension ' syntax . */
struct ast_frame * f ;
struct ast_channel * who ;
2005-01-04 04:01:40 +00:00
char chan_featurecode [ FEATURE_MAX_LEN + 1 ] = " " ;
char peer_featurecode [ FEATURE_MAX_LEN + 1 ] = " " ;
2001-12-27 11:07:33 +00:00
int res ;
2004-08-06 13:54:07 +00:00
int diff ;
2005-01-04 04:01:40 +00:00
int hasfeatures = 0 ;
int hadfeatures = 0 ;
2001-12-27 11:07:33 +00:00
struct ast_option_header * aoh ;
2005-07-25 17:31:53 +00:00
struct timeval start = { 0 , 0 } ;
2005-01-04 04:01:40 +00:00
struct ast_bridge_config backup_config ;
2004-09-17 03:49:57 +00:00
2005-01-04 04:01:40 +00:00
memset ( & backup_config , 0 , sizeof ( backup_config ) ) ;
2005-08-03 04:42:59 +00:00
config - > start_time = ast_tvnow ( ) ;
2004-10-27 22:01:33 +00:00
if ( chan & & peer ) {
2004-10-28 15:53:36 +00:00
pbx_builtin_setvar_helper ( chan , " BRIDGEPEER " , peer - > name ) ;
pbx_builtin_setvar_helper ( peer , " BRIDGEPEER " , chan - > name ) ;
} else if ( chan )
2004-10-27 22:01:33 +00:00
pbx_builtin_setvar_helper ( chan , " BLINDTRANSFER " , NULL ) ;
2004-09-17 14:15:11 +00:00
if ( monitor_ok ) {
2005-12-03 19:25:33 +00:00
const char * monitor_exec ;
struct ast_channel * src = NULL ;
2004-09-17 14:15:11 +00:00
if ( ! monitor_app ) {
if ( ! ( monitor_app = pbx_findapp ( " Monitor " ) ) )
2004-09-17 03:49:57 +00:00
monitor_ok = 0 ;
2004-09-17 14:15:11 +00:00
}
if ( ( monitor_exec = pbx_builtin_getvar_helper ( chan , " AUTO_MONITOR " ) ) )
2005-12-03 19:25:33 +00:00
src = chan ;
2004-09-17 14:15:11 +00:00
else if ( ( monitor_exec = pbx_builtin_getvar_helper ( peer , " AUTO_MONITOR " ) ) )
2005-12-03 19:25:33 +00:00
src = peer ;
2006-01-17 18:20:33 +00:00
if ( monitor_app & & src ) {
2005-12-03 19:25:33 +00:00
char * tmp = ast_strdupa ( monitor_exec ) ;
2006-05-10 13:22:15 +00:00
pbx_exec ( src , monitor_app , tmp ) ;
2005-12-03 19:25:33 +00:00
}
2004-09-17 03:49:57 +00:00
}
2005-09-07 21:36:30 +00:00
set_config_flags ( chan , peer , config ) ;
2004-08-06 14:43:25 +00:00
config - > firstpass = 1 ;
2003-07-02 14:06:12 +00:00
2001-12-27 11:07:33 +00:00
/* Answer if need be */
2003-02-02 19:37:23 +00:00
if ( ast_answer ( chan ) )
return - 1 ;
2001-12-27 11:07:33 +00:00
peer - > appl = " Bridged Call " ;
2006-04-16 20:32:14 +00:00
peer - > data = chan - > name ;
2005-07-25 17:31:53 +00:00
2004-02-03 16:57:00 +00:00
/* copy the userfield from the B-leg to A-leg if applicable */
2004-09-17 14:15:11 +00:00
if ( chan - > cdr & & peer - > cdr & & ! ast_strlen_zero ( peer - > cdr - > userfield ) ) {
2004-02-03 16:57:00 +00:00
char tmp [ 256 ] ;
2004-09-17 14:15:11 +00:00
if ( ! ast_strlen_zero ( chan - > cdr - > userfield ) ) {
2005-07-25 17:31:53 +00:00
snprintf ( tmp , sizeof ( tmp ) , " %s;%s " , chan - > cdr - > userfield , peer - > cdr - > userfield ) ;
2004-02-03 16:57:00 +00:00
ast_cdr_appenduserfield ( chan , tmp ) ;
} else
ast_cdr_setuserfield ( chan , peer - > cdr - > userfield ) ;
/* free the peer's cdr without ast_cdr_free complaining */
free ( peer - > cdr ) ;
peer - > cdr = NULL ;
}
2001-12-27 11:07:33 +00:00
for ( ; ; ) {
2006-04-14 23:20:29 +00:00
struct ast_channel * other ; /* used later */
2005-08-03 04:42:59 +00:00
if ( config - > feature_timer )
2005-07-15 23:00:47 +00:00
start = ast_tvnow ( ) ;
2005-08-03 04:42:59 +00:00
2005-07-25 17:31:53 +00:00
res = ast_channel_bridge ( chan , peer , config , & f , & who ) ;
2005-08-03 04:42:59 +00:00
if ( config - > feature_timer ) {
2004-08-06 13:54:07 +00:00
/* Update time limit for next pass */
2005-07-15 23:00:47 +00:00
diff = ast_tvdiff_ms ( ast_tvnow ( ) , start ) ;
2005-08-03 04:42:59 +00:00
config - > feature_timer - = diff ;
2005-01-04 04:01:40 +00:00
if ( hasfeatures ) {
/* Running on backup config, meaning a feature might be being
activated , but that ' s no excuse to keep things going
indefinitely ! */
2005-08-03 04:42:59 +00:00
if ( backup_config . feature_timer & & ( ( backup_config . feature_timer - = diff ) < = 0 ) ) {
2006-04-17 04:31:21 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Timed out, realtime this time! \n " ) ;
2005-08-03 04:42:59 +00:00
config - > feature_timer = 0 ;
2005-01-04 04:01:40 +00:00
who = chan ;
if ( f )
ast_frfree ( f ) ;
f = NULL ;
res = 0 ;
2005-08-03 04:42:59 +00:00
} else if ( config - > feature_timer < = 0 ) {
2005-01-04 04:01:40 +00:00
/* Not *really* out of time, just out of time for
digits to come in for features . */
2006-04-17 04:31:21 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Timed out for feature! \n " ) ;
2005-01-04 04:01:40 +00:00
if ( ! ast_strlen_zero ( peer_featurecode ) ) {
ast_dtmf_stream ( chan , peer , peer_featurecode , 0 ) ;
memset ( peer_featurecode , 0 , sizeof ( peer_featurecode ) ) ;
}
if ( ! ast_strlen_zero ( chan_featurecode ) ) {
ast_dtmf_stream ( peer , chan , chan_featurecode , 0 ) ;
memset ( chan_featurecode , 0 , sizeof ( chan_featurecode ) ) ;
}
if ( f )
ast_frfree ( f ) ;
hasfeatures = ! ast_strlen_zero ( chan_featurecode ) | | ! ast_strlen_zero ( peer_featurecode ) ;
if ( ! hasfeatures ) {
/* Restore original (possibly time modified) bridge config */
memcpy ( config , & backup_config , sizeof ( struct ast_bridge_config ) ) ;
memset ( & backup_config , 0 , sizeof ( backup_config ) ) ;
}
hadfeatures = hasfeatures ;
/* Continue as we were */
continue ;
}
} else {
2005-08-03 04:42:59 +00:00
if ( config - > feature_timer < = 0 ) {
2005-01-04 04:01:40 +00:00
/* We ran out of time */
2005-08-03 04:42:59 +00:00
config - > feature_timer = 0 ;
2005-01-04 04:01:40 +00:00
who = chan ;
if ( f )
ast_frfree ( f ) ;
f = NULL ;
res = 0 ;
}
2004-08-06 13:54:07 +00:00
}
}
2001-12-27 11:07:33 +00:00
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Bridge failed on channels %s and %s \n " , chan - > name , peer - > name ) ;
return - 1 ;
}
2006-04-16 18:37:01 +00:00
if ( ! f | | ( f - > frametype = = AST_FRAME_CONTROL & &
( f - > subclass = = AST_CONTROL_HANGUP | | f - > subclass = = AST_CONTROL_BUSY | |
f - > subclass = = AST_CONTROL_CONGESTION ) ) ) {
res = - 1 ;
break ;
2001-12-27 11:07:33 +00:00
}
2006-04-14 23:20:29 +00:00
/* many things should be sent to the 'other' channel */
other = ( who = = chan ) ? peer : chan ;
if ( f - > frametype = = AST_FRAME_CONTROL ) {
if ( f - > subclass = = AST_CONTROL_RINGING )
ast_indicate ( other , AST_CONTROL_RINGING ) ;
else if ( f - > subclass = = - 1 )
ast_indicate ( other , - 1 ) ;
else if ( f - > subclass = = AST_CONTROL_FLASH )
ast_indicate ( other , AST_CONTROL_FLASH ) ;
else if ( f - > subclass = = AST_CONTROL_OPTION ) {
aoh = f - > data ;
/* Forward option Requests */
2006-04-16 20:32:14 +00:00
if ( aoh & & aoh - > flag = = AST_OPTION_FLAG_REQUEST )
2006-04-14 23:20:29 +00:00
ast_channel_setoption ( other , ntohs ( aoh - > option ) , aoh - > data , f - > datalen - sizeof ( struct ast_option_header ) , 0 ) ;
2001-12-27 11:07:33 +00:00
}
}
2004-07-28 19:25:14 +00:00
/* check for '*', if we find it it's time to disconnect */
2006-04-16 20:09:01 +00:00
if ( f - > frametype = = AST_FRAME_DTMF ) {
2005-01-04 04:01:40 +00:00
char * featurecode ;
int sense ;
2005-07-25 17:31:53 +00:00
2005-01-04 04:01:40 +00:00
hadfeatures = hasfeatures ;
/* This cannot overrun because the longest feature is one shorter than our buffer */
if ( who = = chan ) {
sense = FEATURE_SENSE_CHAN ;
featurecode = chan_featurecode ;
} else {
sense = FEATURE_SENSE_PEER ;
featurecode = peer_featurecode ;
}
2006-04-17 04:31:21 +00:00
/*! append the event to featurecode. we rely on the string being zero-filled, and
* not overflowing it .
* \ todo XXX how do we guarantee the latter ?
2006-04-14 23:20:29 +00:00
*/
2005-01-04 04:01:40 +00:00
featurecode [ strlen ( featurecode ) ] = f - > subclass ;
2006-05-23 16:37:40 +00:00
/* Get rid of the frame before we start doing "stuff" with the channels */
ast_frfree ( f ) ;
f = NULL ;
2005-08-03 04:42:59 +00:00
config - > feature_timer = backup_config . feature_timer ;
2005-01-04 04:01:40 +00:00
res = ast_feature_interpret ( chan , peer , config , featurecode , sense ) ;
switch ( res ) {
case FEATURE_RETURN_PASSDIGITS :
ast_dtmf_stream ( other , who , featurecode , 0 ) ;
/* Fall through */
case FEATURE_RETURN_SUCCESS :
memset ( featurecode , 0 , sizeof ( chan_featurecode ) ) ;
break ;
}
if ( res > = FEATURE_RETURN_PASSDIGITS ) {
2001-12-27 11:07:33 +00:00
res = 0 ;
2006-05-23 16:37:40 +00:00
} else
2005-01-04 04:01:40 +00:00
break ;
hasfeatures = ! ast_strlen_zero ( chan_featurecode ) | | ! ast_strlen_zero ( peer_featurecode ) ;
if ( hadfeatures & & ! hasfeatures ) {
/* Restore backup */
memcpy ( config , & backup_config , sizeof ( struct ast_bridge_config ) ) ;
memset ( & backup_config , 0 , sizeof ( struct ast_bridge_config ) ) ;
} else if ( hasfeatures ) {
if ( ! hadfeatures ) {
/* Backup configuration */
memcpy ( & backup_config , config , sizeof ( struct ast_bridge_config ) ) ;
/* Setup temporary config options */
config - > play_warning = 0 ;
2005-01-10 14:46:59 +00:00
ast_clear_flag ( & ( config - > features_caller ) , AST_FEATURE_PLAY_WARNING ) ;
2005-07-25 17:31:53 +00:00
ast_clear_flag ( & ( config - > features_callee ) , AST_FEATURE_PLAY_WARNING ) ;
2005-01-04 04:01:40 +00:00
config - > warning_freq = 0 ;
config - > warning_sound = NULL ;
config - > end_sound = NULL ;
config - > start_sound = NULL ;
config - > firstpass = 0 ;
}
2005-08-03 04:42:59 +00:00
config - > feature_timer = featuredigittimeout ;
2006-04-17 04:31:21 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Set time limit to %ld \n " , config - > feature_timer ) ;
2001-12-27 11:07:33 +00:00
}
2005-01-04 04:01:40 +00:00
}
if ( f )
ast_frfree ( f ) ;
2001-12-27 11:07:33 +00:00
}
return res ;
}
2006-04-21 16:04:25 +00:00
static void post_manager_event ( const char * s , int num , struct ast_channel * chan )
{
manager_event ( EVENT_FLAG_CALL , s ,
" Exten: %d \r \n "
" Channel: %s \r \n "
" CallerID: %s \r \n "
" CallerIDName: %s \r \n \r \n " ,
num , chan - > name ,
S_OR ( chan - > cid . cid_num , " <unknown> " ) ,
S_OR ( chan - > cid . cid_name , " <unknown> " )
) ;
}
2006-03-14 07:09:57 +00:00
/*! \brief Take care of parked calls and unpark them if needed */
2001-12-27 11:07:33 +00:00
static void * do_parking_thread ( void * ignore )
{
2006-04-16 20:32:14 +00:00
fd_set rfds , efds ; /* results from previous select, to be preserved across loops. */
2001-12-27 11:07:33 +00:00
FD_ZERO ( & rfds ) ;
FD_ZERO ( & efds ) ;
2004-12-09 22:39:14 +00:00
2001-12-27 11:07:33 +00:00
for ( ; ; ) {
2006-04-16 19:05:19 +00:00
struct parkeduser * pu , * pl , * pt = NULL ;
int ms = - 1 ; /* select timeout, uninitialized */
int max = - 1 ; /* max fd, none there yet */
2006-04-16 20:32:14 +00:00
fd_set nrfds , nefds ; /* args for the next select */
2006-04-16 19:05:19 +00:00
FD_ZERO ( & nrfds ) ;
FD_ZERO ( & nefds ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
pl = NULL ;
pu = parkinglot ;
2006-04-16 20:32:14 +00:00
/* navigate the list with prev-cur pointers to support removals */
2001-12-27 11:07:33 +00:00
while ( pu ) {
2006-04-16 19:41:12 +00:00
struct ast_channel * chan = pu - > chan ; /* shorthand */
2006-04-16 19:05:19 +00:00
int tms ; /* timeout for this item */
int x ; /* fd index in channel */
struct ast_context * con ;
2006-04-16 20:32:14 +00:00
if ( pu - > notquiteyet ) { /* Pretend this one isn't here yet */
2004-08-03 06:31:20 +00:00
pl = pu ;
pu = pu - > next ;
continue ;
}
2005-07-15 23:00:47 +00:00
tms = ast_tvdiff_ms ( ast_tvnow ( ) , pu - > start ) ;
2003-02-02 19:37:23 +00:00
if ( tms > pu - > parkingtime ) {
2002-05-30 01:34:15 +00:00
/* Stop music on hold */
2006-04-16 19:41:12 +00:00
ast_moh_stop ( chan ) ;
ast_indicate ( chan , AST_CONTROL_UNHOLD ) ;
2004-12-09 22:39:14 +00:00
/* Get chan, exten from derived kludge */
2004-12-28 23:49:46 +00:00
if ( pu - > peername [ 0 ] ) {
2006-04-16 20:32:14 +00:00
char * peername = ast_strdupa ( pu - > peername ) ;
char * cp = strrchr ( peername , ' - ' ) ;
2004-12-28 23:49:46 +00:00
if ( cp )
* cp = 0 ;
2004-12-09 22:39:14 +00:00
con = ast_context_find ( parking_con_dial ) ;
if ( ! con ) {
2004-12-28 23:49:46 +00:00
con = ast_context_create ( NULL , parking_con_dial , registrar ) ;
2004-12-09 22:39:14 +00:00
if ( ! con ) {
ast_log ( LOG_ERROR , " Parking dial context '%s' does not exist and unable to create \n " , parking_con_dial ) ;
}
}
if ( con ) {
2006-04-16 19:05:19 +00:00
char returnexten [ AST_MAX_EXTENSION ] ;
2005-03-24 05:37:59 +00:00
snprintf ( returnexten , sizeof ( returnexten ) , " %s||t " , peername ) ;
2005-05-09 14:28:53 +00:00
ast_add_extension2 ( con , 1 , peername , 1 , NULL , NULL , " Dial " , strdup ( returnexten ) , FREE , registrar ) ;
2004-12-09 22:39:14 +00:00
}
2006-04-16 19:41:12 +00:00
set_c_e_p ( chan , parking_con_dial , peername , 1 ) ;
2004-12-09 22:39:14 +00:00
} else {
/* They've been waiting too long, send them back to where they came. Theoretically they
should have their original extensions and such , but we copy to be on the safe side */
2006-04-16 19:41:12 +00:00
set_c_e_p ( chan , pu - > context , pu - > exten , pu - > priority ) ;
2004-12-09 22:39:14 +00:00
}
2005-02-26 07:54:28 +00:00
2006-04-21 16:04:25 +00:00
post_manager_event ( " ParkedCallTimeOut " , pu - > parkingnum , chan ) ;
2005-02-26 07:54:28 +00:00
2004-12-09 22:39:14 +00:00
if ( option_verbose > 1 )
2006-04-16 19:41:12 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Timeout for %s parked on %d. Returning to %s,%s,%d \n " , chan - > name , pu - > parkingnum , chan - > context , chan - > exten , chan - > priority ) ;
2001-12-27 11:07:33 +00:00
/* Start up the PBX, or hang them up */
2006-04-16 19:41:12 +00:00
if ( ast_pbx_start ( chan ) ) {
ast_log ( LOG_WARNING , " Unable to restart the PBX for user on '%s', hanging them up... \n " , chan - > name ) ;
2006-04-16 21:41:06 +00:00
ast_hangup ( chan ) ;
2001-12-27 11:07:33 +00:00
}
/* And take them out of the parking lot */
if ( pl )
pl - > next = pu - > next ;
else
parkinglot = pu - > next ;
pt = pu ;
pu = pu - > next ;
2004-08-03 06:31:20 +00:00
con = ast_context_find ( parking_con ) ;
if ( con ) {
2006-04-16 20:32:14 +00:00
char exten [ AST_MAX_EXTENSION ] ;
2004-08-03 06:31:20 +00:00
snprintf ( exten , sizeof ( exten ) , " %d " , pt - > parkingnum ) ;
if ( ast_context_remove_extension2 ( con , exten , 1 , NULL ) )
ast_log ( LOG_WARNING , " Whoa, failed to remove the extension! \n " ) ;
} else
ast_log ( LOG_WARNING , " Whoa, no parking context? \n " ) ;
2001-12-27 11:07:33 +00:00
free ( pt ) ;
2006-04-16 20:32:14 +00:00
} else { /* still within parking time, process descriptors */
2005-07-25 17:31:53 +00:00
for ( x = 0 ; x < AST_MAX_FDS ; x + + ) {
2006-04-16 19:05:19 +00:00
struct ast_frame * f ;
2006-04-16 20:32:14 +00:00
if ( chan - > fds [ x ] = = - 1 | | ( ! FD_ISSET ( chan - > fds [ x ] , & rfds ) & & ! FD_ISSET ( chan - > fds [ x ] , & efds ) ) )
continue ; /* nothing on this descriptor */
2006-04-16 19:05:19 +00:00
2006-04-16 19:41:12 +00:00
if ( FD_ISSET ( chan - > fds [ x ] , & efds ) )
ast_set_flag ( chan , AST_FLAG_EXCEPTION ) ;
2006-04-16 19:12:51 +00:00
else
2006-04-16 19:41:12 +00:00
ast_clear_flag ( chan , AST_FLAG_EXCEPTION ) ;
chan - > fdno = x ;
2006-04-16 19:12:51 +00:00
/* See if they need servicing */
2006-04-16 19:41:12 +00:00
f = ast_read ( chan ) ;
2006-04-16 20:32:14 +00:00
if ( ! f | | ( f - > frametype = = AST_FRAME_CONTROL & & f - > subclass = = AST_CONTROL_HANGUP ) ) {
2006-04-16 19:12:51 +00:00
if ( f )
2001-12-27 11:07:33 +00:00
ast_frfree ( f ) ;
2006-04-21 16:04:25 +00:00
post_manager_event ( " ParkedCallGiveUp " , pu - > parkingnum , chan ) ;
2006-04-16 19:12:51 +00:00
/* There's a problem, hang them up*/
if ( option_verbose > 1 )
2006-04-16 19:41:12 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " %s got tired of being parked \n " , chan - > name ) ;
ast_hangup ( chan ) ;
2006-04-16 19:12:51 +00:00
/* And take them out of the parking lot */
if ( pl )
pl - > next = pu - > next ;
else
parkinglot = pu - > next ;
pt = pu ;
pu = pu - > next ;
con = ast_context_find ( parking_con ) ;
if ( con ) {
2006-04-16 20:32:14 +00:00
char exten [ AST_MAX_EXTENSION ] ;
2006-04-16 19:12:51 +00:00
snprintf ( exten , sizeof ( exten ) , " %d " , pt - > parkingnum ) ;
if ( ast_context_remove_extension2 ( con , exten , 1 , NULL ) )
ast_log ( LOG_WARNING , " Whoa, failed to remove the extension! \n " ) ;
} else
ast_log ( LOG_WARNING , " Whoa, no parking context? \n " ) ;
free ( pt ) ;
break ;
} else {
2006-04-17 04:31:21 +00:00
/*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
2006-04-16 19:12:51 +00:00
ast_frfree ( f ) ;
2006-04-16 19:41:12 +00:00
if ( pu - > moh_trys < 3 & & ! chan - > generatordata ) {
2006-04-17 04:31:21 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " MOH on parked call stopped by outside source. Restarting. \n " ) ;
2006-04-16 19:41:12 +00:00
ast_moh_start ( chan , NULL ) ;
2006-04-16 19:12:51 +00:00
pu - > moh_trys + + ;
2001-12-27 11:07:33 +00:00
}
2006-04-17 04:31:21 +00:00
goto std ; /*! \todo XXX Ick: jumping into an else statement??? XXX */
2006-04-16 19:12:51 +00:00
}
2006-04-16 19:05:19 +00:00
2006-04-16 18:37:01 +00:00
} /* end for */
2001-12-27 11:07:33 +00:00
if ( x > = AST_MAX_FDS ) {
2006-04-16 20:32:14 +00:00
std : for ( x = 0 ; x < AST_MAX_FDS ; x + + ) { /* mark fds for next round */
2006-04-16 19:41:12 +00:00
if ( chan - > fds [ x ] > - 1 ) {
FD_SET ( chan - > fds [ x ] , & nrfds ) ;
FD_SET ( chan - > fds [ x ] , & nefds ) ;
if ( chan - > fds [ x ] > max )
max = chan - > fds [ x ] ;
2001-12-27 11:07:33 +00:00
}
}
2006-04-16 20:32:14 +00:00
/* Keep track of our shortest wait */
if ( tms < ms | | ms < 0 )
2001-12-27 11:07:33 +00:00
ms = tms ;
pl = pu ;
pu = pu - > next ;
}
}
2006-04-16 18:37:01 +00:00
} /* end while */
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
rfds = nrfds ;
efds = nefds ;
2006-04-16 20:32:14 +00:00
{
struct timeval tv = ast_samp2tv ( ms , 1000 ) ;
/* Wait for something to happen */
ast_select ( max + 1 , & rfds , NULL , & efds , ( ms > - 1 ) ? & tv : NULL ) ;
}
2001-12-27 11:07:33 +00:00
pthread_testcancel ( ) ;
}
return NULL ; /* Never reached */
}
2004-08-03 06:31:20 +00:00
static int park_call_exec ( struct ast_channel * chan , void * data )
{
/* Data is unused at the moment but could contain a parking
lot context eventually */
int res = 0 ;
struct localuser * u ;
LOCAL_USER_ADD ( u ) ;
/* Setup the exten/priority to be s/1 since we don't know
where this call should return */
strcpy ( chan - > exten , " s " ) ;
chan - > priority = 1 ;
if ( chan - > _state ! = AST_STATE_UP )
res = ast_answer ( chan ) ;
if ( ! res )
res = ast_safe_sleep ( chan , 1000 ) ;
if ( ! res )
res = ast_park_call ( chan , chan , 0 , NULL ) ;
LOCAL_USER_REMOVE ( u ) ;
if ( ! res )
res = AST_PBX_KEEPALIVE ;
return res ;
}
2001-12-27 11:07:33 +00:00
static int park_exec ( struct ast_channel * chan , void * data )
{
int res = 0 ;
struct localuser * u ;
struct ast_channel * peer = NULL ;
struct parkeduser * pu , * pl = NULL ;
2004-08-03 06:31:20 +00:00
struct ast_context * con ;
2001-12-27 11:07:33 +00:00
int park ;
2004-04-26 23:22:34 +00:00
struct ast_bridge_config config ;
2001-12-27 11:07:33 +00:00
if ( ! data ) {
ast_log ( LOG_WARNING , " Park requires an argument (extension number) \n " ) ;
return - 1 ;
}
LOCAL_USER_ADD ( u ) ;
park = atoi ( ( char * ) data ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
pu = parkinglot ;
while ( pu ) {
if ( pu - > parkingnum = = park ) {
if ( pl )
pl - > next = pu - > next ;
else
parkinglot = pu - > next ;
break ;
}
2003-02-02 19:37:23 +00:00
pl = pu ;
2001-12-27 11:07:33 +00:00
pu = pu - > next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
if ( pu ) {
peer = pu - > chan ;
2004-08-03 06:31:20 +00:00
con = ast_context_find ( parking_con ) ;
if ( con ) {
2006-04-16 20:32:14 +00:00
char exten [ AST_MAX_EXTENSION ] ;
2004-08-03 06:31:20 +00:00
snprintf ( exten , sizeof ( exten ) , " %d " , pu - > parkingnum ) ;
if ( ast_context_remove_extension2 ( con , exten , 1 , NULL ) )
ast_log ( LOG_WARNING , " Whoa, failed to remove the extension! \n " ) ;
} else
ast_log ( LOG_WARNING , " Whoa, no parking context? \n " ) ;
2005-02-26 07:54:28 +00:00
manager_event ( EVENT_FLAG_CALL , " UnParkedCall " ,
" Exten: %d \r \n "
" Channel: %s \r \n "
" From: %s \r \n "
" CallerID: %s \r \n "
2006-04-21 10:47:07 +00:00
" CallerIDName: %s \r \n " ,
pu - > parkingnum , pu - > chan - > name , chan - > name ,
S_OR ( pu - > chan - > cid . cid_num , " <unknown> " ) ,
S_OR ( pu - > chan - > cid . cid_name , " <unknown> " )
2005-02-26 07:54:28 +00:00
) ;
2001-12-27 11:07:33 +00:00
free ( pu ) ;
}
2003-02-02 19:37:23 +00:00
/* JK02: it helps to answer the channel if not already up */
2006-04-16 20:32:14 +00:00
if ( chan - > _state ! = AST_STATE_UP )
2003-02-02 19:37:23 +00:00
ast_answer ( chan ) ;
2001-12-27 11:07:33 +00:00
if ( peer ) {
2006-01-08 00:25:31 +00:00
/* Play a courtesy to the source(s) configured to prefix the bridge connecting */
2004-08-31 13:32:11 +00:00
if ( ! ast_strlen_zero ( courtesytone ) ) {
2006-04-16 20:32:14 +00:00
int error = 0 ;
ast_moh_stop ( peer ) ;
ast_indicate ( peer , AST_CONTROL_UNHOLD ) ;
2006-01-08 00:25:31 +00:00
if ( parkedplay = = 0 ) {
2006-04-21 20:28:32 +00:00
error = ast_stream_and_wait ( chan , courtesytone , chan - > language , " " ) ;
2006-04-16 21:41:06 +00:00
} else if ( parkedplay = = 1 ) {
2006-04-21 20:28:32 +00:00
error = ast_stream_and_wait ( peer , courtesytone , chan - > language , " " ) ;
2006-04-16 20:32:14 +00:00
} else if ( parkedplay = = 2 ) {
if ( ! ast_streamfile ( chan , courtesytone , chan - > language ) & &
! ast_streamfile ( peer , courtesytone , chan - > language ) ) {
2006-04-17 04:31:21 +00:00
/*! \todo XXX we would like to wait on both! */
2006-04-16 20:32:14 +00:00
res = ast_waitstream ( chan , " " ) ;
if ( res > = 0 )
res = ast_waitstream ( peer , " " ) ;
if ( res < 0 )
error = 1 ;
2004-08-31 13:32:11 +00:00
}
2006-04-16 20:32:14 +00:00
}
if ( error ) {
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
ast_hangup ( peer ) ;
return - 1 ;
2004-08-31 13:32:11 +00:00
}
2006-05-03 21:39:24 +00:00
} else {
ast_moh_stop ( peer ) ;
ast_indicate ( peer , AST_CONTROL_UNHOLD ) ;
2004-08-31 13:32:11 +00:00
}
2006-05-03 21:39:24 +00:00
2001-12-27 11:07:33 +00:00
res = ast_channel_make_compatible ( chan , peer ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Could not make channels %s and %s compatible for bridge \n " , chan - > name , peer - > name ) ;
ast_hangup ( peer ) ;
return - 1 ;
}
/* This runs sorta backwards, since we give the incoming channel control, as if it
were the person called . */
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Channel %s connected to parked call %d \n " , chan - > name , park ) ;
2004-04-26 23:22:34 +00:00
2005-07-25 17:31:53 +00:00
memset ( & config , 0 , sizeof ( struct ast_bridge_config ) ) ;
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_callee ) , AST_FEATURE_REDIRECT ) ;
ast_set_flag ( & ( config . features_caller ) , AST_FEATURE_REDIRECT ) ;
2004-04-27 21:21:57 +00:00
config . timelimit = 0 ;
config . play_warning = 0 ;
config . warning_freq = 0 ;
config . warning_sound = NULL ;
2005-07-25 17:31:53 +00:00
res = ast_bridge_call ( chan , peer , & config ) ;
2004-04-26 23:22:34 +00:00
2001-12-27 11:07:33 +00:00
/* Simulate the PBX hanging up */
2004-04-07 04:11:00 +00:00
if ( res ! = AST_PBX_NO_HANGUP_PEER )
2001-12-27 11:07:33 +00:00
ast_hangup ( peer ) ;
2004-04-07 04:11:00 +00:00
return res ;
2001-12-27 11:07:33 +00:00
} else {
2006-04-17 04:31:21 +00:00
/*! \todo XXX Play a message XXX */
2006-04-21 20:28:32 +00:00
if ( ast_stream_and_wait ( chan , " pbx-invalidpark " , chan - > language , " " ) )
2004-12-28 23:49:46 +00:00
ast_log ( LOG_WARNING , " ast_streamfile of %s failed on %s \n " , " pbx-invalidpark " , chan - > name ) ;
2001-12-27 11:07:33 +00:00
if ( option_verbose > 2 )
2005-04-13 23:33:47 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Channel %s tried to talk to nonexistent parked call %d \n " , chan - > name , park ) ;
2001-12-27 11:07:33 +00:00
res = - 1 ;
}
LOCAL_USER_REMOVE ( u ) ;
return res ;
}
2005-02-06 23:36:35 +00:00
static int handle_showfeatures ( int fd , int argc , char * argv [ ] )
{
int i ;
int fcount ;
2005-08-23 02:22:33 +00:00
struct ast_call_feature * feature ;
2005-02-06 23:36:35 +00:00
char format [ ] = " %-25s %-7s %-7s \n " ;
2005-08-23 02:22:33 +00:00
ast_cli ( fd , format , " Builtin Feature " , " Default " , " Current " ) ;
ast_cli ( fd , format , " --------------- " , " ------- " , " ------- " ) ;
2005-02-06 23:36:35 +00:00
ast_cli ( fd , format , " Pickup " , " *8 " , ast_pickup_ext ( ) ) ; /* default hardcoded above, so we'll hardcode it here */
fcount = sizeof ( builtin_features ) / sizeof ( builtin_features [ 0 ] ) ;
for ( i = 0 ; i < fcount ; i + + )
{
ast_cli ( fd , format , builtin_features [ i ] . fname , builtin_features [ i ] . default_exten , builtin_features [ i ] . exten ) ;
}
2005-08-23 02:22:33 +00:00
ast_cli ( fd , " \n " ) ;
ast_cli ( fd , format , " Dynamic Feature " , " Default " , " Current " ) ;
ast_cli ( fd , format , " --------------- " , " ------- " , " ------- " ) ;
2005-08-23 14:40:03 +00:00
if ( AST_LIST_EMPTY ( & feature_list ) ) {
ast_cli ( fd , " (none) \n " ) ;
}
else {
AST_LIST_LOCK ( & feature_list ) ;
AST_LIST_TRAVERSE ( & feature_list , feature , feature_entry ) {
ast_cli ( fd , format , feature - > sname , " no def " , feature - > exten ) ;
}
AST_LIST_UNLOCK ( & feature_list ) ;
2005-08-23 02:22:33 +00:00
}
2005-10-13 23:58:33 +00:00
ast_cli ( fd , " \n Call parking \n " ) ;
ast_cli ( fd , " ------------ \n " ) ;
ast_cli ( fd , " %-20s: %s \n " , " Parking extension " , parking_ext ) ;
ast_cli ( fd , " %-20s: %s \n " , " Parking context " , parking_con ) ;
ast_cli ( fd , " %-20s: %d-%d \n " , " Parked call extensions " , parking_start , parking_stop ) ;
ast_cli ( fd , " \n " ) ;
2005-08-23 02:22:33 +00:00
2005-02-06 23:36:35 +00:00
return RESULT_SUCCESS ;
}
static char showfeatures_help [ ] =
" Usage: show features \n "
" Lists currently configured features. \n " ;
static struct ast_cli_entry showfeatures =
{ { " show " , " features " , NULL } , handle_showfeatures , " Lists configured features " , showfeatures_help } ;
2003-07-02 14:06:12 +00:00
static int handle_parkedcalls ( int fd , int argc , char * argv [ ] )
{
struct parkeduser * cur ;
2005-04-20 16:27:44 +00:00
int numparked = 0 ;
2003-07-02 14:06:12 +00:00
ast_cli ( fd , " %4s %25s (%-15s %-12s %-4s) %-6s \n " , " Num " , " Channel "
, " Context " , " Extension " , " Pri " , " Timeout " ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & parking_lock ) ;
2003-07-02 14:06:12 +00:00
2006-04-16 19:05:19 +00:00
for ( cur = parkinglot ; cur ; cur = cur - > next ) {
2003-09-10 05:24:49 +00:00
ast_cli ( fd , " %4d %25s (%-15s %-12s %-4d) %6lds \n "
2003-07-02 14:06:12 +00:00
, cur - > parkingnum , cur - > chan - > name , cur - > context , cur - > exten
, cur - > priority , cur - > start . tv_sec + ( cur - > parkingtime / 1000 ) - time ( NULL ) ) ;
2005-04-20 16:27:44 +00:00
numparked + + ;
2003-07-02 14:06:12 +00:00
}
2006-04-16 22:02:30 +00:00
ast_mutex_unlock ( & parking_lock ) ;
2005-07-07 22:32:20 +00:00
ast_cli ( fd , " %d parked call%s. \n " , numparked , ( numparked ! = 1 ) ? " s " : " " ) ;
2003-07-02 14:06:12 +00:00
return RESULT_SUCCESS ;
}
static char showparked_help [ ] =
" Usage: show parkedcalls \n "
" Lists currently parked calls. \n " ;
static struct ast_cli_entry showparked =
{ { " show " , " parkedcalls " , NULL } , handle_parkedcalls , " Lists parked calls " , showparked_help } ;
2005-07-25 17:31:53 +00:00
2006-01-19 22:09:18 +00:00
/*! \brief Dump lot status */
2004-01-30 05:49:44 +00:00
static int manager_parking_status ( struct mansession * s , struct message * m )
{
struct parkeduser * cur ;
2004-07-14 07:53:57 +00:00
char * id = astman_get_header ( m , " ActionID " ) ;
char idText [ 256 ] = " " ;
2005-11-08 01:55:31 +00:00
if ( ! ast_strlen_zero ( id ) )
2004-07-14 07:53:57 +00:00
snprintf ( idText , 256 , " ActionID: %s \r \n " , id ) ;
2004-01-30 05:49:44 +00:00
2004-01-30 06:31:25 +00:00
astman_send_ack ( s , m , " Parked calls will follow " ) ;
2004-01-30 05:49:44 +00:00
ast_mutex_lock ( & parking_lock ) ;
2006-04-16 21:41:06 +00:00
for ( cur = parkinglot ; cur ; cur = cur - > next ) {
astman_append ( s , " Event: ParkedCall \r \n "
2004-01-30 05:49:44 +00:00
" Exten: %d \r \n "
" Channel: %s \r \n "
2005-12-21 08:51:44 +00:00
" From: %s \r \n "
2004-01-30 06:31:25 +00:00
" Timeout: %ld \r \n "
2004-01-30 05:49:44 +00:00
" CallerID: %s \r \n "
2004-10-02 00:58:31 +00:00
" CallerIDName: %s \r \n "
2004-07-14 07:53:57 +00:00
" %s "
2006-04-21 10:47:07 +00:00
" \r \n " ,
cur - > parkingnum , cur - > chan - > name , cur - > peername ,
( long ) cur - > start . tv_sec + ( long ) ( cur - > parkingtime / 1000 ) - ( long ) time ( NULL ) ,
S_OR ( cur - > chan - > cid . cid_num , " " ) , /* XXX in other places it is <unknown> */
S_OR ( cur - > chan - > cid . cid_name , " " ) ,
idText ) ;
2004-01-30 05:49:44 +00:00
}
2006-03-25 23:50:09 +00:00
astman_append ( s ,
2006-04-16 21:41:06 +00:00
" Event: ParkedCallsComplete \r \n "
" %s "
" \r \n " , idText ) ;
2004-07-14 07:53:57 +00:00
2004-01-30 05:49:44 +00:00
ast_mutex_unlock ( & parking_lock ) ;
return RESULT_SUCCESS ;
}
2006-03-06 23:12:48 +00:00
static char mandescr_park [ ] =
" Description: Park a channel. \n "
" Variables: (Names marked with * are required) \n "
" *Channel: Channel name to park \n "
" *Channel2: Channel to announce park info to (and return to if timeout) \n "
" Timeout: Number of milliseconds to wait before callback. \n " ;
static int manager_park ( struct mansession * s , struct message * m )
{
char * channel = astman_get_header ( m , " Channel " ) ;
char * channel2 = astman_get_header ( m , " Channel2 " ) ;
char * timeout = astman_get_header ( m , " Timeout " ) ;
char buf [ BUFSIZ ] ;
int to = 0 ;
int res = 0 ;
int parkExt = 0 ;
struct ast_channel * ch1 , * ch2 ;
if ( ast_strlen_zero ( channel ) ) {
astman_send_error ( s , m , " Channel not specified " ) ;
return 0 ;
}
if ( ast_strlen_zero ( channel2 ) ) {
astman_send_error ( s , m , " Channel2 not specified " ) ;
return 0 ;
}
ch1 = ast_get_channel_by_name_locked ( channel ) ;
if ( ! ch1 ) {
snprintf ( buf , sizeof ( buf ) , " Channel does not exist: %s " , channel ) ;
astman_send_error ( s , m , buf ) ;
return 0 ;
}
ch2 = ast_get_channel_by_name_locked ( channel2 ) ;
if ( ! ch2 ) {
snprintf ( buf , sizeof ( buf ) , " Channel does not exist: %s " , channel2 ) ;
astman_send_error ( s , m , buf ) ;
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( ch1 ) ;
2006-03-06 23:12:48 +00:00
return 0 ;
}
if ( ! ast_strlen_zero ( timeout ) ) {
sscanf ( timeout , " %d " , & to ) ;
}
res = ast_masq_park_call ( ch1 , ch2 , to , & parkExt ) ;
if ( ! res ) {
ast_softhangup ( ch2 , AST_SOFTHANGUP_EXPLICIT ) ;
astman_send_ack ( s , m , " Park successful " ) ;
} else {
astman_send_error ( s , m , " Park failure " ) ;
}
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( ch1 ) ;
ast_channel_unlock ( ch2 ) ;
2006-03-06 23:12:48 +00:00
return 0 ;
}
2003-07-02 14:06:12 +00:00
2005-07-25 17:31:53 +00:00
int ast_pickup_call ( struct ast_channel * chan )
{
struct ast_channel * cur = NULL ;
int res = - 1 ;
while ( ( cur = ast_channel_walk_locked ( cur ) ) ! = NULL ) {
if ( ! cur - > pbx & &
( cur ! = chan ) & &
( chan - > pickupgroup & cur - > callgroup ) & &
( ( cur - > _state = = AST_STATE_RINGING ) | |
( cur - > _state = = AST_STATE_RING ) ) ) {
break ;
}
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( cur ) ;
2005-07-25 17:31:53 +00:00
}
if ( cur ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " Call pickup on chan '%s' by '%s' \n " , cur - > name , chan - > name ) ;
res = ast_answer ( chan ) ;
if ( res )
ast_log ( LOG_WARNING , " Unable to answer '%s' \n " , chan - > name ) ;
res = ast_queue_control ( chan , AST_CONTROL_ANSWER ) ;
if ( res )
ast_log ( LOG_WARNING , " Unable to queue answer on '%s' \n " , chan - > name ) ;
res = ast_channel_masquerade ( cur , chan ) ;
if ( res )
ast_log ( LOG_WARNING , " Unable to masquerade '%s' into '%s' \n " , chan - > name , cur - > name ) ; /* Done */
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( cur ) ;
2005-07-25 17:31:53 +00:00
} else {
if ( option_debug )
ast_log ( LOG_DEBUG , " No call pickup possible... \n " ) ;
}
return res ;
}
2005-01-10 04:03:30 +00:00
static int load_config ( void )
2001-12-27 11:07:33 +00:00
{
2005-01-10 04:03:30 +00:00
int start = 0 , end = 0 ;
struct ast_context * con = NULL ;
struct ast_config * cfg = NULL ;
struct ast_variable * var = NULL ;
2005-10-13 23:58:33 +00:00
char old_parking_ext [ AST_MAX_EXTENSION ] ;
2005-11-10 23:26:40 +00:00
char old_parking_con [ AST_MAX_EXTENSION ] = " " ;
2005-10-13 23:58:33 +00:00
if ( ! ast_strlen_zero ( parking_con ) ) {
strcpy ( old_parking_ext , parking_ext ) ;
strcpy ( old_parking_con , parking_con ) ;
}
/* Reset to defaults */
strcpy ( parking_con , " parkedcalls " ) ;
strcpy ( parking_con_dial , " park-dial " ) ;
strcpy ( parking_ext , " 700 " ) ;
strcpy ( pickup_ext , " *8 " ) ;
courtesytone [ 0 ] = ' \0 ' ;
strcpy ( xfersound , " beep " ) ;
strcpy ( xferfailsound , " pbx-invalid " ) ;
parking_start = 701 ;
parking_stop = 750 ;
parkfindnext = 0 ;
2006-03-30 01:35:56 +00:00
adsipark = 0 ;
2005-10-13 23:58:33 +00:00
2005-01-04 04:01:40 +00:00
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
2006-05-23 18:23:05 +00:00
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER ;
2005-01-04 04:01:40 +00:00
2005-01-25 06:10:20 +00:00
cfg = ast_config_load ( " features.conf " ) ;
2002-05-30 01:34:15 +00:00
if ( cfg ) {
2006-04-15 00:05:08 +00:00
for ( var = ast_variable_browse ( cfg , " general " ) ; var ; var = var - > next ) {
2002-05-30 01:34:15 +00:00
if ( ! strcasecmp ( var - > name , " parkext " ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( parking_ext , var - > value , sizeof ( parking_ext ) ) ;
2002-05-30 01:34:15 +00:00
} else if ( ! strcasecmp ( var - > name , " context " ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( parking_con , var - > value , sizeof ( parking_con ) ) ;
2003-02-02 19:37:23 +00:00
} else if ( ! strcasecmp ( var - > name , " parkingtime " ) ) {
if ( ( sscanf ( var - > value , " %d " , & parkingtime ) ! = 1 ) | | ( parkingtime < 1 ) ) {
ast_log ( LOG_WARNING , " %s is not a valid parkingtime \n " , var - > value ) ;
parkingtime = DEFAULT_PARK_TIME ;
} else
parkingtime = parkingtime * 1000 ;
2002-05-30 01:34:15 +00:00
} else if ( ! strcasecmp ( var - > name , " parkpos " ) ) {
2005-04-29 17:00:33 +00:00
if ( sscanf ( var - > value , " %d-%d " , & start , & end ) ! = 2 ) {
2002-05-30 01:34:15 +00:00
ast_log ( LOG_WARNING , " Format for parking positions is a-b, where a and b are numbers at line %d of parking.conf \n " , var - > lineno ) ;
} else {
parking_start = start ;
parking_stop = end ;
}
2005-04-27 03:58:40 +00:00
} else if ( ! strcasecmp ( var - > name , " findslot " ) ) {
parkfindnext = ( ! strcasecmp ( var - > value , " next " ) ) ;
2004-09-15 19:49:33 +00:00
} else if ( ! strcasecmp ( var - > name , " adsipark " ) ) {
adsipark = ast_true ( var - > value ) ;
2004-09-17 14:15:11 +00:00
} else if ( ! strcasecmp ( var - > name , " transferdigittimeout " ) ) {
2004-08-01 01:38:15 +00:00
if ( ( sscanf ( var - > value , " %d " , & transferdigittimeout ) ! = 1 ) | | ( transferdigittimeout < 1 ) ) {
ast_log ( LOG_WARNING , " %s is not a valid transferdigittimeout \n " , var - > value ) ;
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
} else
transferdigittimeout = transferdigittimeout * 1000 ;
2005-01-04 04:01:40 +00:00
} else if ( ! strcasecmp ( var - > name , " featuredigittimeout " ) ) {
2005-02-11 06:53:18 +00:00
if ( ( sscanf ( var - > value , " %d " , & featuredigittimeout ) ! = 1 ) | | ( featuredigittimeout < 1 ) ) {
2005-01-04 04:01:40 +00:00
ast_log ( LOG_WARNING , " %s is not a valid featuredigittimeout \n " , var - > value ) ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
}
2006-05-23 18:23:05 +00:00
} else if ( ! strcasecmp ( var - > name , " atxfernoanswertimeout " ) ) {
if ( ( sscanf ( var - > value , " %d " , & atxfernoanswertimeout ) ! = 1 ) | | ( atxfernoanswertimeout < 1 ) ) {
ast_log ( LOG_WARNING , " %s is not a valid atxfernoanswertimeout \n " , var - > value ) ;
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER ;
} else
atxfernoanswertimeout = atxfernoanswertimeout * 1000 ;
2004-09-17 14:15:11 +00:00
} else if ( ! strcasecmp ( var - > name , " courtesytone " ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( courtesytone , var - > value , sizeof ( courtesytone ) ) ;
2006-01-08 00:25:31 +00:00
} else if ( ! strcasecmp ( var - > name , " parkedplay " ) ) {
if ( ! strcasecmp ( var - > value , " both " ) )
parkedplay = 2 ;
else if ( ! strcasecmp ( var - > value , " parked " ) )
parkedplay = 1 ;
else
parkedplay = 0 ;
2005-01-05 21:00:20 +00:00
} else if ( ! strcasecmp ( var - > name , " xfersound " ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( xfersound , var - > value , sizeof ( xfersound ) ) ;
2005-01-05 21:00:20 +00:00
} else if ( ! strcasecmp ( var - > name , " xferfailsound " ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( xferfailsound , var - > value , sizeof ( xferfailsound ) ) ;
2004-09-26 20:47:01 +00:00
} else if ( ! strcasecmp ( var - > name , " pickupexten " ) ) {
2005-05-15 23:26:45 +00:00
ast_copy_string ( pickup_ext , var - > value , sizeof ( pickup_ext ) ) ;
2002-05-30 01:34:15 +00:00
}
}
2005-08-23 02:22:33 +00:00
2005-01-04 04:01:40 +00:00
unmap_features ( ) ;
2006-04-15 00:05:08 +00:00
for ( var = ast_variable_browse ( cfg , " featuremap " ) ; var ; var = var - > next ) {
2005-01-04 04:01:40 +00:00
if ( remap_feature ( var - > name , var - > value ) )
ast_log ( LOG_NOTICE , " Unknown feature '%s' \n " , var - > name ) ;
}
2005-08-23 02:22:33 +00:00
/* Map a key combination to an application*/
2005-08-23 14:40:03 +00:00
ast_unregister_features ( ) ;
2006-04-15 00:05:08 +00:00
for ( var = ast_variable_browse ( cfg , " applicationmap " ) ; var ; var = var - > next ) {
char * tmp_val = ast_strdup ( var - > value ) ;
2005-08-23 15:33:27 +00:00
char * exten , * party = NULL , * app = NULL , * app_args = NULL ;
2005-08-23 02:22:33 +00:00
if ( ! tmp_val ) {
2006-04-17 04:31:21 +00:00
/*! \todo XXX No memory. We should probably break, but at least we do not
2006-04-15 00:05:08 +00:00
* insist on this entry or we could be stuck in an
* infinite loop .
*/
2005-08-23 02:22:33 +00:00
continue ;
}
2006-04-15 00:05:08 +00:00
/* strsep() sets the argument to NULL if match not found, and it
* is safe to use it with a NULL argument , so we don ' t check
* between calls .
*/
exten = strsep ( & tmp_val , " , " ) ;
party = strsep ( & tmp_val , " , " ) ;
app = strsep ( & tmp_val , " , " ) ;
app_args = strsep ( & tmp_val , " , " ) ;
2005-08-23 02:22:33 +00:00
2006-04-17 04:31:21 +00:00
/*! \todo XXX var_name or app_args ? */
2006-04-15 00:05:08 +00:00
if ( ast_strlen_zero ( app ) | | ast_strlen_zero ( exten ) | | ast_strlen_zero ( party ) | | ast_strlen_zero ( var - > name ) ) {
2005-08-23 14:40:03 +00:00
ast_log ( LOG_NOTICE , " Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s \n " , app , exten , party , var - > name ) ;
2005-08-23 02:22:33 +00:00
free ( tmp_val ) ;
continue ;
}
{
2006-01-21 22:09:06 +00:00
struct ast_call_feature * feature ;
int mallocd = 0 ;
2005-08-23 02:22:33 +00:00
2006-01-21 22:09:06 +00:00
if ( ! ( feature = find_feature ( var - > name ) ) ) {
mallocd = 1 ;
if ( ! ( feature = ast_calloc ( 1 , sizeof ( * feature ) ) ) ) {
free ( tmp_val ) ;
continue ;
}
2005-08-23 02:22:33 +00:00
}
ast_copy_string ( feature - > sname , var - > name , FEATURE_SNAME_LEN ) ;
ast_copy_string ( feature - > app , app , FEATURE_APP_LEN ) ;
ast_copy_string ( feature - > exten , exten , FEATURE_EXTEN_LEN ) ;
free ( tmp_val ) ;
if ( app_args )
ast_copy_string ( feature - > app_args , app_args , FEATURE_APP_ARGS_LEN ) ;
ast_copy_string ( feature - > exten , exten , sizeof ( feature - > exten ) ) ;
feature - > operation = feature_exec_app ;
ast_set_flag ( feature , AST_FEATURE_FLAG_NEEDSDTMF ) ;
if ( ! strcasecmp ( party , " caller " ) )
ast_set_flag ( feature , AST_FEATURE_FLAG_CALLER ) ;
2005-09-07 21:36:30 +00:00
else if ( ! strcasecmp ( party , " callee " ) )
2005-08-23 02:22:33 +00:00
ast_set_flag ( feature , AST_FEATURE_FLAG_CALLEE ) ;
2005-09-07 21:36:30 +00:00
else {
ast_log ( LOG_NOTICE , " Invalid party specification for feature '%s', must be caller, or callee \n " , var - > name ) ;
continue ;
}
2005-08-23 02:22:33 +00:00
ast_register_feature ( feature ) ;
2006-04-21 16:18:12 +00:00
/* XXX do we need to free it if mallocd ? */
2005-08-23 02:22:33 +00:00
2006-04-15 00:05:08 +00:00
if ( option_verbose > = 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Mapping Feature '%s' to app '%s' with code '%s' \n " , var - > name , app , exten ) ;
2005-08-23 02:22:33 +00:00
}
}
2002-05-30 01:34:15 +00:00
}
2005-08-23 02:22:33 +00:00
ast_config_destroy ( cfg ) ;
2005-10-13 23:58:33 +00:00
/* Remove the old parking extension */
if ( ! ast_strlen_zero ( old_parking_con ) & & ( con = ast_context_find ( old_parking_con ) ) ) {
ast_context_remove_extension2 ( con , old_parking_ext , 1 , registrar ) ;
2006-04-17 04:31:21 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Removed old parking extension %s@%s \n " , old_parking_ext , old_parking_con ) ;
2005-10-13 23:58:33 +00:00
}
2005-01-10 04:03:30 +00:00
2006-04-15 00:05:08 +00:00
if ( ! ( con = ast_context_find ( parking_con ) ) & & ! ( con = ast_context_create ( NULL , parking_con , registrar ) ) ) {
ast_log ( LOG_ERROR , " Parking context '%s' does not exist and unable to create \n " , parking_con ) ;
return - 1 ;
2001-12-27 11:07:33 +00:00
}
2005-05-09 14:28:53 +00:00
return ast_add_extension2 ( con , 1 , ast_parking_ext ( ) , 1 , NULL , NULL , parkcall , strdup ( " " ) , FREE , registrar ) ;
2005-01-10 04:03:30 +00:00
}
2006-04-14 14:08:19 +00:00
static int reload ( void * mod )
{
2005-01-10 04:03:30 +00:00
return load_config ( ) ;
}
2006-04-14 14:08:19 +00:00
static int load_module ( void * mod )
2005-01-10 04:03:30 +00:00
{
int res ;
2005-08-23 02:22:33 +00:00
2006-04-14 14:08:19 +00:00
__mod_desc = mod ;
2005-10-13 23:58:33 +00:00
memset ( parking_ext , 0 , sizeof ( parking_ext ) ) ;
memset ( parking_con , 0 , sizeof ( parking_con ) ) ;
2005-08-23 02:22:33 +00:00
2005-01-10 04:03:30 +00:00
if ( ( res = load_config ( ) ) )
return res ;
ast_cli_register ( & showparked ) ;
2005-02-06 23:36:35 +00:00
ast_cli_register ( & showfeatures ) ;
2004-08-08 17:15:02 +00:00
ast_pthread_create ( & parking_thread , NULL , do_parking_thread , NULL ) ;
2004-01-30 06:31:25 +00:00
res = ast_register_application ( parkedcall , park_exec , synopsis , descrip ) ;
2004-08-03 06:31:20 +00:00
if ( ! res )
res = ast_register_application ( parkcall , park_call_exec , synopsis2 , descrip2 ) ;
2004-01-30 05:49:44 +00:00
if ( ! res ) {
2005-07-25 17:31:53 +00:00
ast_manager_register ( " ParkedCalls " , 0 , manager_parking_status , " List parked calls " ) ;
2006-03-06 23:12:48 +00:00
ast_manager_register2 ( " Park " , EVENT_FLAG_CALL , manager_park ,
" Park a channel " , mandescr_park ) ;
2004-01-30 05:49:44 +00:00
}
2001-12-27 11:07:33 +00:00
return res ;
}
2003-04-09 04:00:43 +00:00
2006-04-14 14:08:19 +00:00
static int unload_module ( void * mod )
2001-12-27 11:07:33 +00:00
{
STANDARD_HANGUP_LOCALUSERS ;
2003-07-02 14:06:12 +00:00
2005-07-25 17:31:53 +00:00
ast_manager_unregister ( " ParkedCalls " ) ;
2006-03-06 23:12:48 +00:00
ast_manager_unregister ( " Park " ) ;
2005-02-06 23:36:35 +00:00
ast_cli_unregister ( & showfeatures ) ;
2003-07-02 14:06:12 +00:00
ast_cli_unregister ( & showparked ) ;
2004-08-03 06:31:20 +00:00
ast_unregister_application ( parkcall ) ;
2001-12-27 11:07:33 +00:00
return ast_unregister_application ( parkedcall ) ;
}
2006-04-14 14:08:19 +00:00
static const char * description ( void )
2001-12-27 11:07:33 +00:00
{
2005-07-25 17:31:53 +00:00
return " Call Features Resource " ;
2001-12-27 11:07:33 +00:00
}
2006-04-14 14:08:19 +00:00
static const char * key ( void )
2001-12-27 11:07:33 +00:00
{
return ASTERISK_GPL_KEY ;
}
2006-04-14 14:08:19 +00:00
STD_MOD ( MOD_0 | NO_UNLOAD , reload , NULL , NULL ) ;