aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authormmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2010-04-09 15:31:32 +0000
committermmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2010-04-09 15:31:32 +0000
commit0eb1e5407a6eacd46d98e134dc81e8b857c103b7 (patch)
tree0b1d16ff83df2f35441f03a082b848262b8a2557 /apps
parent6c57cdc6ac82a6a6700ebdb788d690471d8fc49d (diff)
Merge Call completion support into trunk.
From Reviewboard: CCSS stands for Call Completion Supplementary Services. An admittedly out-of-date overview of the architecture can be found in the file doc/CCSS_architecture.pdf in the CCSS branch. Off the top of my head, the big differences between what is implemented and what is in the document are as follows: 1. We did not end up modifying the Hangup application at all. 2. The document states that a single call completion monitor may be used across multiple calls to the same device. This proved to not be such a good idea when implementing protocol-specific monitors, and so we ended up using one monitor per-device per-call. 3. There are some configuration options which were conceived after the document was written. These are documented in the ccss.conf.sample that is on this review request. For some basic understanding of terminology used throughout this code, see the ccss.tex document that is on this review. This implements CCBS and CCNR in several flavors. First up is a "generic" implementation, which can work over any channel technology provided that the channel technology can accurately report device state. Call completion is requested using the dialplan application CallCompletionRequest and can be canceled using CallCompletionCancel. Device state subscriptions are used in order to monitor the state of called parties. Next, there is a SIP-specific implementation of call completion. This method uses the methods outlined in draft-ietf-bliss-call-completion-06 to implement call completion using SIP signaling. There are a few things to note here: * The agent/monitor terminology used throughout Asterisk sometimes is the reverse of what is defined in the referenced draft. * Implementation of the draft required support for SIP PUBLISH. I attempted to write this in a generic-enough fashion such that if someone were to want to write PUBLISH support for other event packages, such as dialog-state or presence, most of the effort would be in writing callbacks specific to the event package. * A subportion of supporting PUBLISH reception was that we had to implement a PIDF parser. The PIDF support added is a bit minimal. I first wrote a validation routine to ensure that the PIDF document is formatted properly. The rest of the PIDF reading is done in-line in the call-completion-specific PUBLISH-handling code. In other words, while there is PIDF support here, it is not in any state where it could easily be applied to other event packages as is. Finally, there are a variety of ISDN-related call completion protocols supported. These were written by Richard Mudgett, and as such I can't really say much about their implementation. There are notes in the CHANGES file that indicate the ISDN protocols over which call completion is supported. Review: https://reviewboard.asterisk.org/r/523 git-svn-id: http://svn.digium.com/svn/asterisk/trunk@256528 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'apps')
-rw-r--r--apps/app_dial.c107
1 files changed, 98 insertions, 9 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 8a58932a8..b1de21d5f 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
#include "asterisk/cel.h"
+#include "asterisk/ccss.h"
#include "asterisk/indications.h"
/*** DOCUMENTATION
@@ -810,6 +811,12 @@ static void do_forward(struct chanlist *o,
ast_channel_make_compatible(o->chan, in);
ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(in, 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.
+ */
+ ast_ignore_cc(o->chan);
+ ast_log(LOG_NOTICE, "Not accepting call completion offers from call-forward recipient %s\n", o->chan->name);
} else
ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
}
@@ -904,7 +911,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
char *opt_args[],
struct privacy_args *pa,
- const struct cause_args *num_in, int *result, char *dtmf_progress)
+ const struct cause_args *num_in, int *result, char *dtmf_progress,
+ const int ignore_cc)
{
struct cause_args num = *num_in;
int prestart = num.busy + num.congestion + num.nochan;
@@ -917,6 +925,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
#endif
struct ast_party_connected_line connected_caller;
struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1);
+ int cc_recall_core_id;
+ int is_cc_recall;
+ int cc_frame_received = 0;
+ int num_ringing = 0;
ast_party_connected_line_init(&connected_caller);
if (single) {
@@ -938,6 +950,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
}
+ is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
+
#ifdef HAVE_EPOLL
for (epollo = outgoing; epollo; epollo = epollo->next)
ast_poll_channel_add(in, epollo->chan);
@@ -970,6 +984,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
}
*to = 0;
+ if (is_cc_recall) {
+ ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
+ }
return NULL;
}
winner = ast_waitfor_n(watchers, pos, to);
@@ -1014,6 +1031,15 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
/* here, o->chan == c == winner */
if (!ast_strlen_zero(c->call_forward)) {
pa->sentringing = 0;
+ if (!ignore_cc && (f = ast_read(c))) {
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_CC) {
+ /* This channel is forwarding the call, and is capable of CC, so
+ * be sure to add the new device interface to the list
+ */
+ ast_handle_cc_control_frame(in, c, f->data.ptr);
+ }
+ ast_frfree(f);
+ }
do_forward(o, &num, peerflags, single, to);
continue;
}
@@ -1088,13 +1114,41 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
handle_cause(AST_CAUSE_CONGESTION, &num);
break;
case AST_CONTROL_RINGING:
- ast_verb(3, "%s is ringing\n", c->name);
- /* Setup early media if appropriate */
- if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
- ast_channel_early_bridge(in, c);
- if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
- ast_indicate(in, AST_CONTROL_RINGING);
- pa->sentringing++;
+ /* This is a tricky area to get right when using a native
+ * CC agent. The reason is that we do the best we can to send only a
+ * single ringing notification to the caller.
+ *
+ * Call completion complicates the logic used here. CCNR is typically
+ * offered during a ringing message. Let's say that party A calls
+ * parties B, C, and D. B and C do not support CC requests, but D
+ * does. If we were to receive a ringing notification from B before
+ * the others, then we would end up sending a ringing message to
+ * A with no CCNR offer present.
+ *
+ * The approach that we have taken is that if we receive a ringing
+ * response from a party and no CCNR offer is present, we need to
+ * wait. Specifically, we need to wait until either a) a called party
+ * offers CCNR in its ringing response or b) all called parties have
+ * responded in some way to our call and none offers CCNR.
+ *
+ * The drawback to this is that if one of the parties has a delayed
+ * response or, god forbid, one just plain doesn't respond to our
+ * outgoing call, then this will result in a significant delay between
+ * when the caller places the call and hears ringback.
+ *
+ * Note also that if CC is disabled for this call, then it is perfectly
+ * fine for ringing frames to get sent through.
+ */
+ ++num_ringing;
+ if (ignore_cc || cc_frame_received || num_ringing == numlines) {
+ ast_verb(3, "%s is ringing\n", c->name);
+ /* Setup early media if appropriate */
+ if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
+ ast_channel_early_bridge(in, c);
+ if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
+ ast_indicate(in, AST_CONTROL_RINGING);
+ pa->sentringing++;
+ }
}
break;
case AST_CONTROL_PROGRESS:
@@ -1163,6 +1217,12 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
case AST_CONTROL_FLASH:
/* Ignore going off hook and flash */
break;
+ case AST_CONTROL_CC:
+ if (!ignore_cc) {
+ ast_handle_cc_control_frame(in, c, f->data.ptr);
+ cc_frame_received = 1;
+ }
+ break;
case -1:
if (!ast_test_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
ast_verb(3, "%s stopped sounds\n", c->name);
@@ -1212,6 +1272,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
ast_frfree(f);
}
+ if (is_cc_recall) {
+ ast_cc_completed(in, "CC completed, although the caller hung up (cancelled)");
+ }
return NULL;
}
@@ -1229,6 +1292,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
strcpy(pa->status, "CANCEL");
ast_frfree(f);
ast_channel_unlock(in);
+ if (is_cc_recall) {
+ ast_cc_completed(in, "CC completed, but the caller used DTMF to exit");
+ }
return NULL;
}
ast_channel_unlock(in);
@@ -1241,6 +1307,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
strcpy(pa->status, "CANCEL");
ast_cdr_noanswer(in->cdr);
ast_frfree(f);
+ if (is_cc_recall) {
+ ast_cc_completed(in, "CC completed, but the caller hung up with DTMF");
+ }
return NULL;
}
}
@@ -1283,6 +1352,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
#endif
+ if (is_cc_recall) {
+ ast_cc_completed(in, "Recall completed!");
+ }
return peer;
}
@@ -1656,6 +1728,8 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
char *opt_args[OPT_ARG_ARRAY_SIZE];
struct ast_datastore *datastore = NULL;
int fulldial = 0, num_dialed = 0;
+ int ignore_cc = 0;
+ char device_name[AST_CHANNEL_NAME];
/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
pbx_builtin_setvar_helper(chan, "DIALSTATUS", "");
@@ -1686,6 +1760,10 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
goto done;
}
+ if (ast_cc_call_init(chan, &ignore_cc)) {
+ goto done;
+ }
+
if (ast_test_flag64(&opts, OPT_SCREEN_NOINTRO) && !ast_strlen_zero(opt_args[OPT_ARG_SCREEN_NOINTRO])) {
delprivintro = atoi(opt_args[OPT_ARG_SCREEN_NOINTRO]);
@@ -1871,8 +1949,17 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
if (!rest) /* we are on the last destination */
chan->hangupcause = cause;
chanlist_free(tmp);
+ if (!ignore_cc && (cause == AST_CAUSE_BUSY || cause == AST_CAUSE_CONGESTION)) {
+ if (!ast_cc_callback(chan, tech, numsubst, ast_cc_busy_interface)) {
+ ast_cc_extension_monitor_add_dialstring(chan, interface, "");
+ }
+ }
continue;
}
+ ast_channel_get_device_name(tc, device_name, sizeof(device_name));
+ if (!ignore_cc) {
+ ast_cc_extension_monitor_add_dialstring(chan, interface, device_name);
+ }
pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
ast_channel_lock(tc);
@@ -1965,6 +2052,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
chan->hangupcause = tc->hangupcause;
}
ast_channel_unlock(chan);
+ ast_cc_call_failed(chan, tc, interface);
ast_hangup(tc);
tc = NULL;
chanlist_free(tmp);
@@ -2038,7 +2126,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
}
}
- peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress);
+ peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress, ignore_cc);
/* The ast_channel_datastore_remove() function could fail here if the
* datastore was moved to another channel during a masquerade. If this is
@@ -2513,6 +2601,7 @@ done:
if (config.start_sound) {
ast_free((char *)config.start_sound);
}
+ ast_ignore_cc(chan);
return res;
}