From a33416f7358216499a7441eb2b6c8b55a91ea916 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Thu, 24 May 2012 22:29:23 +0000 Subject: [PATCH] Fix Dial I option ignored if dial forked and one fork redirects. The Dial and Queue I option is intended to block connected line updates and redirecting updates. However, it is a feature that when a call is locally redirected, the I option is disabled if the redirected call runs as a local channel so the administrator can have an opportunity to setup new connected line information. Unfortunately, the Dial and Queue I option is disabled for *all* forked calls if one of those calls is redirected. * Make the Dial and Queue I option apply to each outgoing call leg independently. Now if one outgoing call leg is locally redirected, the other outgoing calls are not affected. * Made Dial not pass any redirecting updates when forking calls. Redirecting updates do not make sense for this scenario. * Made Queue not pass any redirecting updates when using the ringall strategy. Redirecting updates do not make sense for this scenario. * Fixed deadlock potential with chan_local when Dial and Queue send redirecting updates for a local redirect. * Converted the Queue stillgoing flag to a boolean bitfield. (closes issue ASTERISK-19511) Reported by: rmudgett Tested by: rmudgett Review: https://reviewboard.asterisk.org/r/1920/ ........ Merged revisions 367678 from http://svn.asterisk.org/svn/asterisk/branches/1.8 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10@367679 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_dial.c | 171 +++++++++++++++++++++++++++++++++-------------- apps/app_queue.c | 153 +++++++++++++++++++++++++++++------------- 2 files changed, 226 insertions(+), 98 deletions(-) diff --git a/apps/app_dial.c b/apps/app_dial.c index 63bcbab544..e09a3c2ff3 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -823,7 +823,8 @@ static void senddialendevent(struct ast_channel *src, const char *dialstatus) * \param o Outgoing call channel list. * \param num Incoming call channel cause accumulation * \param peerflags Dial option flags - * \param single_caller_bored From wait_for_answer: single && !caller_entertained + * \param single TRUE if there is only one outgoing call. + * \param caller_entertained TRUE if the caller is being entertained by MOH or ringback. * \param to Remaining call timeout time. * \param forced_clid OPT_FORCECLID caller id to send * \param stored_clid Caller id representing the called party if needed @@ -833,8 +834,8 @@ static void senddialendevent(struct ast_channel *src, const char *dialstatus) * * \todo eventually this function should be intergrated into and replaced by ast_call_forward() */ -static void do_forward(struct chanlist *o, - struct cause_args *num, struct ast_flags64 *peerflags, int single_caller_bored, int *to, +static void do_forward(struct chanlist *o, struct cause_args *num, + struct ast_flags64 *peerflags, int single, int caller_entertained, int *to, struct ast_party_id *forced_clid, struct ast_party_id *stored_clid) { char tmpchan[256]; @@ -862,6 +863,14 @@ static void do_forward(struct chanlist *o, stuff = tmpchan; tech = "Local"; } + if (!strcasecmp(tech, "Local")) { + /* + * Drop the connected line update block for local channels since + * this is going to run dialplan and the user can change his + * mind about what connected line information he wants to send. + */ + ast_clear_flag64(o, OPT_IGNORE_CONNECTEDLINE); + } ast_cel_report_event(in, AST_CEL_FORWARD, NULL, c->call_forward, NULL); @@ -876,11 +885,14 @@ static void do_forward(struct chanlist *o, /* Setup parameters */ c = o->chan = ast_request(tech, in->nativeformats, in, stuff, &cause); if (c) { - if (single_caller_bored) { + if (single && !caller_entertained) { ast_channel_make_compatible(o->chan, in); } + ast_channel_lock_both(in, o->chan); ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); + ast_channel_unlock(in); + ast_channel_unlock(o->chan); /* When a call is forwarded, we don't want to track new interfaces * dialed for CC purposes. Setting the done flag will ensure that * any Dial operations that happen later won't record CC interfaces. @@ -897,17 +909,17 @@ static void do_forward(struct chanlist *o, handle_cause(cause, num); ast_hangup(original); } else { - struct ast_party_redirecting redirecting; + ast_channel_lock_both(c, original); + ast_party_redirecting_copy(&c->redirecting, &original->redirecting); + ast_channel_unlock(c); + ast_channel_unlock(original); + + ast_channel_lock_both(c, in); - if (single_caller_bored && CAN_EARLY_BRIDGE(peerflags, c, in)) { + if (single && !caller_entertained && CAN_EARLY_BRIDGE(peerflags, c, in)) { ast_rtp_instance_early_bridge_make_compatible(c, in); } - ast_channel_set_redirecting(c, &original->redirecting, NULL); - ast_channel_lock(c); - while (ast_channel_trylock(in)) { - CHANNEL_DEADLOCK_AVOIDANCE(c); - } if (!c->redirecting.from.number.valid || ast_strlen_zero(c->redirecting.from.number.str)) { /* @@ -928,6 +940,7 @@ static void do_forward(struct chanlist *o, if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { caller.id = *stored_clid; ast_channel_set_caller_event(c, &caller, NULL); + ast_set_flag64(o, DIAL_CALLERID_ABSENT); } else if (ast_strlen_zero(S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL))) { /* @@ -936,6 +949,9 @@ static void do_forward(struct chanlist *o, */ caller.id = *stored_clid; ast_channel_set_caller_event(c, &caller, NULL); + ast_set_flag64(o, DIAL_CALLERID_ABSENT); + } else { + ast_clear_flag64(o, DIAL_CALLERID_ABSENT); } /* Determine CallerID for outgoing channel to send. */ @@ -953,22 +969,32 @@ static void do_forward(struct chanlist *o, c->appl = "AppDial"; c->data = "(Outgoing Line)"; - /* - * We must unlock c before calling ast_channel_redirecting_macro, because - * we put c into autoservice there. That is pretty much a guaranteed - * deadlock. This is why the handling of c's lock may seem a bit unusual - * here. - */ - ast_party_redirecting_init(&redirecting); - ast_party_redirecting_copy(&redirecting, &c->redirecting); - ast_channel_unlock(c); - if (ast_channel_redirecting_macro(c, in, &redirecting, 1, 0)) { - ast_channel_update_redirecting(in, &redirecting, NULL); - } - ast_party_redirecting_free(&redirecting); + ast_channel_unlock(in); + if (single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) { + struct ast_party_redirecting redirecting; + + /* + * Redirecting updates to the caller make sense only on single + * calls. + * + * We must unlock c before calling + * ast_channel_redirecting_macro, because we put c into + * autoservice there. That is pretty much a guaranteed + * deadlock. This is why the handling of c's lock may seem a + * bit unusual here. + */ + ast_party_redirecting_init(&redirecting); + ast_party_redirecting_copy(&redirecting, &c->redirecting); + ast_channel_unlock(c); + if (ast_channel_redirecting_macro(c, in, &redirecting, 1, 0)) { + ast_channel_update_redirecting(in, &redirecting, NULL); + } + ast_party_redirecting_free(&redirecting); + } else { + ast_channel_unlock(c); + } - ast_clear_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE); if (ast_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) { *to = -1; } @@ -982,17 +1008,14 @@ static void do_forward(struct chanlist *o, c = o->chan = NULL; num->nochan++; } else { - ast_channel_lock(c); - while (ast_channel_trylock(in)) { - CHANNEL_DEADLOCK_AVOIDANCE(c); - } + ast_channel_lock_both(c, in); senddialevent(in, c, stuff); ast_channel_unlock(in); ast_channel_unlock(c); /* Hangup the original channel now, in case we needed it */ ast_hangup(original); } - if (single_caller_bored) { + if (single && !caller_entertained) { ast_indicate(in, -1); } } @@ -1052,7 +1075,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } } - if (!ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE) && !ast_test_flag64(outgoing, DIAL_CALLERID_ABSENT)) { + if (!ast_test_flag64(outgoing, OPT_IGNORE_CONNECTEDLINE) + && !ast_test_flag64(outgoing, DIAL_CALLERID_ABSENT)) { ast_channel_lock(outgoing->chan); ast_connected_line_copy_from_caller(&connected_caller, &outgoing->chan->caller); ast_channel_unlock(outgoing->chan); @@ -1113,7 +1137,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, if (ast_test_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) { if (!peer) { ast_verb(3, "%s answered %s\n", c->name, in->name); - if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) { if (o->pending_connected_update) { if (ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) { ast_channel_update_connected_line(in, &o->connected, NULL); @@ -1164,8 +1188,35 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } ast_frfree(f); } - do_forward(o, &num, peerflags, single && !caller_entertained, to, + + if (o->pending_connected_update) { + /* + * Re-seed the chanlist's connected line information with + * previously acquired connected line info from the incoming + * channel. The previously acquired connected line info could + * have been set through the CONNECTED_LINE dialplan function. + */ + o->pending_connected_update = 0; + ast_channel_lock(in); + ast_party_connected_line_copy(&o->connected, &in->connected); + ast_channel_unlock(in); + } + + do_forward(o, &num, peerflags, single, caller_entertained, to, forced_clid, stored_clid); + + if (single && o->chan + && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE) + && !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) { + ast_channel_lock(o->chan); + ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); + ast_channel_unlock(o->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) { + ast_channel_update_connected_line(in, &connected_caller, NULL); + } + ast_party_connected_line_free(&connected_caller); + } continue; } f = ast_read(winner); @@ -1187,7 +1238,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, /* This is our guy if someone answered. */ if (!peer) { ast_verb(3, "%s answered %s\n", c->name, in->name); - if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) { if (o->pending_connected_update) { if (ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) { ast_channel_update_connected_line(in, &o->connected, NULL); @@ -1319,20 +1370,24 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, ast_indicate(in, f->subclass.integer); break; case AST_CONTROL_CONNECTED_LINE: - if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) { ast_verb(3, "Connected line update to %s prevented.\n", in->name); - } else if (!single) { + break; + } + if (!single) { struct ast_party_connected_line connected; - ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", c->name, in->name); + + ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", + c->name, in->name); ast_party_connected_line_set_init(&connected, &o->connected); ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); ast_party_connected_line_set(&o->connected, &connected, NULL); ast_party_connected_line_free(&connected); o->pending_connected_update = 1; - } else { - if (ast_channel_connected_line_macro(c, in, f, 1, 1)) { - ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); - } + break; + } + if (ast_channel_connected_line_macro(c, in, f, 1, 1)) { + ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); } break; case AST_CONTROL_AOC: @@ -1347,15 +1402,23 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } break; case AST_CONTROL_REDIRECTING: - if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + if (!single) { + /* + * Redirecting updates to the caller make sense only on single + * calls. + */ + break; + } + if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) { ast_verb(3, "Redirecting update to %s prevented.\n", in->name); - } else if (single) { - ast_verb(3, "%s redirecting info has changed, passing it to %s\n", c->name, in->name); - if (ast_channel_redirecting_macro(c, in, f, 1, 1)) { - ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); - } - pa->sentringing = 0; + break; } + ast_verb(3, "%s redirecting info has changed, passing it to %s\n", + c->name, in->name); + if (ast_channel_redirecting_macro(c, in, f, 1, 1)) { + ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); + } + pa->sentringing = 0; break; case AST_CONTROL_PROCEEDING: ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name); @@ -2162,9 +2225,12 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast } else if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"))) { outbound_group = ast_strdupa(outbound_group); } - ast_channel_unlock(chan); - ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_IGNORE_CONNECTEDLINE | - OPT_CANCEL_TIMEOUT | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID); + ast_channel_unlock(chan); + + /* Set per dial instance flags. These flags are also passed back to RetryDial. */ + ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID + | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_CANCEL_TIMEOUT + | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID); /* loop through the list of dial destinations */ rest = args.peers; @@ -2186,6 +2252,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast if (!(tmp = ast_calloc(1, sizeof(*tmp)))) goto out; if (opts.flags) { + /* Set per outgoing call leg options. */ ast_copy_flags64(tmp, &opts, OPT_CANCEL_ELSEWHERE | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | @@ -2193,7 +2260,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK | OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR | - OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID); + OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID | OPT_IGNORE_CONNECTEDLINE); ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML); } ast_copy_string(numsubst, number, sizeof(numsubst)); diff --git a/apps/app_queue.c b/apps/app_queue.c index cba347f12a..21dd4a7f42 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -1004,7 +1004,6 @@ struct callattempt { struct callattempt *call_next; struct ast_channel *chan; char interface[256]; - int stillgoing; int metric; time_t lastcall; struct call_queue *lastqueue; @@ -1013,8 +1012,12 @@ struct callattempt { struct ast_party_connected_line connected; /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */ unsigned int pending_connected_update:1; + /*! TRUE if the connected line update is blocked. */ + unsigned int block_connected_update:1; /*! TRUE if caller id is not available for connected line */ unsigned int dial_callerid_absent:1; + /*! TRUE if the call is still active */ + unsigned int stillgoing:1; struct ast_aoc_decoded *aoc_s_rate_list; }; @@ -3511,15 +3514,13 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue() - * \param[in] update_connectedline Allow connected line and redirecting updates to pass through. * * \todo eventually all call forward logic should be intergerated into and replaced by ast_call_forward() */ -static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int update_connectedline) +static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed) { const char *queue = qe->parent->name; struct callattempt *o, *start = NULL, *prev = NULL; - int res; int status; int numbusies = prebusies; int numnochan = 0; @@ -3600,10 +3601,11 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte /* Service all of the outgoing channels */ for (o = start; o; o = o->call_next) { - /* We go with a static buffer here instead of using ast_strdupa. Using + /* We go with a fixed buffer here instead of using ast_strdupa. Using * ast_strdupa in a loop like this one can cause a stack overflow */ char ochan_name[AST_CHANNEL_NAME]; + if (o->chan) { ast_channel_lock(o->chan); ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); @@ -3612,7 +3614,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { if (!peer) { ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); - if (update_connectedline) { + if (!o->block_connected_update) { if (o->pending_connected_update) { if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { ast_channel_update_connected_line(in, &o->connected, NULL); @@ -3643,6 +3645,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte ast_copy_string(on, o->member->interface, sizeof(on)); ast_copy_string(membername, o->member->membername, sizeof(membername)); + /* Before processing channel, go ahead and check for forwarding */ if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); numnochan++; @@ -3664,10 +3667,17 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte stuff = tmpchan; tech = "Local"; } + if (!strcasecmp(tech, "Local")) { + /* + * Drop the connected line update block for local channels since + * this is going to run dialplan and the user can change his + * mind about what connected line information he wants to send. + */ + o->block_connected_update = 0; + } ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL); - /* Before processing channel, go ahead and check for forwarding */ ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); /* Setup parameters */ o->chan = ast_request(tech, in->nativeformats, in, stuff, &status); @@ -3678,15 +3688,28 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte o->stillgoing = 0; numnochan++; } else { - struct ast_party_redirecting redirecting; + ast_channel_lock_both(o->chan, original); + ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting); + ast_channel_unlock(o->chan); + ast_channel_unlock(original); ast_channel_lock_both(o->chan, in); ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); + if (o->pending_connected_update) { + /* + * Re-seed the callattempt's connected line information with + * previously acquired connected line info from the queued + * channel. The previously acquired connected line info could + * have been set through the CONNECTED_LINE dialplan function. + */ + o->pending_connected_update = 0; + ast_party_connected_line_copy(&o->connected, &in->connected); + } + ast_string_field_set(o->chan, accountcode, in->accountcode); - ast_channel_set_redirecting(o->chan, &original->redirecting, NULL); if (!o->chan->redirecting.from.number.valid || ast_strlen_zero(o->chan->redirecting.from.number.str)) { /* @@ -3702,27 +3725,35 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte o->chan->dialed.transit_network_select = in->dialed.transit_network_select; - ast_party_caller_copy(&o->chan->caller, &in->caller); - ast_party_connected_line_copy(&o->chan->connected, &original->connected); + o->dial_callerid_absent = !o->chan->caller.id.number.valid + || ast_strlen_zero(o->chan->caller.id.number.str); + ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller); - /* - * We must unlock o->chan before calling - * ast_channel_redirecting_macro, because we put o->chan into - * autoservice there. That is pretty much a guaranteed - * deadlock. This is why the handling of o->chan's lock may - * seem a bit unusual here. - */ - ast_party_redirecting_init(&redirecting); - ast_party_redirecting_copy(&redirecting, &o->chan->redirecting); - ast_channel_unlock(o->chan); - res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0); - if (res) { - ast_channel_update_redirecting(in, &redirecting, NULL); - } - ast_party_redirecting_free(&redirecting); ast_channel_unlock(in); + if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL + && !o->block_connected_update) { + struct ast_party_redirecting redirecting; - update_connectedline = 1; + /* + * Redirecting updates to the caller make sense only on single + * call at a time strategies. + * + * We must unlock o->chan before calling + * ast_channel_redirecting_macro, because we put o->chan into + * autoservice there. That is pretty much a guaranteed + * deadlock. This is why the handling of o->chan's lock may + * seem a bit unusual here. + */ + ast_party_redirecting_init(&redirecting); + ast_party_redirecting_copy(&redirecting, &o->chan->redirecting); + ast_channel_unlock(o->chan); + if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) { + ast_channel_update_redirecting(in, &redirecting, NULL); + } + ast_party_redirecting_free(&redirecting); + } else { + ast_channel_unlock(o->chan); + } if (ast_call(o->chan, stuff, 0)) { ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", @@ -3743,7 +3774,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte /* This is our guy if someone answered. */ if (!peer) { ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); - if (update_connectedline) { + if (!o->block_connected_update) { if (o->pending_connected_update) { if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { ast_channel_update_connected_line(in, &o->connected, NULL); @@ -3820,20 +3851,30 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte /* Ignore going off hook */ break; case AST_CONTROL_CONNECTED_LINE: - if (!update_connectedline) { + if (o->block_connected_update) { ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); - } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { + break; + } + if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { struct ast_party_connected_line connected; + ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name); ast_party_connected_line_set_init(&connected, &o->connected); ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); ast_party_connected_line_set(&o->connected, &connected, NULL); ast_party_connected_line_free(&connected); o->pending_connected_update = 1; - } else { - if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) { - ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); - } + break; + } + + /* + * Prevent using the CallerID from the outgoing channel since we + * got a connected line update from it. + */ + o->dial_callerid_absent = 1; + + if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) { + ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); } break; case AST_CONTROL_AOC: @@ -3848,13 +3889,22 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte } break; case AST_CONTROL_REDIRECTING: - if (!update_connectedline) { - ast_verb(3, "Redirecting update to %s prevented\n", inchan_name); - } else if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { - ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name); - if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) { - ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); - } + if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { + /* + * Redirecting updates to the caller make sense only on single + * call at a time strategies. + */ + break; + } + if (o->block_connected_update) { + ast_verb(3, "Redirecting update to %s prevented\n", + inchan_name); + break; + } + ast_verb(3, "%s redirecting info has changed, passing it to %s\n", + ochan_name, inchan_name); + if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) { + ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); } break; default: @@ -3893,6 +3943,14 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte } return NULL; } + + /*! + * \todo + * XXX Queue like Dial really should send any connected line + * updates (AST_CONTROL_CONNECTED_LINE) from the caller to each + * ringing queue member. + */ + if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) { ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer); *to = 0; @@ -4431,7 +4489,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce char *p; char vars[2048]; int forwardsallowed = 1; - int update_connectedline = 1; + int block_connected_line = 0; int callcompletedinsl; struct ao2_iterator memi; struct ast_datastore *datastore, *transfer_ds; @@ -4498,7 +4556,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce forwardsallowed = 0; break; case 'I': - update_connectedline = 0; + block_connected_line = 1; break; case 'x': ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); @@ -4598,17 +4656,18 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce AST_LIST_UNLOCK(dialed_interfaces); } - ast_channel_lock(qe->chan); /* * Seed the callattempt's connected line information with previously * acquired connected line info from the queued channel. The * previously acquired connected line info could have been set * through the CONNECTED_LINE dialplan function. */ + ast_channel_lock(qe->chan); ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); ast_channel_unlock(qe->chan); - tmp->stillgoing = -1; + tmp->block_connected_update = block_connected_line; + tmp->stillgoing = 1; tmp->member = cur;/* Place the reference for cur into callattempt. */ tmp->lastcall = cur->lastcall; tmp->lastqueue = cur->lastqueue; @@ -4650,7 +4709,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce ++qe->pending; ao2_unlock(qe->parent); ring_one(qe, outgoing, &numbusies); - lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline); + lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, + ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), + forwardsallowed); /* The ast_channel_datastore_remove() function could fail here if the * datastore was moved to another channel during a masquerade. If this is * the case, don't free the datastore here because later, when the channel -- 2.47.2