From e128c2a314b48bf41e775a447091ec3da970c237 Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Fri, 13 Jun 2008 16:29:07 +0000 Subject: [PATCH] This should fix the behavior of the 'T' dial feature being passed incorrectly to the transferee when builtin_atxfers are used. Also, doing a builtin_atxfer to parking was broken and is fixed here as well. (closes issue #11898) Reported by: sergee Tested by: otherwiseguy git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@122589 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_dial.c | 39 ++++++++++++++++++++------------ res/res_features.c | 56 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/apps/app_dial.c b/apps/app_dial.c index bf98a32167..533783cbd8 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -789,7 +789,7 @@ static int valid_priv_reply(struct ast_flags *opts, int res) return 0; } -static void set_dial_features(struct ast_flags *opts, struct ast_dial_features *features) +static void set_dial_features(struct ast_flags *opts, struct ast_dial_features *features, struct ast_channel *chan) { struct ast_flags perm_opts = {.flags = 0}; @@ -800,6 +800,22 @@ static void set_dial_features(struct ast_flags *opts, struct ast_dial_features * memset(features->options, 0, sizeof(features->options)); ast_app_options2str(dial_exec_options, &perm_opts, features->options, sizeof(features->options)); + if (ast_test_flag(&perm_opts, OPT_CALLEE_TRANSFER)) + ast_set_flag(&(features->features_callee), AST_FEATURE_REDIRECT); + if (ast_test_flag(&perm_opts, OPT_CALLER_TRANSFER)) + ast_set_flag(&(features->features_caller), AST_FEATURE_REDIRECT); + if (ast_test_flag(&perm_opts, OPT_CALLEE_HANGUP)) + ast_set_flag(&(features->features_callee), AST_FEATURE_DISCONNECT); + if (ast_test_flag(&perm_opts, OPT_CALLER_HANGUP)) + ast_set_flag(&(features->features_caller), AST_FEATURE_DISCONNECT); + if (ast_test_flag(&perm_opts, OPT_CALLEE_MONITOR)) + ast_set_flag(&(features->features_callee), AST_FEATURE_AUTOMON); + if (ast_test_flag(&perm_opts, OPT_CALLER_MONITOR)) + ast_set_flag(&(features->features_caller), AST_FEATURE_AUTOMON); + if (ast_test_flag(&perm_opts, OPT_CALLEE_PARK)) + ast_set_flag(&(features->features_callee), AST_FEATURE_PARKCALL); + if (ast_test_flag(&perm_opts, OPT_CALLER_PARK)) + ast_set_flag(&(features->features_caller), AST_FEATURE_PARKCALL); } static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec) @@ -1109,18 +1125,16 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags goto out; } - if (!(caller_features = ast_malloc(sizeof(*caller_features)))) { + if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) { ast_log(LOG_WARNING, "Unable to allocate memory for feature flags. Aborting!\n"); goto out; } + ast_channel_lock(chan); caller_features->is_caller = 1; - set_dial_features(&opts, caller_features); - - ds_caller_features->inheritance = DATASTORE_INHERIT_FOREVER; + set_dial_features(&opts, caller_features, chan); + ds_caller_features->inheritance = -1; ds_caller_features->data = caller_features; - - ast_channel_lock(chan); ast_channel_datastore_add(chan, ds_caller_features); ast_channel_unlock(chan); @@ -1291,23 +1305,20 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags goto out; } - if (!(callee_features = ast_malloc(sizeof(*callee_features)))) { + if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) { ast_log(LOG_WARNING, "Unable to allocate memory for feature flags. Aborting!\n"); ast_free(tmp); goto out; } + ast_channel_lock(tmp->chan); callee_features->is_caller = 0; - set_dial_features(&opts, callee_features); - - ds_callee_features->inheritance = DATASTORE_INHERIT_FOREVER; + set_dial_features(&opts, callee_features, tmp->chan); + ds_callee_features->inheritance = -1; ds_callee_features->data = callee_features; - - ast_channel_lock(tmp->chan); ast_channel_datastore_add(tmp->chan, ds_callee_features); ast_channel_unlock(tmp->chan); - /* Place the call, but don't wait on the answer */ res = ast_call(tmp->chan, numsubst, 0); diff --git a/res/res_features.c b/res/res_features.c index e275a8c33f..b33d7a92a9 100644 --- a/res/res_features.c +++ b/res/res_features.c @@ -391,8 +391,30 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in if (extout) *extout = x; - if (peer) - ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); + if (peer) { + /* This is so ugly that it hurts, but implementing get_base_channel() on local channels + could have ugly side effects. We could have transferer<->local,1<->local,2<->parking + and we need the callback name to be that of transferer. Since local,1/2 have the same + name we can be tricky and just grab the bridged channel from the other side of the local + */ + if (!strcasecmp(peer->tech->type, "Local")) { + struct ast_channel *tmpchan, *base_peer; + char other_side[AST_CHANNEL_NAME]; + char *c; + ast_copy_string(other_side, peer->name, sizeof(other_side)); + if ((c = strrchr(other_side, ','))) { + *++c = '1'; + } + if ((tmpchan = ast_get_channel_by_name_locked(other_side))) { + if ((base_peer = ast_bridged_channel(tmpchan))) { + ast_copy_string(pu->peername, base_peer->name, sizeof(pu->peername)); + } + ast_channel_unlock(tmpchan); + } + } else { + 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 */ @@ -792,6 +814,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st struct ast_bridge_config bconfig; struct ast_frame *f; int l; + struct ast_datastore *features_datastore; + struct ast_dial_features *dialfeatures = NULL; if (option_debug) ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense); @@ -834,9 +858,22 @@ 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 */ + snprintf(xferto + l, sizeof(xferto) - l, "@%s", 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, transferer->language); + + /* If we are the callee and we are being transferred, after the masquerade + * caller features will really be the original callee features */ + ast_channel_lock(transferee); + if ((features_datastore = ast_channel_datastore_find(transferee, &dial_features_info, NULL))) { + dialfeatures = features_datastore->data; + } + ast_channel_unlock(transferee); + + if (dialfeatures && !dialfeatures->is_caller) { + ast_copy_flags(&(config->features_caller), &(dialfeatures->features_callee), AST_FLAGS_ALL); + } + ast_indicate(transferer, -1); if (!newchan) { finishup(transferee); @@ -909,6 +946,19 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st ast_hangup(newchan); return -1; } + + /* For the case where the transfer target is being connected with the original + caller store the target's original features, and apply to the bridge */ + 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) { + ast_copy_flags(&(config->features_callee), &(dialfeatures->features_callee), AST_FLAGS_ALL); + } + tobj->chan = newchan; tobj->peer = xferchan; tobj->bconfig = *config;