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
........

Merged revisions 367679 from http://svn.asterisk.org/svn/asterisk/branches/10


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@367693 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Richard Mudgett
2012-05-24 23:52:40 +00:00
parent bdaecbb66b
commit e518536773
2 changed files with 238 additions and 99 deletions

View File

@@ -859,7 +859,8 @@ static void senddialendevent(struct ast_channel *src, const char *dialstatus)
* \param o Outgoing call channel list. * \param o Outgoing call channel list.
* \param num Incoming call channel cause accumulation * \param num Incoming call channel cause accumulation
* \param peerflags Dial option flags * \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 to Remaining call timeout time.
* \param forced_clid OPT_FORCECLID caller id to send * \param forced_clid OPT_FORCECLID caller id to send
* \param stored_clid Caller id representing the called party if needed * \param stored_clid Caller id representing the called party if needed
@@ -869,8 +870,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() * \todo eventually this function should be intergrated into and replaced by ast_call_forward()
*/ */
static void do_forward(struct chanlist *o, static void do_forward(struct chanlist *o, struct cause_args *num,
struct cause_args *num, struct ast_flags64 *peerflags, int single_caller_bored, int *to, struct ast_flags64 *peerflags, int single, int caller_entertained, int *to,
struct ast_party_id *forced_clid, struct ast_party_id *stored_clid) struct ast_party_id *forced_clid, struct ast_party_id *stored_clid)
{ {
char tmpchan[256]; char tmpchan[256];
@@ -898,6 +899,14 @@ static void do_forward(struct chanlist *o,
stuff = tmpchan; stuff = tmpchan;
tech = "Local"; 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, ast_channel_call_forward(c), NULL); ast_cel_report_event(in, AST_CEL_FORWARD, NULL, ast_channel_call_forward(c), NULL);
@@ -912,11 +921,14 @@ static void do_forward(struct chanlist *o,
/* Setup parameters */ /* Setup parameters */
c = o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &cause); c = o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &cause);
if (c) { if (c) {
if (single_caller_bored) { if (single && !caller_entertained) {
ast_channel_make_compatible(o->chan, in); ast_channel_make_compatible(o->chan, in);
} }
ast_channel_lock_both(in, o->chan);
ast_channel_inherit_variables(in, o->chan); ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(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 /* When a call is forwarded, we don't want to track new interfaces
* dialed for CC purposes. Setting the done flag will ensure that * dialed for CC purposes. Setting the done flag will ensure that
* any Dial operations that happen later won't record CC interfaces. * any Dial operations that happen later won't record CC interfaces.
@@ -933,15 +945,18 @@ static void do_forward(struct chanlist *o,
handle_cause(cause, num); handle_cause(cause, num);
ast_hangup(original); ast_hangup(original);
} else { } else {
struct ast_party_redirecting redirecting; ast_channel_lock_both(c, original);
ast_party_redirecting_copy(ast_channel_redirecting(c),
ast_channel_redirecting(original));
ast_channel_unlock(c);
ast_channel_unlock(original);
ast_channel_lock_both(c, in); 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_rtp_instance_early_bridge_make_compatible(c, in);
} }
ast_channel_set_redirecting(c, ast_channel_redirecting(original), NULL);
if (!ast_channel_redirecting(c)->from.number.valid if (!ast_channel_redirecting(c)->from.number.valid
|| ast_strlen_zero(ast_channel_redirecting(c)->from.number.str)) { || ast_strlen_zero(ast_channel_redirecting(c)->from.number.str)) {
/* /*
@@ -962,6 +977,7 @@ static void do_forward(struct chanlist *o,
if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
caller.id = *stored_clid; caller.id = *stored_clid;
ast_channel_set_caller_event(c, &caller, NULL); ast_channel_set_caller_event(c, &caller, NULL);
ast_set_flag64(o, DIAL_CALLERID_ABSENT);
} else if (ast_strlen_zero(S_COR(ast_channel_caller(c)->id.number.valid, } else if (ast_strlen_zero(S_COR(ast_channel_caller(c)->id.number.valid,
ast_channel_caller(c)->id.number.str, NULL))) { ast_channel_caller(c)->id.number.str, NULL))) {
/* /*
@@ -970,6 +986,9 @@ static void do_forward(struct chanlist *o,
*/ */
caller.id = *stored_clid; caller.id = *stored_clid;
ast_channel_set_caller_event(c, &caller, NULL); 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. */ /* Determine CallerID for outgoing channel to send. */
@@ -987,23 +1006,33 @@ static void do_forward(struct chanlist *o,
ast_channel_appl_set(c, "AppDial"); ast_channel_appl_set(c, "AppDial");
ast_channel_data_set(c, "(Outgoing Line)"); ast_channel_data_set(c, "(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, ast_channel_redirecting(c));
ast_channel_unlock(c);
if (ast_channel_redirecting_sub(c, in, &redirecting, 0) &&
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);
ast_clear_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE); 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, ast_channel_redirecting(c));
ast_channel_unlock(c);
if (ast_channel_redirecting_sub(c, in, &redirecting, 0) &&
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);
}
if (ast_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) { if (ast_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) {
*to = -1; *to = -1;
} }
@@ -1024,7 +1053,7 @@ static void do_forward(struct chanlist *o,
/* Hangup the original channel now, in case we needed it */ /* Hangup the original channel now, in case we needed it */
ast_hangup(original); ast_hangup(original);
} }
if (single_caller_bored) { if (single && !caller_entertained) {
ast_indicate(in, -1); ast_indicate(in, -1);
} }
} }
@@ -1085,7 +1114,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_channel_lock(outgoing->chan);
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(outgoing->chan)); ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(outgoing->chan));
ast_channel_unlock(outgoing->chan); ast_channel_unlock(outgoing->chan);
@@ -1148,7 +1178,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
if (ast_test_flag64(o, DIAL_STILLGOING) && ast_channel_state(c) == AST_STATE_UP) { if (ast_test_flag64(o, DIAL_STILLGOING) && ast_channel_state(c) == AST_STATE_UP) {
if (!peer) { if (!peer) {
ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in)); ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
if (o->pending_connected_update) { if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) && if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) { ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
@@ -1201,8 +1231,37 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
} }
ast_frfree(f); 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, ast_channel_connected(in));
ast_channel_unlock(in);
}
do_forward(o, &num, peerflags, single, caller_entertained, to,
forced_clid, stored_clid); 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,
ast_channel_caller(o->chan));
ast_channel_unlock(o->chan);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
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; continue;
} }
f = ast_read(winner); f = ast_read(winner);
@@ -1224,7 +1283,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
/* This is our guy if someone answered. */ /* This is our guy if someone answered. */
if (!peer) { if (!peer) {
ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in)); ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
if (o->pending_connected_update) { if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) && if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) { ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
@@ -1358,21 +1417,25 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
ast_indicate(in, f->subclass.integer); ast_indicate(in, f->subclass.integer);
break; break;
case AST_CONTROL_CONNECTED_LINE: 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", ast_channel_name(in)); ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(in));
} else if (!single) { break;
}
if (!single) {
struct ast_party_connected_line connected; struct ast_party_connected_line connected;
ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ast_channel_name(c), ast_channel_name(in));
ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n",
ast_channel_name(c), ast_channel_name(in));
ast_party_connected_line_set_init(&connected, &o->connected); ast_party_connected_line_set_init(&connected, &o->connected);
ast_connected_line_parse_data(f->data.ptr, f->datalen, &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_set(&o->connected, &connected, NULL);
ast_party_connected_line_free(&connected); ast_party_connected_line_free(&connected);
o->pending_connected_update = 1; o->pending_connected_update = 1;
} else { break;
if (ast_channel_connected_line_sub(c, in, f, 1) && }
ast_channel_connected_line_macro(c, in, f, 1, 1)) { if (ast_channel_connected_line_sub(c, in, f, 1) &&
ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); ast_channel_connected_line_macro(c, in, f, 1, 1)) {
} ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
} }
break; break;
case AST_CONTROL_AOC: case AST_CONTROL_AOC:
@@ -1387,16 +1450,24 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
} }
break; break;
case AST_CONTROL_REDIRECTING: case AST_CONTROL_REDIRECTING:
if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { if (!single) {
ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(in)); /*
} else if (single) { * Redirecting updates to the caller make sense only on single
ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ast_channel_name(c), ast_channel_name(in)); * calls.
if (ast_channel_redirecting_sub(c, in, f, 1) && */
ast_channel_redirecting_macro(c, in, f, 1, 1)) { break;
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
}
pa->sentringing = 0;
} }
if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(in));
break;
}
ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
ast_channel_name(c), ast_channel_name(in));
if (ast_channel_redirecting_sub(c, in, f, 1) &&
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; break;
case AST_CONTROL_PROCEEDING: case AST_CONTROL_PROCEEDING:
ast_verb(3, "%s is proceeding passing it to %s\n", ast_channel_name(c), ast_channel_name(in)); ast_verb(3, "%s is proceeding passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
@@ -2202,8 +2273,11 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
outbound_group = ast_strdupa(outbound_group); outbound_group = ast_strdupa(outbound_group);
} }
ast_channel_unlock(chan); 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); /* 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);
/* PREDIAL: Run gosub on the caller's channel */ /* PREDIAL: Run gosub on the caller's channel */
if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER) if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER)
@@ -2251,6 +2325,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
tmp->number = cur; tmp->number = cur;
if (opts.flags) { if (opts.flags) {
/* Set per outgoing call leg options. */
ast_copy_flags64(tmp, &opts, ast_copy_flags64(tmp, &opts,
OPT_CANCEL_ELSEWHERE | OPT_CANCEL_ELSEWHERE |
OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
@@ -2258,7 +2333,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
OPT_CALLEE_PARK | OPT_CALLER_PARK | OPT_CALLEE_PARK | OPT_CALLER_PARK |
OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR | 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_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
} }

View File

@@ -893,7 +893,7 @@ enum {
OPT_CALLEE_HANGUP = (1 << 4), OPT_CALLEE_HANGUP = (1 << 4),
OPT_CALLER_HANGUP = (1 << 5), OPT_CALLER_HANGUP = (1 << 5),
OPT_IGNORE_CALL_FW = (1 << 6), OPT_IGNORE_CALL_FW = (1 << 6),
OPT_UPDATE_CONNECTED = (1 << 7), OPT_IGNORE_CONNECTEDLINE = (1 << 7),
OPT_CALLEE_PARK = (1 << 8), OPT_CALLEE_PARK = (1 << 8),
OPT_CALLER_PARK = (1 << 9), OPT_CALLER_PARK = (1 << 9),
OPT_NO_RETRY = (1 << 10), OPT_NO_RETRY = (1 << 10),
@@ -921,7 +921,7 @@ AST_APP_OPTIONS(queue_exec_options, BEGIN_OPTIONS
AST_APP_OPTION('h', OPT_CALLEE_HANGUP), AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
AST_APP_OPTION('H', OPT_CALLER_HANGUP), AST_APP_OPTION('H', OPT_CALLER_HANGUP),
AST_APP_OPTION('i', OPT_IGNORE_CALL_FW), AST_APP_OPTION('i', OPT_IGNORE_CALL_FW),
AST_APP_OPTION('I', OPT_UPDATE_CONNECTED), AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE),
AST_APP_OPTION('k', OPT_CALLEE_PARK), AST_APP_OPTION('k', OPT_CALLEE_PARK),
AST_APP_OPTION('K', OPT_CALLER_PARK), AST_APP_OPTION('K', OPT_CALLER_PARK),
AST_APP_OPTION('n', OPT_NO_RETRY), AST_APP_OPTION('n', OPT_NO_RETRY),
@@ -1097,7 +1097,6 @@ struct callattempt {
struct callattempt *call_next; struct callattempt *call_next;
struct ast_channel *chan; struct ast_channel *chan;
char interface[256]; /*!< An Asterisk dial string (not a channel name) */ char interface[256]; /*!< An Asterisk dial string (not a channel name) */
int stillgoing;
int metric; int metric;
time_t lastcall; time_t lastcall;
struct call_queue *lastqueue; struct call_queue *lastqueue;
@@ -1106,8 +1105,12 @@ struct callattempt {
struct ast_party_connected_line connected; struct ast_party_connected_line connected;
/*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */ /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
unsigned int pending_connected_update:1; 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 */ /*! TRUE if caller id is not available for connected line */
unsigned int dial_callerid_absent:1; 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; struct ast_aoc_decoded *aoc_s_rate_list;
}; };
@@ -3725,15 +3728,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] 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] 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] 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() * \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; const char *queue = qe->parent->name;
struct callattempt *o, *start = NULL, *prev = NULL; struct callattempt *o, *start = NULL, *prev = NULL;
int res;
int status; int status;
int numbusies = prebusies; int numbusies = prebusies;
int numnochan = 0; int numnochan = 0;
@@ -3817,10 +3818,11 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
/* Service all of the outgoing channels */ /* Service all of the outgoing channels */
for (o = start; o; o = o->call_next) { 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 * ast_strdupa in a loop like this one can cause a stack overflow
*/ */
char ochan_name[AST_CHANNEL_NAME]; char ochan_name[AST_CHANNEL_NAME];
if (o->chan) { if (o->chan) {
ast_channel_lock(o->chan); ast_channel_lock(o->chan);
ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name)); ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
@@ -3829,7 +3831,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) { if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
if (!peer) { if (!peer) {
ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 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 (o->pending_connected_update) {
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) && if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
@@ -3862,6 +3864,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(on, o->member->interface, sizeof(on));
ast_copy_string(membername, o->member->membername, sizeof(membername)); ast_copy_string(membername, o->member->membername, sizeof(membername));
/* Before processing channel, go ahead and check for forwarding */
if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) { if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan)); ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
numnochan++; numnochan++;
@@ -3883,10 +3886,17 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
stuff = tmpchan; stuff = tmpchan;
tech = "Local"; 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, ast_channel_call_forward(o->chan), NULL); ast_cel_report_event(in, AST_CEL_FORWARD, NULL, ast_channel_call_forward(o->chan), 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); ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
/* Setup parameters */ /* Setup parameters */
o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &status); o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &status);
@@ -3897,15 +3907,29 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
o->stillgoing = 0; o->stillgoing = 0;
numnochan++; numnochan++;
} else { } else {
struct ast_party_redirecting redirecting; ast_channel_lock_both(o->chan, original);
ast_party_redirecting_copy(ast_channel_redirecting(o->chan),
ast_channel_redirecting(original));
ast_channel_unlock(o->chan);
ast_channel_unlock(original);
ast_channel_lock_both(o->chan, in); ast_channel_lock_both(o->chan, in);
ast_channel_inherit_variables(in, o->chan); ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(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, ast_channel_connected(in));
}
ast_channel_accountcode_set(o->chan, ast_channel_accountcode(in)); ast_channel_accountcode_set(o->chan, ast_channel_accountcode(in));
ast_channel_set_redirecting(o->chan, ast_channel_redirecting(original), NULL);
if (!ast_channel_redirecting(o->chan)->from.number.valid if (!ast_channel_redirecting(o->chan)->from.number.valid
|| ast_strlen_zero(ast_channel_redirecting(o->chan)->from.number.str)) { || ast_strlen_zero(ast_channel_redirecting(o->chan)->from.number.str)) {
/* /*
@@ -3921,27 +3945,37 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select; ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
ast_party_caller_copy(ast_channel_caller(o->chan), ast_channel_caller(in)); o->dial_callerid_absent = !ast_channel_caller(o->chan)->id.number.valid
ast_party_connected_line_copy(ast_channel_connected(o->chan), ast_channel_connected(original)); || ast_strlen_zero(ast_channel_caller(o->chan)->id.number.str);
ast_connected_line_copy_from_caller(ast_channel_connected(o->chan),
ast_channel_caller(in));
/*
* 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, ast_channel_redirecting(o->chan));
ast_channel_unlock(o->chan);
if ((res = ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) &&
(res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0))) {
ast_channel_update_redirecting(in, &redirecting, NULL);
}
ast_party_redirecting_free(&redirecting);
ast_channel_unlock(in); 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, ast_channel_redirecting(o->chan));
ast_channel_unlock(o->chan);
if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0) &&
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)) { if (ast_call(o->chan, stuff, 0)) {
ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
@@ -3962,7 +3996,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
/* This is our guy if someone answered. */ /* This is our guy if someone answered. */
if (!peer) { if (!peer) {
ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 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 (o->pending_connected_update) {
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) && if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
@@ -4045,21 +4079,31 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
/* Ignore going off hook */ /* Ignore going off hook */
break; break;
case AST_CONTROL_CONNECTED_LINE: 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); 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; 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_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_party_connected_line_set_init(&connected, &o->connected);
ast_connected_line_parse_data(f->data.ptr, f->datalen, &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_set(&o->connected, &connected, NULL);
ast_party_connected_line_free(&connected); ast_party_connected_line_free(&connected);
o->pending_connected_update = 1; o->pending_connected_update = 1;
} else { break;
if (ast_channel_connected_line_sub(o->chan, in, f, 1) && }
ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); /*
} * 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_sub(o->chan, in, f, 1) &&
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; break;
case AST_CONTROL_AOC: case AST_CONTROL_AOC:
@@ -4074,14 +4118,23 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
} }
break; break;
case AST_CONTROL_REDIRECTING: case AST_CONTROL_REDIRECTING:
if (!update_connectedline) { if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
ast_verb(3, "Redirecting update to %s prevented\n", inchan_name); /*
} else if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { * Redirecting updates to the caller make sense only on single
ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name); * call at a time strategies.
if (ast_channel_redirecting_sub(o->chan, in, f, 1) && */
ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) { break;
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); }
} 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_sub(o->chan, in, f, 1) &&
ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
} }
break; break;
case AST_CONTROL_PVT_CAUSE_CODE: case AST_CONTROL_PVT_CAUSE_CODE:
@@ -4124,6 +4177,14 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
} }
return NULL; 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 == '*')) { if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer); ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
*to = 0; *to = 0;
@@ -4679,7 +4740,7 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
char *p; char *p;
char vars[2048]; char vars[2048];
int forwardsallowed = 1; int forwardsallowed = 1;
int update_connectedline = 1; int block_connected_line = 0;
int callcompletedinsl; int callcompletedinsl;
struct ao2_iterator memi; struct ao2_iterator memi;
struct ast_datastore *datastore, *transfer_ds; struct ast_datastore *datastore, *transfer_ds;
@@ -4745,8 +4806,8 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) { if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
forwardsallowed = 0; forwardsallowed = 0;
} }
if (ast_test_flag(&opts, OPT_UPDATE_CONNECTED)) { if (ast_test_flag(&opts, OPT_IGNORE_CONNECTEDLINE)) {
update_connectedline = 0; block_connected_line = 1;
} }
if (ast_test_flag(&opts, OPT_CALLEE_AUTOMIXMON)) { if (ast_test_flag(&opts, OPT_CALLEE_AUTOMIXMON)) {
ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
@@ -4847,17 +4908,18 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
AST_LIST_UNLOCK(dialed_interfaces); AST_LIST_UNLOCK(dialed_interfaces);
} }
ast_channel_lock(qe->chan);
/* /*
* Seed the callattempt's connected line information with previously * Seed the callattempt's connected line information with previously
* acquired connected line info from the queued channel. The * acquired connected line info from the queued channel. The
* previously acquired connected line info could have been set * previously acquired connected line info could have been set
* through the CONNECTED_LINE dialplan function. * through the CONNECTED_LINE dialplan function.
*/ */
ast_channel_lock(qe->chan);
ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(qe->chan)); ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(qe->chan));
ast_channel_unlock(qe->chan); 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->member = cur;/* Place the reference for cur into callattempt. */
tmp->lastcall = cur->lastcall; tmp->lastcall = cur->lastcall;
tmp->lastqueue = cur->lastqueue; tmp->lastqueue = cur->lastqueue;
@@ -4900,7 +4962,9 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
++qe->pending; ++qe->pending;
ao2_unlock(qe->parent); ao2_unlock(qe->parent);
ring_one(qe, outgoing, &numbusies); 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 /* The ast_channel_datastore_remove() function could fail here if the
* datastore was moved to another channel during a masquerade. If this is * 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 * the case, don't free the datastore here because later, when the channel