2001-12-27 11:07:33 +00:00
/*
* Asterisk - - A telephony toolkit for Linux .
*
* Routines implementing call parking
*
2004-08-31 13:32:11 +00:00
* Copyright ( C ) 1999 - 2004 , 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
*
* This program is free software , distributed under the terms of
* the GNU General Public License
*/
2002-05-30 01:34:15 +00:00
# include <asterisk/lock.h>
2001-12-27 11:07:33 +00:00
# include <asterisk/file.h>
# include <asterisk/logger.h>
# include <asterisk/channel.h>
# include <asterisk/pbx.h>
# include <asterisk/options.h>
# include <asterisk/module.h>
# include <asterisk/translate.h>
2005-01-04 04:01:40 +00:00
# include <asterisk/app.h>
2001-12-27 11:07:33 +00:00
# include <asterisk/say.h>
# include <asterisk/channel_pvt.h>
2004-07-17 20:58:01 +00:00
# include <asterisk/features.h>
2002-05-30 01:34:15 +00:00
# include <asterisk/musiconhold.h>
# include <asterisk/config.h>
2003-07-02 14:06:12 +00:00
# include <asterisk/cli.h>
2004-01-30 05:49:44 +00:00
# include <asterisk/manager.h>
2004-07-14 10:34:25 +00:00
# include <asterisk/utils.h>
2004-09-15 19:49:33 +00:00
# include <asterisk/adsi.h>
2004-12-09 22:39:14 +00:00
# include <pthread.h>
2001-12-27 11:07:33 +00:00
# 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>
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
2003-02-02 19:37:23 +00:00
2001-12-27 11:07:33 +00:00
static char * parkedcall = " ParkedCall " ;
/* No more than 45 seconds parked before you do something with them */
2003-02-02 19:37:23 +00:00
static int parkingtime = DEFAULT_PARK_TIME ;
2001-12-27 11:07:33 +00:00
/* Context for which parking is made accessible */
static char parking_con [ AST_MAX_EXTENSION ] = " parkedcalls " ;
2004-12-09 22:39:14 +00:00
/* Context for dialback for parking (KLUDGE) */
static char parking_con_dial [ AST_MAX_EXTENSION ] = " park-dial " ;
2001-12-27 11:07:33 +00:00
/* Extension you type to park the call */
static char parking_ext [ AST_MAX_EXTENSION ] = " 700 " ;
2003-04-09 04:00:43 +00:00
static char pickup_ext [ AST_MAX_EXTENSION ] = " *8 " ;
2005-01-05 21:31:40 +00:00
/* Default sounds */
static char courtesytone [ 256 ] = " " ;
static char xfersound [ 256 ] = " beep " ;
static char xferfailsound [ 256 ] = " pbx-invalid " ;
2001-12-27 11:07:33 +00:00
/* First available extension for parking */
static int parking_start = 701 ;
/* Last available extension for parking */
static int parking_stop = 750 ;
2004-09-15 19:49:33 +00:00
static int adsipark = 0 ;
2004-08-01 01:38:15 +00:00
static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
2005-01-04 04:01:40 +00:00
static int featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
2004-08-01 01:38:15 +00:00
2004-08-31 13:32:11 +00:00
/* Default courtesy tone played when party joins conference */
2005-01-05 21:00:20 +00:00
2001-12-27 11:07:33 +00:00
/* Registrar for operations */
2004-08-03 06:31:20 +00:00
static char * registrar = " res_features " ;
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 " ;
static char * descrip2 = " Park(exten): "
" 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 " ;
2004-09-17 03:49:57 +00:00
static struct ast_app * monitor_app = NULL ;
static int monitor_ok = 1 ;
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 */
char context [ AST_MAX_EXTENSION ] ;
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 ] ;
2001-12-27 11:07:33 +00:00
struct parkeduser * next ;
} ;
static struct parkeduser * parkinglot ;
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( parking_lock ) ;
2001-12-27 11:07:33 +00:00
static pthread_t parking_thread ;
STANDARD_LOCAL_USER ;
LOCAL_USER_DECL ;
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 ;
} ;
static void * ast_bridge_call_thread ( void * data )
{
struct ast_bridge_thread_obj * tobj = data ;
tobj - > chan - > appl = " Transferred Call " ;
tobj - > chan - > data = tobj - > peer - > name ;
tobj - > peer - > appl = " Transferred Call " ;
tobj - > peer - > data = tobj - > chan - > name ;
if ( tobj - > chan - > cdr ) {
ast_cdr_reset ( tobj - > chan - > cdr , 0 ) ;
ast_cdr_setdestchan ( tobj - > chan - > cdr , tobj - > peer - > name ) ;
}
if ( tobj - > peer - > cdr ) {
ast_cdr_reset ( tobj - > peer - > cdr , 0 ) ;
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 ) ;
tobj - > chan = tobj - > peer = NULL ;
free ( tobj ) ;
tobj = NULL ;
return NULL ;
}
static void ast_bridge_call_thread_launch ( void * data )
{
pthread_t thread ;
pthread_attr_t attr ;
int result ;
result = pthread_attr_init ( & attr ) ;
pthread_attr_setschedpolicy ( & attr , SCHED_RR ) ;
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED ) ;
result = ast_pthread_create ( & thread , & attr , ast_bridge_call_thread , data ) ;
result = pthread_attr_destroy ( & attr ) ;
}
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 } ;
char tmp [ 256 ] = " " ;
char * message [ 5 ] = { NULL , NULL , NULL , NULL , NULL } ;
snprintf ( tmp , sizeof ( tmp ) , " Parked on %d " , parkingnum ) ;
message [ 0 ] = tmp ;
res = adsi_load_session ( chan , NULL , 0 , 1 ) ;
if ( res = = - 1 ) {
return res ;
}
return adsi_print ( chan , message , justify , 1 ) ;
}
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
{
/* We put the user in the parking list, then wake up the parking thread to be sure it looks
after these channels too */
struct parkeduser * pu , * cur ;
int x ;
2004-08-03 06:31:20 +00:00
char exten [ AST_MAX_EXTENSION ] ;
struct ast_context * con ;
2001-12-27 11:07:33 +00:00
pu = malloc ( sizeof ( struct parkeduser ) ) ;
if ( pu ) {
2004-12-09 22:39:14 +00:00
memset ( pu , 0 , sizeof ( struct parkeduser ) ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
for ( x = parking_start ; x < = parking_stop ; x + + ) {
cur = parkinglot ;
while ( cur ) {
if ( cur - > parkingnum = = x )
break ;
cur = cur - > next ;
}
if ( ! cur )
break ;
}
if ( x < = parking_stop ) {
2003-07-02 14:06:12 +00:00
chan - > appl = " Parked Call " ;
chan - > data = NULL ;
2001-12-27 11:07:33 +00:00
pu - > chan = chan ;
2003-07-02 14:06:12 +00:00
/* Start music on hold */
2004-08-03 06:31:20 +00:00
if ( chan ! = peer )
ast_moh_start ( pu - > chan , NULL ) ;
2001-12-27 11:07:33 +00:00
gettimeofday ( & pu - > start , NULL ) ;
pu - > parkingnum = x ;
2003-02-02 19:37:23 +00:00
if ( timeout > 0 )
pu - > parkingtime = timeout ;
else
pu - > parkingtime = parkingtime ;
if ( extout )
* extout = x ;
2004-12-09 22:39:14 +00:00
if ( peer )
{
strncpy ( pu - > peername , peer - > name , sizeof ( pu - > peername ) - 1 ) ;
}
2001-12-27 11:07:33 +00:00
/* Remember what had been dialed, so that if the parking
expires , we try to come back to the same place */
2004-09-17 14:15:11 +00:00
if ( ! ast_strlen_zero ( chan - > macrocontext ) )
2003-08-07 05:35:45 +00:00
strncpy ( pu - > context , chan - > macrocontext , sizeof ( pu - > context ) - 1 ) ;
else
strncpy ( pu - > context , chan - > context , sizeof ( pu - > context ) - 1 ) ;
2004-09-17 14:15:11 +00:00
if ( ! ast_strlen_zero ( chan - > macroexten ) )
2003-08-07 05:35:45 +00:00
strncpy ( pu - > exten , chan - > macroexten , sizeof ( pu - > exten ) - 1 ) ;
else
strncpy ( pu - > exten , chan - > exten , sizeof ( pu - > exten ) - 1 ) ;
if ( chan - > macropriority )
pu - > priority = chan - > macropriority ;
else
pu - > priority = chan - > priority ;
2001-12-27 11:07:33 +00:00
pu - > next = parkinglot ;
parkinglot = pu ;
2004-08-03 06:31:20 +00:00
/* If parking a channel directly, don't quiet yet get parking running on it */
2004-12-09 22:39:14 +00:00
if ( peer = = chan )
2004-08-03 06:31:20 +00:00
pu - > notquiteyet = 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
/* Wake up the (presumably select()ing) thread */
pthread_kill ( parking_thread , SIGURG ) ;
if ( option_verbose > 1 )
2004-09-01 19:51:19 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Parked %s on %d. Will timeout back to %s,%s,%d in %d seconds \n " , pu - > chan - > name , pu - > parkingnum , pu - > context , pu - > exten , pu - > priority , ( pu - > parkingtime / 1000 ) ) ;
2004-01-30 05:49:44 +00:00
manager_event ( EVENT_FLAG_CALL , " ParkedCall " ,
" Exten: %d \r \n "
" Channel: %s \r \n "
" 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 \r \n "
2004-01-30 05:49:44 +00:00
, pu - > parkingnum , pu - > chan - > name , peer - > name
2004-01-30 06:31:25 +00:00
, ( long ) pu - > start . tv_sec + ( long ) ( pu - > parkingtime / 1000 ) - ( long ) time ( NULL )
2004-10-02 00:58:31 +00:00
, ( pu - > chan - > cid . cid_num ? pu - > chan - > cid . cid_num : " " )
, ( pu - > chan - > cid . cid_name ? pu - > chan - > cid . cid_name : " " )
2004-01-30 05:49:44 +00:00
) ;
2004-08-03 06:31:20 +00:00
if ( peer ) {
2004-09-15 19:49:33 +00:00
if ( adsipark & & adsi_available ( peer ) ) {
adsi_announce_park ( peer , pu - > parkingnum ) ;
}
if ( adsipark & & adsi_available ( peer ) ) {
adsi_unload_session ( peer ) ;
}
2004-08-03 06:31:20 +00:00
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 ) ;
}
}
con = ast_context_find ( parking_con ) ;
if ( ! con ) {
con = ast_context_create ( NULL , parking_con , registrar ) ;
if ( ! con ) {
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 ) ;
2004-10-03 04:19:59 +00:00
ast_add_extension2 ( con , 1 , exten , 1 , NULL , NULL , parkedcall , strdup ( exten ) , free , registrar ) ;
2004-08-03 06:31:20 +00:00
}
2004-12-09 22:39:14 +00:00
if ( peer ) ast_say_digits ( peer , pu - > parkingnum , " " , peer - > language ) ;
2001-12-27 11:07:33 +00:00
return 0 ;
} else {
ast_log ( LOG_WARNING , " No more parking spaces \n " ) ;
free ( pu ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & parking_lock ) ;
2001-12-27 11:07:33 +00:00
return - 1 ;
}
} else {
ast_log ( LOG_WARNING , " Out of memory \n " ) ;
return - 1 ;
}
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 ;
/* Make a new, fake channel that we'll use to masquerade in the real one */
2002-05-30 01:34:15 +00:00
chan = ast_channel_alloc ( 0 ) ;
2001-12-27 11:07:33 +00:00
if ( chan ) {
/* Let us keep track of the channel name */
snprintf ( chan - > name , sizeof ( chan - > name ) , " Parked/%s " , rchan - > name ) ;
/* Make formats okay */
chan - > readformat = rchan - > readformat ;
chan - > writeformat = rchan - > writeformat ;
ast_channel_masquerade ( chan , rchan ) ;
/* Setup the extensions and such */
strncpy ( chan - > context , rchan - > context , sizeof ( chan - > context ) - 1 ) ;
strncpy ( chan - > exten , rchan - > exten , sizeof ( chan - > exten ) - 1 ) ;
chan - > priority = rchan - > priority ;
/* 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)
# define FEATURE_MAX_LEN 11
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 )
{
char * args ;
if ( option_verbose > 3 )
ast_verbose ( VERBOSE_PREFIX_3 " User hit '%s' to record call. \n " , code ) ;
if ( monitor_ok ) {
if ( ! monitor_app ) {
2005-01-05 22:38:07 +00:00
if ( ! ( monitor_app = pbx_findapp ( " Monitor " ) ) ) {
2005-01-05 19:56:47 +00:00
monitor_ok = 0 ;
2005-01-05 22:38:07 +00:00
return - 1 ;
}
2005-01-05 19:56:47 +00:00
}
/* Copy to local variable just in case one of the channels goes away */
args = pbx_builtin_getvar_helper ( chan , " TOUCH_MONITOR " ) ;
if ( ! args )
args = pbx_builtin_getvar_helper ( peer , " TOUCH_MONITOR " ) ;
if ( ! args )
args = " WAV||m " ;
pbx_exec ( peer , monitor_app , args , 1 ) ;
2005-01-05 22:38:07 +00:00
if ( ! ast_strlen_zero ( courtesytone ) ) {
if ( ast_autoservice_start ( peer ) )
return - 1 ;
if ( ! ast_streamfile ( chan , courtesytone , chan - > language ) ) {
if ( ast_waitstream ( chan , " " ) < 0 ) {
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
ast_autoservice_stop ( peer ) ;
return - 1 ;
}
}
if ( ast_autoservice_stop ( peer ) )
return - 1 ;
}
2005-01-05 19:56:47 +00:00
return FEATURE_RETURN_SUCCESS ;
}
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 ;
}
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 ;
char * transferer_real_context ;
char newext [ 256 ] , * ptr ;
int res ;
int len ;
if ( sense = = FEATURE_SENSE_PEER ) {
transferer = peer ;
transferee = chan ;
} else {
transferer = chan ;
transferee = peer ;
}
if ( ! ( transferer_real_context = pbx_builtin_getvar_helper ( transferee , " TRANSFER_CONTEXT " ) ) & &
! ( transferer_real_context = pbx_builtin_getvar_helper ( transferer , " TRANSFER_CONTEXT " ) ) ) {
/* Use the non-macro context to transfer the call */
if ( ! ast_strlen_zero ( transferer - > macrocontext ) )
transferer_real_context = transferer - > macrocontext ;
else
transferer_real_context = transferer - > context ;
}
/* Start autoservice on chan while we talk
to the originator */
ast_autoservice_start ( transferee ) ;
ast_moh_start ( transferee , NULL ) ;
memset ( newext , 0 , sizeof ( newext ) ) ;
ptr = newext ;
/* Transfer */
if ( ( res = ast_streamfile ( transferer , " pbx-transfer " , transferer - > language ) ) ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return res ;
}
if ( ( res = ast_waitstream ( transferer , AST_DIGIT_ANY ) ) < 0 ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return res ;
}
ast_stopstream ( transferer ) ;
if ( res > 0 ) {
/* If they've typed a digit already, handle it */
newext [ 0 ] = res ;
ptr + + ;
len - - ;
}
2005-01-05 19:56:47 +00:00
res = ast_app_dtget ( transferer , transferer_real_context , newext , sizeof ( newext ) , 100 , transferdigittimeout ) ;
2005-01-04 04:01:40 +00:00
if ( res < 0 ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return res ;
}
if ( ! strcmp ( newext , ast_parking_ext ( ) ) ) {
ast_moh_stop ( transferee ) ;
if ( ast_autoservice_stop ( transferee ) )
res = - 1 ;
else if ( ! ast_park_call ( transferee , transferer , 0 , NULL ) ) {
/* 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 ! */
if ( transferer = = peer )
res = AST_PBX_KEEPALIVE ;
else
res = AST_PBX_NO_HANGUP_PEER ;
return res ;
} else {
ast_log ( LOG_WARNING , " Unable to park call %s \n " , transferee - > name ) ;
}
/* XXX Maybe we should have another message here instead of invalid extension XXX */
} else if ( ast_exists_extension ( transferee , transferer_real_context , newext , 1 , transferer - > cid . cid_num ) ) {
pbx_builtin_setvar_helper ( peer , " BLINDTRANSFER " , chan - > name ) ;
pbx_builtin_setvar_helper ( chan , " BLINDTRANSFER " , peer - > name ) ;
ast_moh_stop ( transferee ) ;
res = ast_autoservice_stop ( transferee ) ;
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 "
, transferee - > name , newext , transferer_real_context ) ;
if ( ast_async_goto ( transferee , transferer_real_context , newext , 1 ) )
ast_log ( LOG_WARNING , " Async goto failed :-( \n " ) ;
res = - 1 ;
} else {
/* Set the channel's new extension, since it exists, using transferer context */
strncpy ( transferee - > exten , newext , sizeof ( transferee - > exten ) - 1 ) ;
strncpy ( transferee - > context , transferer_real_context , sizeof ( transferee - > context ) - 1 ) ;
transferee - > priority = 0 ;
}
return res ;
} else {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Unable to find extension '%s' in context '%s' \n " , newext , transferer_real_context ) ;
}
2005-01-05 21:31:40 +00:00
if ( ! ast_strlen_zero ( xferfailsound ) )
res = ast_streamfile ( transferer , xferfailsound , transferee - > language ) ;
else
res = 0 ;
2005-01-04 04:01:40 +00:00
if ( res ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return res ;
}
res = ast_waitstream ( transferer , AST_DIGIT_ANY ) ;
ast_stopstream ( transferer ) ;
ast_moh_stop ( transferee ) ;
res = ast_autoservice_stop ( transferee ) ;
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 ;
}
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 ;
struct ast_channel * newchan , * xferchan = NULL ;
int outstate = 0 ;
struct ast_bridge_config bconfig ;
char * transferer_real_context ;
char xferto [ 256 ] , dialstr [ 265 ] ;
char * cid_num ;
char * cid_name ;
int res ;
struct ast_frame * f = NULL ;
struct ast_bridge_thread_obj * tobj ;
ast_log ( LOG_DEBUG , " Executing Attended Transfer %s, %s (sense=%d) XXX \n " , chan - > name , peer - > name , sense ) ;
if ( sense = = FEATURE_SENSE_PEER ) {
transferer = peer ;
transferee = chan ;
} else {
transferer = chan ;
transferee = peer ;
}
if ( ! ( transferer_real_context = pbx_builtin_getvar_helper ( transferee , " TRANSFER_CONTEXT " ) ) & &
! ( transferer_real_context = pbx_builtin_getvar_helper ( transferer , " TRANSFER_CONTEXT " ) ) ) {
/* Use the non-macro context to transfer the call */
if ( ! ast_strlen_zero ( transferer - > macrocontext ) )
transferer_real_context = transferer - > macrocontext ;
else
transferer_real_context = transferer - > context ;
}
/* Start autoservice on chan while we talk
to the originator */
ast_autoservice_start ( transferee ) ;
ast_moh_start ( transferee , NULL ) ;
/* Transfer */
if ( ( res = ast_streamfile ( transferer , " pbx-transfer " , transferer - > language ) ) ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return res ;
}
if ( ( res = ast_waitstream ( transferer , AST_DIGIT_ANY ) ) < 0 ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return res ;
}
if ( ( ast_app_dtget ( transferer , transferer_real_context , xferto , sizeof ( xferto ) , 100 , transferdigittimeout ) ) ) {
cid_num = transferer - > cid . cid_num ;
cid_name = transferer - > cid . cid_name ;
if ( ast_exists_extension ( transferer , transferer_real_context , xferto , 1 , cid_num ) ) {
snprintf ( dialstr , sizeof ( dialstr ) , " %s@%s/n " , xferto , transferer_real_context ) ;
if ( ( newchan = ast_request_and_dial ( " Local " , ast_best_codec ( transferer - > nativeformats ) , dialstr , 30000 , & outstate , cid_num , cid_name ) ) ) {
res = ast_channel_make_compatible ( transferer , newchan ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Had to drop call because I couldn't make %s compatible with %s \n " , transferer - > name , newchan - > name ) ;
ast_hangup ( newchan ) ;
return - 1 ;
}
memset ( & bconfig , 0 , sizeof ( struct ast_bridge_config ) ) ;
bconfig . features_caller | = AST_FEATURE_DISCONNECT ;
bconfig . features_callee | = AST_FEATURE_DISCONNECT ;
res = ast_bridge_call ( transferer , newchan , & bconfig ) ;
if ( newchan - > _softhangup | | newchan - > _state ! = AST_STATE_UP ) {
ast_hangup ( newchan ) ;
if ( f ) {
ast_frfree ( f ) ;
f = NULL ;
}
2005-01-05 21:00:20 +00:00
if ( ! ast_strlen_zero ( xfersound ) & & ! ast_streamfile ( transferer , xfersound , transferer - > language ) ) {
2005-01-05 19:56:47 +00:00
if ( ast_waitstream ( transferer , " " ) < 0 ) {
2005-01-05 21:31:40 +00:00
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
2005-01-05 19:56:47 +00:00
}
}
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
transferer - > _softhangup = 0 ;
return FEATURE_RETURN_SUCCESS ;
}
res = ast_channel_make_compatible ( transferee , newchan ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Had to drop call because I couldn't make %s compatible with %s \n " , transferee - > name , newchan - > name ) ;
ast_hangup ( 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 ) ;
res = - 1 ;
return - 1 ;
}
if ( ( xferchan = ast_channel_alloc ( 0 ) ) ) {
snprintf ( xferchan - > name , sizeof ( 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 ;
xferchan - > flags = 0 ;
xferchan - > _softhangup = 0 ;
if ( ( f = ast_read ( xferchan ) ) ) {
ast_frfree ( f ) ;
f = NULL ;
}
} else {
ast_hangup ( newchan ) ;
return - 1 ;
}
newchan - > _state = AST_STATE_UP ;
newchan - > flags = 0 ;
newchan - > _softhangup = 0 ;
tobj = malloc ( sizeof ( struct ast_bridge_thread_obj ) ) ;
if ( tobj ) {
memset ( tobj , 0 , sizeof ( struct ast_bridge_thread_obj ) ) ;
tobj - > chan = xferchan ;
tobj - > peer = newchan ;
tobj - > bconfig = * config ;
2005-01-05 21:00:20 +00:00
if ( ! ast_strlen_zero ( xfersound ) & & ! ast_streamfile ( newchan , xfersound , newchan - > language ) ) {
2005-01-05 19:56:47 +00:00
if ( ast_waitstream ( newchan , " " ) < 0 ) {
2005-01-05 21:31:40 +00:00
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
2005-01-05 19:56:47 +00:00
}
}
ast_bridge_call_thread_launch ( tobj ) ;
} else {
ast_log ( LOG_WARNING , " Out of memory! \n " ) ;
ast_hangup ( xferchan ) ;
ast_hangup ( newchan ) ;
}
return - 1 ;
} else {
ast_log ( LOG_WARNING , " Unable to create channel Local/%s do you have chan_local? \n " , dialstr ) ;
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-05 21:31:40 +00:00
if ( ! ast_strlen_zero ( xferfailsound ) ) {
2005-01-05 21:00:20 +00:00
res = ast_streamfile ( transferer , xferfailsound , transferer - > language ) ;
if ( ! res & & ( ast_waitstream ( transferer , " " ) < 0 ) ) {
return - 1 ;
}
2005-01-05 19:56:47 +00:00
}
return - 1 ;
}
} else {
ast_log ( LOG_WARNING , " Extension %s does not exist in context %s \n " , xferto , transferer_real_context ) ;
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-05 21:31:40 +00:00
res = ast_streamfile ( transferer , " beeperr " , transferer - > language ) ;
if ( ! res & & ( ast_waitstream ( transferer , " " ) < 0 ) ) {
return - 1 ;
2005-01-05 19:56:47 +00:00
}
}
} else {
ast_log ( LOG_WARNING , " Did not read data. \n " ) ;
2005-01-05 21:31:40 +00:00
res = ast_streamfile ( transferer , " beeperr " , transferer - > language ) ;
if ( ast_waitstream ( transferer , " " ) < 0 ) {
return - 1 ;
2005-01-05 19:56:47 +00:00
}
}
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
return FEATURE_RETURN_SUCCESS ;
}
2005-01-04 04:01:40 +00:00
struct ast_call_feature {
int feature_mask ;
char * fname ;
char * sname ;
char exten [ FEATURE_MAX_LEN ] ;
char default_exten [ FEATURE_MAX_LEN ] ;
int ( * operation ) ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense ) ;
unsigned int flags ;
} ;
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]))
struct ast_call_feature builtin_features [ ] =
{
{ 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 } ,
} ;
static void unmap_features ( void )
{
int x ;
for ( x = 0 ; x < FEATURES_COUNT ; x + + )
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 ;
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
if ( ! strcasecmp ( name , builtin_features [ x ] . sname ) ) {
strncpy ( builtin_features [ x ] . exten , value , sizeof ( builtin_features [ x ] . exten ) - 1 ) ;
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 ;
unsigned int features ;
int res = FEATURE_RETURN_PASSDIGITS ;
if ( sense = = FEATURE_SENSE_CHAN )
features = config - > features_caller ;
else
features = config - > features_callee ;
ast_log ( LOG_DEBUG , " Feature interpret: chan=%s, peer=%s, sense=%d, features=%d \n " , chan - > name , peer - > name , sense , features ) ;
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
if ( ( features & builtin_features [ x ] . feature_mask ) & &
! 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 )
res = FEATURE_RETURN_STOREDIGITS ;
}
}
}
return res ;
}
static void set_config_flags ( struct ast_bridge_config * config )
{
int x ;
config - > flags = 0 ;
for ( x = 0 ; x < FEATURES_COUNT ; x + + ) {
if ( config - > features_caller & builtin_features [ x ] . feature_mask ) {
if ( builtin_features [ x ] . flags & AST_FEATURE_FLAG_NEEDSDTMF )
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 ) ;
}
if ( config - > features_callee & builtin_features [ x ] . feature_mask ) {
if ( builtin_features [ x ] . flags & AST_FEATURE_FLAG_NEEDSDTMF )
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 ) ;
}
}
}
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 ;
2004-08-06 13:54:07 +00:00
struct timeval start , end ;
2005-01-04 04:01:40 +00:00
struct ast_bridge_config backup_config ;
2004-07-28 19:25:14 +00:00
int allowdisconnect_in , allowdisconnect_out , allowredirect_in , allowredirect_out ;
2004-09-17 03:49:57 +00:00
char * monitor_exec ;
2005-01-04 04:01:40 +00:00
memset ( & backup_config , 0 , sizeof ( backup_config ) ) ;
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 ) {
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 " ) ) )
2004-09-17 03:49:57 +00:00
pbx_exec ( chan , monitor_app , monitor_exec , 1 ) ;
2004-09-17 14:15:11 +00:00
else if ( ( monitor_exec = pbx_builtin_getvar_helper ( peer , " AUTO_MONITOR " ) ) )
2004-09-17 03:49:57 +00:00
pbx_exec ( peer , monitor_app , monitor_exec , 1 ) ;
}
2005-01-04 04:01:40 +00:00
allowdisconnect_in = ( config - > features_callee & AST_FEATURE_DISCONNECT ) ;
allowdisconnect_out = ( config - > features_caller & AST_FEATURE_DISCONNECT ) ;
allowredirect_in = ( config - > features_callee & AST_FEATURE_REDIRECT ) ;
allowredirect_out = ( config - > features_caller & AST_FEATURE_REDIRECT ) ;
set_config_flags ( 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 " ;
peer - > data = chan - > name ;
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 ) ) {
2004-02-03 16:57:00 +00:00
snprintf ( tmp , sizeof ( tmp ) , " %s;%s " , chan - > cdr - > userfield , peer - > cdr - > userfield ) ;
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 ( ; ; ) {
2004-08-06 13:54:07 +00:00
if ( config - > timelimit )
gettimeofday ( & start , NULL ) ;
res = ast_channel_bridge ( chan , peer , config , & f , & who ) ;
if ( config - > timelimit ) {
/* Update time limit for next pass */
gettimeofday ( & end , NULL ) ;
diff = ( end . tv_sec - start . tv_sec ) * 1000 ;
diff + = ( end . tv_usec - start . tv_usec ) / 1000 ;
config - > timelimit - = 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 ! */
if ( backup_config . timelimit & & ( ( backup_config . timelimit - = diff ) < = 0 ) ) {
ast_log ( LOG_DEBUG , " Timed out, realtime this time! \n " ) ;
config - > timelimit = 0 ;
who = chan ;
if ( f )
ast_frfree ( f ) ;
f = NULL ;
res = 0 ;
} else if ( config - > timelimit < = 0 ) {
/* Not *really* out of time, just out of time for
digits to come in for features . */
ast_log ( LOG_DEBUG , " Timed out for feature! \n " ) ;
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 {
if ( config - > timelimit < = 0 ) {
/* We ran out of time */
config - > timelimit = 0 ;
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 ;
}
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 ;
}
if ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_RINGING ) ) {
if ( who = = chan )
ast_indicate ( peer , AST_CONTROL_RINGING ) ;
else
ast_indicate ( chan , AST_CONTROL_RINGING ) ;
}
2003-02-02 19:37:23 +00:00
if ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = - 1 ) ) {
if ( who = = chan )
ast_indicate ( peer , - 1 ) ;
else
ast_indicate ( chan , - 1 ) ;
}
2004-11-03 22:37:55 +00:00
if ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_FLASH ) ) {
if ( who = = chan )
ast_indicate ( peer , AST_CONTROL_FLASH ) ;
else
ast_indicate ( chan , AST_CONTROL_FLASH ) ;
}
2001-12-27 11:07:33 +00:00
if ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_OPTION ) ) {
aoh = f - > data ;
/* Forward option Requests */
if ( aoh & & ( aoh - > flag = = AST_OPTION_FLAG_REQUEST ) ) {
if ( who = = chan )
ast_channel_setoption ( peer , ntohs ( aoh - > option ) , aoh - > data , f - > datalen - sizeof ( struct ast_option_header ) , 0 ) ;
else
ast_channel_setoption ( chan , ntohs ( aoh - > option ) , aoh - > data , f - > datalen - sizeof ( struct ast_option_header ) , 0 ) ;
}
}
2004-07-28 19:25:14 +00:00
/* check for '*', if we find it it's time to disconnect */
2005-01-04 04:01:40 +00:00
if ( f & & ( f - > frametype = = AST_FRAME_DTMF ) ) {
char * featurecode ;
int sense ;
struct ast_channel * other ;
hadfeatures = hasfeatures ;
/* This cannot overrun because the longest feature is one shorter than our buffer */
if ( who = = chan ) {
other = peer ;
sense = FEATURE_SENSE_CHAN ;
featurecode = chan_featurecode ;
} else {
other = chan ;
sense = FEATURE_SENSE_PEER ;
featurecode = peer_featurecode ;
}
featurecode [ strlen ( featurecode ) ] = f - > subclass ;
2005-01-05 22:13:37 +00:00
config - > timelimit = backup_config . timelimit ;
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 ;
} else {
2005-01-04 04:01:40 +00:00
ast_frfree ( f ) ;
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 ;
config - > features_caller & = ~ ( AST_FEATURE_PLAY_WARNING ) ;
config - > features_callee & = ~ ( AST_FEATURE_PLAY_WARNING ) ;
config - > warning_freq = 0 ;
config - > warning_sound = NULL ;
config - > end_sound = NULL ;
config - > start_sound = NULL ;
config - > firstpass = 0 ;
}
config - > timelimit = featuredigittimeout ;
ast_log ( LOG_DEBUG , " Set time limit to %ld \n " , config - > timelimit ) ;
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 ;
}
static void * do_parking_thread ( void * ignore )
{
int ms , tms , max ;
struct parkeduser * pu , * pl , * pt = NULL ;
struct timeval tv ;
struct ast_frame * f ;
2004-08-03 06:31:20 +00:00
char exten [ AST_MAX_EXTENSION ] ;
2004-12-09 22:39:14 +00:00
char * peername , * cp ;
2004-08-03 06:31:20 +00:00
struct ast_context * con ;
2001-12-27 11:07:33 +00:00
int x ;
2004-08-03 14:09:48 +00:00
int gc = 0 ;
2001-12-27 11:07:33 +00:00
fd_set rfds , efds ;
fd_set nrfds , nefds ;
FD_ZERO ( & rfds ) ;
FD_ZERO ( & efds ) ;
2004-12-09 22:39:14 +00:00
2001-12-27 11:07:33 +00:00
for ( ; ; ) {
ms = - 1 ;
max = - 1 ;
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 ;
FD_ZERO ( & nrfds ) ;
FD_ZERO ( & nefds ) ;
while ( pu ) {
2004-08-03 06:31:20 +00:00
if ( pu - > notquiteyet ) {
/* Pretend this one isn't here yet */
pl = pu ;
pu = pu - > next ;
continue ;
}
2004-08-03 14:09:48 +00:00
if ( gc < 5 & & ! pu - > chan - > generator ) {
gc + + ;
ast_moh_start ( pu - > chan , NULL ) ;
}
2004-12-09 22:39:14 +00:00
gettimeofday ( & tv , NULL ) ;
2001-12-27 11:07:33 +00:00
tms = ( tv . tv_sec - pu - > start . tv_sec ) * 1000 + ( tv . tv_usec - pu - > start . tv_usec ) / 1000 ;
2003-02-02 19:37:23 +00:00
if ( tms > pu - > parkingtime ) {
2002-05-30 01:34:15 +00:00
/* Stop music on hold */
ast_moh_stop ( pu - > chan ) ;
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 ] ) {
peername = ast_strdupa ( pu - > peername ) ;
cp = strrchr ( peername , ' - ' ) ;
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 ) {
ast_add_extension2 ( con , 1 , peername , 1 , NULL , NULL , " Dial " , strdup ( peername ) , free , registrar ) ;
}
2004-12-28 23:49:46 +00:00
strncpy ( pu - > chan - > exten , peername , sizeof ( pu - > chan - > exten ) - 1 ) ;
strncpy ( pu - > chan - > context , parking_con_dial , sizeof ( pu - > chan - > context ) - 1 ) ;
2004-12-09 22:39:14 +00:00
pu - > chan - > priority = 1 ;
} 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 */
strncpy ( pu - > chan - > exten , pu - > exten , sizeof ( pu - > chan - > exten ) - 1 ) ;
strncpy ( pu - > chan - > context , pu - > context , sizeof ( pu - > chan - > context ) - 1 ) ;
pu - > chan - > priority = pu - > priority ;
}
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Timeout for %s parked on %d. Returning to %s,%s,%d \n " , pu - > chan - > name , pu - > parkingnum , pu - > chan - > context , pu - > chan - > exten , pu - > chan - > priority ) ;
2001-12-27 11:07:33 +00:00
/* Start up the PBX, or hang them up */
if ( ast_pbx_start ( pu - > chan ) ) {
ast_log ( LOG_WARNING , " Unable to restart the PBX for user on '%s', hanging them up... \n " , pu - > chan - > name ) ;
ast_hangup ( pu - > chan ) ;
}
/* 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 ) {
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 ) ;
} else {
2004-12-28 23:49:46 +00:00
for ( x = 0 ; x < AST_MAX_FDS ; x + + ) {
2001-12-27 11:07:33 +00:00
if ( ( pu - > chan - > fds [ x ] > - 1 ) & & ( FD_ISSET ( pu - > chan - > fds [ x ] , & rfds ) | | FD_ISSET ( pu - > chan - > fds [ x ] , & efds ) ) ) {
2003-07-02 14:06:12 +00:00
if ( FD_ISSET ( pu - > chan - > fds [ x ] , & efds ) )
2004-12-07 20:38:43 +00:00
ast_set_flag ( pu - > chan , AST_FLAG_EXCEPTION ) ;
else
ast_clear_flag ( pu - > chan , AST_FLAG_EXCEPTION ) ;
2003-07-02 14:06:12 +00:00
pu - > chan - > fdno = x ;
/* See if they need servicing */
f = ast_read ( pu - > chan ) ;
if ( ! f | | ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_HANGUP ) ) ) {
2001-12-27 11:07:33 +00:00
/* There's a problem, hang them up*/
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " %s got tired of being parked \n " , pu - > chan - > name ) ;
ast_hangup ( pu - > chan ) ;
/* 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 ) {
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 ) ;
break ;
} else {
/* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
ast_frfree ( f ) ;
goto std ; /* XXX Ick: jumping into an else statement??? XXX */
}
}
}
if ( x > = AST_MAX_FDS ) {
2004-12-28 23:49:46 +00:00
std : for ( x = 0 ; x < AST_MAX_FDS ; x + + ) {
2001-12-27 11:07:33 +00:00
/* Keep this one for next one */
if ( pu - > chan - > fds [ x ] > - 1 ) {
FD_SET ( pu - > chan - > fds [ x ] , & nrfds ) ;
FD_SET ( pu - > chan - > fds [ x ] , & nefds ) ;
if ( pu - > chan - > fds [ x ] > max )
max = pu - > chan - > fds [ x ] ;
}
}
/* Keep track of our longest wait */
if ( ( tms < ms ) | | ( ms < 0 ) )
ms = tms ;
pl = pu ;
pu = pu - > next ;
}
}
}
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 ;
tv . tv_sec = ms / 1000 ;
tv . tv_usec = ( ms % 1000 ) * 1000 ;
/* Wait for something to happen */
2003-04-27 18:13:11 +00:00
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
char exten [ AST_MAX_EXTENSION ] ;
struct ast_context * con ;
2001-12-27 11:07:33 +00:00
int park ;
2003-02-02 19:37:23 +00:00
int dres ;
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 ) {
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 " ) ;
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 */
if ( chan - > _state ! = AST_STATE_UP ) {
ast_answer ( chan ) ;
}
2001-12-27 11:07:33 +00:00
if ( peer ) {
2004-08-31 13:32:11 +00:00
/* Play a courtesy beep in the calling channel to prefix the bridge connecting */
if ( ! ast_strlen_zero ( courtesytone ) ) {
if ( ! ast_streamfile ( chan , courtesytone , chan - > language ) ) {
if ( ast_waitstream ( chan , " " ) < 0 ) {
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
ast_hangup ( peer ) ;
return - 1 ;
}
}
}
2002-05-30 01:34:15 +00:00
ast_moh_stop ( peer ) ;
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
memset ( & config , 0 , sizeof ( struct ast_bridge_config ) ) ;
2005-01-04 04:01:40 +00:00
config . features_callee | = AST_FEATURE_REDIRECT ;
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 ;
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 {
/* XXX Play a message XXX */
2004-12-28 23:49:46 +00:00
dres = ast_streamfile ( chan , " pbx-invalidpark " , chan - > language ) ;
if ( ! dres )
dres = ast_waitstream ( chan , " " ) ;
else {
ast_log ( LOG_WARNING , " ast_streamfile of %s failed on %s \n " , " pbx-invalidpark " , chan - > name ) ;
dres = 0 ;
}
2001-12-27 11:07:33 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Channel %s tried to talk to non-existant parked call %d \n " , chan - > name , park ) ;
res = - 1 ;
}
LOCAL_USER_REMOVE ( u ) ;
return res ;
}
2003-07-02 14:06:12 +00:00
static int handle_parkedcalls ( int fd , int argc , char * argv [ ] )
{
struct parkeduser * cur ;
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
2004-12-28 23:49:46 +00:00
cur = parkinglot ;
2003-07-02 14:06:12 +00:00
while ( cur ) {
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 ) ) ;
cur = cur - > next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & parking_lock ) ;
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 } ;
2004-01-30 05:49:44 +00:00
/* Dump lot status */
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 ] = " " ;
if ( id & & ! ast_strlen_zero ( id ) )
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 ) ;
cur = parkinglot ;
while ( cur ) {
2004-09-10 15:11:38 +00:00
ast_mutex_lock ( & s - > lock ) ;
2004-01-30 05:49:44 +00:00
ast_cli ( s - > fd , " Event: ParkedCall \r \n "
" Exten: %d \r \n "
" Channel: %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 "
2004-01-30 05:49:44 +00:00
" \r \n "
, cur - > parkingnum , cur - > chan - > name
2004-01-30 06:31:25 +00:00
, ( long ) cur - > start . tv_sec + ( long ) ( cur - > parkingtime / 1000 ) - ( long ) time ( NULL )
2004-10-02 00:58:31 +00:00
, ( cur - > chan - > cid . cid_num ? cur - > chan - > cid . cid_num : " " )
, ( cur - > chan - > cid . cid_name ? cur - > chan - > cid . cid_name : " " )
2004-07-14 07:53:57 +00:00
, idText ) ;
2004-09-10 15:11:38 +00:00
ast_mutex_unlock ( & s - > lock ) ;
2004-01-30 05:49:44 +00:00
2004-09-10 15:11:38 +00:00
cur = cur - > next ;
2004-01-30 05:49:44 +00:00
}
2004-07-14 07:53:57 +00:00
ast_cli ( s - > fd ,
" Event: ParkedCallsComplete \r \n "
" %s "
" \r \n " , idText ) ;
2004-01-30 05:49:44 +00:00
ast_mutex_unlock ( & parking_lock ) ;
return RESULT_SUCCESS ;
}
2003-07-02 14:06:12 +00:00
2001-12-27 11:07:33 +00:00
int load_module ( void )
{
int res ;
2002-05-30 01:34:15 +00:00
int start , end ;
2001-12-27 11:07:33 +00:00
struct ast_context * con ;
2002-05-30 01:34:15 +00:00
struct ast_config * cfg ;
struct ast_variable * var ;
2003-07-02 14:06:12 +00:00
2005-01-04 04:01:40 +00:00
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
2003-07-02 14:06:12 +00:00
ast_cli_register ( & showparked ) ;
2004-07-17 20:58:01 +00:00
cfg = ast_load ( " features.conf " ) ;
if ( ! cfg ) {
cfg = ast_load ( " parking.conf " ) ;
if ( cfg )
ast_log ( LOG_NOTICE , " parking.conf is deprecated in favor of 'features.conf'. Please rename it. \n " ) ;
}
2002-05-30 01:34:15 +00:00
if ( cfg ) {
var = ast_variable_browse ( cfg , " general " ) ;
while ( var ) {
if ( ! strcasecmp ( var - > name , " parkext " ) ) {
strncpy ( parking_ext , var - > value , sizeof ( parking_ext ) - 1 ) ;
} else if ( ! strcasecmp ( var - > name , " context " ) ) {
strncpy ( parking_con , var - > value , sizeof ( parking_con ) - 1 ) ;
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 " ) ) {
if ( sscanf ( var - > value , " %i-%i " , & start , & end ) ! = 2 ) {
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 ;
}
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 " ) ) {
if ( ( sscanf ( var - > value , " %d " , & featuredigittimeout ) ! = 1 ) | | ( transferdigittimeout < 1 ) ) {
ast_log ( LOG_WARNING , " %s is not a valid featuredigittimeout \n " , var - > value ) ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
}
2004-09-17 14:15:11 +00:00
} else if ( ! strcasecmp ( var - > name , " courtesytone " ) ) {
2005-01-05 21:31:40 +00:00
strncpy ( courtesytone , var - > value , sizeof ( courtesytone ) - 1 ) ;
2005-01-05 21:00:20 +00:00
} else if ( ! strcasecmp ( var - > name , " xfersound " ) ) {
2005-01-05 21:31:40 +00:00
strncpy ( xfersound , var - > value , sizeof ( xfersound ) - 1 ) ;
2005-01-05 21:00:20 +00:00
} else if ( ! strcasecmp ( var - > name , " xferfailsound " ) ) {
2005-01-05 21:31:40 +00:00
strncpy ( xferfailsound , var - > value , sizeof ( xferfailsound ) - 1 ) ;
2004-09-26 20:47:01 +00:00
} else if ( ! strcasecmp ( var - > name , " pickupexten " ) ) {
2005-01-05 21:31:40 +00:00
strncpy ( pickup_ext , var - > value , sizeof ( pickup_ext ) - 1 ) ;
2002-05-30 01:34:15 +00:00
}
var = var - > next ;
}
2005-01-04 04:01:40 +00:00
unmap_features ( ) ;
var = ast_variable_browse ( cfg , " featuremap " ) ;
while ( var ) {
if ( remap_feature ( var - > name , var - > value ) )
ast_log ( LOG_NOTICE , " Unknown feature '%s' \n " , var - > name ) ;
var = var - > next ;
}
2002-05-30 01:34:15 +00:00
ast_destroy ( cfg ) ;
}
2001-12-27 11:07:33 +00:00
con = ast_context_find ( parking_con ) ;
if ( ! con ) {
2003-07-14 15:33:21 +00:00
con = ast_context_create ( NULL , parking_con , registrar ) ;
2001-12-27 11:07:33 +00:00
if ( ! con ) {
ast_log ( LOG_ERROR , " Parking context '%s' does not exist and unable to create \n " , parking_con ) ;
return - 1 ;
}
}
2004-10-03 04:19:59 +00:00
ast_add_extension2 ( con , 1 , ast_parking_ext ( ) , 1 , NULL , NULL , parkcall , strdup ( " " ) , free , registrar ) ;
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 ) {
ast_manager_register ( " ParkedCalls " , 0 , manager_parking_status , " List parked calls " ) ;
}
2001-12-27 11:07:33 +00:00
return res ;
}
2003-04-09 04:00:43 +00:00
int ast_pickup_call ( struct ast_channel * chan )
{
struct ast_channel * cur ;
int res = - 1 ;
2004-05-20 16:30:10 +00:00
cur = ast_channel_walk_locked ( NULL ) ;
2003-04-09 04:00:43 +00:00
while ( cur ) {
if ( ! cur - > pbx & &
( cur ! = chan ) & &
( chan - > pickupgroup & cur - > callgroup ) & &
( ( cur - > _state = = AST_STATE_RINGING ) | |
( cur - > _state = = AST_STATE_RING ) ) ) {
break ;
}
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & cur - > lock ) ;
cur = ast_channel_walk_locked ( cur ) ;
2003-04-09 04:00:43 +00:00
}
if ( cur ) {
2004-12-28 23:49:46 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Call pickup on chan '%s' by '%s' \n " , cur - > name , chan - > name ) ;
2003-04-09 04:00:43 +00:00
res = ast_answer ( chan ) ;
if ( res )
ast_log ( LOG_WARNING , " Unable to answer '%s' \n " , chan - > name ) ;
2004-04-06 22:17:32 +00:00
res = ast_queue_control ( chan , AST_CONTROL_ANSWER ) ;
2003-04-09 04:00:43 +00:00
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 */
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & cur - > lock ) ;
2003-04-09 04:00:43 +00:00
} else {
2004-12-28 23:49:46 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " No call pickup possible... \n " ) ;
2003-04-09 04:00:43 +00:00
}
return res ;
}
2001-12-27 11:07:33 +00:00
int unload_module ( void )
{
STANDARD_HANGUP_LOCALUSERS ;
2003-07-02 14:06:12 +00:00
2004-01-30 05:49:44 +00:00
ast_manager_unregister ( " ParkedCalls " ) ;
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 ) ;
}
char * description ( void )
{
return " Call Parking Resource " ;
}
int usecount ( void )
{
/* Never allow parking to be unloaded because it will
unresolve needed symbols in the dialer */
#if 0
int res ;
STANDARD_USECOUNT ( res ) ;
return res ;
# else
return 1 ;
# endif
}
char * key ( )
{
return ASTERISK_GPL_KEY ;
}