summaryrefslogtreecommitdiff
path: root/apps/app_queue.c
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2009-04-03 22:41:46 +0000
committerMark Michelson <mmichelson@digium.com>2009-04-03 22:41:46 +0000
commit6f53ed4c6707b30078ed4863e27facb7b454b600 (patch)
tree2e466f746a2e29094d6dcc3c6f2577f4dd85f4c0 /apps/app_queue.c
parent3525e37e633b8b7bcf59262fbab21c16afadfa35 (diff)
This commit introduces COLP/CONP and Redirecting party information into Asterisk.
The channel drivers which have been most heavily tested with these enhancements are chan_sip and chan_misdn. Further work is being done to add Q.SIG support and will be introduced in a later commit. chan_skinny has code added to it here, but according to user pj, the support on chan_skinny is not working as of now. This will be fixed in a later commit. A special thanks goes out to bugtracker user gareth for getting the ball rolling and providing the initial support for this work. Without his initial work on this, this would not have been nearly as painless as it was. This functionality has been tested by Digium's product quality department, as well as a customer site running thousands of calls every day. In addition, many many many many bugtracker users have tested this, too. (closes issue #8824) Reported by: gareth Review: http://reviewboard.digium.com/r/201 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@186525 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps/app_queue.c')
-rw-r--r--apps/app_queue.c186
1 files changed, 151 insertions, 35 deletions
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