diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/app_dial.c | 190 | ||||
-rw-r--r-- | apps/app_directed_pickup.c | 13 | ||||
-rw-r--r-- | apps/app_queue.c | 186 |
3 files changed, 311 insertions, 78 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c index 96bb57081..ee3953fcc 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -157,6 +157,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <option name="i"> <para>Asterisk will ignore any forwarding requests it may receive on this dial attempt.</para> </option> + <option name="I"> + <para>Asterisk will ignore any connected line update requests or redirecting party update + requests it may receiveon this dial attempt.</para> + </option> <option name="k"> <para>Allow the called party to enable parking of the call by sending the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para> @@ -382,7 +386,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") This application will report normal termination if the originating channel hangs up, or if the call is bridged and either of the parties in the bridge ends the call.</para> - <para>If the <variable>OUTBOUND_GROUP</variable> variable is set, all peer channels created by this application will be put into that group (as in Set(GROUP()=...). If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this @@ -464,12 +467,13 @@ enum { OPT_GO_ON = (1 << 5), OPT_CALLEE_HANGUP = (1 << 6), OPT_CALLER_HANGUP = (1 << 7), + OPT_ORIGINAL_CLID = (1 << 8), OPT_DURATION_LIMIT = (1 << 9), OPT_MUSICBACK = (1 << 10), OPT_CALLEE_MACRO = (1 << 11), OPT_SCREEN_NOINTRO = (1 << 12), - OPT_SCREEN_NOCLID = (1 << 13), - OPT_ORIGINAL_CLID = (1 << 14), + OPT_SCREEN_NOCALLERID = (1 << 13), + OPT_IGNORE_CONNECTEDLINE = (1 << 14), OPT_SCREENING = (1 << 15), OPT_PRIVACY = (1 << 16), OPT_RINGBACK = (1 << 17), @@ -490,9 +494,10 @@ enum { #define DIAL_STILLGOING (1 << 31) #define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */ -#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) -#define OPT_PEER_H ((uint64_t)1 << 34) -#define OPT_CALLEE_GO_ON ((uint64_t)1 << 35) +#define DIAL_NOCONNECTEDLINE ((uint64_t)1 << 33) +#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 34) +#define OPT_PEER_H ((uint64_t)1 << 35) +#define OPT_CALLEE_GO_ON ((uint64_t)1 << 36) enum { OPT_ARG_ANNOUNCE = 0, @@ -524,13 +529,14 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS AST_APP_OPTION('h', OPT_CALLEE_HANGUP), AST_APP_OPTION('H', OPT_CALLER_HANGUP), AST_APP_OPTION('i', OPT_IGNORE_FORWARDING), + AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE), AST_APP_OPTION('k', OPT_CALLEE_PARK), AST_APP_OPTION('K', OPT_CALLER_PARK), AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT), AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK), AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO), AST_APP_OPTION('n', OPT_SCREEN_NOINTRO), - AST_APP_OPTION('N', OPT_SCREEN_NOCLID), + AST_APP_OPTION('N', OPT_SCREEN_NOCALLERID), AST_APP_OPTION('o', OPT_ORIGINAL_CLID), AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE), AST_APP_OPTION('p', OPT_SCREENING), @@ -558,6 +564,7 @@ struct chanlist { struct chanlist *next; struct ast_channel *chan; uint64_t flags; + struct ast_party_connected_line connected; }; static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode); @@ -654,7 +661,6 @@ static int onedigit_goto(struct ast_channel *chan, const char *context, char ext return 0; } - static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan) { const char *context = S_OR(chan->macrocontext, chan->context); @@ -702,6 +708,8 @@ static void do_forward(struct chanlist *o, struct ast_channel *original = o->chan; struct ast_channel *c = o->chan; /* the winner */ struct ast_channel *in = num->chan; /* the input channel */ + struct ast_party_redirecting *apr = &o->chan->redirecting; + struct ast_party_connected_line *apc = &o->chan->connected; char *stuff; char *tech; int cause; @@ -742,30 +750,38 @@ static void do_forward(struct chanlist *o, handle_cause(cause, num); ast_hangup(original); } else { - char *new_cid_num, *new_cid_name; - struct ast_channel *src; - if (single) { ast_rtp_instance_early_bridge_make_compatible(c, in); } + + c->cdrflags = in->cdrflags; + + ast_channel_set_redirecting(c, apr); + ast_channel_lock(c); + while (ast_channel_trylock(in)) { + CHANNEL_DEADLOCK_AVOIDANCE(c); + } + S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(original->cid.cid_rdnis, S_OR(in->macroexten, in->exten)))); + + c->cid.cid_tns = in->cid.cid_tns; + if (ast_test_flag64(o, OPT_FORCECLID)) { - new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten)); - new_cid_name = NULL; /* XXX no name ? */ - src = c; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */ + S_REPLACE(c->cid.cid_num, ast_strdupa(S_OR(in->macroexten, in->exten))); + S_REPLACE(c->cid.cid_name, NULL); + ast_string_field_set(c, accountcode, c->accountcode); } else { - new_cid_num = ast_strdup(in->cid.cid_num); - new_cid_name = ast_strdup(in->cid.cid_name); - src = in; + ast_party_caller_copy(&c->cid, &in->cid); + ast_string_field_set(c, accountcode, in->accountcode); } - ast_string_field_set(c, accountcode, src->accountcode); - c->cdrflags = src->cdrflags; - S_REPLACE(c->cid.cid_num, new_cid_num); - S_REPLACE(c->cid.cid_name, new_cid_name); + ast_party_connected_line_copy(&c->connected, apc); + + S_REPLACE(in->cid.cid_rdnis, ast_strdup(c->cid.cid_rdnis)); + ast_channel_unlock(in); + ast_channel_unlock(c); + ast_channel_update_redirecting(in, apr); + + ast_clear_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE); - if (in->cid.cid_ani) { /* XXX or maybe unconditional ? */ - S_REPLACE(c->cid.cid_ani, ast_strdup(in->cid.cid_ani)); - } - S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(in->macroexten, in->exten))); if (ast_call(c, tmpchan, 0)) { ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); ast_clear_flag64(o, DIAL_STILLGOING); @@ -775,7 +791,6 @@ static void do_forward(struct chanlist *o, num->nochan++; } else { senddialevent(in, c, stuff); - /* After calling, set callerid to extension */ if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { char cidname[AST_MAX_EXTENSION] = ""; ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL); @@ -808,16 +823,28 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, int orig = *to; struct ast_channel *peer = NULL; /* single is set if only one destination is enabled */ - int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK); + int single = outgoing && !outgoing->next; #ifdef HAVE_EPOLL struct chanlist *epollo; #endif + struct ast_party_connected_line connected_caller; struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1); if (single) { /* Turn off hold music, etc */ - ast_deactivate_generator(in); + if (!ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK)) + ast_deactivate_generator(in); + /* If we are calling a single channel, make them compatible for in-band tone purpose */ ast_channel_make_compatible(outgoing->chan, in); + + if (!ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE) && !ast_test_flag64(outgoing, DIAL_NOCONNECTEDLINE)) { + ast_channel_lock(outgoing->chan); + ast_connected_line_copy_from_caller(&connected_caller, &outgoing->chan->cid); + ast_channel_unlock(outgoing->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } } #ifdef HAVE_EPOLL @@ -864,6 +891,18 @@ 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 (o->connected.id.number) { + ast_channel_update_connected_line(in, &o->connected); + } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) { + ast_channel_lock(c); + ast_connected_line_copy_from_caller(&connected_caller, &c->cid); + ast_channel_unlock(c); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = c; ast_copy_flags64(peerflags, o, OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | @@ -902,6 +941,18 @@ 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 (o->connected.id.number) { + ast_channel_update_connected_line(in, &o->connected); + } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) { + ast_channel_lock(c); + ast_connected_line_copy_from_caller(&connected_caller, &c->cid); + ast_channel_unlock(c); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = c; if (peer->cdr) { peer->cdr->answer = ast_tvnow(); @@ -970,6 +1021,29 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, ast_verb(3, "%s requested a source update, passing it to %s\n", c->name, in->name); ast_indicate(in, AST_CONTROL_SRCUPDATE); break; + case AST_CONTROL_CONNECTED_LINE: + if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + ast_verb(3, "Connected line update to %s prevented.\n", in->name); + } else 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_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); + ast_party_connected_line_free(&connected); + } else { + ast_verb(3, "%s connected line has changed, passing it to %s\n", c->name, in->name); + ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); + } + break; + case AST_CONTROL_REDIRECTING: + if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + ast_verb(3, "Redirecting update to %s prevented.\n", in->name); + } else { + ast_verb(3, "%s redirecting info has changed, passing it to %s\n", c->name, in->name); + ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); + } + break; case AST_CONTROL_PROCEEDING: ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name); if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) @@ -1084,7 +1158,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD) || (f->subclass == AST_CONTROL_VIDUPDATE) || - (f->subclass == AST_CONTROL_SRCUPDATE))) { + (f->subclass == AST_CONTROL_SRCUPDATE) || + (f->subclass == AST_CONTROL_CONNECTED_LINE) || + (f->subclass == AST_CONTROL_REDIRECTING))) { ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name); ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen); } @@ -1423,11 +1499,11 @@ static int setup_privacy_args(struct privacy_args *pa, ast_copy_string(pa->privcid, l, sizeof(pa->privcid)); - if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCLID)) { - /* if callerid is set and OPT_SCREEN_NOCLID is set also */ + if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCALLERID)) { + /* if callerid is set and OPT_SCREEN_NOCALLERID is set also */ ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid); pa->privdb_val = AST_PRIVACY_ALLOW; - } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { + } else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val); } @@ -1637,7 +1713,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags 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); + ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_IGNORE_CONNECTEDLINE); /* loop through the list of dial destinations */ rest = args.peers; @@ -1674,6 +1750,14 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); + /* If the incoming channel has previously had connected line information + * set on it (perhaps through the CONNECTED_LINE dialplan function) then + * seed the calllist's connected line information with this previously + * acquired info + */ + if (chan->connected.id.number) { + ast_party_connected_line_copy(&tmp->connected, &chan->connected); + } ast_channel_unlock(chan); if (datastore) @@ -1746,6 +1830,10 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags } pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); + ast_channel_lock(tc); + while (ast_channel_trylock(chan)) { + CHANNEL_DEADLOCK_AVOIDANCE(tc); + } /* Setup outgoing SDP to match incoming one */ if (!outgoing && !rest) { ast_rtp_instance_early_bridge_make_compatible(tc, chan); @@ -1759,20 +1847,31 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags tc->data = "(Outgoing Line)"; memset(&tc->whentohangup, 0, sizeof(tc->whentohangup)); - S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num)); - S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name)); - S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani)); - S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis)); + /* If the new channel has no callerid, try to guess what it should be */ + if (ast_strlen_zero(tc->cid.cid_num)) { + if (!ast_strlen_zero(chan->connected.id.number)) { + ast_set_callerid(tc, chan->connected.id.number, chan->connected.id.name, chan->connected.ani); + } else if (!ast_strlen_zero(chan->cid.cid_dnid)) { + ast_set_callerid(tc, chan->cid.cid_dnid, NULL, NULL); + } else if (!ast_strlen_zero(S_OR(chan->macroexten, chan->exten))) { + ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), NULL, NULL); + } + ast_set_flag64(tmp, DIAL_NOCONNECTEDLINE); + } + ast_connected_line_copy_from_caller(&tc->connected, &chan->cid); + + S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis)); + ast_party_redirecting_copy(&tc->redirecting, &chan->redirecting); + + tc->cid.cid_tns = chan->cid.cid_tns; + ast_string_field_set(tc, accountcode, chan->accountcode); tc->cdrflags = chan->cdrflags; if (ast_strlen_zero(tc->musicclass)) ast_string_field_set(tc, musicclass, chan->musicclass); - /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */ - tc->cid.cid_pres = chan->cid.cid_pres; - tc->cid.cid_ton = chan->cid.cid_ton; - tc->cid.cid_tns = chan->cid.cid_tns; - tc->cid.cid_ani2 = chan->cid.cid_ani2; + + /* Pass ADSI CPE and transfer capability */ tc->adsicpe = chan->adsicpe; tc->transfercapability = chan->transfercapability; @@ -1809,6 +1908,8 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags if (tc->hangupcause) { chan->hangupcause = tc->hangupcause; } + ast_channel_unlock(chan); + ast_channel_unlock(tc); ast_hangup(tc); tc = NULL; ast_free(tmp); @@ -1816,8 +1917,11 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags } else { senddialevent(chan, tc, numsubst); ast_verb(3, "Called %s\n", numsubst); - if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) + if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL); + } + ast_channel_unlock(chan); + ast_channel_unlock(tc); } /* Put them in the list of outgoing thingies... We're ready now. XXX If we're forcibly removed, these outgoing calls won't get diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c index 605d11faf..cfba99fce 100644 --- a/apps/app_directed_pickup.c +++ b/apps/app_directed_pickup.c @@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/app.h" #include "asterisk/features.h" +#include "asterisk/callerid.h" #define PICKUPMARK "PICKUPMARK" @@ -91,9 +92,21 @@ static const char *app2 = "PickupChan"; static int pickup_do(struct ast_channel *chan, struct ast_channel *target) { int res = 0; + struct ast_party_connected_line connected_caller; ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name); + connected_caller = target->connected; + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(chan, &connected_caller); + + ast_channel_lock(chan); + ast_connected_line_copy_from_caller(&connected_caller, &chan->cid); + ast_channel_unlock(chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_queue_connected_line_update(chan, &connected_caller); + ast_party_connected_line_free(&connected_caller); + if ((res = ast_answer(chan))) { ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); return -1; diff --git a/apps/app_queue.c b/apps/app_queue.c index 5726d4c5c..58b1c09ba 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -94,6 +94,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/strings.h" #include "asterisk/global_datastores.h" #include "asterisk/taskprocessor.h" +#include "asterisk/callerid.h" /*! * \par Please read before modifying this file. @@ -141,6 +142,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>Ignore call forward requests from queue members and do nothing when they are requested.</para> </option> + <option name="I"> + <para>Asterisk will ignore any connected line update requests or any redirecting party + update requests it may receive on this dial attempt.</para> + </option> <option name="r"> <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para> </option> @@ -625,6 +630,8 @@ struct callattempt { time_t lastcall; struct call_queue *lastqueue; struct member *member; + unsigned int update_connectedline:1; + struct ast_party_connected_line connected; }; @@ -2479,22 +2486,40 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies (*busies)++; return 0; } - + + ast_channel_lock(tmp->chan); + while (ast_channel_trylock(qe->chan)) { + CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan); + } + if (qe->cancel_answered_elsewhere) { ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); } tmp->chan->appl = "AppQueue"; tmp->chan->data = "(Outgoing Line)"; memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); - if (tmp->chan->cid.cid_num) - ast_free(tmp->chan->cid.cid_num); - tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); - if (tmp->chan->cid.cid_name) - ast_free(tmp->chan->cid.cid_name); - tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); - if (tmp->chan->cid.cid_ani) - ast_free(tmp->chan->cid.cid_ani); - tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); + + /* If the new channel has no callerid, try to guess what it should be */ + if (ast_strlen_zero(tmp->chan->cid.cid_num)) { + if (!ast_strlen_zero(qe->chan->connected.id.number)) { + ast_set_callerid(tmp->chan, qe->chan->connected.id.number, qe->chan->connected.id.name, qe->chan->connected.ani); + tmp->chan->cid.cid_pres = qe->chan->connected.id.number_presentation; + } else if (!ast_strlen_zero(qe->chan->cid.cid_dnid)) { + ast_set_callerid(tmp->chan, qe->chan->cid.cid_dnid, NULL, NULL); + } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) { + ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); + } + tmp->update_connectedline = 0; + } + + if (tmp->chan->cid.cid_rdnis) + ast_free(tmp->chan->cid.cid_rdnis); + tmp->chan->cid.cid_rdnis = ast_strdup(qe->chan->cid.cid_rdnis); + ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting); + + tmp->chan->cid.cid_tns = qe->chan->cid.cid_tns; + + ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->cid); /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(qe->chan, tmp->chan); @@ -2503,7 +2528,6 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies tmp->chan->adsicpe = qe->chan->adsicpe; /* Inherit context and extension */ - ast_channel_lock(qe->chan); macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); @@ -2511,13 +2535,14 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); else ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); - ast_channel_unlock(qe->chan); /* Place the call, but don't wait on the answer */ if ((res = ast_call(tmp->chan, location, 0))) { /* Again, keep going even if there's an error */ ast_debug(1, "ast call on peer returned %d\n", res); ast_verb(3, "Couldn't call %s\n", tmp->interface); + ast_channel_unlock(tmp->chan); + ast_channel_unlock(qe->chan); do_hang(tmp); (*busies)++; update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); @@ -2545,6 +2570,8 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); ast_verb(3, "Called %s\n", tmp->interface); } + ast_channel_unlock(tmp->chan); + ast_channel_unlock(qe->chan); update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); return 1; @@ -2775,7 +2802,7 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member * \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() */ -static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed) +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) { const char *queue = qe->parent->name; struct callattempt *o, *start = NULL, *prev = NULL; @@ -2795,6 +2822,12 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte #ifdef HAVE_EPOLL struct callattempt *epollo; #endif + struct ast_party_connected_line connected_caller; + char *inchan_name; + + ast_channel_lock(qe->chan); + inchan_name = ast_strdupa(qe->chan->name); + ast_channel_unlock(qe->chan); starttime = (long) time(NULL); #ifdef HAVE_EPOLL @@ -2845,9 +2878,28 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte } winner = ast_waitfor_n(watchers, pos, to); for (o = start; o; o = o->call_next) { + /* We go with a static 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]; + ast_channel_lock(o->chan); + ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); + ast_channel_unlock(o->chan); if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { if (!peer) { - ast_verb(3, "%s answered %s\n", o->chan->name, in->name); + ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); + if (update_connectedline) { + if (o->connected.id.number) { + ast_channel_update_connected_line(in, &o->connected); + } else if (o->update_connectedline) { + ast_channel_lock(o->chan); + ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid); + ast_channel_unlock(o->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = o; } } else if (o->chan && (o->chan == winner)) { @@ -2856,12 +2908,15 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte ast_copy_string(membername, o->member->membername, sizeof(membername)); if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { - ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); + ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); numnochan++; do_hang(o); winner = NULL; continue; } else if (!ast_strlen_zero(o->chan->call_forward)) { + struct ast_party_redirecting *apr = &o->chan->redirecting; + struct ast_party_connected_line *apc = &o->chan->connected; + struct ast_channel *original = o->chan; char tmpchan[256]; char *stuff; char *tech; @@ -2876,7 +2931,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte tech = "Local"; } /* Before processing channel, go ahead and check for forwarding */ - ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); + 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, stuff, &status); if (!o->chan) { @@ -2884,32 +2939,42 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte o->stillgoing = 0; numnochan++; } else { + ast_channel_lock(o->chan); + while (ast_channel_trylock(in)) { + CHANNEL_DEADLOCK_AVOIDANCE(o->chan); + } ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); - if (o->chan->cid.cid_num) - ast_free(o->chan->cid.cid_num); - o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); - - if (o->chan->cid.cid_name) - ast_free(o->chan->cid.cid_name); - o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); ast_string_field_set(o->chan, accountcode, in->accountcode); o->chan->cdrflags = in->cdrflags; - if (in->cid.cid_ani) { - if (o->chan->cid.cid_ani) - ast_free(o->chan->cid.cid_ani); - o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); - } + ast_channel_set_redirecting(o->chan, apr); + if (o->chan->cid.cid_rdnis) ast_free(o->chan->cid.cid_rdnis); - o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); + o->chan->cid.cid_rdnis = ast_strdup(S_OR(original->cid.cid_rdnis,S_OR(in->macroexten, in->exten))); + + o->chan->cid.cid_tns = in->cid.cid_tns; + + ast_party_caller_copy(&o->chan->cid, &in->cid); + ast_party_connected_line_copy(&o->chan->connected, apc); + + ast_channel_update_redirecting(in, apr); + if (in->cid.cid_rdnis) { + ast_free(in->cid.cid_rdnis); + } + in->cid.cid_rdnis = ast_strdup(o->chan->cid.cid_rdnis); + + update_connectedline = 1; + if (ast_call(o->chan, tmpchan, 0)) { ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); do_hang(o); numnochan++; } + ast_channel_unlock(in); + ast_channel_unlock(o->chan); } /* Hangup the original channel now, in case we needed it */ ast_hangup(winner); @@ -2922,12 +2987,24 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte case AST_CONTROL_ANSWER: /* This is our guy if someone answered. */ if (!peer) { - ast_verb(3, "%s answered %s\n", o->chan->name, in->name); + ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); + if (update_connectedline) { + if (o->connected.id.number) { + ast_channel_update_connected_line(in, &o->connected); + } else if (o->update_connectedline) { + ast_channel_lock(o->chan); + ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid); + ast_channel_unlock(o->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = o; } break; case AST_CONTROL_BUSY: - ast_verb(3, "%s is busy\n", o->chan->name); + ast_verb(3, "%s is busy\n", ochan_name); if (in->cdr) ast_cdr_busy(in->cdr); do_hang(o); @@ -2942,7 +3019,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte numbusies++; break; case AST_CONTROL_CONGESTION: - ast_verb(3, "%s is circuit-busy\n", o->chan->name); + ast_verb(3, "%s is circuit-busy\n", ochan_name); if (in->cdr) ast_cdr_busy(in->cdr); endtime = (long) time(NULL); @@ -2957,13 +3034,37 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte numbusies++; break; case AST_CONTROL_RINGING: - ast_verb(3, "%s is ringing\n", o->chan->name); + ast_verb(3, "%s is ringing\n", ochan_name); break; case AST_CONTROL_OFFHOOK: /* Ignore going off hook */ break; + case AST_CONTROL_CONNECTED_LINE: + if (!update_connectedline) { + ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); + } else 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); + ast_party_connected_line_free(&connected); + } else { + ast_verb(3, "%s connected line has changed, passing it to %s\n", ochan_name, inchan_name); + ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); + } + break; + case AST_CONTROL_REDIRECTING: + if (!update_connectedline) { + ast_verb(3, "Redirecting update to %s prevented\n", inchan_name); + } else { + ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name); + ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); + } + break; default: ast_debug(1, "Dunno what to do with control type %d\n", f->subclass); + break; } } ast_frfree(f); @@ -3517,6 +3618,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 callcompletedinsl; struct ao2_iterator memi; struct ast_datastore *datastore, *transfer_ds; @@ -3582,6 +3684,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce case 'i': forwardsallowed = 0; break; + case 'I': + update_connectedline = 0; + break; case 'x': ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); break; @@ -3591,7 +3696,6 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce case 'C': qe->cancel_answered_elsewhere = 1; break; - } /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. @@ -3661,6 +3765,17 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce } } AST_LIST_UNLOCK(dialed_interfaces); + + ast_channel_lock(qe->chan); + /* If any pre-existing connected line information exists on this + * channel, like from the CONNECTED_LINE dialplan function, use this + * to seed the connected line information. It may, of course, be updated + * during the call + */ + if (qe->chan->connected.id.number) { + ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); + } + ast_channel_unlock(qe->chan); if (di) { free(tmp); @@ -3692,6 +3807,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce tmp->oldstatus = cur->status; tmp->lastcall = cur->lastcall; tmp->lastqueue = cur->lastqueue; + tmp->update_connectedline = 1; ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); /* Special case: If we ring everyone, go ahead and ring them, otherwise just calculate their metric for the appropriate strategy */ @@ -3732,7 +3848,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce ring_one(qe, outgoing, &numbusies); if (use_weight) ao2_unlock(queues); - lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); + lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline); /* 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 |