@ -63,6 +63,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
# define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
# define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
# define DEFAULT_ATXFER_DROP_CALL 0
# define DEFAULT_ATXFER_LOOP_DELAY 10000
# define DEFAULT_ATXFER_CALLBACK_RETRIES 2
# define AST_MAX_WATCHERS 256
@ -104,6 +107,9 @@ static int featuredigittimeout;
static int comebacktoorigin = 1 ;
static int atxfernoanswertimeout ;
static unsigned int atxferdropcall ;
static unsigned int atxferloopdelay ;
static unsigned int atxfercallbackretries ;
static char * registrar = " res_features " ; /*!< Registrar for operations */
@ -219,7 +225,7 @@ static void check_goto_on_transfer(struct ast_channel *chan)
}
}
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_nam e) ;
static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel * caller , struct ast_channel * transferee , const char * type , int format , void * data , int timeout , int * outstate , const char * cid_num , const char * cid_nam e, int igncallerstat e) ;
static void * ast_bridge_call_thread ( void * data )
@ -770,6 +776,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
struct ast_channel * transferee ;
const char * transferer_real_context ;
char xferto [ 256 ] = " " ;
char callbackto [ 256 ] = " " ;
int res ;
int outstate = 0 ;
struct ast_channel * newchan ;
@ -821,8 +828,11 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
l = strlen ( xferto ) ;
snprintf ( xferto + l , sizeof ( xferto ) - l , " @%s/n " , transferer_real_context ) ; /* append context */
newchan = ast_feature_request_and_dial ( transferer , " Local " , ast_best_codec ( transferer - > nativeformats ) ,
xferto , atxfernoanswertimeout , & outstate , transferer - > cid . cid_num , transferer - > cid . cid_name ) ;
newchan = ast_feature_request_and_dial ( transferer , transferee , " Local " , ast_best_codec ( transferer - > nativeformats ) ,
xferto , atxfernoanswertimeout , & outstate , transferer - > cid . cid_num , transferer - > cid . cid_name , 1 ) ;
if ( ! ast_check_hangup ( transferer ) ) {
/* Transferer is up - old behaviour */
ast_indicate ( transferer , - 1 ) ;
if ( ! newchan ) {
finishup ( transferee ) ;
@ -830,6 +840,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
if ( outstate ! = AST_CONTROL_UNHOLD & & outstate ! = AST_CONTROL_BUSY & &
ast_stream_and_wait ( transferer , xferfailsound , " " ) )
return - 1 ;
if ( ast_stream_and_wait ( transferer , xfersound , " " ) )
ast_log ( LOG_WARNING , " Failed to play transfer sound! \n " ) ;
return FEATURE_RETURN_SUCCESS ;
}
@ -847,10 +859,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
transferer - > _softhangup = 0 ;
return FEATURE_RETURN_SUCCESS ;
}
if ( check_compat ( transferee , newchan ) )
return - 1 ;
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
if ( ( ast_autoservice_stop ( transferee ) < 0 )
@ -861,7 +871,6 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_hangup ( newchan ) ;
return - 1 ;
}
xferchan = ast_channel_alloc ( 0 , AST_STATE_DOWN , 0 , 0 , " " , " " , " " , 0 , " Transfered/%s " , transferee - > name ) ;
if ( ! xferchan ) {
ast_hangup ( newchan ) ;
@ -875,16 +884,12 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
xferchan - > _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL ) ;
xferchan - > _softhangup = 0 ;
if ( ( f = ast_read ( xferchan ) ) )
ast_frfree ( f ) ;
newchan - > _state = AST_STATE_UP ;
ast_clear_flag ( newchan , AST_FLAGS_ALL ) ;
newchan - > _softhangup = 0 ;
tobj = ast_calloc ( 1 , sizeof ( struct ast_bridge_thread_obj ) ) ;
if ( ! tobj ) {
if ( ! ( tobj = ast_calloc ( 1 , sizeof ( * tobj ) ) ) ) {
ast_hangup ( xferchan ) ;
ast_hangup ( newchan ) ;
return - 1 ;
@ -897,11 +902,107 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_log ( LOG_WARNING , " Failed to play transfer sound! \n " ) ;
ast_bridge_call_thread_launch ( tobj ) ;
return - 1 ; /* XXX meaning the channel is bridged ? */
} else if ( ! ast_check_hangup ( transferee ) ) {
/* act as blind transfer */
if ( ast_autoservice_stop ( transferee ) < 0 ) {
ast_hangup ( newchan ) ;
return - 1 ;
}
if ( ! newchan ) {
unsigned int tries = 0 ;
/* newchan wasn't created - we should callback to transferer */
if ( ! ast_exists_extension ( transferer , transferer_real_context , transferer - > cid . cid_num , 1 , transferee - > cid . cid_num ) ) {
ast_log ( LOG_WARNING , " Extension %s does not exist in context %s - callback failed \n " , transferer - > cid . cid_num , transferer_real_context ) ;
if ( ast_stream_and_wait ( transferee , " beeperr " , " " ) )
return - 1 ;
return FEATURE_RETURN_SUCCESS ;
}
snprintf ( callbackto , sizeof ( callbackto ) , " %s@%s/n " , transferer - > cid . cid_num , transferer_real_context ) ; /* append context */
newchan = ast_feature_request_and_dial ( transferee , NULL , " Local " , ast_best_codec ( transferee - > nativeformats ) ,
callbackto , atxfernoanswertimeout , & outstate , transferee - > cid . cid_num , transferee - > cid . cid_name , 0 ) ;
while ( ! newchan & & ! atxferdropcall & & tries < atxfercallbackretries ) {
/* Trying to transfer again */
ast_autoservice_start ( transferee ) ;
ast_indicate ( transferee , AST_CONTROL_HOLD ) ;
newchan = ast_feature_request_and_dial ( transferer , transferee , " Local " , ast_best_codec ( transferer - > nativeformats ) ,
xferto , atxfernoanswertimeout , & outstate , transferer - > cid . cid_num , transferer - > cid . cid_name , 1 ) ;
if ( ast_autoservice_stop ( transferee ) < 0 ) {
ast_hangup ( newchan ) ;
return - 1 ;
}
if ( ! newchan ) {
/* Transfer failed, sleeping */
if ( option_debug )
ast_log ( LOG_DEBUG , " Sleeping for %d ms before callback. \n " , atxferloopdelay ) ;
ast_safe_sleep ( transferee , atxferloopdelay ) ;
if ( option_debug )
ast_log ( LOG_DEBUG , " Trying to callback... \n " ) ;
newchan = ast_feature_request_and_dial ( transferee , NULL , " Local " , ast_best_codec ( transferee - > nativeformats ) ,
callbackto , atxfernoanswertimeout , & outstate , transferee - > cid . cid_num , transferee - > cid . cid_name , 0 ) ;
}
tries + + ;
}
}
if ( ! newchan )
return - 1 ;
/* newchan is up, we should prepare transferee and bridge them */
if ( check_compat ( transferee , newchan ) )
return - 1 ;
ast_indicate ( transferee , AST_CONTROL_UNHOLD ) ;
if ( ( ast_waitfordigit ( transferee , 100 ) < 0 )
| | ( ast_waitfordigit ( newchan , 100 ) < 0 )
| | ast_check_hangup ( transferee )
| | ast_check_hangup ( newchan ) ) {
ast_hangup ( newchan ) ;
return - 1 ;
}
xferchan = ast_channel_alloc ( 0 , AST_STATE_DOWN , 0 , 0 , " " , " " , " " , 0 , " Transfered/%s " , transferee - > name ) ;
if ( ! xferchan ) {
ast_hangup ( newchan ) ;
return - 1 ;
}
/* Make formats okay */
xferchan - > readformat = transferee - > readformat ;
xferchan - > writeformat = transferee - > writeformat ;
ast_channel_masquerade ( xferchan , transferee ) ;
ast_explicit_goto ( xferchan , transferee - > context , transferee - > exten , transferee - > priority ) ;
xferchan - > _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL ) ;
xferchan - > _softhangup = 0 ;
if ( ( f = ast_read ( xferchan ) ) )
ast_frfree ( f ) ;
newchan - > _state = AST_STATE_UP ;
ast_clear_flag ( newchan , AST_FLAGS_ALL ) ;
newchan - > _softhangup = 0 ;
if ( ! ( tobj = ast_calloc ( 1 , sizeof ( * tobj ) ) ) ) {
ast_hangup ( xferchan ) ;
ast_hangup ( newchan ) ;
return - 1 ;
}
tobj - > chan = xferchan ;
tobj - > peer = newchan ;
tobj - > bconfig = * config ;
if ( ast_stream_and_wait ( newchan , xfersound , " " ) )
ast_log ( LOG_WARNING , " Failed to play transfer sound! \n " ) ;
ast_bridge_call_thread_launch ( tobj ) ;
return - 1 ; /* XXX meaning the channel is bridged ? */
} else {
/* Transferee hung up */
finishup ( transferee ) ;
return - 1 ;
}
}
/* add atxfer and automon as undefined so you can only use em if you configure them */
# define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
# define FEATURES_COUNT ARRAY_LEN(builtin_features )
struct ast_call_feature builtin_features [ ] =
{
@ -1152,7 +1253,7 @@ static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer,
}
/*! \todo XXX Check - this is very similar to the code in channel.c */
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_nam e)
static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel * caller , struct ast_channel * transferee , const char * type , int format , void * data , int timeout , int * outstate , const char * cid_num , const char * cid_nam e, int igncallerstat e)
{
int state = 0 ;
int cause = 0 ;
@ -1194,7 +1295,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
x = 0 ;
started = ast_tvnow ( ) ;
to = timeout ;
while ( ! ast_check_hangup ( caller ) & & timeout & & ( chan - > _state ! = AST_STATE_UP ) ) {
while ( ! ( ( transferee & & transferee - > _softhangup ) & & ( ! igncallerstate & & ast_check_hangup ( caller ) ) ) & & timeout & & ( chan - > _state ! = AST_STATE_UP ) ) {
struct ast_frame * f = NULL ;
monitor_chans [ 0 ] = caller ;
@ -1249,6 +1350,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
} else if ( caller & & ( active_channel = = caller ) ) {
f = ast_read ( caller ) ;
if ( f = = NULL ) { /*doh! where'd he go?*/
if ( ! igncallerstate ) {
if ( caller - > _softhangup & & ! chan - > _softhangup ) {
/* make this a blind transfer */
ready = 1 ;
@ -1258,6 +1360,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
res = 0 ;
break ;
}
} else {
if ( f - > frametype = = AST_FRAME_DTMF ) {
dialed_code [ x + + ] = f - > subclass ;
@ -1277,6 +1380,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
}
}
}
}
if ( f )
ast_frfree ( f ) ;
} /* end while */
@ -2345,6 +2449,9 @@ static int load_config(void)
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER ;
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY ;
atxferdropcall = DEFAULT_ATXFER_DROP_CALL ;
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES ;
cfg = ast_config_load ( " features.conf " ) ;
if ( ! cfg ) {
@ -2406,6 +2513,19 @@ static int load_config(void)
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER ;
} else
atxfernoanswertimeout = atxfernoanswertimeout * 1000 ;
} else if ( ! strcasecmp ( var - > name , " atxferloopdelay " ) ) {
if ( ( sscanf ( var - > value , " %u " , & atxferloopdelay ) ! = 1 ) ) {
ast_log ( LOG_WARNING , " %s is not a valid atxferloopdelay \n " , var - > value ) ;
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY ;
} else
atxferloopdelay * = 1000 ;
} else if ( ! strcasecmp ( var - > name , " atxferdropcall " ) ) {
atxferdropcall = ast_true ( var - > value ) ;
} else if ( ! strcasecmp ( var - > name , " atxfercallbackretries " ) ) {
if ( ( sscanf ( var - > value , " %u " , & atxferloopdelay ) ! = 1 ) ) {
ast_log ( LOG_WARNING , " %s is not a valid atxfercallbackretries \n " , var - > value ) ;
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES ;
}
} else if ( ! strcasecmp ( var - > name , " courtesytone " ) ) {
ast_copy_string ( courtesytone , var - > value , sizeof ( courtesytone ) ) ;
} else if ( ! strcasecmp ( var - > name , " parkedplay " ) ) {