From 7f0dce3bd1f1b2d520ac8e4cd96903513a7b40ab Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Wed, 25 Apr 2012 01:26:44 +0000 Subject: [PATCH] Fix recalled party B feature flags for a failed DTMF atxfer. 1) B calls A with Dial option T 2) B DTMF atxfer to C 3) B hangs up 4) C does not answer 5) B is called back 6) B answers 7) B cannot initiate transfers anymore * Add dial features datastore to recalled party B channel that is a copy of the original party B channel's dial features datastore. * Extracted add_features_datastore() from add_features_datastores(). * Renamed struct ast_dial_features features_caller and features_callee members to my_features and peer_features respectively. These better names eliminate the need for some explanatory comments. * Simplified code accessing the struct ast_dial_features datastore. (closes issue ASTERISK-19383) Reported by: lgfsantos ........ Merged revisions 363428 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 363429 from http://svn.asterisk.org/svn/asterisk/branches/10 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@363430 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- main/features.c | 209 ++++++++++++++++++++++++++---------------------- 1 file changed, 112 insertions(+), 97 deletions(-) diff --git a/main/features.c b/main/features.c index 9a09058bd9..d75b7b9a77 100644 --- a/main/features.c +++ b/main/features.c @@ -681,9 +681,10 @@ static int stopmixmonitor_ok = 1; static pthread_t parking_thread; struct ast_dial_features { - struct ast_flags features_caller; - struct ast_flags features_callee; - int is_caller; + /*! Channel's feature flags. */ + struct ast_flags my_features; + /*! Bridge peer's feature flags. */ + struct ast_flags peer_features; }; #if defined(ATXFER_NULL_TECH) @@ -788,6 +789,51 @@ static const struct ast_datastore_info dial_features_info = { .duplicate = dial_features_duplicate, }; +/*! + * \internal + * \brief Set the features datastore if it doesn't exist. + * + * \param chan Channel to add features datastore + * \param my_features The channel's feature flags + * \param peer_features The channel's bridge peer feature flags + * + * \retval TRUE if features datastore already existed. + */ +static int add_features_datastore(struct ast_channel *chan, const struct ast_flags *my_features, const struct ast_flags *peer_features) +{ + struct ast_datastore *datastore; + struct ast_dial_features *dialfeatures; + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL); + ast_channel_unlock(chan); + if (datastore) { + /* Already exists. */ + return 1; + } + + /* Create a new datastore with specified feature flags. */ + datastore = ast_datastore_alloc(&dial_features_info, NULL); + if (!datastore) { + ast_log(LOG_WARNING, "Unable to create channel features datastore.\n"); + return 0; + } + dialfeatures = ast_calloc(1, sizeof(*dialfeatures)); + if (!dialfeatures) { + ast_log(LOG_WARNING, "Unable to allocate memory for feature flags.\n"); + ast_datastore_free(datastore); + return 0; + } + ast_copy_flags(&dialfeatures->my_features, my_features, AST_FLAGS_ALL); + ast_copy_flags(&dialfeatures->peer_features, peer_features, AST_FLAGS_ALL); + datastore->inheritance = DATASTORE_INHERIT_FOREVER; + datastore->data = dialfeatures; + ast_channel_lock(chan); + ast_channel_datastore_add(chan, datastore); + ast_channel_unlock(chan); + return 0; +} + /* Forward declarations */ static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot); static void parkinglot_unref(struct ast_parkinglot *parkinglot); @@ -2497,7 +2543,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st int l; struct ast_party_connected_line connected_line; struct ast_datastore *features_datastore; - struct ast_dial_features *dialfeatures = NULL; + struct ast_dial_features *dialfeatures; char *transferer_tech; char *transferer_name; char *transferer_name_orig; @@ -2547,7 +2593,13 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st return xfer_park_call_helper(transferee, transferer, park_exten); } - /* Append context to dialed transfer number. */ + /* + * Append context to dialed transfer number. + * + * NOTE: The local channel needs the /n flag so party C will use + * the feature flags set by the dialplan when calling that + * party. + */ snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* If we are performing an attended transfer and we have two channels involved then @@ -2700,7 +2752,31 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st atxfernoanswertimeout, &outstate, ast_channel_language(transferer)); ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n", !!newchan, outstate); - if (newchan || ast_check_hangup(transferee)) { + if (newchan) { + /* + * We have recalled party B (newchan). We need to give this + * call leg the same feature flags as the original party B call + * leg. + */ + ast_channel_lock(transferer); + features_datastore = ast_channel_datastore_find(transferer, + &dial_features_info, NULL); + if (features_datastore && (dialfeatures = features_datastore->data)) { + struct ast_flags my_features = { 0 }; + struct ast_flags peer_features = { 0 }; + + ast_copy_flags(&my_features, &dialfeatures->my_features, + AST_FLAGS_ALL); + ast_copy_flags(&peer_features, &dialfeatures->peer_features, + AST_FLAGS_ALL); + ast_channel_unlock(transferer); + add_features_datastore(newchan, &my_features, &peer_features); + } else { + ast_channel_unlock(transferer); + } + break; + } + if (ast_check_hangup(transferee)) { break; } @@ -2799,33 +2875,26 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st return -1; } - ast_channel_lock(newchan); - if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) { - dialfeatures = features_datastore->data; - } - ast_channel_unlock(newchan); - - if (dialfeatures) { - /* newchan should always be the callee and shows up as callee in dialfeatures, but for some reason - I don't currently understand, the abilities of newchan seem to be stored on the caller side */ - ast_copy_flags(&(config->features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL); - dialfeatures = NULL; - } - - ast_channel_lock(xferchan); - if ((features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL))) { - dialfeatures = features_datastore->data; - } - ast_channel_unlock(xferchan); - - if (dialfeatures) { - ast_copy_flags(&(config->features_caller), &(dialfeatures->features_caller), AST_FLAGS_ALL); - } - tobj->chan = newchan; tobj->peer = xferchan; tobj->bconfig = *config; + ast_channel_lock(newchan); + features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL); + if (features_datastore && (dialfeatures = features_datastore->data)) { + ast_copy_flags(&tobj->bconfig.features_callee, &dialfeatures->my_features, + AST_FLAGS_ALL); + } + ast_channel_unlock(newchan); + + ast_channel_lock(xferchan); + features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL); + if (features_datastore && (dialfeatures = features_datastore->data)) { + ast_copy_flags(&tobj->bconfig.features_caller, &dialfeatures->my_features, + AST_FLAGS_ALL); + } + ast_channel_unlock(xferchan); + if (tobj->bconfig.end_bridge_callback_data_fixup) { tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan); } @@ -3825,60 +3894,16 @@ static void set_bridge_features_on_config(struct ast_bridge_config *config, cons static void add_features_datastores(struct ast_channel *caller, struct ast_channel *callee, struct ast_bridge_config *config) { - struct ast_datastore *ds_callee_features = NULL, *ds_caller_features = NULL; - struct ast_dial_features *callee_features = NULL, *caller_features = NULL; - - ast_channel_lock(caller); - ds_caller_features = ast_channel_datastore_find(caller, &dial_features_info, NULL); - ast_channel_unlock(caller); - if (!ds_caller_features) { - if (!(ds_caller_features = ast_datastore_alloc(&dial_features_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for caller features. Aborting!\n"); - return; - } - if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) { - ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n"); - ast_datastore_free(ds_caller_features); - return; - } - ds_caller_features->inheritance = DATASTORE_INHERIT_FOREVER; - caller_features->is_caller = 1; - ast_copy_flags(&(caller_features->features_callee), &(config->features_callee), AST_FLAGS_ALL); - ast_copy_flags(&(caller_features->features_caller), &(config->features_caller), AST_FLAGS_ALL); - ds_caller_features->data = caller_features; - ast_channel_lock(caller); - ast_channel_datastore_add(caller, ds_caller_features); - ast_channel_unlock(caller); - } else { - /* If we don't return here, then when we do a builtin_atxfer we will copy the disconnect - * flags over from the atxfer to the caller */ + if (add_features_datastore(caller, &config->features_caller, &config->features_callee)) { + /* + * If we don't return here, then when we do a builtin_atxfer we + * will copy the disconnect flags over from the atxfer to the + * callee (Party C). + */ return; } - ast_channel_lock(callee); - ds_callee_features = ast_channel_datastore_find(callee, &dial_features_info, NULL); - ast_channel_unlock(callee); - if (!ds_callee_features) { - if (!(ds_callee_features = ast_datastore_alloc(&dial_features_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for callee features. Aborting!\n"); - return; - } - if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) { - ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n"); - ast_datastore_free(ds_callee_features); - return; - } - ds_callee_features->inheritance = DATASTORE_INHERIT_FOREVER; - callee_features->is_caller = 0; - ast_copy_flags(&(callee_features->features_callee), &(config->features_caller), AST_FLAGS_ALL); - ast_copy_flags(&(callee_features->features_caller), &(config->features_callee), AST_FLAGS_ALL); - ds_callee_features->data = callee_features; - ast_channel_lock(callee); - ast_channel_datastore_add(callee, ds_callee_features); - ast_channel_unlock(callee); - } - - return; + add_features_datastore(callee, &config->features_callee, &config->features_caller); } static void clear_dialed_interfaces(struct ast_channel *chan) @@ -4710,8 +4735,8 @@ static int manage_parked_call(struct parkeduser *pu, const struct pollfd *pfds, snprintf(returnexten, sizeof(returnexten), "%s,%u,%s", peername, pu->parkinglot->cfg.comebackdialtime, - callback_dialoptions(&(dialfeatures->features_callee), - &(dialfeatures->features_caller), buf, sizeof(buf))); + callback_dialoptions(&dialfeatures->peer_features, + &dialfeatures->my_features, buf, sizeof(buf))); } else { /* Existing default */ ast_log(LOG_NOTICE, "Dial features not found on %s, using default!\n", ast_channel_name(chan)); @@ -5261,7 +5286,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data) if (peer) { struct ast_datastore *features_datastore; - struct ast_dial_features *dialfeatures = NULL; + struct ast_dial_features *dialfeatures; /* Play a courtesy to the source(s) configured to prefix the bridge connecting */ if (!ast_strlen_zero(courtesytone)) { @@ -5305,20 +5330,10 @@ static int parked_call_exec(struct ast_channel *chan, const char *data) /* Get datastore for peer and apply it's features to the callee side of the bridge config */ ast_channel_lock(peer); - if ((features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL))) { - dialfeatures = features_datastore->data; - } - - /* - * When the datastores for both caller and callee are created, - * both the callee and caller channels use the features_caller - * flag variable to represent themselves. With that said, the - * config.features_callee flags should be copied from the - * datastore's caller feature flags regardless if peer was a - * callee or caller. - */ - if (dialfeatures) { - ast_copy_flags(&(config.features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL); + features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL); + if (features_datastore && (dialfeatures = features_datastore->data)) { + ast_copy_flags(&config.features_callee, &dialfeatures->my_features, + AST_FLAGS_ALL); } ast_channel_unlock(peer);