diff --git a/main/bridge_basic.c b/main/bridge_basic.c index b5305a61fa..48b791d9df 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/dial.h" #include "asterisk/stasis_bridges.h" +#include "asterisk/stasis_channels.h" #include "asterisk/features.h" #include "asterisk/format_cache.h" #include "asterisk/test.h" @@ -1347,6 +1348,8 @@ struct attended_transfer_properties { struct ast_dial *dial; /*! The bridging features the transferer has available */ struct ast_flags transferer_features; + /*! Saved transferer connected line data for recalling the transferer. */ + struct ast_party_connected_line original_transferer_colp; }; static void attended_transfer_properties_destructor(void *obj) @@ -1361,6 +1364,7 @@ static void attended_transfer_properties_destructor(void *obj) ast_channel_cleanup(props->transferer); ast_channel_cleanup(props->transfer_target); ast_channel_cleanup(props->recall_target); + ast_party_connected_line_free(&props->original_transferer_colp); ast_string_field_free_memory(props); ast_cond_destroy(&props->cond); } @@ -1428,6 +1432,7 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc( xfer_cfg = ast_get_chan_features_xfer_config(props->transferer); if (!xfer_cfg) { ast_log(LOG_ERROR, "Unable to get transfer configuration from channel %s\n", ast_channel_name(props->transferer)); + ast_channel_unlock(props->transferer); ao2_ref(props, -1); return NULL; } @@ -1443,11 +1448,20 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc( ast_string_field_set(props, failsound, xfer_cfg->xferfailsound); ast_string_field_set(props, xfersound, xfer_cfg->xfersound); + /* + * Save the transferee's party information for any recall calls. + * This is the only piece of information needed that gets overwritten + * on the transferer channel by the inital call to the transfer target. + */ + ast_party_connected_line_copy(&props->original_transferer_colp, + ast_channel_connected(props->transferer)); + tech = ast_strdupa(ast_channel_name(props->transferer)); addr = strchr(tech, '/'); if (!addr) { ast_log(LOG_ERROR, "Transferer channel name does not follow typical channel naming format (tech/address)\n"); - ast_channel_unref(props->transferer); + ast_channel_unlock(props->transferer); + ao2_ref(props, -1); return NULL; } *addr++ = '\0'; @@ -2331,10 +2345,50 @@ static void recall_callback(struct ast_dial *dial) } } +/*! + * \internal + * \brief Setup common things to transferrer and transfer_target recall channels. + * + * \param recall Channel for recalling a party. + * \param transferer Channel supplying recall information. + * + * \details + * Setup callid, variables, datastores, accountcode, and peeraccount. + * + * \pre Both channels are locked on entry. + * + * \pre COLP and CLID on the recall channel are setup by the caller but not + * explicitly published yet. + * + * \return Nothing + */ +static void common_recall_channel_setup(struct ast_channel *recall, struct ast_channel *transferer) +{ + struct ast_callid *callid; + + callid = ast_read_threadstorage_callid(); + if (callid) { + ast_channel_callid_set(recall, callid); + ast_callid_unref(callid); + } + + ast_channel_inherit_variables(transferer, recall); + ast_channel_datastore_inherit(transferer, recall); + + /* + * Stage a snapshot to ensure that a snapshot is always done + * on the recall channel so earler COLP and CLID setup will + * get published. + */ + ast_channel_stage_snapshot(recall); + ast_channel_req_accountcodes(recall, transferer, AST_CHANNEL_REQUESTOR_REPLACEMENT); + ast_channel_stage_snapshot_done(recall); +} static int recalling_enter(struct attended_transfer_properties *props) { RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); + struct ast_channel *recall; if (!cap) { return -1; @@ -2360,7 +2414,26 @@ static int recalling_enter(struct attended_transfer_properties *props) return -1; } - ast_dial_set_state_callback(props->dial, &recall_callback); + /* + * Setup callid, variables, datastores, accountcode, peeraccount, + * COLP, and CLID on the recalled transferrer. + */ + recall = ast_dial_get_channel(props->dial, 0); + if (!recall) { + return -1; + } + ast_channel_lock_both(recall, props->transferer); + + ast_party_caller_copy(ast_channel_caller(recall), + ast_channel_caller(props->transferer)); + ast_party_connected_line_copy(ast_channel_connected(recall), + &props->original_transferer_colp); + + common_recall_channel_setup(recall, props->transferer); + ast_channel_unlock(recall); + ast_channel_unlock(props->transferer); + + ast_dial_set_state_callback(props->dial, recall_callback); ao2_ref(props, +1); ast_dial_set_user_data(props->dial, props); @@ -2487,6 +2560,20 @@ static int retransfer_enter(struct attended_transfer_properties *props) return -1; } + /* + * Setup callid, variables, datastores, accountcode, peeraccount, + * and COLP on the recalled transfer target. + */ + ast_channel_lock_both(props->recall_target, props->transferer); + + ast_party_connected_line_copy(ast_channel_connected(props->recall_target), + &props->original_transferer_colp); + ast_party_id_reset(&ast_channel_connected(props->recall_target)->priv); + + common_recall_channel_setup(props->recall_target, props->recall_target); + ast_channel_unlock(props->recall_target); + ast_channel_unlock(props->transferer); + if (ast_call(props->recall_target, destination, 0)) { ast_log(LOG_ERROR, "Unable to place outbound call to recall target\n"); ast_hangup(props->recall_target); @@ -2881,6 +2968,18 @@ static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfe static void *attended_transfer_monitor_thread(void *data) { struct attended_transfer_properties *props = data; + struct ast_callid *callid; + + /* + * Set thread callid to the transferer's callid because we + * are doing all this on that channel's behalf. + */ + ast_channel_lock(props->transferer); + callid = ast_channel_callid(props->transferer); + ast_channel_unlock(props->transferer); + if (callid) { + ast_callid_threadassoc_add(callid); + } for (;;) { enum attended_transfer_stimulus stimulus; @@ -2913,6 +3012,11 @@ static void *attended_transfer_monitor_thread(void *data) attended_transfer_properties_shutdown(props); + if (callid) { + ast_callid_unref(callid); + ast_callid_threadassoc_remove(); + } + return NULL; }