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
*
2005-01-21 07:06:25 +00:00
* Copyright ( C ) 1999 - 2005 , 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 .
*/
/*
*
* Routines implementing call parking
*
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"
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
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 " ;
/* 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 */
2005-10-13 23:58:33 +00:00
static char parking_con [ AST_MAX_EXTENSION ] ;
2001-12-27 11:07:33 +00:00
2004-12-09 22:39:14 +00:00
/* Context for dialback for parking (KLUDGE) */
2005-10-13 23:58:33 +00:00
static char parking_con_dial [ AST_MAX_EXTENSION ] ;
2004-12-09 22:39:14 +00:00
2001-12-27 11:07:33 +00:00
/* Extension you type to park the call */
2005-10-13 23:58:33 +00:00
static char parking_ext [ AST_MAX_EXTENSION ] ;
2001-12-27 11:07:33 +00:00
2005-10-13 23:58:33 +00:00
static char pickup_ext [ AST_MAX_EXTENSION ] ;
2003-04-09 04:00:43 +00:00
2005-01-05 21:31:40 +00:00
/* Default sounds */
2005-10-13 23:58:33 +00:00
static char courtesytone [ 256 ] ;
static char xfersound [ 256 ] ;
static char xferfailsound [ 256 ] ;
2005-01-05 21:31:40 +00:00
2001-12-27 11:07:33 +00:00
/* First available extension for parking */
2005-10-13 23:58:33 +00:00
static int parking_start ;
2001-12-27 11:07:33 +00:00
/* Last available extension for parking */
2005-10-13 23:58:33 +00:00
static int parking_stop ;
2001-12-27 11:07:33 +00:00
2005-10-13 23:58:33 +00:00
static int parking_offset ;
2005-04-27 03:58:40 +00:00
2005-10-13 23:58:33 +00:00
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
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 */
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 ;
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 ;
} ;
2005-04-22 02:55:14 +00:00
static void check_goto_on_transfer ( struct ast_channel * chan )
{
struct ast_channel * xferchan ;
char * goto_on_transfer ;
goto_on_transfer = pbx_builtin_getvar_helper ( chan , " GOTO_ON_BLINDXFR " ) ;
if ( goto_on_transfer & & ! ast_strlen_zero ( goto_on_transfer ) & & ( xferchan = ast_channel_alloc ( 0 ) ) ) {
char * x ;
struct ast_frame * f ;
for ( x = goto_on_transfer ; x & & * x ; x + + )
if ( * x = = ' ^ ' )
* x = ' | ' ;
strcpy ( 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-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 ;
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 } ;
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 ) ;
if ( res = = - 1 ) {
return res ;
}
return adsi_print ( chan , message , justify , 1 ) ;
}
2005-07-25 17:31:53 +00:00
/*--- ast_park_call: 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 ;
2005-07-25 17:31:53 +00:00
2001-12-27 11:07:33 +00:00
pu = malloc ( sizeof ( struct parkeduser ) ) ;
2005-07-25 17:31:53 +00:00
if ( ! pu ) {
ast_log ( LOG_WARNING , " Out of memory \n " ) ;
return - 1 ;
}
memset ( pu , 0 , sizeof ( struct parkeduser ) ) ;
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 ;
if ( timeout > 0 )
pu - > parkingtime = timeout ;
else
pu - > parkingtime = parkingtime ;
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 */
if ( ! ast_strlen_zero ( chan - > macrocontext ) )
ast_copy_string ( pu - > context , chan - > macrocontext , sizeof ( pu - > context ) ) ;
else
ast_copy_string ( pu - > context , chan - > context , sizeof ( pu - > context ) ) ;
if ( ! ast_strlen_zero ( chan - > macroexten ) )
ast_copy_string ( pu - > exten , chan - > macroexten , sizeof ( pu - > exten ) ) ;
else
ast_copy_string ( pu - > exten , chan - > exten , sizeof ( pu - > exten ) ) ;
if ( chan - > macropriority )
pu - > priority = chan - > macropriority ;
else
pu - > priority = chan - > priority ;
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 "
" CallerIDName: %s \r \n \r \n "
, pu - > parkingnum , pu - > chan - > name , peer - > name
, ( long ) pu - > start . tv_sec + ( long ) ( pu - > parkingtime / 1000 ) - ( long ) time ( NULL )
, ( pu - > chan - > cid . cid_num ? pu - > chan - > cid . cid_num : " <unknown> " )
, ( pu - > chan - > cid . cid_name ? pu - > chan - > cid . cid_name : " <unknown> " )
) ;
2003-07-02 14:06:12 +00:00
2005-07-25 17:31:53 +00:00
if ( peer ) {
if ( adsipark & & adsi_available ( peer ) ) {
adsi_announce_park ( peer , pu - > parkingnum ) ;
2001-12-27 11:07:33 +00:00
}
2005-07-25 17:31:53 +00:00
if ( adsipark & & adsi_available ( peer ) ) {
adsi_unload_session ( peer ) ;
}
}
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 ) ;
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 */
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 ) ;
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 */
2005-05-15 23:26:45 +00:00
ast_copy_string ( chan - > context , rchan - > context , sizeof ( chan - > context ) ) ;
ast_copy_string ( chan - > exten , rchan - > exten , sizeof ( chan - > exten ) ) ;
2001-12-27 11:07:33 +00:00
chan - > priority = 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
2005-01-04 04:01:40 +00:00
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-05-16 00:43:16 +00:00
char * touch_monitor = NULL , * caller_chan_id = NULL , * callee_chan_id = NULL , * args = NULL , * touch_format = NULL ;
2005-01-10 04:03:30 +00:00
int x = 0 ;
size_t len ;
2005-01-11 18:24:27 +00:00
struct ast_channel * caller_chan = NULL , * callee_chan = NULL ;
if ( sense = = 2 ) {
caller_chan = peer ;
callee_chan = chan ;
} else {
callee_chan = peer ;
caller_chan = chan ;
}
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
2005-01-10 04:03:30 +00:00
if ( ! monitor_app ) {
if ( ! ( monitor_app = pbx_findapp ( " Monitor " ) ) ) {
monitor_ok = 0 ;
ast_log ( LOG_ERROR , " Cannot record the call. The monitor application is disabled. \n " ) ;
return - 1 ;
}
}
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 ;
2005-01-11 18:24:27 +00:00
if ( ! ast_streamfile ( caller_chan , courtesytone , caller_chan - > language ) ) {
if ( ast_waitstream ( caller_chan , " " ) < 0 ) {
2005-01-10 04:03:30 +00:00
ast_log ( LOG_WARNING , " Failed to play courtesy tone! \n " ) ;
2005-01-11 18:24:27 +00:00
ast_autoservice_stop ( callee_chan ) ;
2005-01-05 22:38:07 +00:00
return - 1 ;
}
}
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-05-16 00:43:16 +00:00
touch_format = pbx_builtin_getvar_helper ( caller_chan , " TOUCH_MONITOR_FORMAT " ) ;
if ( ! touch_format )
touch_format = pbx_builtin_getvar_helper ( callee_chan , " TOUCH_MONITOR_FORMAT " ) ;
2005-01-11 18:24:27 +00:00
touch_monitor = pbx_builtin_getvar_helper ( caller_chan , " TOUCH_MONITOR " ) ;
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-01-10 04:03:30 +00:00
if ( touch_monitor ) {
len = strlen ( touch_monitor ) + 50 ;
args = alloca ( len ) ;
2005-06-03 12:50:05 +00:00
snprintf ( args , len , " %s|auto-%ld-%s|m " , ( touch_format ) ? touch_format : " wav " , time ( NULL ) , touch_monitor ) ;
2005-01-10 04:03:30 +00:00
} else {
2005-01-11 18:24:27 +00:00
caller_chan_id = ast_strdupa ( caller_chan - > cid . cid_num ? caller_chan - > cid . cid_num : caller_chan - > name ) ;
callee_chan_id = ast_strdupa ( callee_chan - > cid . cid_num ? callee_chan - > cid . cid_num : callee_chan - > name ) ;
len = strlen ( caller_chan_id ) + strlen ( callee_chan_id ) + 50 ;
2005-01-10 04:03:30 +00:00
args = alloca ( len ) ;
2005-06-03 12:50:05 +00:00
snprintf ( args , len , " %s|auto-%ld-%s-%s|m " , ( touch_format ) ? touch_format : " wav " , time ( NULL ) , caller_chan_id , callee_chan_id ) ;
2005-01-10 04:03:30 +00:00
}
for ( x = 0 ; x < strlen ( args ) ; x + + )
if ( args [ x ] = = ' / ' )
args [ x ] = ' - ' ;
if ( option_verbose > 3 )
ast_verbose ( VERBOSE_PREFIX_3 " User hit '%s' to record call. filename: %s \n " , code , args ) ;
2005-01-11 18:24:27 +00:00
pbx_exec ( callee_chan , monitor_app , args , 1 ) ;
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 ;
}
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 ;
2005-02-17 20:04:10 +00:00
char newext [ 256 ] ;
2005-01-04 04:01:40 +00:00
int res ;
if ( sense = = FEATURE_SENSE_PEER ) {
transferer = peer ;
transferee = chan ;
} else {
transferer = chan ;
transferee = peer ;
}
2005-07-25 17:31:53 +00:00
if ( ! ( transferer_real_context = pbx_builtin_getvar_helper ( transferee , " TRANSFER_CONTEXT " ) ) & &
! ( transferer_real_context = pbx_builtin_getvar_helper ( transferer , " TRANSFER_CONTEXT " ) ) ) {
2005-01-04 04:01:40 +00:00
/* 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 */
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 ) ;
memset ( newext , 0 , sizeof ( newext ) ) ;
2005-02-17 20:04:10 +00:00
2005-01-04 04:01:40 +00:00
/* Transfer */
if ( ( res = ast_streamfile ( transferer , " pbx-transfer " , transferer - > language ) ) ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-04 04:01:40 +00:00
return res ;
}
if ( ( res = ast_waitstream ( transferer , AST_DIGIT_ANY ) ) < 0 ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-04 04:01:40 +00:00
return res ;
2005-02-17 20:04:10 +00:00
} else if ( res > 0 ) {
2005-01-04 04:01:40 +00:00
/* If they've typed a digit already, handle it */
2005-02-17 20:04:10 +00:00
newext [ 0 ] = ( char ) res ;
2005-01-04 04:01:40 +00:00
}
2005-02-17 20:04:10 +00:00
ast_stopstream ( transferer ) ;
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 ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-04 04:01:40 +00:00
return res ;
}
if ( ! strcmp ( newext , ast_parking_ext ( ) ) ) {
ast_moh_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
res = ast_autoservice_stop ( transferee ) ;
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
if ( res )
2005-01-04 04:01:40 +00:00
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 ! */
2005-07-25 17:31:53 +00:00
if ( transferer = = peer )
res = AST_PBX_KEEPALIVE ;
2005-01-04 04:01:40 +00:00
else
2005-07-25 17:31:53 +00:00
res = AST_PBX_NO_HANGUP_PEER ;
2005-01-04 04:01:40 +00:00
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 ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
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 "
, 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 */
2005-05-15 23:26:45 +00:00
ast_copy_string ( transferee - > exten , newext , sizeof ( transferee - > exten ) ) ;
ast_copy_string ( transferee - > context , transferer_real_context , sizeof ( transferee - > context ) ) ;
2005-01-04 04:01:40 +00:00
transferee - > priority = 0 ;
}
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 )
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 ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-04 04:01:40 +00:00
return res ;
}
res = ast_waitstream ( transferer , AST_DIGIT_ANY ) ;
ast_stopstream ( transferer ) ;
ast_moh_stop ( transferee ) ;
res = ast_autoservice_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
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 ;
}
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 */
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 */
2005-07-25 17:31:53 +00:00
if ( ( res = ast_streamfile ( transferer , " pbx-transfer " , transferer - > language ) ) ) {
2005-01-05 19:56:47 +00:00
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-05 19:56:47 +00:00
return res ;
}
if ( ( res = ast_waitstream ( transferer , AST_DIGIT_ANY ) ) < 0 ) {
ast_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-05 19:56:47 +00:00
return res ;
2005-02-17 20:04:10 +00:00
} else if ( res > 0 ) {
/* If they've typed a digit already, handle it */
xferto [ 0 ] = ( char ) res ;
2005-01-05 19:56:47 +00:00
}
2005-01-10 04:03:30 +00:00
if ( ( ast_app_dtget ( transferer , transferer_real_context , xferto , sizeof ( xferto ) , 100 , transferdigittimeout ) ) ) {
2005-01-05 19:56:47 +00:00
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 ) ;
2005-06-23 22:12:01 +00:00
newchan = ast_feature_request_and_dial ( transferer , " Local " , ast_best_codec ( transferer - > nativeformats ) , dialstr , 15000 , & outstate , cid_num , cid_name ) ;
ast_indicate ( transferer , - 1 ) ;
2005-07-25 17:31:53 +00:00
if ( newchan ) {
2005-01-05 19:56:47 +00:00
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 ) ) ;
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( bconfig . features_caller ) , AST_FEATURE_DISCONNECT ) ;
ast_set_flag ( & ( bconfig . features_callee ) , AST_FEATURE_DISCONNECT ) ;
2005-01-05 19:56:47 +00:00
res = ast_bridge_call ( transferer , newchan , & bconfig ) ;
2005-06-23 22:12:01 +00:00
if ( newchan - > _softhangup | | newchan - > _state ! = AST_STATE_UP | | ! transferer - > _softhangup ) {
2005-01-05 19:56:47 +00:00
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 ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-05 19:56:47 +00:00
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 ) ;
2005-01-10 04:03:30 +00:00
if ( ( ast_autoservice_stop ( transferee ) < 0 )
2005-07-25 17:31:53 +00:00
| | ( ast_waitfordigit ( transferee , 100 ) < 0 )
| | ( ast_waitfordigit ( newchan , 100 ) < 0 )
2005-01-05 19:56:47 +00:00
| | 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 ;
2005-01-10 14:46:59 +00:00
ast_clear_flag ( xferchan , AST_FLAGS_ALL ) ;
2005-01-05 19:56:47 +00:00
xferchan - > _softhangup = 0 ;
2005-01-10 04:03:30 +00:00
if ( ( f = ast_read ( xferchan ) ) ) {
2005-01-05 19:56:47 +00:00
ast_frfree ( f ) ;
f = NULL ;
}
} else {
ast_hangup ( newchan ) ;
return - 1 ;
}
newchan - > _state = AST_STATE_UP ;
2005-01-10 14:46:59 +00:00
ast_clear_flag ( newchan , AST_FLAGS_ALL ) ;
2005-01-05 19:56:47 +00:00
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_moh_stop ( transferee ) ;
ast_autoservice_stop ( transferee ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-06-23 22:12:01 +00:00
/* any reason besides user requested cancel and busy triggers the failed sound */
if ( outstate ! = AST_CONTROL_UNHOLD & & outstate ! = AST_CONTROL_BUSY & & ! 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
}
2005-06-23 22:12:01 +00:00
return FEATURE_RETURN_SUCCESS ;
2005-01-05 19:56:47 +00:00
}
} 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-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
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 ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
2005-01-05 19:56:47 +00:00
return FEATURE_RETURN_SUCCESS ;
}
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]))
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 } ,
} ;
2005-08-23 02:22:33 +00:00
static AST_LIST_HEAD ( feature_list , ast_call_feature ) feature_list ;
/* register new feature into feature_list*/
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 ) ;
}
/* unregister feature from feature_list */
void ast_unregister_feature ( struct ast_call_feature * feature )
{
if ( ! feature ) return ;
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
/* find a feature by name */
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 ;
}
/* exec an app by feature */
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 ) {
if ( ! strcasecmp ( feature - > exten , code ) ) break ;
}
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 ) {
struct ast_channel * work = chan ;
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_CALLEE ) ) work = peer ;
res = pbx_exec ( work , app , feature - > app_args , 1 ) ;
if ( res < 0 ) return res ;
} else {
ast_log ( LOG_WARNING , " Could not find application (%s) \n " , feature - > app ) ;
res = - 2 ;
}
return FEATURE_RETURN_SUCCESS ;
}
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 ;
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 ) ;
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-09-07 21:36:30 +00:00
if ( dynamic_features & & ! ast_strlen_zero ( dynamic_features ) ) {
char * tmp = ast_strdupa ( dynamic_features ) ;
2005-08-23 02:22:33 +00:00
char * tok ;
2005-09-07 21:36:30 +00:00
if ( ! tmp )
2005-08-23 02:22:33 +00:00
return res ;
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 ) ;
res = feature - > operation ( chan , peer , config , code , sense ) ;
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 ) ) ) {
char * dynamic_features ;
dynamic_features = pbx_builtin_getvar_helper ( chan , " DYNAMIC_FEATURES " ) ;
if ( dynamic_features ) {
char * tmp = ast_strdupa ( dynamic_features ) ;
char * tok ;
struct ast_call_feature * feature ;
if ( ! tmp ) {
return ;
}
/* while we have a feature */
while ( NULL ! = ( tok = strsep ( & tmp , " # " ) ) ) {
if ( ( feature = find_feature ( tok ) ) ) {
if ( 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-01-04 04:01:40 +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 )
{
int state = 0 ;
int cause = 0 ;
int to ;
struct ast_channel * chan ;
struct ast_channel * monitor_chans [ 2 ] ;
struct ast_channel * active_channel ;
struct ast_frame * f = NULL ;
int res = 0 , ready = 0 ;
if ( ( chan = ast_request ( type , format , data , & cause ) ) ) {
ast_set_callerid ( chan , cid_num , cid_name , cid_num ) ;
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 ) ) {
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*/
}
if ( ! active_channel ) {
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 ;
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 ;
}
}
}
if ( f ) {
ast_frfree ( f ) ;
}
}
} 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 ) {
if ( ! chan - > cdr ) {
chan - > cdr = ast_cdr_alloc ( ) ;
}
if ( chan - > cdr ) {
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 ;
2005-07-25 17:31:53 +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 ) ) ;
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 ) {
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-10 14:46:59 +00:00
allowdisconnect_in = ast_test_flag ( & ( config - > features_callee ) , AST_FEATURE_DISCONNECT ) ;
allowdisconnect_out = ast_test_flag ( & ( config - > features_caller ) , AST_FEATURE_DISCONNECT ) ;
allowredirect_in = ast_test_flag ( & ( config - > features_callee ) , AST_FEATURE_REDIRECT ) ;
allowredirect_out = ast_test_flag ( & ( config - > features_caller ) , AST_FEATURE_REDIRECT ) ;
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 " ;
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 ( ; ; ) {
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 ) ) {
2005-01-04 04:01:40 +00:00
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 . */
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 {
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 ;
}
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 ;
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 ) {
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-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 ;
} 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 ;
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 ;
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 ;
}
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 ;
2005-03-24 05:37:59 +00:00
char returnexten [ AST_MAX_EXTENSION ] ;
2004-08-03 06:31:20 +00:00
struct ast_context * con ;
2001-12-27 11:07:33 +00:00
int x ;
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 ;
}
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 */
ast_moh_stop ( pu - > chan ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( pu - > 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 ] ) {
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 ) {
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
}
2005-05-15 23:26:45 +00:00
ast_copy_string ( pu - > chan - > exten , peername , sizeof ( pu - > chan - > exten ) ) ;
ast_copy_string ( pu - > chan - > context , parking_con_dial , sizeof ( pu - > chan - > context ) ) ;
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 */
2005-05-15 23:26:45 +00:00
ast_copy_string ( pu - > chan - > exten , pu - > exten , sizeof ( pu - > chan - > exten ) ) ;
ast_copy_string ( pu - > chan - > context , pu - > context , sizeof ( pu - > chan - > context ) ) ;
2004-12-09 22:39:14 +00:00
pu - > chan - > priority = pu - > priority ;
}
2005-02-26 07:54:28 +00:00
manager_event ( EVENT_FLAG_CALL , " ParkedCallTimeOut " ,
" Exten: %d \r \n "
" Channel: %s \r \n "
" CallerID: %s \r \n "
" CallerIDName: %s \r \n \r \n "
, pu - > parkingnum , pu - > chan - > name
, ( pu - > chan - > cid . cid_num ? pu - > chan - > cid . cid_num : " <unknown> " )
, ( pu - > chan - > cid . cid_name ? pu - > chan - > cid . cid_name : " <unknown> " )
) ;
2004-12-09 22:39:14 +00:00
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 {
2005-07-25 17:31:53 +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 ) ) ) {
2005-02-26 07:54:28 +00:00
manager_event ( EVENT_FLAG_CALL , " ParkedCallGiveUp " ,
" Exten: %d \r \n "
" Channel: %s \r \n "
" CallerID: %s \r \n "
" CallerIDName: %s \r \n \r \n "
, pu - > parkingnum , pu - > chan - > name
, ( pu - > chan - > cid . cid_num ? pu - > chan - > cid . cid_num : " <unknown> " )
, ( pu - > chan - > cid . cid_name ? pu - > chan - > cid . cid_name : " <unknown> " )
) ;
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 ) ;
2005-03-17 21:52:57 +00:00
if ( pu - > moh_trys < 3 & & ! pu - > chan - > generatordata ) {
ast_log ( LOG_DEBUG , " MOH on parked call stopped by outside source. Restarting. \n " ) ;
ast_moh_start ( pu - > chan , NULL ) ;
pu - > moh_trys + + ;
}
2001-12-27 11:07:33 +00:00
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 ;
2005-07-15 23:00:47 +00:00
tv = ast_samp2tv ( ms , 1000 ) ;
2001-12-27 11:07:33 +00:00
/* 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 " ) ;
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 "
" CallerIDName: %s \r \n \r \n "
, pu - > parkingnum , pu - > chan - > name , chan - > name
, ( pu - > chan - > cid . cid_num ? pu - > chan - > cid . cid_num : " <unknown> " )
, ( pu - > chan - > cid . cid_name ? pu - > chan - > cid . cid_name : " <unknown> " )
) ;
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 ) ;
2005-01-17 12:37:55 +00:00
ast_indicate ( peer , AST_CONTROL_UNHOLD ) ;
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 {
/* 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 , " " ) ;
2005-07-25 17:31:53 +00:00
else {
2004-12-28 23:49:46 +00:00
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 )
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
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 ;
2005-04-20 16:27:44 +00:00
numparked + + ;
2003-07-02 14:06:12 +00:00
}
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
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 } ;
2005-07-25 17:31:53 +00:00
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 ) {
2005-09-28 23:10:14 +00:00
ast_cli ( s - > fd , " Event: ParkedCall \r \n "
2004-01-30 05:49:44 +00:00
" 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-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
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 ;
}
ast_mutex_unlock ( & cur - > lock ) ;
}
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 */
ast_mutex_unlock ( & cur - > lock ) ;
} 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 ] ;
char old_parking_con [ AST_MAX_EXTENSION ] ;
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 ;
2005-01-04 04:01:40 +00:00
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
2005-01-25 06:10:20 +00:00
cfg = ast_config_load ( " features.conf " ) ;
2004-07-17 20:58:01 +00:00
if ( ! cfg ) {
2005-01-25 06:10:20 +00:00
cfg = ast_config_load ( " parking.conf " ) ;
2004-07-17 20:58:01 +00:00
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 " ) ) {
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 ;
}
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 ) ) ;
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
}
var = var - > next ;
}
2005-08-23 02:22:33 +00:00
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 ;
}
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 ( ) ;
2005-08-23 02:22:33 +00:00
var = ast_variable_browse ( cfg , " applicationmap " ) ;
while ( var ) {
char * tmp_val = 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 ) {
ast_log ( LOG_ERROR , " res_features: strdup failed " ) ;
continue ;
}
exten = strsep ( & tmp_val , " , " ) ;
if ( exten ) party = strsep ( & tmp_val , " , " ) ;
if ( party ) app = strsep ( & tmp_val , " , " ) ;
if ( app ) app_args = strsep ( & tmp_val , " , " ) ;
if ( ! ( app & & strlen ( app ) ) | | ! ( exten & & strlen ( exten ) ) | | ! ( party & & strlen ( party ) ) | | ! ( var - > name & & strlen ( 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 ) ;
var = var - > next ;
continue ;
}
{
struct ast_call_feature * feature = find_feature ( var - > name ) ;
int mallocd = 0 ;
if ( ! feature ) {
feature = malloc ( sizeof ( struct ast_call_feature ) ) ;
mallocd = 1 ;
}
if ( ! feature ) {
ast_log ( LOG_NOTICE , " Malloc failed at feature mapping \n " ) ;
free ( tmp_val ) ;
var = var - > next ;
continue ;
}
memset ( feature , 0 , sizeof ( struct ast_call_feature ) ) ;
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 ) ;
var = var - > next ;
continue ;
}
2005-08-23 02:22:33 +00:00
ast_register_feature ( feature ) ;
if ( option_verbose > = 1 ) ast_verbose ( VERBOSE_PREFIX_2 " Mapping Feature '%s' to app '%s' with code '%s' \n " , var - > name , app , exten ) ;
}
var = var - > next ;
}
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 ) ;
ast_log ( LOG_DEBUG , " Removed old parking extension %s@%s \n " , old_parking_ext , old_parking_con ) ;
}
2005-01-10 04:03:30 +00:00
if ( ! ( con = ast_context_find ( parking_con ) ) ) {
if ( ! ( con = ast_context_create ( NULL , parking_con , registrar ) ) ) {
2001-12-27 11:07:33 +00:00
ast_log ( LOG_ERROR , " Parking context '%s' does not exist and unable to create \n " , parking_con ) ;
return - 1 ;
}
}
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
}
int reload ( void ) {
return load_config ( ) ;
}
int load_module ( void )
{
int res ;
2005-08-23 02:22:33 +00:00
AST_LIST_HEAD_INIT ( & feature_list ) ;
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 " ) ;
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
2001-12-27 11:07:33 +00:00
int unload_module ( void )
{
STANDARD_HANGUP_LOCALUSERS ;
2003-07-02 14:06:12 +00:00
2005-07-25 17:31:53 +00:00
ast_manager_unregister ( " ParkedCalls " ) ;
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 ) ;
}
char * description ( void )
{
2005-07-25 17:31:53 +00:00
return " Call Features Resource " ;
2001-12-27 11:07:33 +00:00
}
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 ;
}