aboutsummaryrefslogtreecommitdiffstats
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
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
-rw-r--r--CHANGES7
-rw-r--r--apps/app_dial.c107
-rw-r--r--channels/chan_dahdi.c915
-rw-r--r--channels/chan_local.c16
-rw-r--r--channels/chan_sip.c1914
-rw-r--r--channels/sig_analog.c26
-rw-r--r--channels/sig_analog.h2
-rw-r--r--channels/sig_pri.c1552
-rw-r--r--channels/sig_pri.h102
-rw-r--r--channels/sip/include/sip.h365
-rw-r--r--configs/ccss.conf.sample150
-rw-r--r--configs/chan_dahdi.conf.sample56
-rw-r--r--configs/manager.conf.sample3
-rw-r--r--configure.ac2
-rw-r--r--doc/tex/asterisk.tex3
-rw-r--r--doc/tex/ccss.tex414
-rw-r--r--funcs/func_callcompletion.c114
-rw-r--r--include/asterisk/ccss.h1582
-rw-r--r--include/asterisk/channel.h138
-rw-r--r--include/asterisk/channelstate.h53
-rw-r--r--include/asterisk/devicestate.h2
-rw-r--r--include/asterisk/frame.h9
-rw-r--r--include/asterisk/manager.h1
-rw-r--r--include/asterisk/rtp_engine.h1
-rw-r--r--include/asterisk/xml.h10
-rw-r--r--main/asterisk.c6
-rw-r--r--main/ccss.c4157
-rw-r--r--main/channel.c105
-rw-r--r--main/manager.c1
-rw-r--r--main/xml.c30
30 files changed, 11611 insertions, 232 deletions
diff --git a/CHANGES b/CHANGES
index 2ea3e5ff7..218da9cfa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -387,6 +387,13 @@ Calendaring for Asterisk
iCalendar, CalDAV, and Exchange Server calendars are supported (Exchange support
only tested on Exchange Server 2003 with no support for forms-based authentication).
+Call Completion Supplementary Services for Asterisk
+---------------------------------------------------
+ * Call completion support has been added for SIP, DAHDI/ISDN, and DAHDI/analog.
+ DAHDI/ISDN supports call completion for the following switch types:
+ EuroIsdn(ETSI) for PTP and PTMP modes, and Qsig.
+ See doc/CCSS_architecture.pdf and doc/tex/ccss.tex(asterisk.pdf) for details.
+
Multicast RTP Support
---------------------
* A new RTP engine and channel driver have been added which supports Multicast RTP.
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;
}
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 356106e4a..d9ae8a6f5 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -116,6 +116,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/event.h"
#include "asterisk/devicestate.h"
#include "asterisk/paths.h"
+#include "asterisk/ccss.h"
/*** DOCUMENTATION
<application name="DAHDISendKeypadFacility" language="en_US">
@@ -608,6 +609,11 @@ struct dahdi_pri {
static struct dahdi_pri pris[NUM_SPANS];
+#if defined(HAVE_PRI_CCSS)
+/*! DAHDI PRI CCSS agent and monitor type name. */
+static const char dahdi_pri_cc_type[] = "DAHDI/PRI";
+#endif /* defined(HAVE_PRI_CCSS) */
+
#else
/*! Shut up the compiler */
struct dahdi_pri;
@@ -1252,6 +1258,14 @@ struct dahdi_pvt {
/*! \brief TRUE if confrence is muted. */
int muting;
void *sig_pvt;
+ struct ast_cc_config_params *cc_params;
+ /* DAHDI channel names may differ greatly from the
+ * string that was provided to an app such as Dial. We
+ * need to save the original string passed to dahdi_request
+ * for call completion purposes. This way, we can replicate
+ * the original dialed string later.
+ */
+ char dialstring[AST_CHANNEL_NAME];
};
static struct dahdi_pvt *iflist = NULL; /*!< Main interface list start */
@@ -1315,6 +1329,12 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
.nodetype = PRI_CPE,
.qsigchannelmapping = DAHDI_CHAN_MAPPING_PHYSICAL,
+#if defined(HAVE_PRI_CCSS)
+ .cc_ptmp_recall_mode = 1,/* specificRecall */
+ .cc_qsig_signaling_link_req = 1,/* retain */
+ .cc_qsig_signaling_link_rsp = 1,/* retain */
+#endif /* defined(HAVE_PRI_CCSS) */
+
.minunused = 2,
.idleext = "",
.idledial = "",
@@ -1398,6 +1418,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
.buf_policy = DAHDI_POLICY_IMMEDIATE,
.buf_no = numbufs,
.usefaxbuffers = 0,
+ .cc_params = ast_cc_config_params_init(),
},
.timing = {
.prewinktime = -1,
@@ -1433,6 +1454,8 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int
static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen);
static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len);
static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value);
+static int dahdi_devicestate(void *data);
+static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback);
static const struct ast_channel_tech dahdi_tech = {
.type = "DAHDI",
@@ -1455,6 +1478,8 @@ static const struct ast_channel_tech dahdi_tech = {
.queryoption = dahdi_queryoption,
.func_channel_read = dahdi_func_read,
.func_channel_write = dahdi_func_write,
+ .devicestate = dahdi_devicestate,
+ .cc_callback = dahdi_cc_callback,
};
#define GET_CHANNEL(p) ((p)->channel)
@@ -2152,6 +2177,13 @@ static void my_set_pulsedial(void *pvt, int flag)
p->pulsedial = flag;
}
+static const char *my_get_orig_dialstring(void *pvt)
+{
+ struct dahdi_pvt *p = pvt;
+
+ return p->dialstring;
+}
+
static void my_increase_ss_count(void)
{
ast_mutex_lock(&ss_thread_lock);
@@ -2785,6 +2817,160 @@ static void my_pri_set_rdnis(void *pvt, const char *rdnis)
ast_copy_string(p->rdnis, rdnis, sizeof(p->rdnis));
}
+/*!
+ * \internal
+ * \brief Make a dialstring for native ISDN CC to recall properly.
+ * \since 1.8
+ *
+ * \param priv Channel private control structure.
+ * \param buf Where to put the modified dialstring.
+ * \param buf_size Size of modified dialstring buffer.
+ *
+ * \details
+ * original dialstring:
+ * DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]]
+ * DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]
+ *
+ * The modified dialstring will have prefixed the channel-group section
+ * with the ISDN channel restriction.
+ *
+ * buf:
+ * DAHDI/i<span>-<channel#>[c|r<cadance#>|d][/extension[/options]]
+ * DAHDI/i<span>-(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]
+ *
+ * The routine will check to see if the ISDN channel restriction is already
+ * in the original dialstring.
+ *
+ * \return Nothing
+ */
+static void my_pri_make_cc_dialstring(void *priv, char *buf, size_t buf_size)
+{
+ char *dial;
+ struct dahdi_pvt *pvt;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(tech); /* channel technology token */
+ AST_APP_ARG(group); /* channel/group token */
+ //AST_APP_ARG(ext); /* extension token */
+ //AST_APP_ARG(opts); /* options token */
+ //AST_APP_ARG(other); /* Any remining unused arguments */
+ );
+
+ pvt = priv;
+ dial = ast_strdupa(pvt->dialstring);
+ AST_NONSTANDARD_APP_ARGS(args, dial, '/');
+ if (!args.tech) {
+ ast_copy_string(buf, pvt->dialstring, buf_size);
+ return;
+ }
+ if (!args.group) {
+ /* Append the ISDN span channel restriction to the dialstring. */
+ snprintf(buf, buf_size, "%s/i%d-", args.tech, pvt->pri->span);
+ return;
+ }
+ if (args.group[0] == 'i') {
+ /* The ISDN span channel restriction is already in the dialstring. */
+ ast_copy_string(buf, pvt->dialstring, buf_size);
+ return;
+ }
+ /* Insert the ISDN span channel restriction into the dialstring. */
+ snprintf(buf, buf_size, "%s/i%d-%s", args.tech, pvt->pri->span, args.group);
+}
+
+/*!
+ * \internal
+ * \brief Reevaluate the PRI span device state.
+ * \since 1.8
+ *
+ * \param pri Asterisk D channel control structure.
+ *
+ * \return Nothing
+ *
+ * \note Assumes the pri->lock is already obtained.
+ */
+static void dahdi_pri_update_span_devstate(struct sig_pri_pri *pri)
+{
+ unsigned idx;
+ unsigned num_b_chans; /* Number of B channels provisioned on the span. */
+ unsigned in_use; /* Number of B channels in use on the span. */
+ unsigned in_alarm; /* TRUE if the span is in alarm condition. */
+ enum ast_device_state new_state;
+
+ /* Count the number of B channels and the number of B channels in use. */
+ num_b_chans = 0;
+ in_use = 0;
+ in_alarm = 1;
+ for (idx = pri->numchans; idx--;) {
+ if (pri->pvts[idx] && !pri->pvts[idx]->no_b_channel) {
+ /* This is a B channel interface. */
+ ++num_b_chans;
+ if (pri->pvts[idx]->owner
+#if defined(HAVE_PRI_SERVICE_MESSAGES)
+ /* Out-of-service B channels are "in-use". */
+ && pri->pvts[idx]->service_status
+#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */
+ ) {
+ ++in_use;
+ }
+ if (!pri->pvts[idx]->inalarm) {
+ /* There is a channel that is not in alarm. */
+ in_alarm = 0;
+ }
+ }
+ }
+
+ /* Update the span congestion device state and report any change. */
+ if (in_alarm) {
+ new_state = AST_DEVICE_UNAVAILABLE;
+ } else {
+ new_state = num_b_chans == in_use ? AST_DEVICE_BUSY : AST_DEVICE_NOT_INUSE;
+ }
+ if (pri->congestion_devstate != new_state) {
+ pri->congestion_devstate = new_state;
+ ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/congestion", pri->span);
+ }
+#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
+ /* Update the span threshold device state and report any change. */
+ if (in_alarm) {
+ new_state = AST_DEVICE_UNAVAILABLE;
+ } else if (!in_use) {
+ new_state = AST_DEVICE_NOT_INUSE;
+ } else if (!pri->user_busy_threshold) {
+ new_state = in_use < num_b_chans ? AST_DEVICE_INUSE : AST_DEVICE_BUSY;
+ } else {
+ new_state = in_use < pri->user_busy_threshold ? AST_DEVICE_INUSE
+ : AST_DEVICE_BUSY;
+ }
+ if (pri->threshold_devstate != new_state) {
+ pri->threshold_devstate = new_state;
+ ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/threshold", pri->span);
+ }
+#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
+}
+
+/*!
+ * \internal
+ * \brief Reference this module.
+ * \since 1.8
+ *
+ * \return Nothing
+ */
+static void my_module_ref(void)
+{
+ ast_module_ref(ast_module_info->self);
+}
+
+/*!
+ * \internal
+ * \brief Unreference this module.
+ * \since 1.8
+ *
+ * \return Nothing
+ */
+static void my_module_unref(void)
+{
+ ast_module_unref(ast_module_info->self);
+}
+
static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri);
static struct sig_pri_callback dahdi_pri_callbacks =
@@ -2803,6 +2989,11 @@ static struct sig_pri_callback dahdi_pri_callbacks =
.set_dnid = my_pri_set_dnid,
.set_rdnis = my_pri_set_rdnis,
.new_nobch_intf = dahdi_new_pri_nobch_channel,
+ .get_orig_dialstring = my_get_orig_dialstring,
+ .make_cc_dialstring = my_pri_make_cc_dialstring,
+ .update_span_devstate = dahdi_pri_update_span_devstate,
+ .module_ref = my_module_ref,
+ .module_unref = my_module_unref,
};
#endif /* defined(HAVE_PRI) */
@@ -2932,6 +3123,7 @@ static struct analog_callback dahdi_analog_callbacks =
.cancel_cidspill = my_cancel_cidspill,
.confmute = my_confmute,
.set_pulsedial = my_set_pulsedial,
+ .get_orig_dialstring = my_get_orig_dialstring,
};
static struct dahdi_pvt *round_robin[32];
@@ -5122,6 +5314,9 @@ static void destroy_dahdi_pvt(struct dahdi_pvt *pvt)
if (p->vars) {
ast_variables_destroy(p->vars);
}
+ if (p->cc_params) {
+ ast_cc_config_params_destroy(p->cc_params);
+ }
ast_mutex_destroy(&p->lock);
dahdi_close_sub(p, SUB_REAL);
if (p->owner)
@@ -5957,6 +6152,18 @@ static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, i
*cp = (p->callprogress & CALLPROGRESS_FAX) ? 0 : 1;
ast_debug(1, "Reporting fax tone detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
break;
+ case AST_OPTION_CC_AGENT_TYPE:
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CCSS)
+ if (dahdi_sig_pri_lib_handles(p->sig)) {
+ ast_copy_string((char *) data, dahdi_pri_cc_type, *datalen);
+ break;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+#endif /* defined(HAVE_PRI) */
+ return -1;
+ default:
+ return -1;
}
errno = 0;
@@ -8582,37 +8789,28 @@ static int dahdi_indicate(struct ast_channel *chan, int condition, const void *d
return res;
}
-static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid)
+#if defined(HAVE_PRI)
+static struct ast_str *create_channel_name(struct dahdi_pvt *i, int is_outgoing, char *address)
+#else
+static struct ast_str *create_channel_name(struct dahdi_pvt *i)
+#endif /* defined(HAVE_PRI) */
{
- struct ast_channel *tmp;
- format_t deflaw;
- int res;
- int x,y;
- int features;
struct ast_str *chan_name;
- struct ast_variable *v;
- struct dahdi_params ps;
+ int x, y;
- if (i->subs[idx].owner) {
- ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
+ /* Create the new channel name tail. */
+ if (!(chan_name = ast_str_create(32))) {
return NULL;
}
-
- /* Create the new channel name tail. */
- chan_name = ast_str_alloca(32);
if (i->channel == CHAN_PSEUDO) {
ast_str_set(&chan_name, 0, "pseudo-%ld", ast_random());
#if defined(HAVE_PRI)
} else if (i->pri) {
ast_mutex_lock(&i->pri->lock);
y = ++i->pri->new_chan_seq;
- if (i->outgoing) {
- /*
- * The dnid has been stuffed with the called-number[:subaddress]
- * by dahdi_request().
- */
- ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, i->dnid, y);
- i->dnid[0] = '\0';
+ if (is_outgoing) {
+ ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, address, y);
+ address[0] = '\0';
} else if (ast_strlen_zero(i->cid_subaddr)) {
/* Put in caller-id number only since there is no subaddress. */
ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, i->cid_num, y);
@@ -8636,11 +8834,49 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
++y;
} while (x < 3);
}
+ return chan_name;
+}
+
+static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid)
+{
+ struct ast_channel *tmp;
+ format_t deflaw;
+ int res;
+ int x;
+ int features;
+ struct ast_str *chan_name;
+ struct ast_variable *v;
+ struct dahdi_params ps;
+
+ if (i->subs[idx].owner) {
+ ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
+ return NULL;
+ }
+
+#if defined(HAVE_PRI)
+ /*
+ * The dnid has been stuffed with the called-number[:subaddress]
+ * by dahdi_request() for outgoing calls.
+ */
+ chan_name = create_channel_name(i, i->outgoing, i->dnid);
+#else
+ chan_name = create_channel_name(i);
+#endif /* defined(HAVE_PRI) */
+ if (!chan_name) {
+ return NULL;
+ }
tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "DAHDI/%s", ast_str_buffer(chan_name));
+ ast_free(chan_name);
if (!tmp)
return NULL;
tmp->tech = &dahdi_tech;
+#if defined(HAVE_PRI)
+ if (i->pri) {
+ ast_cc_copy_config_params(i->cc_params, i->pri->cc_params);
+ }
+#endif /* defined(HAVE_PRI) */
+ ast_channel_cc_params_init(tmp, i->cc_params);
memset(&ps, 0, sizeof(ps));
res = ioctl(i->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps);
if (res) {
@@ -11169,6 +11405,11 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
if (!tmp) {
return NULL;
}
+ tmp->cc_params = ast_cc_config_params_init();
+ if (!tmp->cc_params) {
+ ast_free(tmp);
+ return NULL;
+ }
ast_mutex_init(&tmp->lock);
ifcount++;
for (x = 0; x < 3; x++)
@@ -11412,6 +11653,16 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
tmp->sig_pvt = pchan;
tmp->pri = &pris[span].pri;
+ if (!tmp->pri->cc_params) {
+ tmp->pri->cc_params = ast_cc_config_params_init();
+ if (!tmp->pri->cc_params) {
+ destroy_dahdi_pvt(tmp);
+ return NULL;
+ }
+ }
+ ast_cc_copy_config_params(tmp->pri->cc_params,
+ conf->chan.cc_params);
+
pris[span].pri.sig = chan_sig;
pris[span].pri.nodetype = conf->pri.pri.nodetype;
pris[span].pri.switchtype = myswitchtype;
@@ -11434,6 +11685,14 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
pris[span].pri.hold_disconnect_transfer =
conf->pri.pri.hold_disconnect_transfer;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
+#if defined(HAVE_PRI_CCSS)
+ pris[span].pri.cc_ptmp_recall_mode =
+ conf->pri.pri.cc_ptmp_recall_mode;
+ pris[span].pri.cc_qsig_signaling_link_req =
+ conf->pri.pri.cc_qsig_signaling_link_req;
+ pris[span].pri.cc_qsig_signaling_link_rsp =
+ conf->pri.pri.cc_qsig_signaling_link_rsp;
+#endif /* defined(HAVE_PRI_CCSS) */
pris[span].pri.facilityenable = conf->pri.pri.facilityenable;
ast_copy_string(pris[span].pri.msn_list, conf->pri.pri.msn_list, sizeof(pris[span].pri.msn_list));
ast_copy_string(pris[span].pri.idledial, conf->pri.pri.idledial, sizeof(pris[span].pri.idledial));
@@ -11742,6 +12001,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
tmp->answeronpolarityswitch = conf->chan.answeronpolarityswitch;
tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
tmp->sendcalleridafter = conf->chan.sendcalleridafter;
+ ast_cc_copy_config_params(tmp->cc_params, conf->chan.cc_params);
if (!here) {
tmp->locallyblocked = tmp->remotelyblocked = 0;
@@ -11881,21 +12141,36 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
return tmp;
}
-static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *channelmatched, int *groupmatched)
+static int is_group_or_channel_match(struct dahdi_pvt *p, int span, ast_group_t groupmatch, int *groupmatched, int channelmatch, int *channelmatched)
{
- /* First, check group matching */
+#if defined(HAVE_PRI)
+ if (0 < span) {
+ /* The channel must be on the specified PRI span. */
+ if (!p->pri || p->pri->span != span) {
+ return 0;
+ }
+ }
+#endif /* defined(HAVE_PRI) */
+ /* check group matching */
if (groupmatch) {
if ((p->group & groupmatch) != groupmatch)
+ /* Doesn't match the specified group, try the next one */
return 0;
*groupmatched = 1;
}
/* Check to see if we have a channel match */
if (channelmatch != -1) {
if (p->channel != channelmatch)
+ /* Doesn't match the specified channel, try the next one */
return 0;
*channelmatched = 1;
}
+ return 1;
+}
+
+static int available(struct dahdi_pvt *p)
+{
if (p->inalarm)
return 0;
@@ -11988,6 +12263,11 @@ static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri)
if (!pvt) {
return -1;
}
+ pvt->cc_params = ast_cc_config_params_init();
+ if (!pvt->cc_params) {
+ ast_free(pvt);
+ return -1;
+ }
ast_mutex_init(&pvt->lock);
for (idx = 0; idx < ARRAY_LEN(pvt->subs); ++idx) {
pvt->subs[idx].dfd = -1;
@@ -12089,24 +12369,31 @@ static struct dahdi_pvt *duplicate_pseudo(struct dahdi_pvt *src)
return p;
}
-static struct ast_channel *dahdi_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
+struct dahdi_starting_point {
+ /*! Group matching mask. Zero if not specified. */
+ ast_group_t groupmatch;
+ /*! DAHDI channel to match with. -1 if not specified. */
+ int channelmatch;
+ /*! Round robin saved search location index. (Valid if roundrobin TRUE) */
+ int rr_starting_point;
+ /*! ISDN span where channels can be picked (Zero if not specified) */
+ int span;
+ /*! Analog channel distinctive ring cadance index. */
+ int cadance;
+ /*! Dialing option. c/r/d if present and valid. */
+ char opt;
+ /*! TRUE if to search the channel list backwards. */
+ char backwards;
+ /*! TRUE if search is done with round robin sequence. */
+ char roundrobin;
+};
+static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi_starting_point *param)
{
- ast_group_t groupmatch = 0;
- int channelmatch = -1;
- int roundrobin = 0;
- int callwait = 0;
- struct dahdi_pvt *p;
- struct ast_channel *tmp = NULL;
char *dest;
- int x;
char *s;
- char opt=0;
- int res=0, y=0;
- int backwards = 0;
- struct dahdi_pvt *exitpvt;
- int channelmatched = 0;
- int groupmatched = 0;
- int transcapdigital = 0;
+ int x;
+ int res = 0;
+ struct dahdi_pvt *p;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(group); /* channel/group token */
//AST_APP_ARG(ext); /* extension token */
@@ -12117,8 +12404,11 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
/*
* data is ---v
* Dial(DAHDI/pseudo[/extension[/options]])
- * Dial(DAHDI/<channel#>[c|r<cadance#>|d][/extension[/options]])
- * Dial(DAHDI/(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]])
+ * Dial(DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]])
+ * Dial(DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]])
+ *
+ * i - ISDN span channel restriction.
+ * Used by CC to ensure that the CC recall goes out the same span.
*
* g - channel group allocation search forward
* G - channel group allocation search backward
@@ -12131,7 +12421,7 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
*/
if (data) {
- dest = ast_strdupa((char *)data);
+ dest = ast_strdupa(data);
} else {
ast_log(LOG_WARNING, "Channel requested with no data\n");
return NULL;
@@ -12142,27 +12432,47 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
return NULL;
}
+ /* Initialize the output parameters */
+ memset(param, 0, sizeof(*param));
+ param->channelmatch = -1;
+
+ if (args.group[0] == 'i') {
+ /* Extract the ISDN span channel restriction specifier. */
+ res = sscanf(args.group + 1, "%30d", &x);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to determine ISDN span for data %s\n", data);
+ return NULL;
+ }
+ param->span = x;
+
+ /* Remove the ISDN span channel restriction specifier. */
+ s = strchr(args.group, '-');
+ if (!s) {
+ ast_log(LOG_WARNING, "Bad ISDN span format for data %s\n", data);
+ return NULL;
+ }
+ args.group = s + 1;
+ res = 0;
+ }
if (toupper(args.group[0]) == 'G' || toupper(args.group[0])=='R') {
/* Retrieve the group number */
s = args.group + 1;
- if ((res = sscanf(s, "%30d%1c%30d", &x, &opt, &y)) < 1) {
- ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data);
+ res = sscanf(s, "%30d%1c%30d", &x, &param->opt, &param->cadance);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to determine group for data %s\n", data);
return NULL;
}
- groupmatch = ((ast_group_t) 1 << x);
-
- /* Lock the interface list */
- ast_mutex_lock(&iflock);
+ param->groupmatch = ((ast_group_t) 1 << x);
if (toupper(args.group[0]) == 'G') {
if (args.group[0] == 'G') {
- backwards = 1;
+ param->backwards = 1;
p = ifend;
} else
p = iflist;
} else {
if (args.group[0] == 'R') {
- backwards = 1;
+ param->backwards = 1;
p = round_robin[x]?round_robin[x]->prev:ifend;
if (!p)
p = ifend;
@@ -12171,36 +12481,62 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
if (!p)
p = iflist;
}
- roundrobin = 1;
+ param->roundrobin = 1;
+ param->rr_starting_point = x;
}
} else {
s = args.group;
if (!strcasecmp(s, "pseudo")) {
/* Special case for pseudo */
x = CHAN_PSEUDO;
- channelmatch = x;
- } else if ((res = sscanf(s, "%30d%1c%30d", &x, &opt, &y)) < 1) {
- ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data);
- return NULL;
+ param->channelmatch = x;
} else {
- channelmatch = x;
+ res = sscanf(s, "%30d%1c%30d", &x, &param->opt, &param->cadance);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", data);
+ return NULL;
+ } else {
+ param->channelmatch = x;
+ }
}
- /* Lock the interface list */
- ast_mutex_lock(&iflock);
-
p = iflist;
}
+
+ if (param->opt == 'r' && res < 3) {
+ ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", data);
+ param->opt = '\0';
+ }
+
+ return p;
+}
+
+static struct ast_channel *dahdi_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
+{
+ int callwait = 0;
+ struct dahdi_pvt *p;
+ struct ast_channel *tmp = NULL;
+ struct dahdi_pvt *exitpvt;
+ int channelmatched = 0;
+ int groupmatched = 0;
+ int transcapdigital = 0;
+ struct dahdi_starting_point start;
+
+ p = determine_starting_point(data, &start);
+ if (!p) {
+ /* We couldn't determine a starting point, which likely means badly-formatted channel name. Abort! */
+ return NULL;
+ }
+
/* Search for an unowned channel */
exitpvt = p;
+ ast_mutex_lock(&iflock);
while (p && !tmp) {
- if (roundrobin)
- round_robin[x] = p;
-#if 0
- ast_verbose("name = %s, %d, %d, %llu\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch);
-#endif
+ if (start.roundrobin)
+ round_robin[start.rr_starting_point] = p;
- if (p && available(p, channelmatch, groupmatch, &channelmatched, &groupmatched)) {
+ if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)
+ && available(p)) {
ast_debug(1, "Using channel %d\n", p->channel);
callwait = (p->owner != NULL);
@@ -12224,22 +12560,25 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
}
/* Make special notes */
- if (res > 1) {
- if (opt == 'c') {
- /* Confirm answer */
- p->confirmanswer = 1;
- } else if (opt == 'r') {
- /* Distinctive ring */
- if (res < 3)
- ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", (char *)data);
- else
- p->distinctivering = y;
- } else if (opt == 'd') {
- /* If this is an ISDN call, make it digital */
- transcapdigital = AST_TRANS_CAP_DIGITAL;
- } else {
- ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data);
- }
+ switch (start.opt) {
+ case '\0':
+ /* No option present. */
+ break;
+ case 'c':
+ /* Confirm answer */
+ p->confirmanswer = 1;
+ break;
+ case 'r':
+ /* Distinctive ring */
+ p->distinctivering = start.cadance;
+ break;
+ case 'd':
+ /* If this is an ISDN call, make it digital */
+ transcapdigital = AST_TRANS_CAP_DIGITAL;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", start.opt, (char *)data);
+ break;
}
p->outgoing = 1;
@@ -12256,13 +12595,15 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
}
if (!tmp) {
p->outgoing = 0;
+ } else {
+ snprintf(p->dialstring, sizeof(p->dialstring), "DAHDI/%s", (char *) data);
}
break;
}
#ifdef HAVE_OPENR2
next:
#endif
- if (backwards) {
+ if (start.backwards) {
p = p->prev;
if (!p)
p = ifend;
@@ -12293,6 +12634,167 @@ next:
return tmp;
}
+/*!
+ * \internal
+ * \brief Determine the device state for a given DAHDI device if we can.
+ * \since 1.8
+ *
+ * \param data DAHDI device name after "DAHDI/".
+ *
+ * \retval device_state enum ast_device_state value.
+ * \retval AST_DEVICE_UNKNOWN if we could not determine the device's state.
+ */
+static int dahdi_devicestate(void *data)
+{
+#if defined(HAVE_PRI)
+ char *device;
+ unsigned span;
+ int res;
+
+ device = data;
+
+ if (*device != 'I') {
+ /* The request is not for an ISDN span device. */
+ return AST_DEVICE_UNKNOWN;
+ }
+ res = sscanf(device, "I%30u", &span);
+ if (res != 1 || !span || NUM_SPANS < span) {
+ /* Bad format for ISDN span device name. */
+ return AST_DEVICE_UNKNOWN;
+ }
+ device = strchr(device, '/');
+ if (!device) {
+ /* Bad format for ISDN span device name. */
+ return AST_DEVICE_UNKNOWN;
+ }
+
+ /*
+ * Since there are currently no other span devstate's defined,
+ * it must be congestion.
+ */
+#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
+ ++device;
+ if (!strcmp(device, "congestion"))
+#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
+ {
+ return pris[span - 1].pri.congestion_devstate;
+ }
+#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
+ else if (!strcmp(device, "threshold")) {
+ return pris[span - 1].pri.threshold_devstate;
+ }
+ return AST_DEVICE_UNKNOWN;
+#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
+#else
+ return AST_DEVICE_UNKNOWN;
+#endif /* defined(HAVE_PRI) */
+}
+
+/*!
+ * \brief Callback made when dial failed to get a channel out of dahdi_request().
+ * \since 1.8
+ *
+ * \param inbound Incoming asterisk channel.
+ * \param dest Same dial string passed to dahdi_request().
+ * \param callback Callback into CC core to announce a busy channel available for CC.
+ *
+ * \details
+ * This callback acts like a forked dial with all prongs of the fork busy.
+ * Essentially, for each channel that could have taken the call, indicate that
+ * it is busy.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback)
+{
+ struct dahdi_pvt *p;
+ struct dahdi_pvt *exitpvt;
+ struct dahdi_starting_point start;
+ int groupmatched = 0;
+ int channelmatched = 0;
+
+ p = determine_starting_point(dest, &start);
+ if (!p) {
+ return -1;
+ }
+ ast_mutex_lock(&iflock);
+ exitpvt = p;
+ for (;;) {
+ if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)) {
+ /* We found a potential match. call the callback */
+ struct ast_str *device_name;
+ char *dash;
+ const char *monitor_type;
+ char dialstring[AST_CHANNEL_NAME];
+ char full_device_name[AST_CHANNEL_NAME];
+
+ switch (ast_get_cc_monitor_policy(p->cc_params)) {
+ case AST_CC_MONITOR_NEVER:
+ break;
+ case AST_CC_MONITOR_NATIVE:
+ case AST_CC_MONITOR_ALWAYS:
+ case AST_CC_MONITOR_GENERIC:
+#if defined(HAVE_PRI)
+ if (dahdi_sig_pri_lib_handles(p->sig)) {
+ /*
+ * ISDN is in a trunk busy condition so we need to monitor
+ * the span congestion device state.
+ */
+ snprintf(full_device_name, sizeof(full_device_name),
+ "DAHDI/I%d/congestion", p->pri->span);
+ } else
+#endif /* defined(HAVE_PRI) */
+ {
+#if defined(HAVE_PRI)
+ device_name = create_channel_name(p, 1, "");
+#else
+ device_name = create_channel_name(p);
+#endif /* defined(HAVE_PRI) */
+ snprintf(full_device_name, sizeof(full_device_name), "DAHDI/%s",
+ device_name ? ast_str_buffer(device_name) : "");
+ ast_free(device_name);
+ /*
+ * The portion after the '-' in the channel name is either a random
+ * number, a sequence number, or a subchannel number. None are
+ * necessary so strip them off.
+ */
+ dash = strrchr(full_device_name, '-');
+ if (dash) {
+ *dash = '\0';
+ }
+ }
+ snprintf(dialstring, sizeof(dialstring), "DAHDI/%s", dest);
+
+ /*
+ * Analog can only do generic monitoring.
+ * ISDN is in a trunk busy condition and any "device" is going
+ * to be busy until a B channel becomes available. The generic
+ * monitor can do this task.
+ */
+ monitor_type = AST_CC_GENERIC_MONITOR_TYPE;
+ callback(inbound,
+#if defined(HAVE_PRI)
+ p->pri ? p->pri->cc_params : p->cc_params,
+#else
+ p->cc_params,
+#endif /* defined(HAVE_PRI) */
+ monitor_type, full_device_name, dialstring, NULL);
+ break;
+ }
+ }
+ p = start.backwards ? p->prev : p->next;
+ if (!p) {
+ p = start.backwards ? ifend : iflist;
+ }
+ if (p == exitpvt) {
+ break;
+ }
+ }
+ ast_mutex_unlock(&iflock);
+ return 0;
+}
+
#if defined(HAVE_SS7)
static int ss7_find_cic(struct dahdi_ss7 *linkset, int cic, unsigned int dpc)
{
@@ -13480,9 +13982,7 @@ static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
for (x = 0; x < NUM_DCHANS; x++) {
if (pris[span-1].pri.dchans[x]) {
if (level == 1) {
- pri_set_debug(pris[span-1].pri.dchans[x], PRI_DEBUG_APDU |
- PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE |
- PRI_DEBUG_Q921_STATE);
+ pri_set_debug(pris[span-1].pri.dchans[x], SIG_PRI_DEBUG_NORMAL);
ast_cli(a->fd, "Enabled debugging on span %d\n", span);
} else if (level == 0) {
pri_set_debug(pris[span-1].pri.dchans[x], 0);
@@ -13493,9 +13993,7 @@ static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
ast_cli(a->fd, "PRI debug output to file disabled\n");
ast_mutex_unlock(&pridebugfdlock);
} else {
- pri_set_debug(pris[span-1].pri.dchans[x], PRI_DEBUG_APDU |
- PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE |
- PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_STATE);
+ pri_set_debug(pris[span-1].pri.dchans[x], SIG_PRI_DEBUG_INTENSE);
ast_cli(a->fd, "Enabled debugging on span %d\n", span);
}
}
@@ -13583,6 +14081,8 @@ static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct
if (*why) {
snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS, *why);
ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+ } else {
+ dahdi_pri_update_span_devstate(tmp->pri);
}
break;
/* case 1: -- loop */
@@ -13592,6 +14092,7 @@ static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct
*why |= SRVST_NEAREND;
snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS, *why);
ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+ dahdi_pri_update_span_devstate(tmp->pri);
break;
/* case 3: -- continuity */
/* case 4: -- shutdown */
@@ -15612,6 +16113,110 @@ static struct ast_cli_entry dahdi_ss7_cli[] = {
};
#endif /* defined(HAVE_SS7) */
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief CC agent initialization.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ * \param chan Original channel the agent will attempt to recall.
+ *
+ * \details
+ * This callback is called when the CC core is initialized. Agents should allocate
+ * any private data necessary for the call and assign it to the private_data
+ * on the agent. Additionally, if any ast_cc_agent_flags are pertinent to the
+ * specific agent type, they should be set in this function as well.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int dahdi_pri_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan)
+{
+ struct dahdi_pvt *pvt;
+ struct sig_pri_chan *pvt_chan;
+ int res;
+
+ ast_assert(!strcmp(chan->tech->type, "DAHDI"));
+
+ pvt = chan->tech_pvt;
+ if (dahdi_sig_pri_lib_handles(pvt->sig)) {
+ pvt_chan = pvt->sig_pvt;
+ } else {
+ pvt_chan = NULL;
+ }
+ if (!pvt_chan) {
+ return -1;
+ }
+
+ ast_module_ref(ast_module_info->self);
+
+ res = sig_pri_cc_agent_init(agent, pvt_chan);
+ if (res) {
+ ast_module_unref(ast_module_info->self);
+ }
+ return res;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+#endif /* defined(HAVE_PRI) */
+
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Destroy private data on the agent.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function upon completion
+ * or failure of CC.
+ *
+ * \return Nothing
+ */
+static void dahdi_pri_cc_agent_destructor(struct ast_cc_agent *agent)
+{
+ sig_pri_cc_agent_destructor(agent);
+
+ ast_module_unref(ast_module_info->self);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+#endif /* defined(HAVE_PRI) */
+
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CCSS)
+static struct ast_cc_agent_callbacks dahdi_pri_cc_agent_callbacks = {
+ .type = dahdi_pri_cc_type,
+ .init = dahdi_pri_cc_agent_init,
+ .start_offer_timer = sig_pri_cc_agent_start_offer_timer,
+ .stop_offer_timer = sig_pri_cc_agent_stop_offer_timer,
+ .ack = sig_pri_cc_agent_req_ack,
+ .status_request = sig_pri_cc_agent_status_req,
+ .stop_ringing = sig_pri_cc_agent_stop_ringing,
+ .party_b_free = sig_pri_cc_agent_party_b_free,
+ .start_monitoring = sig_pri_cc_agent_start_monitoring,
+ .callee_available = sig_pri_cc_agent_callee_available,
+ .destructor = dahdi_pri_cc_agent_destructor,
+};
+#endif /* defined(HAVE_PRI_CCSS) */
+#endif /* defined(HAVE_PRI) */
+
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CCSS)
+static struct ast_cc_monitor_callbacks dahdi_pri_cc_monitor_callbacks = {
+ .type = dahdi_pri_cc_type,
+ .request_cc = sig_pri_cc_monitor_req_cc,
+ .suspend = sig_pri_cc_monitor_suspend,
+ .unsuspend = sig_pri_cc_monitor_unsuspend,
+ .status_response = sig_pri_cc_monitor_status_rsp,
+ .cancel_available_timer = sig_pri_cc_monitor_cancel_available_timer,
+ .destructor = sig_pri_cc_monitor_destructor,
+};
+#endif /* defined(HAVE_PRI_CCSS) */
+#endif /* defined(HAVE_PRI) */
+
static int __unload_module(void)
{
struct dahdi_pvt *p;
@@ -15680,6 +16285,11 @@ static int __unload_module(void)
dahdi_close_pri_fd(&(pris[i]), j);
}
}
+#if defined(HAVE_PRI_CCSS)
+ ast_cc_agent_unregister(&dahdi_pri_cc_agent_callbacks);
+ ast_cc_monitor_unregister(&dahdi_pri_cc_monitor_callbacks);
+#endif /* defined(HAVE_PRI_CCSS) */
+ sig_pri_unload();
#endif
#if defined(HAVE_SS7)
@@ -16100,6 +16710,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
confp->chan.sendcalleridafter = atoi(v->value);
} else if (!strcasecmp(v->name, "mwimonitornotify")) {
ast_copy_string(mwimonitornotify, v->value, sizeof(mwimonitornotify));
+ } else if (ast_cc_is_config_param(v->name)) {
+ ast_cc_set_param(confp->chan.cc_params, v->name, v->value);
} else if (!strcasecmp(v->name, "mwisendtype")) {
#ifndef HAVE_DAHDI_LINEREVERSE_VMWI /* backward compatibility for older dahdi VMWI implementation */
if (!strcasecmp(v->value, "rpas")) { /* Ring Pulse Alert Signal */
@@ -16478,6 +17090,34 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
} else if (!strcasecmp(v->name, "hold_disconnect_transfer")) {
confp->pri.pri.hold_disconnect_transfer = ast_true(v->value);
#endif /* defined(HAVE_PRI_CALL_HOLD) */
+#if defined(HAVE_PRI_CCSS)
+ } else if (!strcasecmp(v->name, "cc_ptmp_recall_mode")) {
+ if (!strcasecmp(v->value, "global")) {
+ confp->pri.pri.cc_ptmp_recall_mode = 0;/* globalRecall */
+ } else if (!strcasecmp(v->value, "specific")) {
+ confp->pri.pri.cc_ptmp_recall_mode = 1;/* specificRecall */
+ } else {
+ confp->pri.pri.cc_ptmp_recall_mode = 1;/* specificRecall */
+ }
+ } else if (!strcasecmp(v->name, "cc_qsig_signaling_link_req")) {
+ if (!strcasecmp(v->value, "release")) {
+ confp->pri.pri.cc_qsig_signaling_link_req = 0;/* release */
+ } else if (!strcasecmp(v->value, "retain")) {
+ confp->pri.pri.cc_qsig_signaling_link_req = 1;/* retain */
+ } else if (!strcasecmp(v->value, "do_not_care")) {
+ confp->pri.pri.cc_qsig_signaling_link_req = 2;/* do-not-care */
+ } else {
+ confp->pri.pri.cc_qsig_signaling_link_req = 1;/* retain */
+ }
+ } else if (!strcasecmp(v->name, "cc_qsig_signaling_link_rsp")) {
+ if (!strcasecmp(v->value, "release")) {
+ confp->pri.pri.cc_qsig_signaling_link_rsp = 0;/* release */
+ } else if (!strcasecmp(v->value, "retain")) {
+ confp->pri.pri.cc_qsig_signaling_link_rsp = 1;/* retain */
+ } else {
+ confp->pri.pri.cc_qsig_signaling_link_rsp = 1;/* retain */
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
#endif /* HAVE_PRI */
#ifdef HAVE_SS7
} else if (!strcasecmp(v->name, "ss7type")) {
@@ -16800,23 +17440,57 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
*/
struct dahdi_chan_conf conf = dahdi_chan_conf_default();
- tmp = mkintf(CHAN_PSEUDO, &conf, reload);
-
+ if (conf.chan.cc_params) {
+ tmp = mkintf(CHAN_PSEUDO, &conf, reload);
+ } else {
+ tmp = NULL;
+ }
if (tmp) {
ast_verb(3, "Automatically generated pseudo channel\n");
} else {
ast_log(LOG_WARNING, "Unable to register pseudo channel!\n");
}
+ ast_cc_config_params_destroy(conf.chan.cc_params);
}
return 0;
}
-static int setup_dahdi(int reload)
+/*!
+ * \internal
+ * \brief Deep copy struct dahdi_chan_conf.
+ * \since 1.8
+ *
+ * \param dest Destination.
+ * \param src Source.
+ *
+ * \return Nothing
+ */
+static void deep_copy_dahdi_chan_conf(struct dahdi_chan_conf *dest, const struct dahdi_chan_conf *src)
+{
+ struct ast_cc_config_params *cc_params;
+
+ cc_params = dest->chan.cc_params;
+ memcpy(dest, src, sizeof(dest));
+ dest->chan.cc_params = cc_params;
+ ast_cc_copy_config_params(dest->chan.cc_params, src->chan.cc_params);
+}
+
+/*!
+ * \internal
+ * \brief Setup DAHDI channel driver.
+ *
+ * \param reload enum: load_module(0), reload(1), restart(2).
+ * \param base_conf Default config parameters. So cc_params can be properly destroyed.
+ * \param conf Local config parameters. So cc_params can be properly destroyed.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int setup_dahdi_int(int reload, struct dahdi_chan_conf *base_conf, struct dahdi_chan_conf *conf)
{
- struct ast_config *cfg, *ucfg;
+ struct ast_config *cfg;
+ struct ast_config *ucfg;
struct ast_variable *v;
- struct dahdi_chan_conf base_conf = dahdi_chan_conf_default();
- struct dahdi_chan_conf conf;
struct ast_flags config_flags = { reload == 1 ? CONFIG_FLAG_FILEUNCHANGED : 0 };
const char *cat;
int res;
@@ -16931,7 +17605,7 @@ static int setup_dahdi(int reload)
mwimonitornotify[0] = '\0';
v = ast_variable_browse(cfg, "channels");
- if ((res = process_dahdi(&base_conf, "", v, reload, 0))) {
+ if ((res = process_dahdi(base_conf, "", v, reload, 0))) {
ast_mutex_unlock(&iflock);
ast_config_destroy(cfg);
if (ucfg) {
@@ -16952,9 +17626,10 @@ static int setup_dahdi(int reload)
continue;
}
- memcpy(&conf, &base_conf, sizeof(conf));
+ /* Copy base_conf to conf. */
+ deep_copy_dahdi_chan_conf(conf, base_conf);
- if ((res = process_dahdi(&conf, cat, ast_variable_browse(cfg, cat), reload, PROC_DAHDI_OPT_NOCHAN))) {
+ if ((res = process_dahdi(conf, cat, ast_variable_browse(cfg, cat), reload, PROC_DAHDI_OPT_NOCHAN))) {
ast_mutex_unlock(&iflock);
ast_config_destroy(cfg);
if (ucfg) {
@@ -16969,7 +17644,7 @@ static int setup_dahdi(int reload)
if (ucfg) {
const char *chans;
- process_dahdi(&base_conf, "", ast_variable_browse(ucfg, "general"), 1, 0);
+ process_dahdi(base_conf, "", ast_variable_browse(ucfg, "general"), 1, 0);
for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
if (!strcasecmp(cat, "general")) {
@@ -16982,9 +17657,10 @@ static int setup_dahdi(int reload)
continue;
}
- memcpy(&conf, &base_conf, sizeof(conf));
+ /* Copy base_conf to conf. */
+ deep_copy_dahdi_chan_conf(conf, base_conf);
- if ((res = process_dahdi(&conf, cat, ast_variable_browse(ucfg, cat), reload, PROC_DAHDI_OPT_NOCHAN | PROC_DAHDI_OPT_NOWARN))) {
+ if ((res = process_dahdi(conf, cat, ast_variable_browse(ucfg, cat), reload, PROC_DAHDI_OPT_NOCHAN | PROC_DAHDI_OPT_NOWARN))) {
ast_config_destroy(ucfg);
ast_mutex_unlock(&iflock);
return res;
@@ -17041,6 +17717,32 @@ static int setup_dahdi(int reload)
return 0;
}
+/*!
+ * \internal
+ * \brief Setup DAHDI channel driver.
+ *
+ * \param reload enum: load_module(0), reload(1), restart(2).
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int setup_dahdi(int reload)
+{
+ int res;
+ struct dahdi_chan_conf base_conf = dahdi_chan_conf_default();
+ struct dahdi_chan_conf conf = dahdi_chan_conf_default();
+
+ if (base_conf.chan.cc_params && conf.chan.cc_params) {
+ res = setup_dahdi_int(reload, &base_conf, &conf);
+ } else {
+ res = -1;
+ }
+ ast_cc_config_params_destroy(base_conf.chan.cc_params);
+ ast_cc_config_params_destroy(conf.chan.cc_params);
+
+ return res;
+}
+
static int load_module(void)
{
int res;
@@ -17061,6 +17763,23 @@ static int load_module(void)
#ifdef HAVE_PRI_PROG_W_CAUSE
ast_register_application_xml(dahdi_send_callrerouting_facility_app, dahdi_send_callrerouting_facility_exec);
#endif
+#if defined(HAVE_PRI_CCSS)
+ if (ast_cc_agent_register(&dahdi_pri_cc_agent_callbacks)
+ || ast_cc_monitor_register(&dahdi_pri_cc_monitor_callbacks)) {
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+ if (sig_pri_load(
+#if defined(HAVE_PRI_CCSS)
+ dahdi_pri_cc_type
+#else
+ NULL
+#endif /* defined(HAVE_PRI_CCSS) */
+ )) {
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
#endif
#ifdef HAVE_SS7
memset(linksets, 0, sizeof(linksets));
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 5e522e797..b8052b0fd 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -545,6 +545,8 @@ static int local_call(struct ast_channel *ast, char *dest, int timeout)
int res;
struct ast_var_t *varptr = NULL, *new;
size_t len, namelen;
+ char *reduced_dest = ast_strdupa(dest);
+ char *slash;
if (!p)
return -1;
@@ -594,6 +596,8 @@ start_over:
ast_string_field_set(p->chan, musicclass, p->owner->musicclass);
ast_cdr_update(p->chan);
+ ast_channel_cc_params_init(p->chan, ast_channel_get_cc_config_params(p->owner));
+
if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
ast_mutex_unlock(&p->lock);
@@ -618,6 +622,14 @@ start_over:
}
}
ast_channel_datastore_inherit(p->owner, p->chan);
+ /* If the local channel has /n or /b on the end of it,
+ * we need to lop that off for our argument to setting
+ * up the CC_INTERFACES variable
+ */
+ if ((slash = strrchr(reduced_dest, '/'))) {
+ *slash = '\0';
+ }
+ ast_set_cc_interfaces_chanvar(p->chan, reduced_dest);
/* Start switch on sub channel */
if (!(res = ast_pbx_start(p->chan)))
@@ -857,6 +869,10 @@ static struct ast_channel *local_request(const char *type, format_t format, cons
AST_LIST_UNLOCK(&locals);
p = local_pvt_destroy(p);
}
+ if (ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params((struct ast_channel *)requestor))) {
+ chan = ast_channel_release(chan);
+ p = local_pvt_destroy(p);
+ }
}
return chan;
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index bd6cb1889..91773b05c 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -266,6 +266,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "sip/include/config_parser.h"
#include "sip/include/reqresp_parser.h"
#include "sip/include/sip_utils.h"
+#include "asterisk/ccss.h"
+#include "asterisk/xml.h"
#include "sip/include/dialog.h"
#include "sip/include/dialplan_functions.h"
@@ -625,7 +627,7 @@ static const struct cfsip_methods {
{ SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG },
{ SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG },
{ SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG },
- { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD },
+ { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG },
{ SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }
};
@@ -784,6 +786,14 @@ static int global_max_se; /*!< Highest threshold for session
static int global_dynamic_exclude_static = 0; /*!< Exclude static peers from contact registrations */
/*@}*/
+/*!
+ * We use libxml2 in order to parse XML that may appear in the body of a SIP message. Currently,
+ * the only usage is for parsing PIDF bodies of incoming PUBLISH requests in the call-completion
+ * event package. This variable is set at module load time and may be checked at runtime to determine
+ * if XML parsing support was found.
+ */
+static int can_parse_xml;
+
/*! \name Object counters @{
* \bug These counters are not handled in a thread-safe way ast_atomic_fetchadd_int()
* should be used to modify these values. */
@@ -851,6 +861,251 @@ static const int HASH_PEER_SIZE = 563; /*!< Size of peer hash table, prime numbe
static const int HASH_DIALOG_SIZE = 563;
#endif
+static const struct {
+ enum ast_cc_service_type service;
+ const char *service_string;
+} sip_cc_service_map [] = {
+ [AST_CC_NONE] = { AST_CC_NONE, "" },
+ [AST_CC_CCBS] = { AST_CC_CCBS, "BS" },
+ [AST_CC_CCNR] = { AST_CC_CCNR, "NR" },
+ [AST_CC_CCNL] = { AST_CC_CCNL, "NL" },
+};
+
+static enum ast_cc_service_type service_string_to_service_type(const char * const service_string)
+{
+ enum ast_cc_service_type service;
+ for (service = AST_CC_CCBS; service <= AST_CC_CCNL; ++service) {
+ if (!strcasecmp(service_string, sip_cc_service_map[service].service_string)) {
+ return service;
+ }
+ }
+ return AST_CC_NONE;
+}
+
+static const struct {
+ enum sip_cc_notify_state state;
+ const char *state_string;
+} sip_cc_notify_state_map [] = {
+ [CC_QUEUED] = {CC_QUEUED, "cc-state: queued"},
+ [CC_READY] = {CC_READY, "cc-state: ready"},
+};
+
+AST_LIST_HEAD_STATIC(epa_static_data_list, epa_backend);
+
+static int sip_epa_register(const struct epa_static_data *static_data)
+{
+ struct epa_backend *backend = ast_calloc(1, sizeof(*backend));
+
+ if (!backend) {
+ return -1;
+ }
+
+ backend->static_data = static_data;
+
+ AST_LIST_LOCK(&epa_static_data_list);
+ AST_LIST_INSERT_TAIL(&epa_static_data_list, backend, next);
+ AST_LIST_UNLOCK(&epa_static_data_list);
+ return 0;
+}
+
+static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry);
+
+static void cc_epa_destructor(void *data)
+{
+ struct sip_epa_entry *epa_entry = data;
+ struct cc_epa_entry *cc_entry = epa_entry->instance_data;
+ ast_free(cc_entry);
+}
+
+static const struct epa_static_data cc_epa_static_data = {
+ .event = CALL_COMPLETION,
+ .name = "call-completion",
+ .handle_error = cc_handle_publish_error,
+ .destructor = cc_epa_destructor,
+};
+
+static const struct epa_static_data *find_static_data(const char * const event_package)
+{
+ const struct epa_backend *backend = NULL;
+
+ AST_LIST_LOCK(&epa_static_data_list);
+ AST_LIST_TRAVERSE(&epa_static_data_list, backend, next) {
+ if (!strcmp(backend->static_data->name, event_package)) {
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&epa_static_data_list);
+ return backend ? backend->static_data : NULL;
+}
+
+static struct sip_epa_entry *create_epa_entry (const char * const event_package, const char * const destination)
+{
+ struct sip_epa_entry *epa_entry;
+ const struct epa_static_data *static_data;
+
+ if (!(static_data = find_static_data(event_package))) {
+ return NULL;
+ }
+
+ if (!(epa_entry = ao2_t_alloc(sizeof(*epa_entry), static_data->destructor, "Allocate new EPA entry"))) {
+ return NULL;
+ }
+
+ epa_entry->static_data = static_data;
+ ast_copy_string(epa_entry->destination, destination, sizeof(epa_entry->destination));
+ return epa_entry;
+}
+
+/*!
+ * Used to create new entity IDs by ESCs.
+ */
+static int esc_etag_counter;
+static const int DEFAULT_PUBLISH_EXPIRES = 3600;
+
+#ifdef HAVE_LIBXML2
+static int cc_esc_publish_handler(struct sip_pvt *pvt, struct sip_request *req, struct event_state_compositor *esc, struct sip_esc_entry *esc_entry);
+
+static const struct sip_esc_publish_callbacks cc_esc_publish_callbacks = {
+ .initial_handler = cc_esc_publish_handler,
+ .modify_handler = cc_esc_publish_handler,
+};
+#endif
+
+/*!
+ * \brief The Event State Compositors
+ *
+ * An Event State Compositor is an entity which
+ * accepts PUBLISH requests and acts appropriately
+ * based on these requests.
+ *
+ * The actual event_state_compositor structure is simply
+ * an ao2_container of sip_esc_entrys. When an incoming
+ * PUBLISH is received, we can match the appropriate sip_esc_entry
+ * using the entity ID of the incoming PUBLISH.
+ */
+static struct event_state_compositor {
+ enum subscriptiontype event;
+ const char * name;
+ const struct sip_esc_publish_callbacks *callbacks;
+ struct ao2_container *compositor;
+} event_state_compositors [] = {
+#ifdef HAVE_LIBXML2
+ {CALL_COMPLETION, "call-completion", &cc_esc_publish_callbacks},
+#endif
+};
+
+static const int ESC_MAX_BUCKETS = 37;
+
+static void esc_entry_destructor(void *obj)
+{
+ struct sip_esc_entry *esc_entry = obj;
+ if (esc_entry->sched_id > -1) {
+ AST_SCHED_DEL(sched, esc_entry->sched_id);
+ }
+}
+
+static int esc_hash_fn(const void *obj, const int flags)
+{
+ const struct sip_esc_entry *entry = obj;
+ return ast_str_hash(entry->entity_tag);
+}
+
+static int esc_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct sip_esc_entry *entry1 = obj;
+ struct sip_esc_entry *entry2 = arg;
+
+ return (!strcmp(entry1->entity_tag, entry2->entity_tag)) ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+static struct event_state_compositor *get_esc(const char * const event_package) {
+ int i;
+ for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
+ if (!strcasecmp(event_package, event_state_compositors[i].name)) {
+ return &event_state_compositors[i];
+ }
+ }
+ return NULL;
+}
+
+static struct sip_esc_entry *get_esc_entry(const char * entity_tag, struct event_state_compositor *esc) {
+ struct sip_esc_entry *entry;
+ struct sip_esc_entry finder;
+
+ ast_copy_string(finder.entity_tag, entity_tag, sizeof(finder.entity_tag));
+
+ entry = ao2_find(esc->compositor, &finder, OBJ_POINTER);
+
+ return entry;
+}
+
+static int publish_expire(const void *data)
+{
+ struct sip_esc_entry *esc_entry = (struct sip_esc_entry *) data;
+ struct event_state_compositor *esc = get_esc(esc_entry->event);
+
+ ast_assert(esc != NULL);
+
+ ao2_unlink(esc->compositor, esc_entry);
+ ao2_ref(esc_entry, -1);
+ return 0;
+}
+
+static void create_new_sip_etag(struct sip_esc_entry *esc_entry, int is_linked)
+{
+ int new_etag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
+ struct event_state_compositor *esc = get_esc(esc_entry->event);
+
+ ast_assert(esc != NULL);
+ if (is_linked) {
+ ao2_unlink(esc->compositor, esc_entry);
+ }
+ snprintf(esc_entry->entity_tag, sizeof(esc_entry->entity_tag), "%d", new_etag);
+ ao2_link(esc->compositor, esc_entry);
+}
+
+static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc, struct sip_request *req, const int expires)
+{
+ struct sip_esc_entry *esc_entry;
+ int expires_ms;
+
+ if (!(esc_entry = ao2_alloc(sizeof(*esc_entry), esc_entry_destructor))) {
+ return NULL;
+ }
+
+ esc_entry->event = esc->name;
+
+ expires_ms = expires * 1000;
+ /* Bump refcount for scheduler */
+ ao2_ref(esc_entry, +1);
+ esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry);
+
+ /* Note: This links the esc_entry into the ESC properly */
+ create_new_sip_etag(esc_entry, 0);
+
+ return esc_entry;
+}
+
+static int initialize_escs(void)
+{
+ int i, res = 0;
+ for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
+ if (!((event_state_compositors[i].compositor) =
+ ao2_container_alloc(ESC_MAX_BUCKETS, esc_hash_fn, esc_cmp_fn))) {
+ res = -1;
+ }
+ }
+ return res;
+}
+
+static void destroy_escs(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
+ ao2_ref(event_state_compositors[i].compositor, -1);
+ }
+}
+
/*! \brief
* Here we implement the container for dialogs (sip_pvt), defining
* generic wrapper functions to ease the transition from the current
@@ -1001,6 +1256,7 @@ static int sip_prepare_socket(struct sip_pvt *p);
static int sipsock_read(int *id, int fd, short events, void *ignore);
static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len);
static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod);
+static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp);
static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
static int retrans_pkt(const void *data);
static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg);
@@ -1015,7 +1271,8 @@ static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, cons
static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable);
static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch);
-static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init);
+static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri);
+static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri);
static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp);
static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
static int transmit_info_with_vidupdate(struct sip_pvt *p);
@@ -1023,6 +1280,7 @@ static int transmit_message_with_text(struct sip_pvt *p, const char *text);
static int transmit_refer(struct sip_pvt *p, const char *dest);
static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
+static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state);
static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader);
static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
@@ -1236,7 +1494,7 @@ static int set_address_from_contact(struct sip_pvt *pvt);
static void check_via(struct sip_pvt *p, struct sip_request *req);
static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
-static int get_destination(struct sip_pvt *p, struct sip_request *oreq);
+static int get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline);
static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
@@ -1253,7 +1511,7 @@ static void *sip_tcp_worker_fn(void *);
static void initialize_initreq(struct sip_pvt *p, struct sip_request *req);
static int init_req(struct sip_request *req, int sipmethod, const char *recip);
static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch);
-static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod);
+static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri);
static int init_resp(struct sip_request *resp, const char *msg);
static inline int resp_needs_contact(const char *msg, enum sipmethod method);
static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req);
@@ -1297,6 +1555,7 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock);
/*------Response handling functions */
+static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
@@ -1372,6 +1631,556 @@ const struct ast_channel_tech sip_tech = {
*/
struct ast_channel_tech sip_tech_info;
+static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
+static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent);
+static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent);
+static void sip_cc_agent_ack(struct ast_cc_agent *agent);
+static int sip_cc_agent_status_request(struct ast_cc_agent *agent);
+static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent);
+static int sip_cc_agent_recall(struct ast_cc_agent *agent);
+static void sip_cc_agent_destructor(struct ast_cc_agent *agent);
+
+static struct ast_cc_agent_callbacks sip_cc_agent_callbacks = {
+ .type = "SIP",
+ .init = sip_cc_agent_init,
+ .start_offer_timer = sip_cc_agent_start_offer_timer,
+ .stop_offer_timer = sip_cc_agent_stop_offer_timer,
+ .ack = sip_cc_agent_ack,
+ .status_request = sip_cc_agent_status_request,
+ .start_monitoring = sip_cc_agent_start_monitoring,
+ .callee_available = sip_cc_agent_recall,
+ .destructor = sip_cc_agent_destructor,
+};
+
+static int find_by_notify_uri_helper(void *obj, void *arg, int flags)
+{
+ struct ast_cc_agent *agent = obj;
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ const char *uri = arg;
+
+ return !strcmp(agent_pvt->notify_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static struct ast_cc_agent *find_sip_cc_agent_by_notify_uri(const char * const uri)
+{
+ struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_notify_uri_helper, (char *)uri, "SIP");
+ return agent;
+}
+
+static int find_by_subscribe_uri_helper(void *obj, void *arg, int flags)
+{
+ struct ast_cc_agent *agent = obj;
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ const char *uri = arg;
+
+ return !strcmp(agent_pvt->subscribe_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static struct ast_cc_agent *find_sip_cc_agent_by_subscribe_uri(const char * const uri)
+{
+ struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_subscribe_uri_helper, (char *)uri, "SIP");
+ return agent;
+}
+
+static int find_by_callid_helper(void *obj, void *arg, int flags)
+{
+ struct ast_cc_agent *agent = obj;
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ struct sip_pvt *call_pvt = arg;
+
+ return !strcmp(agent_pvt->original_callid, call_pvt->callid) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static struct ast_cc_agent *find_sip_cc_agent_by_original_callid(struct sip_pvt *pvt)
+{
+ struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_callid_helper, pvt, "SIP");
+ return agent;
+}
+
+static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan)
+{
+ struct sip_cc_agent_pvt *agent_pvt = ast_calloc(1, sizeof(*agent_pvt));
+ struct sip_pvt *call_pvt = chan->tech_pvt;
+
+ if (!agent_pvt) {
+ return -1;
+ }
+
+ ast_assert(!strcmp(chan->tech->type, "SIP"));
+
+ ast_copy_string(agent_pvt->original_callid, call_pvt->callid, sizeof(agent_pvt->original_callid));
+ ast_copy_string(agent_pvt->original_exten, call_pvt->exten, sizeof(agent_pvt->original_exten));
+ agent_pvt->offer_timer_id = -1;
+ agent->private_data = agent_pvt;
+ sip_pvt_lock(call_pvt);
+ ast_set_flag(&call_pvt->flags[0], SIP_OFFER_CC);
+ sip_pvt_unlock(call_pvt);
+ return 0;
+}
+
+static int sip_offer_timer_expire(const void *data)
+{
+ struct ast_cc_agent *agent = (struct ast_cc_agent *) data;
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+
+ agent_pvt->offer_timer_id = -1;
+
+ return ast_cc_failed(agent->core_id, "SIP agent %s's offer timer expired", agent->device_name);
+}
+
+static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent)
+{
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ int when;
+
+ when = ast_get_cc_offer_timer(agent->cc_params) * 1000;
+ agent_pvt->offer_timer_id = ast_sched_add(sched, when, sip_offer_timer_expire, agent);
+ return 0;
+}
+
+static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
+{
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+
+ AST_SCHED_DEL(sched, agent_pvt->offer_timer_id);
+ return 0;
+}
+
+static void sip_cc_agent_ack(struct ast_cc_agent *agent)
+{
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+
+ sip_pvt_lock(agent_pvt->subscribe_pvt);
+ ast_set_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+ transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq);
+ transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED);
+ sip_pvt_unlock(agent_pvt->subscribe_pvt);
+ agent_pvt->is_available = TRUE;
+}
+
+static int sip_cc_agent_status_request(struct ast_cc_agent *agent)
+{
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ enum ast_device_state state = agent_pvt->is_available ? AST_DEVICE_NOT_INUSE : AST_DEVICE_INUSE;
+ return ast_cc_agent_status_response(agent->core_id, state);
+}
+
+static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent)
+{
+ /* To start monitoring just means to wait for an incoming PUBLISH
+ * to tell us that the caller has become available again. No special
+ * action is needed
+ */
+ return 0;
+}
+
+static int sip_cc_agent_recall(struct ast_cc_agent *agent)
+{
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ /* If we have received a PUBLISH beforehand stating that the caller in question
+ * is not available, we can save ourself a bit of effort here and just report
+ * the caller as busy
+ */
+ if (!agent_pvt->is_available) {
+ return ast_cc_agent_caller_busy(agent->core_id, "Caller %s is busy, reporting to the core",
+ agent->device_name);
+ }
+ /* Otherwise, we transmit a NOTIFY to the caller and await either
+ * a PUBLISH or an INVITE
+ */
+ sip_pvt_lock(agent_pvt->subscribe_pvt);
+ transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_READY);
+ sip_pvt_unlock(agent_pvt->subscribe_pvt);
+ return 0;
+}
+
+static void sip_cc_agent_destructor(struct ast_cc_agent *agent)
+{
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+
+ if (!agent_pvt) {
+ /* The agent constructor probably failed. */
+ return;
+ }
+
+ sip_cc_agent_stop_offer_timer(agent);
+ if (agent_pvt->subscribe_pvt) {
+ sip_pvt_lock(agent_pvt->subscribe_pvt);
+ if (!ast_test_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
+ /* If we haven't sent a 200 OK for the SUBSCRIBE dialog yet, then we need to send a response letting
+ * the subscriber know something went wrong
+ */
+ transmit_response(agent_pvt->subscribe_pvt, "500 Internal Server Error", &agent_pvt->subscribe_pvt->initreq);
+ }
+ sip_pvt_unlock(agent_pvt->subscribe_pvt);
+ agent_pvt->subscribe_pvt = dialog_unref(agent_pvt->subscribe_pvt, "SIP CC agent destructor: Remove ref to subscription");
+ }
+ ast_free(agent_pvt);
+}
+
+struct ao2_container *sip_monitor_instances;
+
+static int sip_monitor_instance_hash_fn(const void *obj, const int flags)
+{
+ const struct sip_monitor_instance *monitor_instance = obj;
+ return monitor_instance->core_id;
+}
+
+static int sip_monitor_instance_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct sip_monitor_instance *monitor_instance1 = obj;
+ struct sip_monitor_instance *monitor_instance2 = arg;
+
+ return monitor_instance1->core_id == monitor_instance2->core_id ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void sip_monitor_instance_destructor(void *data)
+{
+ struct sip_monitor_instance *monitor_instance = data;
+ if (monitor_instance->subscription_pvt) {
+ sip_pvt_lock(monitor_instance->subscription_pvt);
+ monitor_instance->subscription_pvt->expiry = 0;
+ transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 0, monitor_instance->subscribe_uri);
+ sip_pvt_unlock(monitor_instance->subscription_pvt);
+ dialog_unref(monitor_instance->subscription_pvt, "Unref monitor instance ref of subscription pvt");
+ }
+ if (monitor_instance->suspension_entry) {
+ monitor_instance->suspension_entry->body[0] = '\0';
+ transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_REMOVE ,monitor_instance->notify_uri);
+ ao2_t_ref(monitor_instance->suspension_entry, -1, "Decrementing suspension entry refcount in sip_monitor_instance_destructor");
+ }
+ ast_string_field_free_memory(monitor_instance);
+}
+
+static struct sip_monitor_instance *sip_monitor_instance_init(int core_id, const char * const subscribe_uri, const char * const peername, const char * const device_name)
+{
+ struct sip_monitor_instance *monitor_instance = ao2_alloc(sizeof(*monitor_instance), sip_monitor_instance_destructor);
+
+ if (!monitor_instance) {
+ return NULL;
+ }
+
+ if (ast_string_field_init(monitor_instance, 256)) {
+ ao2_ref(monitor_instance, -1);
+ return NULL;
+ }
+
+ ast_string_field_set(monitor_instance, subscribe_uri, subscribe_uri);
+ ast_string_field_set(monitor_instance, peername, peername);
+ ast_string_field_set(monitor_instance, device_name, device_name);
+ monitor_instance->core_id = core_id;
+ ao2_link(sip_monitor_instances, monitor_instance);
+ return monitor_instance;
+}
+
+static int find_sip_monitor_instance_by_subscription_pvt(void *obj, void *arg, int flags)
+{
+ struct sip_monitor_instance *monitor_instance = obj;
+ return monitor_instance->subscription_pvt == arg ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int find_sip_monitor_instance_by_suspension_entry(void *obj, void *arg, int flags)
+{
+ struct sip_monitor_instance *monitor_instance = obj;
+ return monitor_instance->suspension_entry == arg ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
+static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor);
+static int sip_cc_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate);
+static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor);
+static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
+static void sip_cc_monitor_destructor(void *private_data);
+
+static struct ast_cc_monitor_callbacks sip_cc_monitor_callbacks = {
+ .type = "SIP",
+ .request_cc = sip_cc_monitor_request_cc,
+ .suspend = sip_cc_monitor_suspend,
+ .status_response = sip_cc_monitor_status_response,
+ .unsuspend = sip_cc_monitor_unsuspend,
+ .cancel_available_timer = sip_cc_monitor_cancel_available_timer,
+ .destructor = sip_cc_monitor_destructor,
+};
+
+static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
+{
+ struct sip_monitor_instance *monitor_instance = monitor->private_data;
+ enum ast_cc_service_type service = monitor->service_offered;
+ int when;
+
+ if (!monitor_instance) {
+ return -1;
+ }
+
+ if (!(monitor_instance->subscription_pvt = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL))) {
+ return -1;
+ }
+
+ when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
+ ast_get_ccnr_available_timer(monitor->interface->config_params);
+
+ sip_pvt_lock(monitor_instance->subscription_pvt);
+ create_addr(monitor_instance->subscription_pvt, monitor_instance->peername, 0, 1);
+ ast_sip_ouraddrfor(&monitor_instance->subscription_pvt->sa.sin_addr, &monitor_instance->subscription_pvt->ourip, monitor_instance->subscription_pvt);
+ monitor_instance->subscription_pvt->subscribed = CALL_COMPLETION;
+ monitor_instance->subscription_pvt->expiry = when;
+
+ transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 2, monitor_instance->subscribe_uri);
+ sip_pvt_unlock(monitor_instance->subscription_pvt);
+
+ ao2_t_ref(monitor, +1, "Adding a ref to the monitor for the scheduler");
+ *available_timer_id = ast_sched_add(sched, when * 1000, ast_cc_available_timer_expire, monitor);
+ return 0;
+}
+
+static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity)
+{
+ struct ast_str *body = ast_str_alloca(size);
+ char tuple_id[32];
+
+ generate_random_string(tuple_id, sizeof(tuple_id));
+
+ /* We'll make this a bare-bones pidf body. In state_notify_build_xml, the PIDF
+ * body gets a lot more extra junk that isn't necessary, so we'll leave it out here.
+ */
+ ast_str_append(&body, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ /* XXX The entity attribute is currently set to the peer name associated with the
+ * dialog. This is because we currently only call this function for call-completion
+ * PUBLISH bodies. In such cases, the entity is completely disregarded. For other
+ * event packages, it may be crucial to have a proper URI as the presentity so this
+ * should be revisited as support is expanded.
+ */
+ ast_str_append(&body, 0, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"%s\">\n", presentity);
+ ast_str_append(&body, 0, "<tuple id=\"%s\">\n", tuple_id);
+ ast_str_append(&body, 0, "<status><basic>%s</basic></status>\n", state == CC_OPEN ? "open" : "closed");
+ ast_str_append(&body, 0, "</tuple>\n");
+ ast_str_append(&body, 0, "</presence>\n");
+ ast_copy_string(pidf_body, ast_str_buffer(body), size);
+ return 0;
+}
+
+static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor)
+{
+ struct sip_monitor_instance *monitor_instance = monitor->private_data;
+ enum sip_publish_type publish_type;
+ struct cc_epa_entry *cc_entry;
+
+ if (!monitor_instance) {
+ return -1;
+ }
+
+ if (!monitor_instance->suspension_entry) {
+ /* We haven't yet allocated the suspension entry, so let's give it a shot */
+ if (!(monitor_instance->suspension_entry = create_epa_entry("call-completion", monitor_instance->peername))) {
+ ast_log(LOG_WARNING, "Unable to allocate sip EPA entry for call-completion\n");
+ ao2_ref(monitor_instance, -1);
+ return -1;
+ }
+ if (!(cc_entry = ast_calloc(1, sizeof(*cc_entry)))) {
+ ast_log(LOG_WARNING, "Unable to allocate space for instance data of EPA entry for call-completion\n");
+ ao2_ref(monitor_instance, -1);
+ return -1;
+ }
+ cc_entry->core_id = monitor->core_id;
+ monitor_instance->suspension_entry->instance_data = cc_entry;
+ publish_type = SIP_PUBLISH_INITIAL;
+ } else {
+ publish_type = SIP_PUBLISH_MODIFY;
+ cc_entry = monitor_instance->suspension_entry->instance_data;
+ }
+
+ cc_entry->current_state = CC_CLOSED;
+
+ if (ast_strlen_zero(monitor_instance->notify_uri)) {
+ /* If we have no set notify_uri, then what this means is that we have
+ * not received a NOTIFY from this destination stating that he is
+ * currently available.
+ *
+ * This situation can arise when the core calls the suspend callbacks
+ * of multiple destinations. If one of the other destinations aside
+ * from this one notified Asterisk that he is available, then there
+ * is no reason to take any suspension action on this device. Rather,
+ * we should return now and if we receive a NOTIFY while monitoring
+ * is still "suspended" then we can immediately respond with the
+ * proper PUBLISH to let this endpoint know what is going on.
+ */
+ return 0;
+ }
+ construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
+ return transmit_publish(monitor_instance->suspension_entry, publish_type, monitor_instance->notify_uri);
+}
+
+static int sip_cc_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate)
+{
+ /* This will never be called because the SIP monitor will never make a status request to
+ * begin with
+ */
+ ast_log(LOG_WARNING, "sip_cc_monitor_status_response called. Something dreadfully wrong must have happened.\n");
+ return 0;
+}
+
+static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor)
+{
+ struct sip_monitor_instance *monitor_instance = monitor->private_data;
+ struct cc_epa_entry *cc_entry;
+
+ if (!monitor_instance) {
+ return -1;
+ }
+
+ ast_assert(monitor_instance->suspension_entry != NULL);
+
+ cc_entry = monitor_instance->suspension_entry->instance_data;
+ cc_entry->current_state = CC_OPEN;
+ if (ast_strlen_zero(monitor_instance->notify_uri)) {
+ /* This means we are being asked to unsuspend a call leg we never
+ * sent a PUBLISH on. As such, there is no reason to send another
+ * PUBLISH at this point either. We can just return instead.
+ */
+ return 0;
+ }
+ construct_pidf_body(CC_OPEN, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
+ return transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_MODIFY, monitor_instance->notify_uri);
+}
+
+static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id)
+{
+ if (*sched_id != -1) {
+ AST_SCHED_DEL(sched, *sched_id);
+ ao2_t_ref(monitor, -1, "Removing scheduler's reference to the monitor");
+ }
+ return 0;
+}
+
+static void sip_cc_monitor_destructor(void *private_data)
+{
+ struct sip_monitor_instance *monitor_instance = private_data;
+ ao2_unlink(sip_monitor_instances, monitor_instance);
+ ast_module_unref(ast_module_info->self);
+}
+
+static int sip_get_cc_information(struct sip_request *req, char *subscribe_uri, size_t size, enum ast_cc_service_type *service)
+{
+ char *call_info = ast_strdupa(get_header(req, "Call-Info"));
+ char *uri;
+ char *purpose;
+ char *service_str;
+ static const char cc_purpose[] = "purpose=call-completion";
+ static const int cc_purpose_len = sizeof(cc_purpose) - 1;
+
+ if (ast_strlen_zero(call_info)) {
+ /* No Call-Info present. Definitely no CC offer */
+ return -1;
+ }
+
+ uri = strsep(&call_info, ";");
+
+ while ((purpose = strsep(&call_info, ";"))) {
+ if (!strncmp(purpose, cc_purpose, cc_purpose_len)) {
+ break;
+ }
+ }
+ if (!purpose) {
+ /* We didn't find the appropriate purpose= parameter. Oh well */
+ return -1;
+ }
+
+ /* Okay, call-completion has been offered. Let's figure out what type of service this is */
+ while ((service_str = strsep(&call_info, ";"))) {
+ if (!strncmp(service_str, "m=", 2)) {
+ break;
+ }
+ }
+ if (!service_str) {
+ /* So they didn't offer a particular service, We'll just go with CCBS since it really
+ * doesn't matter anyway
+ */
+ service_str = "BS";
+ } else {
+ /* We already determined that there is an "m=" so no need to check
+ * the result of this strsep
+ */
+ strsep(&service_str, "=");
+ }
+
+ if ((*service = service_string_to_service_type(service_str)) == AST_CC_NONE) {
+ /* Invalid service offered */
+ return -1;
+ }
+
+ ast_copy_string(subscribe_uri, get_in_brackets(uri), size);
+
+ return 0;
+}
+
+/*
+ * \brief Determine what, if any, CC has been offered and queue a CC frame if possible
+ *
+ * After taking care of some formalities to be sure that this call is eligible for CC,
+ * we first try to see if we can make use of native CC. We grab the information from
+ * the passed-in sip_request (which is always a response to an INVITE). If we can
+ * use native CC monitoring for the call, then so be it.
+ *
+ * If native cc monitoring is not possible or not supported, then we will instead attempt
+ * to use generic monitoring. Falling back to generic from a failed attempt at using native
+ * monitoring will only work if the monitor policy of the endpoint is "always"
+ *
+ * \param pvt The current dialog. Contains CC parameters for the endpoint
+ * \param req The response to the INVITE we want to inspect
+ * \param service The service to use if generic monitoring is to be used. For native
+ * monitoring, we get the service from the SIP response itself
+ */
+static void sip_handle_cc(struct sip_pvt *pvt, struct sip_request *req, enum ast_cc_service_type service)
+{
+ enum ast_cc_monitor_policies monitor_policy = ast_get_cc_monitor_policy(pvt->cc_params);
+ int core_id;
+ char interface_name[AST_CHANNEL_NAME];
+
+ if (monitor_policy == AST_CC_MONITOR_NEVER) {
+ /* Don't bother, just return */
+ return;
+ }
+
+ if ((core_id = ast_cc_get_current_core_id(pvt->owner)) == -1) {
+ /* For some reason, CC is invalid, so don't try it! */
+ return;
+ }
+
+ ast_channel_get_device_name(pvt->owner, interface_name, sizeof(interface_name));
+
+ if (monitor_policy == AST_CC_MONITOR_ALWAYS || monitor_policy == AST_CC_MONITOR_NATIVE) {
+ char subscribe_uri[SIPBUFSIZE];
+ char device_name[AST_CHANNEL_NAME];
+ enum ast_cc_service_type offered_service;
+ struct sip_monitor_instance *monitor_instance;
+ if (sip_get_cc_information(req, subscribe_uri, sizeof(subscribe_uri), &offered_service)) {
+ /* If CC isn't being offered to us, or for some reason the CC offer is
+ * not formatted correctly, then it may still be possible to use generic
+ * call completion since the monitor policy may be "always"
+ */
+ goto generic;
+ }
+ ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name));
+ if (!(monitor_instance = sip_monitor_instance_init(core_id, subscribe_uri, pvt->peername, device_name))) {
+ /* Same deal. We can try using generic still */
+ goto generic;
+ }
+ /* We bump the refcount of chan_sip because once we queue this frame, the CC core
+ * will have a reference to callbacks in this module. We decrement the module
+ * refcount once the monitor destructor is called
+ */
+ ast_module_ref(ast_module_info->self);
+ ast_queue_cc_frame(pvt->owner, "SIP", pvt->dialstring, offered_service, monitor_instance);
+ ao2_ref(monitor_instance, -1);
+ return;
+ }
+
+generic:
+ if (monitor_policy == AST_CC_MONITOR_GENERIC || monitor_policy == AST_CC_MONITOR_ALWAYS) {
+ ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE, interface_name, service, NULL);
+ }
+}
+
/*! \brief Working TLS connection configuration */
static struct ast_tls_config sip_tls_cfg;
@@ -2700,7 +3509,7 @@ static int __sip_autodestruct(const void *data)
struct sip_pvt *p = (struct sip_pvt *)data;
/* If this is a subscription, tell the phone that we got a timeout */
- if (p->subscribed) {
+ if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) {
transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE); /* Send last notification */
p->subscribed = NONE;
append_history(p, "Subscribestatus", "timeout");
@@ -3159,6 +3968,16 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
*cp = p->dsp ? 1 : 0;
ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
break;
+ case AST_OPTION_DEVICE_NAME:
+ if (p && p->outgoing_call) {
+ cp = (char *) data;
+ ast_copy_string(cp, p->dialstring, *datalen);
+ res = 0;
+ }
+ /* We purposely break with a return of -1 in the
+ * implied else case here
+ */
+ break;
default:
break;
}
@@ -3401,6 +4220,8 @@ static void sip_destroy_peer(struct sip_peer *peer)
peer->socket.tcptls_session = NULL;
}
+ ast_cc_config_params_destroy(peer->cc_params);
+
ast_string_field_free_memory(peer);
}
@@ -3943,6 +4764,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
dialog->peerauth = peer->auth;
dialog->maxcallbitrate = peer->maxcallbitrate;
dialog->disallowed_methods = peer->disallowed_methods;
+ ast_cc_copy_config_params(dialog->cc_params, peer->cc_params);
if (ast_strlen_zero(dialog->tohost))
ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr));
if (!ast_strlen_zero(peer->fromdomain)) {
@@ -4131,12 +4953,26 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
struct varshead *headp;
struct ast_var_t *current;
const char *referer = NULL; /* SIP referrer */
+ int cc_core_id;
+ char uri[SIPBUFSIZE] = "";
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
+ if (ast_cc_is_recall(ast, &cc_core_id, "SIP")) {
+ char device_name[AST_CHANNEL_NAME];
+ struct ast_cc_monitor *recall_monitor;
+ struct sip_monitor_instance *monitor_instance;
+ ast_channel_get_device_name(ast, device_name, sizeof(device_name));
+ if ((recall_monitor = ast_cc_get_monitor_by_recall_core_id(cc_core_id, device_name))) {
+ monitor_instance = recall_monitor->private_data;
+ ast_copy_string(uri, monitor_instance->notify_uri, sizeof(uri));
+ ao2_t_ref(recall_monitor, -1, "Got the URI we need so unreffing monitor");
+ }
+ }
+
/* Check whether there is vxml_url, distinctive ring variables */
headp=&ast->varshead;
AST_LIST_TRAVERSE(headp, current, entries) {
@@ -4201,7 +5037,7 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
int xmitres;
sip_pvt_lock(p);
- xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
+ xmitres = transmit_invite(p, SIP_INVITE, 1, 2, uri);
sip_pvt_unlock(p);
if (xmitres == XMIT_ERROR)
return -1;
@@ -4371,6 +5207,13 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
ast_string_field_free_memory(p);
+ ast_cc_config_params_destroy(p->cc_params);
+
+ if (p->epa_entry) {
+ ao2_ref(p->epa_entry, -1);
+ p->epa_entry = NULL;
+ }
+
if (p->socket.tcptls_session) {
ao2_ref(p->socket.tcptls_session, -1);
p->socket.tcptls_session = NULL;
@@ -5446,7 +6289,10 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
sip_pvt_lock(i);
return NULL;
}
+ ast_channel_lock(tmp);
sip_pvt_lock(i);
+ ast_channel_cc_params_init(tmp, i->cc_params);
+ ast_channel_unlock(tmp);
tmp->tech = ( ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO || ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO) ? &sip_tech_info : &sip_tech;
@@ -5911,6 +6757,23 @@ static char *generate_random_string(char *buf, size_t size)
return buf;
}
+static char *generate_uri(struct sip_pvt *pvt, char *buf, size_t size)
+{
+ struct ast_str *uri = ast_str_alloca(size);
+ int ourport = ntohs(pvt->ourip.sin_port);
+ ast_str_set(&uri, 0, "%s", pvt->socket.type == SIP_TRANSPORT_TLS ? "sips:" : "sip:");
+ /* Here would be a great place to generate a UUID, but for now we'll
+ * use the handy random string generation function we already have
+ */
+ ast_str_append(&uri, 0, "%s", generate_random_string(buf, size));
+ ast_str_append(&uri, 0, "@%s", ast_inet_ntoa(pvt->ourip.sin_addr));
+ if (!sip_standard_port(pvt->socket.type, ourport)) {
+ ast_str_append(&uri, 0, ":%d", ourport);
+ }
+ ast_copy_string(buf, ast_str_buffer(uri), size);
+ return buf;
+}
+
/*! \brief Build SIP Call-ID value for a non-REGISTER transaction */
static void build_callid_pvt(struct sip_pvt *pvt)
{
@@ -5975,6 +6838,11 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
return NULL;
}
+ if (!(p->cc_params = ast_cc_config_params_init())) {
+ ao2_t_ref(p, -1, "Yuck, couldn't allocate cc_params struct. Get rid o' p");
+ return NULL;
+ }
+
if (req) {
set_socket_transport(&p->socket, req->socket.type); /* Later in ast_sip_ouraddrfor we need this to choose the right ip and port for the specific transport */
} else {
@@ -8234,6 +9102,9 @@ static int __transmit_response(struct sip_pvt *p, const char *msg, const struct
ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND);
add_rpid(&resp, p);
}
+ if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) {
+ add_cc_call_info_to_response(p, &resp);
+ }
add_header_contentLength(&resp, 0);
/* If we are cancelling an incoming invite for some reason, add information
@@ -8269,6 +9140,19 @@ static int __transmit_response(struct sip_pvt *p, const char *msg, const struct
return send_response(p, &resp, reliable, seqno);
}
+static int transmit_response_with_sip_etag(struct sip_pvt *p, const char *msg, const struct sip_request *req, struct sip_esc_entry *esc_entry, int need_new_etag)
+{
+ struct sip_request resp;
+
+ if (need_new_etag) {
+ create_new_sip_etag(esc_entry, 1);
+ }
+ respprep(&resp, p, msg, req);
+ add_header(&resp, "SIP-ETag", esc_entry->entity_tag);
+
+ return send_response(p, &resp, 0, 0);
+}
+
static int temp_pvt_init(void *data)
{
struct sip_pvt *p = data;
@@ -9305,6 +10189,38 @@ static void copy_request(struct sip_request *dst, const struct sip_request *src)
dst->data->used = src->data->used;
}
+static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp)
+{
+ char uri[SIPBUFSIZE];
+ struct ast_str *header = ast_str_alloca(SIPBUFSIZE);
+ struct ast_cc_agent *agent = find_sip_cc_agent_by_original_callid(p);
+ struct sip_cc_agent_pvt *agent_pvt;
+
+ if (!agent) {
+ /* Um, what? How could the SIP_OFFER_CC flag be set but there not be an
+ * agent? Oh well, we'll just warn and return without adding the header.
+ */
+ ast_log(LOG_WARNING, "Can't find SIP CC agent for call '%s' even though OFFER_CC flag was set?\n", p->callid);
+ return;
+ }
+
+ agent_pvt = agent->private_data;
+
+ if (!ast_strlen_zero(agent_pvt->subscribe_uri)) {
+ ast_copy_string(uri, agent_pvt->subscribe_uri, sizeof(uri));
+ } else {
+ generate_uri(p, uri, sizeof(uri));
+ ast_copy_string(agent_pvt->subscribe_uri, uri, sizeof(agent_pvt->subscribe_uri));
+ }
+ /* XXX Hardcode "NR" as the m reason for now. This should perhaps be changed
+ * to be more accurate. This parameter has no bearing on the actual operation
+ * of the feature; it's just there for informational purposes.
+ */
+ ast_str_set(&header, 0, "<%s>;purpose=call-completion;m=%s", uri, "NR");
+ add_header(resp, "Call-Info", ast_str_buffer(header));
+ ao2_ref(agent, -1);
+}
+
/*! \brief Used for 200 OK and 183 early media
\return Will return XMIT_ERROR for network errors.
*/
@@ -9320,6 +10236,9 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
if (rpid == TRUE) {
add_rpid(&resp, p);
}
+ if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) {
+ add_cc_call_info_to_response(p, &resp);
+ }
if (p->rtp) {
if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
ast_debug(1, "Setting framing from config on incoming call\n");
@@ -9479,7 +10398,7 @@ static void build_contact(struct sip_pvt *p)
}
/*! \brief Initiate new SIP request to peer/user */
-static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod)
+static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri)
{
struct ast_str *invite = ast_str_alloca(256);
char from[256];
@@ -9564,25 +10483,30 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
else
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, d, p->tag);
- /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
- if (!ast_strlen_zero(p->fullcontact)) {
- /* If we have full contact, trust it */
- ast_str_append(&invite, 0, "%s", p->fullcontact);
+
+ if (!ast_strlen_zero(explicit_uri)) {
+ ast_str_set(&invite, 0, "%s", explicit_uri);
} else {
- /* Otherwise, use the username while waiting for registration */
- ast_str_append(&invite, 0, "sip:");
- if (!ast_strlen_zero(p->username)) {
- n = p->username;
- if (sip_cfg.pedanticsipchecking) {
- ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0);
- n = tmp_n;
+ /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
+ if (!ast_strlen_zero(p->fullcontact)) {
+ /* If we have full contact, trust it */
+ ast_str_append(&invite, 0, "%s", p->fullcontact);
+ } else {
+ /* Otherwise, use the username while waiting for registration */
+ ast_str_append(&invite, 0, "sip:");
+ if (!ast_strlen_zero(p->username)) {
+ n = p->username;
+ if (sip_cfg.pedanticsipchecking) {
+ ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0);
+ n = tmp_n;
+ }
+ ast_str_append(&invite, 0, "%s@", n);
}
- ast_str_append(&invite, 0, "%s@", n);
+ ast_str_append(&invite, 0, "%s", p->tohost);
+ if (p->portinuri)
+ ast_str_append(&invite, 0, ":%d", ntohs(p->sa.sin_port));
+ ast_str_append(&invite, 0, "%s", urioptions);
}
- ast_str_append(&invite, 0, "%s", p->tohost);
- if (p->portinuri)
- ast_str_append(&invite, 0, ":%d", ntohs(p->sa.sin_port));
- ast_str_append(&invite, 0, "%s", urioptions);
}
/* If custom URI options have been provided, append them */
@@ -9674,6 +10598,39 @@ static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt)
add_header(req, "Diversion", header_text);
}
+static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri)
+{
+ struct sip_pvt *pvt;
+ int expires;
+
+ epa_entry->publish_type = publish_type;
+
+ if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_PUBLISH, NULL))) {
+ return -1;
+ }
+
+ sip_pvt_lock(pvt);
+
+ if (create_addr(pvt, epa_entry->destination, NULL, TRUE)) {
+ dialog_unlink_all(pvt, TRUE, TRUE);
+ dialog_unref(pvt, "create_addr failed in transmit_publish. Unref dialog");
+ }
+ ast_sip_ouraddrfor(&pvt->sa.sin_addr, &pvt->ourip, pvt);
+ ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
+ expires = (publish_type == SIP_PUBLISH_REMOVE) ? 0 : DEFAULT_PUBLISH_EXPIRES;
+ pvt->expiry = expires;
+
+ /* Bump refcount for sip_pvt's reference */
+ ao2_ref(epa_entry, +1);
+ pvt->epa_entry = epa_entry;
+
+ transmit_invite(pvt, SIP_PUBLISH, FALSE, 2, explicit_uri);
+ sip_pvt_unlock(pvt);
+ sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT);
+ dialog_unref(pvt, "Done with the sip_pvt allocated for transmitting PUBLISH");
+ return 0;
+}
+
/*! \brief Build REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it
\param init 0 = Prepare request within dialog, 1= prepare request, new branch, 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2
\param p sip_pvt structure
@@ -9681,7 +10638,7 @@ static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt)
\param sipmethod unknown
*/
-static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
+static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri)
{
struct sip_request req;
struct ast_variable *var;
@@ -9693,7 +10650,7 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
build_via(p);
}
if (init > 1)
- initreqprep(&req, p, sipmethod);
+ initreqprep(&req, p, sipmethod, explicit_uri);
else
/* If init=1, we should not generate a new branch. If it's 0, we need a new branch. */
reqprep(&req, p, sipmethod, 0, init ? 0 : 1);
@@ -9711,12 +10668,16 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
add_header(&req, "Referred-By", buf);
}
}
- } else if (sipmethod == SIP_SUBSCRIBE) { /* We only support sending MWI subscriptions right now */
+ } else if (sipmethod == SIP_SUBSCRIBE) {
char buf[SIPBUFSIZE];
-
- add_header(&req, "Event", "message-summary");
- add_header(&req, "Accept", "application/simple-message-summary");
- snprintf(buf, sizeof(buf), "%d", mwi_expiry);
+ if (p->subscribed == MWI_NOTIFICATION) {
+ add_header(&req, "Event", "message-summary");
+ add_header(&req, "Accept", "application/simple-message-summary");
+ } else if (p->subscribed == CALL_COMPLETION) {
+ add_header(&req, "Event", "call-completion");
+ add_header(&req, "Accept", "application/call-completion");
+ }
+ snprintf(buf, sizeof(buf), "%d", p->expiry);
add_header(&req, "Expires", buf);
}
@@ -9805,13 +10766,33 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
add_header_contentLength(&req, ast_str_strlen(p->notify->content));
if (ast_str_strlen(p->notify->content))
add_line(&req, ast_str_buffer(p->notify->content));
+ } else if (sipmethod == SIP_PUBLISH) {
+ char expires[SIPBUFSIZE];
+ switch (p->epa_entry->static_data->event) {
+ case CALL_COMPLETION:
+ snprintf(expires, sizeof(expires), "%d", p->expiry);
+ add_header(&req, "Event", "call-completion");
+ add_header(&req, "Expires", expires);
+ if (p->epa_entry->publish_type != SIP_PUBLISH_INITIAL) {
+ add_header(&req, "SIP-If-Match", p->epa_entry->entity_tag);
+ }
+ if (!ast_strlen_zero(p->epa_entry->body)) {
+ add_header(&req, "Content-Type", "application/pidf+xml");
+ add_header_contentLength(&req, strlen(p->epa_entry->body));
+ add_line(&req, p->epa_entry->body);
+ } else {
+ add_header_contentLength(&req, 0);
+ }
+ default:
+ break;
+ }
} else {
add_header_contentLength(&req, 0);
}
if (!p->initreq.headers || init > 2)
initialize_initreq(p, &req);
- if (sipmethod == SIP_INVITE) {
+ if (sipmethod == SIP_INVITE || sipmethod == SIP_SUBSCRIBE) {
p->lastinvite = p->ocseq;
}
return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq);
@@ -9845,7 +10826,7 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
/* If we already have a subscription up simply send a resubscription */
if (mwi->call) {
- transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 0);
+ transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 0, NULL);
return 0;
}
@@ -9866,6 +10847,8 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
mwi->call = dialog_unref(mwi->call, "unref dialog after unlink_all");
return 0;
}
+
+ mwi->call->expiry = mwi_expiry;
if (!mwi->dnsmgr && mwi->portno) {
mwi->call->sa.sin_port = htons(mwi->portno);
@@ -9900,7 +10883,7 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
mwi->call->mwi = ASTOBJ_REF(mwi);
/* Actually send the packet */
- transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 2);
+ transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 2, NULL);
return 0;
}
@@ -10084,6 +11067,34 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
}
}
+static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state)
+{
+ struct sip_request req;
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ char uri[SIPBUFSIZE];
+ char state_str[64];
+
+ if (state < CC_QUEUED || state > CC_READY) {
+ ast_log(LOG_WARNING, "Invalid state provided for transmit_cc_notify (%d)\n", state);
+ return -1;
+ }
+
+ reqprep(&req, subscription, SIP_NOTIFY, 0, TRUE);
+ snprintf(state_str, sizeof(state_str), "%s\r\n", sip_cc_notify_state_map[state].state_string);
+ add_header(&req, "Event", "call-completion");
+ add_header(&req, "Content-Type", "application/call-completion");
+ if (state == CC_READY) {
+ generate_uri(subscription, agent_pvt->notify_uri, sizeof(agent_pvt->notify_uri));
+ snprintf(uri, sizeof(uri) - 1, "cc-URI: %s\r\n", agent_pvt->notify_uri);
+ }
+ add_header_contentLength(&req, strlen(state_str) +
+ (state == CC_READY ? strlen(uri) : 0));
+ add_line(&req, state_str);
+ if (state == CC_READY) {
+ add_line(&req, uri);
+ }
+ return send_request(subscription, &req, XMIT_RELIABLE, subscription->ocseq);
+}
/*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */
static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout)
@@ -10183,7 +11194,7 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs,
int ourport = ntohs(p->ourip.sin_port);
const char *exten = S_OR(vmexten, default_vmexten);
- initreqprep(&req, p, SIP_NOTIFY);
+ initreqprep(&req, p, SIP_NOTIFY, NULL);
add_header(&req, "Event", "message-summary");
add_header(&req, "Content-Type", default_notifymime);
ast_str_append(&out, 0, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no");
@@ -10297,7 +11308,7 @@ static int manager_sipnotify(struct mansession *s, const struct message *m)
dialog_ref(p, "bump the count of p, which transmit_sip_request will decrement.");
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- transmit_invite(p, SIP_NOTIFY, 0, 2);
+ transmit_invite(p, SIP_NOTIFY, 0, 2, NULL);
astman_send_ack(s, m, "Notify Sent");
ast_variables_destroy(vars);
@@ -12438,13 +13449,13 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
the dialplan, so that the outbound call also is a sips: call or encrypted
IAX2 call. If that's not available, the call should FAIL.
*/
-static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
+static int get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id)
{
char tmp[256] = "", *uri, *domain, *dummy = NULL;
char tmpf[256] = "", *from = NULL;
struct sip_request *req;
char *decoded_uri;
-
+
req = oreq;
if (!req)
req = &p->initreq;
@@ -12453,7 +13464,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
if (req->rlPart2)
ast_copy_string(tmp, REQ_OFFSET_TO_STR(req, rlPart2), sizeof(tmp));
- uri = get_in_brackets(tmp);
+ uri = ast_strdupa(get_in_brackets(tmp));
if (parse_uri(uri, "sip:,sips:", &uri, &dummy, &domain, &dummy, NULL)) {
ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri);
@@ -12510,6 +13521,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
char hint[AST_MAX_EXTENSION];
return (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) ? 0 : -1);
} else {
+ struct ast_cc_agent *agent;
decoded_uri = ast_strdupa(uri);
ast_uri_decode(decoded_uri);
/* Check the dialplan for the username part of the request URI,
@@ -12522,6 +13534,22 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
if (!oreq)
ast_string_field_set(p, exten, decoded_uri);
return 0;
+ } else if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) {
+ struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+ /* This is a CC recall. We can set p's extension to the exten from
+ * the original INVITE
+ */
+ ast_string_field_set(p, exten, agent_pvt->original_exten);
+ /* And we need to let the CC core know that the caller is attempting
+ * his recall
+ */
+ ast_cc_agent_recalling(agent->core_id, "SIP caller %s is attempting recall",
+ agent->device_name);
+ if (cc_recall_core_id) {
+ *cc_recall_core_id = agent->core_id;
+ }
+ ao2_ref(agent, -1);
+ return 0;
}
}
@@ -13033,6 +14061,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
ast_string_field_set(p, engine, peer->engine);
p->disallowed_methods = peer->disallowed_methods;
set_pvt_allowed_methods(p, req);
+ ast_cc_copy_config_params(p->cc_params, peer->cc_params);
if (peer->callingpres) /* Peer calling pres setting will override RPID */
p->callingpres = peer->callingpres;
if (peer->maxms && peer->lastms)
@@ -15981,7 +17010,7 @@ static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n", a->argv[2], a->argv[i]);
dialog_ref(p, "bump the count of p, which transmit_sip_request will decrement.");
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- transmit_invite(p, SIP_NOTIFY, 0, 2);
+ transmit_invite(p, SIP_NOTIFY, 0, 2, NULL);
}
return CLI_SUCCESS;
@@ -16061,7 +17090,7 @@ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, enum sip_au
/* Now we have a reply digest */
p->options->auth = digest;
p->options->authheader = respheader;
- return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init);
+ return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init, NULL);
}
/*! \brief reply to authentication for outbound registrations
@@ -16698,6 +17727,106 @@ static void handle_response_update(struct sip_pvt *p, int resp, const char *rest
}
}
+static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry)
+{
+ struct cc_epa_entry *cc_entry = epa_entry->instance_data;
+ struct sip_monitor_instance *monitor_instance = ao2_callback(sip_monitor_instances, 0,
+ find_sip_monitor_instance_by_suspension_entry, epa_entry);
+ const char *min_expires;
+
+ if (!monitor_instance) {
+ ast_log(LOG_WARNING, "Can't find monitor_instance corresponding to epa_entry %p.\n", epa_entry);
+ return;
+ }
+
+ if (resp != 423) {
+ ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name,
+ "Received error response to our PUBLISH");
+ ao2_ref(monitor_instance, -1);
+ return;
+ }
+
+ /* Allrighty, the other end doesn't like our Expires value. They think it's
+ * too small, so let's see if they've provided a more sensible value. If they
+ * haven't, then we'll just double our Expires value and see if they like that
+ * instead.
+ *
+ * XXX Ideally this logic could be placed into its own function so that SUBSCRIBE,
+ * PUBLISH, and REGISTER could all benefit from the same shared code.
+ */
+ min_expires = get_header(req, "Min-Expires");
+ if (ast_strlen_zero(min_expires)) {
+ pvt->expiry *= 2;
+ if (pvt->expiry < 0) {
+ /* You dork! You overflowed! */
+ ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name,
+ "PUBLISH expiry overflowed");
+ ao2_ref(monitor_instance, -1);
+ return;
+ }
+ } else if (sscanf(min_expires, "%d", &pvt->expiry) != 1) {
+ ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name,
+ "Min-Expires has non-numeric value");
+ ao2_ref(monitor_instance, -1);
+ return;
+ }
+ /* At this point, we have most certainly changed pvt->expiry, so try transmitting
+ * again
+ */
+ transmit_invite(pvt, SIP_PUBLISH, FALSE, 0, NULL);
+ ao2_ref(monitor_instance, -1);
+}
+
+static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
+{
+ struct sip_epa_entry *epa_entry = p->epa_entry;
+ const char *etag = get_header(req, "Sip-ETag");
+
+ ast_assert(epa_entry != NULL);
+
+ if (resp == 401 || resp == 407) {
+ ast_string_field_set(p, theirtag, NULL);
+ if (p->options) {
+ p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
+ }
+ if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_PUBLISH, 0)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on PUBLISH to '%s'\n", get_header(&p->initreq, "From"));
+ pvt_set_needdestroy(p, "Failed to authenticate on PUBLISH");
+ sip_alreadygone(p);
+ }
+ return;
+ }
+
+ if (resp == 501 || resp == 405) {
+ mark_method_unallowed(&p->allowed_methods, SIP_PUBLISH);
+ }
+
+ if (resp == 200) {
+ p->authtries = 0;
+ /* If I've read section 6, item 6 of RFC 3903 correctly,
+ * an ESC will only generate a new etag when it sends a 200 OK
+ */
+ if (!ast_strlen_zero(etag)) {
+ ast_copy_string(epa_entry->entity_tag, etag, sizeof(epa_entry->entity_tag));
+ }
+ /* The nominal case. Everything went well. Everybody is happy.
+ * Each EPA will have a specific action to take as a result of this
+ * development, so ... callbacks!
+ */
+ if (epa_entry->static_data->handle_ok) {
+ epa_entry->static_data->handle_ok(p, req, epa_entry);
+ }
+ } else {
+ /* Rather than try to make individual callbacks for each error
+ * type, there is just a single error callback. The callback
+ * can distinguish between error messages and do what it needs to
+ */
+ if (epa_entry->static_data->handle_error) {
+ epa_entry->static_data->handle_error(p, resp, req, epa_entry);
+ }
+ }
+}
+
/*! \brief Handle SIP response to INVITE dialogue */
static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
{
@@ -16769,6 +17898,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
ast_channel_queue_connected_line_update(p->owner, &connected);
}
+ sip_handle_cc(p, req, AST_CC_CCNR);
ast_queue_control(p->owner, AST_CONTROL_RINGING);
if (p->owner->_state != AST_STATE_UP) {
ast_setstate(p->owner, AST_STATE_RINGING);
@@ -16794,6 +17924,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
struct ast_party_redirecting redirecting = {{0,},};
change_redirecting_information(p, req, &redirecting, FALSE);
ast_channel_queue_redirecting_update(p->owner, &redirecting);
+ sip_handle_cc(p, req, AST_CC_CCNR);
}
check_pendings(p);
break;
@@ -16811,6 +17942,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
ast_channel_queue_connected_line_update(p->owner, &connected);
}
+ sip_handle_cc(p, req, AST_CC_CCNR);
}
if (find_sdp(req)) {
if (p->invitestate != INV_CANCELLED)
@@ -17569,6 +18701,11 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
need to hang around for something more "definitive" */
if (resp != 100)
handle_response_peerpoke(p, resp, req);
+ } else if (sipmethod == SIP_PUBLISH) {
+ /* SIP PUBLISH transcends this morass of doodoo and instead
+ * we just always call the response handler. Good gravy!
+ */
+ handle_response_publish(p, resp, rest, req, seqno);
} else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
switch(resp) {
case 100: /* 100 Trying */
@@ -17763,8 +18900,10 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
case 486: /* Busy here */
case 600: /* Busy everywhere */
case 603: /* Decline */
- if (p->owner)
+ if (p->owner) {
+ sip_handle_cc(p, req, AST_CC_CCBS);
ast_queue_control(p->owner, AST_CONTROL_BUSY);
+ }
break;
case 482: /*!
\note SIP is incapable of performing a hairpin call, which
@@ -18214,6 +19353,66 @@ static const char *gettag(const struct sip_request *req, const char *header, cha
return NULL;
}
+static int handle_cc_notify(struct sip_pvt *pvt, struct sip_request *req)
+{
+ struct sip_monitor_instance *monitor_instance = ao2_callback(sip_monitor_instances, 0,
+ find_sip_monitor_instance_by_subscription_pvt, pvt);
+ const char *status = get_body(req, "cc-state", ':');
+ struct cc_epa_entry *cc_entry;
+ char *uri;
+
+ if (!monitor_instance) {
+ transmit_response(pvt, "400 Bad Request", req);
+ return -1;
+ }
+
+ if (ast_strlen_zero(status)) {
+ ao2_ref(monitor_instance, -1);
+ transmit_response(pvt, "400 Bad Request", req);
+ return -1;
+ }
+
+ if (!strcmp(status, "queued")) {
+ /* We've been told that we're queued. This is the endpoint's way of telling
+ * us that it has accepted our CC request. We need to alert the core of this
+ * development
+ */
+ ast_cc_monitor_request_acked(monitor_instance->core_id, "SIP endpoint %s accepted request", monitor_instance->device_name);
+ transmit_response(pvt, "200 OK", req);
+ ao2_ref(monitor_instance, -1);
+ return 0;
+ }
+
+ /* It's open! Yay! */
+ uri = get_body(req, "cc-URI", ':');
+ if (ast_strlen_zero(uri)) {
+ uri = get_in_brackets((char *)get_header(req, "From"));
+ }
+
+ ast_string_field_set(monitor_instance, notify_uri, uri);
+ if (monitor_instance->suspension_entry) {
+ cc_entry = monitor_instance->suspension_entry->instance_data;
+ if (cc_entry->current_state == CC_CLOSED) {
+ /* If we've created a suspension entry and the current state is closed, then that means
+ * we got a notice from the CC core earlier to suspend monitoring, but because this particular
+ * call leg had not yet notified us that it was ready for recall, it meant that we
+ * could not yet send a PUBLISH. Now, however, we can.
+ */
+ construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body,
+ sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
+ transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_INITIAL, monitor_instance->notify_uri);
+ } else {
+ ast_cc_monitor_callee_available(monitor_instance->core_id, "SIP monitored callee has become available");
+ }
+ } else {
+ ast_cc_monitor_callee_available(monitor_instance->core_id, "SIP monitored callee has become available");
+ }
+ ao2_ref(monitor_instance, -1);
+ transmit_response(pvt, "200 OK", req);
+
+ return 0;
+}
+
/*! \brief Handle incoming notifications */
static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e)
{
@@ -18376,6 +19575,8 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
/* Used by Sipura/Linksys for NAT pinhole,
* just confirm that we recieved the packet. */
transmit_response(p, "200 OK", req);
+ } else if (!strcmp(event, "call-completion")) {
+ res = handle_cc_notify(p, req);
} else {
/* We don't understand this event. */
transmit_response(p, "489 Bad event", req);
@@ -18411,7 +19612,7 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req)
return 0;
}
- res = get_destination(p, req);
+ res = get_destination(p, req, NULL);
build_contact(p);
if (ast_strlen_zero(p->context))
@@ -19271,6 +20472,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
/* This is a new invite */
/* Handle authentication if this is our first invite */
struct ast_party_redirecting redirecting = {{0,},};
+ int cc_recall_core_id = -1;
set_pvt_allowed_methods(p, req);
res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
if (res == AUTH_CHALLENGE_SENT) {
@@ -19338,7 +20540,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
res = 0;
goto request_invite_cleanup;
}
- gotdest = get_destination(p, NULL); /* Get destination right away */
+ gotdest = get_destination(p, NULL, &cc_recall_core_id); /* Get destination right away */
change_redirecting_information(p, req, &redirecting, FALSE); /*Will return immediately if no Diversion header is present */
extract_uri(p, req); /* Get the Contact URI */
build_contact(p); /* Build our contact header */
@@ -19376,6 +20578,10 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
make_our_tag(p->tag, sizeof(p->tag));
/* First invitation - create the channel */
c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL);
+ if (cc_recall_core_id != -1) {
+ ast_setup_cc_recall_datastore(c, cc_recall_core_id);
+ ast_cc_agent_set_interfaces_chanvar(c);
+ }
*recount = 1;
/* Save Record-Route for any later requests we make on this dialogue */
@@ -20454,6 +21660,496 @@ static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
return 1;
}
+static enum sip_publish_type determine_sip_publish_type(struct sip_request *req, const char * const event, const char * const etag, const char * const expires, int *expires_int)
+{
+ int etag_present = !ast_strlen_zero(etag);
+ int body_present = req->lines > 0;
+
+ ast_assert(expires_int != NULL);
+
+ if (ast_strlen_zero(expires)) {
+ /* Section 6, item 4, second bullet point of RFC 3903 says to
+ * use a locally-configured default expiration if none is provided
+ * in the request
+ */
+ *expires_int = DEFAULT_PUBLISH_EXPIRES;
+ } else if (sscanf(expires, "%30d", expires_int) != 1) {
+ return SIP_PUBLISH_UNKNOWN;
+ }
+
+ if (*expires_int == 0) {
+ return SIP_PUBLISH_REMOVE;
+ } else if (!etag_present && body_present) {
+ return SIP_PUBLISH_INITIAL;
+ } else if (etag_present && !body_present) {
+ return SIP_PUBLISH_REFRESH;
+ } else if (etag_present && body_present) {
+ return SIP_PUBLISH_MODIFY;
+ }
+
+ return SIP_PUBLISH_UNKNOWN;
+}
+
+#ifdef HAVE_LIBXML2
+static void get_pidf_body(struct sip_request *req, char *pidf_body, size_t size)
+{
+ int i;
+ struct ast_str *str = ast_str_alloca(size);
+ for (i = 0; i < req->lines; ++i) {
+ ast_str_append(&str, 0, "%s", REQ_OFFSET_TO_STR(req, line[i]));
+ }
+ ast_copy_string(pidf_body, ast_str_buffer(str), size);
+}
+
+static int pidf_validate_tuple(struct ast_xml_node *tuple_node)
+{
+ const char *id;
+ int status_found = FALSE;
+ struct ast_xml_node *tuple_children;
+ struct ast_xml_node *tuple_children_iterator;
+ /* Tuples have to have an id attribute or they're invalid */
+ if (!(id = ast_xml_get_attribute(tuple_node, "id"))) {
+ ast_log(LOG_WARNING, "Tuple XML element has no attribute 'id'\n");
+ return FALSE;
+ }
+ /* We don't care what it actually is, just that it's there */
+ ast_xml_free_attr(id);
+ /* This is a tuple. It must have a status element */
+ if (!(tuple_children = ast_xml_node_get_children(tuple_node))) {
+ /* The tuple has no children. It sucks */
+ ast_log(LOG_WARNING, "Tuple XML element has no child elements\n");
+ return FALSE;
+ }
+ for (tuple_children_iterator = tuple_children; tuple_children_iterator;
+ tuple_children_iterator = ast_xml_node_get_next(tuple_children_iterator)) {
+ /* Similar to the wording used regarding tuples, the status element should appear
+ * first. However, we will once again relax things and accept the status at any
+ * position. We will enforce that only a single status element can be present.
+ */
+ if (strcmp(ast_xml_node_get_name(tuple_children_iterator), "status")) {
+ /* Not the status, we don't care */
+ continue;
+ }
+ if (status_found == TRUE) {
+ /* THERE CAN BE ONLY ONE!!! */
+ ast_log(LOG_WARNING, "Multiple status elements found in tuple. Only one allowed\n");
+ return FALSE;
+ }
+ status_found = TRUE;
+ }
+ return status_found;
+}
+
+
+static int pidf_validate_presence(struct ast_xml_doc *doc)
+{
+ struct ast_xml_node *presence_node = ast_xml_get_root(doc);
+ struct ast_xml_node *child_nodes;
+ struct ast_xml_node *node_iterator;
+ struct ast_xml_ns *ns;
+ const char *entity;
+ const char *namespace;
+ const char presence_namespace[] = "urn:ietf:params:xml:ns:pidf";
+
+ if (!presence_node) {
+ ast_log(LOG_WARNING, "Unable to retrieve root node of the XML document\n");
+ return FALSE;
+ }
+ /* Okay, we managed to open the document! YAY! Now, let's start making sure it's all PIDF-ified
+ * correctly.
+ */
+ if (strcmp(ast_xml_node_get_name(presence_node), "presence")) {
+ ast_log(LOG_WARNING, "Root node of PIDF document is not 'presence'. Invalid\n");
+ return FALSE;
+ }
+
+ /* The presence element must have an entity attribute and an xmlns attribute. Furthermore
+ * the xmlns attribute must be "urn:ietf:params:xml:ns:pidf"
+ */
+ if (!(entity = ast_xml_get_attribute(presence_node, "entity"))) {
+ ast_log(LOG_WARNING, "Presence element of PIDF document has no 'entity' attribute\n");
+ return FALSE;
+ }
+ /* We're not interested in what the entity is, just that it exists */
+ ast_xml_free_attr(entity);
+
+ if (!(ns = ast_xml_find_namespace(doc, presence_node, NULL))) {
+ ast_log(LOG_WARNING, "Couldn't find default namespace...\n");
+ return FALSE;
+ }
+
+ namespace = ast_xml_get_ns_href(ns);
+ if (ast_strlen_zero(namespace) || strcmp(namespace, presence_namespace)) {
+ ast_log(LOG_WARNING, "PIDF document has invalid namespace value %s\n", namespace);
+ return FALSE;
+ }
+
+ if (!(child_nodes = ast_xml_node_get_children(presence_node))) {
+ ast_log(LOG_WARNING, "PIDF document has no elements as children of 'presence'. Invalid\n");
+ return FALSE;
+ }
+
+ /* Check for tuple elements. RFC 3863 says that PIDF documents can have any number of
+ * tuples, including 0. The big thing here is that if there are tuple elements present,
+ * they have to have a single status element within.
+ *
+ * The RFC is worded such that tuples should appear as the first elements as children of
+ * the presence element. However, we'll be accepting of documents which may place other elements
+ * before the tuple(s).
+ */
+ for (node_iterator = child_nodes; node_iterator;
+ node_iterator = ast_xml_node_get_next(node_iterator)) {
+ if (strcmp(ast_xml_node_get_name(node_iterator), "tuple")) {
+ /* Not a tuple. We don't give a rat's hind quarters */
+ continue;
+ }
+ if (pidf_validate_tuple(node_iterator) == FALSE) {
+ ast_log(LOG_WARNING, "Unable to validate tuple\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*!
+ * \brief Makes sure that body is properly formatted PIDF
+ *
+ * Specifically, we check that the document has a "presence" element
+ * at the root and that within that, there is at least one "tuple" element
+ * that contains a "status" element.
+ *
+ * XXX This function currently assumes a default namespace is used. Of course
+ * if you're not using a default namespace, you're probably a stupid jerk anyway.
+ *
+ * \param req The SIP request to check
+ * \param[out] pidf_doc The validated PIDF doc.
+ * \retval FALSE The XML was malformed or the basic PIDF structure was marred
+ * \retval TRUE The PIDF document is of a valid format
+ */
+static int sip_pidf_validate(struct sip_request *req, struct ast_xml_doc **pidf_doc)
+{
+ struct ast_xml_doc *doc;
+ int content_length;
+ const char *content_length_str = get_header(req, "Content-Length");
+ const char *content_type = get_header(req, "Content-Type");
+ char pidf_body[SIPBUFSIZE];
+ int res;
+
+ if (ast_strlen_zero(content_type) || strcmp(content_type, "application/pidf+xml")) {
+ ast_log(LOG_WARNING, "Content type is not PIDF\n");
+ return FALSE;
+ }
+
+ if (ast_strlen_zero(content_length_str)) {
+ ast_log(LOG_WARNING, "No content length. Can't determine bounds of PIDF document\n");
+ return FALSE;
+ }
+
+ if (sscanf(content_length_str, "%30d", &content_length) != 1) {
+ ast_log(LOG_WARNING, "Invalid content length provided\n");
+ return FALSE;
+ }
+
+ if (content_length > sizeof(pidf_body)) {
+ ast_log(LOG_WARNING, "Content length of PIDF document truncated to %d bytes\n", (int) sizeof(pidf_body));
+ content_length = sizeof(pidf_body);
+ }
+
+ get_pidf_body(req, pidf_body, content_length);
+
+ if (!(doc = ast_xml_read_memory(pidf_body, content_length))) {
+ ast_log(LOG_WARNING, "Unable to open XML PIDF document. Is it malformed?\n");
+ return FALSE;
+ }
+
+ res = pidf_validate_presence(doc);
+ if (res == TRUE) {
+ *pidf_doc = doc;
+ } else {
+ ast_xml_close(doc);
+ }
+ return res;
+}
+
+static int cc_esc_publish_handler(struct sip_pvt *pvt, struct sip_request *req, struct event_state_compositor *esc, struct sip_esc_entry *esc_entry)
+{
+ const char *uri = REQ_OFFSET_TO_STR(req, rlPart2);
+ struct ast_cc_agent *agent = find_sip_cc_agent_by_notify_uri(uri);
+ struct sip_cc_agent_pvt *agent_pvt;
+ struct ast_xml_doc *pidf_doc = NULL;
+ const char *basic_status = NULL;
+ struct ast_xml_node *presence_node;
+ struct ast_xml_node *presence_children;
+ struct ast_xml_node *tuple_node;
+ struct ast_xml_node *tuple_children;
+ struct ast_xml_node *status_node;
+ struct ast_xml_node *status_children;
+ struct ast_xml_node *basic_node;
+ int res = 0;
+
+ if (!agent) {
+ ast_log(LOG_WARNING, "Could not find agent using uri '%s'\n", uri);
+ transmit_response(pvt, "412 Conditional Request Failed", req);
+ return -1;
+ }
+
+ agent_pvt = agent->private_data;
+
+ if (sip_pidf_validate(req, &pidf_doc) == FALSE) {
+ res = -1;
+ goto cc_publish_cleanup;
+ }
+
+ /* It's important to note that the PIDF validation routine has no knowledge
+ * of what we specifically want in this instance. A valid PIDF document could
+ * have no tuples, or it could have tuples whose status element has no basic
+ * element contained within. While not violating the PIDF spec, these are
+ * insufficient for our needs in this situation
+ */
+ presence_node = ast_xml_get_root(pidf_doc);
+ if (!(presence_children = ast_xml_node_get_children(presence_node))) {
+ ast_log(LOG_WARNING, "No tuples within presence element.\n");
+ res = -1;
+ goto cc_publish_cleanup;
+ }
+
+ if (!(tuple_node = ast_xml_find_element(presence_children, "tuple", NULL, NULL))) {
+ ast_log(LOG_NOTICE, "Couldn't find tuple node?\n");
+ res = -1;
+ goto cc_publish_cleanup;
+ }
+
+ /* We already made sure that the tuple has a status node when we validated the PIDF
+ * document earlier. So there's no need to enclose this operation in an if statement.
+ */
+ tuple_children = ast_xml_node_get_children(tuple_node);
+ status_node = ast_xml_find_element(tuple_children, "status", NULL, NULL);
+
+ if (!(status_children = ast_xml_node_get_children(status_node))) {
+ ast_log(LOG_WARNING, "No basic elements within status element.\n");
+ res = -1;
+ goto cc_publish_cleanup;
+ }
+
+ if (!(basic_node = ast_xml_find_element(status_children, "basic", NULL, NULL))) {
+ ast_log(LOG_WARNING, "Couldn't find basic node?\n");
+ res = -1;
+ goto cc_publish_cleanup;
+ }
+
+ basic_status = ast_xml_get_text(basic_node);
+
+ if (ast_strlen_zero(basic_status)) {
+ ast_log(LOG_NOTICE, "NOthing in basic node?\n");
+ res = -1;
+ goto cc_publish_cleanup;
+ }
+
+ if (!strcmp(basic_status, "open")) {
+ agent_pvt->is_available = TRUE;
+ ast_cc_agent_caller_available(agent->core_id, "Received PUBLISH stating SIP caller %s is available",
+ agent->device_name);
+ } else if (!strcmp(basic_status, "closed")) {
+ agent_pvt->is_available = FALSE;
+ ast_cc_agent_caller_busy(agent->core_id, "Received PUBLISH stating SIP caller %s is busy",
+ agent->device_name);
+ } else {
+ ast_log(LOG_NOTICE, "Invalid content in basic element: %s\n", basic_status);
+ }
+
+cc_publish_cleanup:
+ if (basic_status) {
+ ast_xml_free_text(basic_status);
+ }
+ if (pidf_doc) {
+ ast_xml_close(pidf_doc);
+ }
+ ao2_ref(agent, -1);
+ if (res) {
+ transmit_response(pvt, "400 Bad Request", req);
+ }
+ return res;
+}
+
+#endif /* HAVE_LIBXML2 */
+
+static int handle_sip_publish_initial(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const int expires)
+{
+ struct sip_esc_entry *esc_entry = create_esc_entry(esc, req, expires);
+ int res = 0;
+
+ if (!esc_entry) {
+ transmit_response(p, "503 Internal Server Failure", req);
+ return -1;
+ }
+
+ if (esc->callbacks->initial_handler) {
+ res = esc->callbacks->initial_handler(p, req, esc, esc_entry);
+ }
+
+ if (!res) {
+ transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 0);
+ }
+
+ ao2_ref(esc_entry, -1);
+ return res;
+}
+
+static int handle_sip_publish_refresh(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag, const int expires)
+{
+ struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
+ int expires_ms = expires * 1000;
+ int res = 0;
+
+ if (!esc_entry) {
+ transmit_response(p, "412 Conditional Request Failed", req);
+ return -1;
+ }
+
+ AST_SCHED_REPLACE_UNREF(esc_entry->sched_id, sched, expires_ms, publish_expire, esc_entry,
+ ao2_ref(_data, -1),
+ ao2_ref(esc_entry, -1),
+ ao2_ref(esc_entry, +1));
+
+ if (esc->callbacks->refresh_handler) {
+ res = esc->callbacks->refresh_handler(p, req, esc, esc_entry);
+ }
+
+ if (!res) {
+ transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
+ }
+
+ ao2_ref(esc_entry, -1);
+ return res;
+}
+
+static int handle_sip_publish_modify(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag, const int expires)
+{
+ struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
+ int expires_ms = expires * 1000;
+ int res = 0;
+
+ if (!esc_entry) {
+ transmit_response(p, "412 Conditional Request Failed", req);
+ return -1;
+ }
+
+ AST_SCHED_REPLACE_UNREF(esc_entry->sched_id, sched, expires_ms, publish_expire, esc_entry,
+ ao2_ref(_data, -1),
+ ao2_ref(esc_entry, -1),
+ ao2_ref(esc_entry, +1));
+
+ if (esc->callbacks->modify_handler) {
+ res = esc->callbacks->modify_handler(p, req, esc, esc_entry);
+ }
+
+ if (!res) {
+ transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
+ }
+
+ ao2_ref(esc_entry, -1);
+ return res;
+}
+
+static int handle_sip_publish_remove(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag)
+{
+ struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
+ int res = 0;
+
+ if (!esc_entry) {
+ transmit_response(p, "412 Conditional Request Failed", req);
+ return -1;
+ }
+
+ AST_SCHED_DEL(sched, esc_entry->sched_id);
+ /* Scheduler's ref of the esc_entry */
+ ao2_ref(esc_entry, -1);
+
+ if (esc->callbacks->remove_handler) {
+ res = esc->callbacks->remove_handler(p, req, esc, esc_entry);
+ }
+
+ if (!res) {
+ transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
+ }
+
+ /* Ref from finding the esc_entry earlier in function */
+ ao2_unlink(esc->compositor, esc_entry);
+ ao2_ref(esc_entry, -1);
+ return res;
+}
+
+static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, const int seqno, const char *uri)
+{
+ const char *etag = get_header(req, "SIP-If-Match");
+ const char *event = get_header(req, "Event");
+ struct event_state_compositor *esc;
+ enum sip_publish_type publish_type;
+ const char *expires_str = get_header(req, "Expires");
+ int expires_int;
+ int auth_result;
+ int handler_result = -1;
+
+ if (ast_strlen_zero(event)) {
+ transmit_response(p, "489 Bad Event", req);
+ return -1;
+ }
+
+ if (!(esc = get_esc(event))) {
+ transmit_response(p, "489 Bad Event", req);
+ return -1;
+ }
+
+ auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_RELIABLE, sin);
+ if (auth_result == AUTH_CHALLENGE_SENT) {
+ p->lastinvite = seqno;
+ return 0;
+ } else if (auth_result < 0) {
+ if (auth_result == AUTH_FAKE_AUTH) {
+ ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+ transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE);
+ } else {
+ ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+ transmit_response_reliable(p, "403 Forbidden", req);
+ }
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ ast_string_field_set(p, theirtag, NULL);
+ return 0;
+ } else if (auth_result == AUTH_SUCCESSFUL && p->lastinvite) {
+ /* We need to stop retransmitting the 401 */
+ __sip_ack(p, p->lastinvite, 1, 0);
+ }
+
+ publish_type = determine_sip_publish_type(req, event, etag, expires_str, &expires_int);
+
+ /* It is the responsibility of these handlers to formulate any response
+ * sent for a PUBLISH
+ */
+ switch (publish_type) {
+ case SIP_PUBLISH_UNKNOWN:
+ transmit_response(p, "400 Bad Request", req);
+ break;
+ case SIP_PUBLISH_INITIAL:
+ handler_result = handle_sip_publish_initial(p, req, esc, expires_int);
+ break;
+ case SIP_PUBLISH_REFRESH:
+ handler_result = handle_sip_publish_refresh(p, req, esc, etag, expires_int);
+ break;
+ case SIP_PUBLISH_MODIFY:
+ handler_result = handle_sip_publish_modify(p, req, esc, etag, expires_int);
+ break;
+ case SIP_PUBLISH_REMOVE:
+ handler_result = handle_sip_publish_remove(p, req, esc, etag);
+ break;
+ default:
+ transmit_response(p, "400 Impossible Condition", req);
+ break;
+ }
+
+ return handler_result;
+}
+
static void add_peer_mwi_subs(struct sip_peer *peer)
{
struct sip_mailbox *mailbox;
@@ -20466,6 +22162,63 @@ static void add_peer_mwi_subs(struct sip_peer *peer)
}
}
+static int handle_cc_subscribe(struct sip_pvt *p, struct sip_request *req)
+{
+ const char *uri = REQ_OFFSET_TO_STR(req, rlPart2);
+ char *param_separator;
+ struct ast_cc_agent *agent;
+ struct sip_cc_agent_pvt *agent_pvt;
+ const char *expires_str = get_header(req, "Expires");
+ int expires = -1; /* Just need it to be non-zero */
+
+ if (!ast_strlen_zero(expires_str)) {
+ sscanf(expires_str, "%d", &expires);
+ }
+
+ if ((param_separator = strchr(uri, ';'))) {
+ *param_separator = '\0';
+ }
+
+ if (!(agent = find_sip_cc_agent_by_subscribe_uri(uri))) {
+ if (!expires) {
+ /* Typically, if a 0 Expires reaches us and we can't find
+ * the corresponding agent, it means that the CC transaction
+ * has completed and so the calling side is just trying to
+ * clean up its subscription. We'll just respond with a
+ * 200 OK and be done with it
+ */
+ transmit_response(p, "200 OK", req);
+ return 0;
+ }
+ ast_log(LOG_WARNING, "Invalid URI '%s' in CC subscribe\n", uri);
+ transmit_response(p, "404 Not Found", req);
+ return -1;
+ }
+
+ agent_pvt = agent->private_data;
+
+ if (!expires) {
+ /* We got sent a SUBSCRIBE and found an agent. This means that CC
+ * is being canceled.
+ */
+ ast_cc_failed(agent->core_id, "CC is being canceled by %s", agent->device_name);
+ transmit_response(p, "200 OK", req);
+ ao2_ref(agent, -1);
+ return 0;
+ }
+
+ agent_pvt->subscribe_pvt = dialog_ref(p, "SIP CC agent gains reference to subscription dialog");
+ ast_cc_agent_accept_request(agent->core_id, "SIP caller %s has requested CC via SUBSCRIBE",
+ agent->device_name);
+ p->subscribed = CALL_COMPLETION;
+
+ /* We don't send a response here. That is done in the agent's ack callback or in the
+ * agent destructor, should a failure occur before we have responded
+ */
+ ao2_ref(agent, -1);
+ return 0;
+}
+
/*! \brief Handle incoming SUBSCRIBE request */
static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e)
{
@@ -20578,9 +22331,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
return 0;
}
- if (strcmp(event, "message-summary")) {
+ if (strcmp(event, "message-summary") && strcmp(event, "call-completion")) {
/* Get destination right away */
- gotdest = get_destination(p, NULL);
+ gotdest = get_destination(p, NULL, NULL);
}
/* Get full contact header - this needs to be used as a request URI in NOTIFY's */
@@ -20685,6 +22438,8 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
p->relatedpeer = ref_peer(authpeer, "setting dialog's relatedpeer pointer"); /* already refcounted...Link from pvt to peer UH- should this be dialog_ref()? */
/* Do not release authpeer here */
+ } else if (!strcmp(event, "call-completion")) {
+ handle_cc_subscribe(p, req);
} else { /* At this point, Asterisk does not understand the specified event */
transmit_response(p, "489 Bad Event", req);
ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
@@ -20695,7 +22450,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
}
/* Add subscription for extension state from the PBX core */
- if (p->subscribed != MWI_NOTIFICATION && !resubscribe) {
+ if (p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION && !resubscribe) {
if (p->stateid > -1) {
ast_extension_state_del(p->stateid, cb_extensionstate);
/* we need to dec the refcount, now that the extensionstate is removed */
@@ -20716,10 +22471,13 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
p->expiry = min_expiry;
if (sipdebug) {
- if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer)
+ if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) {
ast_debug(2, "Adding subscription for mailbox notification - peer %s\n", p->relatedpeer->name);
- else
+ } else if (p->subscribed == CALL_COMPLETION) {
+ ast_debug(2, "Adding CC subscription for peer %s\n", p->username);
+ } else {
ast_debug(2, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
+ }
}
if (p->autokillid > -1 && sip_cancel_destroy(p)) /* Remove subscription expiry for renewals */
ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
@@ -20734,7 +22492,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
sip_send_mwi_to_peer(p->relatedpeer, NULL, 0);
ao2_unlock(p->relatedpeer);
}
- } else {
+ } else if (p->subscribed != CALL_COMPLETION) {
struct sip_pvt *p_old;
if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
@@ -21005,7 +22763,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so
}
}
- if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) {
+ if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY || p->method == SIP_PUBLISH)) {
transmit_response(p, "400 Bad request", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return -1;
@@ -21031,6 +22789,9 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so
case SIP_MESSAGE:
res = handle_request_message(p, req);
break;
+ case SIP_PUBLISH:
+ res = handle_request_publish(p, req, sin, seqno, e);
+ break;
case SIP_SUBSCRIBE:
res = handle_request_subscribe(p, req, sin, seqno, e);
break;
@@ -22001,7 +23762,7 @@ static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp)
return;
}
p->stimer->st_interval = minse;
- transmit_invite(p, SIP_INVITE, 1, 2);
+ transmit_invite(p, SIP_INVITE, 1, 2, NULL);
}
@@ -22195,9 +23956,9 @@ static int sip_poke_peer(struct sip_peer *peer, int force)
ast_set_flag(&p->flags[0], SIP_OUTGOING);
#ifdef VOCAL_DATA_HACK
ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
- xmitres = transmit_invite(p, SIP_INVITE, 0, 2); /* sinks the p refcount */
+ xmitres = transmit_invite(p, SIP_INVITE, 0, 2, NULL); /* sinks the p refcount */
#else
- xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2); /* sinks the p refcount */
+ xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2, NULL); /* sinks the p refcount */
#endif
peer->ps = ast_tvnow();
if (xmitres == XMIT_ERROR) {
@@ -22336,6 +24097,7 @@ static struct ast_channel *sip_request_call(const char *type, format_t format, c
char *md5secret = NULL;
char *authname = NULL;
char *trans = NULL;
+ char dialstring[256];
char *remote_address;
enum sip_transport transport = 0;
struct sockaddr_in remote_address_sin = { .sin_family = AF_INET };
@@ -22369,6 +24131,9 @@ static struct ast_channel *sip_request_call(const char *type, format_t format, c
p->outgoing_call = TRUE;
+ snprintf(dialstring, sizeof(dialstring), "%s/%s", type, dest);
+ ast_string_field_set(p, dialstring, dialstring);
+
if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
dialog_unlink_all(p, TRUE, TRUE);
dialog_unref(p, "unref dialog p from mem fail");
@@ -23059,6 +24824,11 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
return NULL;
}
+ if (!(peer->cc_params = ast_cc_config_params_init())) {
+ ao2_t_ref(peer, -1, "failed to allocate cc_params for peer");
+ return NULL;
+ }
+
if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
ast_atomic_fetchadd_int(&rpeerobjs, 1);
ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
@@ -23442,9 +25212,16 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
if (peer->busy_level < 0) {
peer->busy_level = 0;
}
+ } else if (ast_cc_is_config_param(v->name)) {
+ ast_cc_set_param(peer->cc_params, v->name, v->value);
}
}
+ if (!can_parse_xml && (ast_get_cc_agent_policy(peer->cc_params) == AST_CC_AGENT_NATIVE)) {
+ ast_log(LOG_WARNING, "Peer %s has a cc_agent_policy of 'native' but required libxml2 dependency is not installed. Changing policy to 'never'\n", peer->name);
+ ast_set_cc_agent_policy(peer->cc_params, AST_CC_AGENT_NEVER);
+ }
+
/* Note that Timer B is dependent upon T1 and MUST NOT be lower
* than T1 * 64, according to RFC 3261, Section 17.1.1.2 */
if (peer->timer_b < peer->timer_t1 * 64) {
@@ -24961,6 +26738,15 @@ static int sip_sipredirect(struct sip_pvt *p, const char *dest)
return 0;
}
+static int sip_is_xml_parsable(void)
+{
+#ifdef HAVE_LIBXML2
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
/*! \brief Send a poke to all known peers */
static void sip_poke_all_peers(void)
{
@@ -25276,6 +27062,7 @@ static int load_module(void)
sip_reloadreason = CHANNEL_MODULE_LOAD;
+ can_parse_xml = sip_is_xml_parsable();
if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */
return AST_MODULE_LOAD_DECLINE;
@@ -25323,6 +27110,24 @@ static int load_module(void)
sip_poke_all_peers();
sip_send_all_registers();
sip_send_all_mwi_subscriptions();
+ initialize_escs();
+ if (sip_epa_register(&cc_epa_static_data)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ if (can_parse_xml) {
+ /* SIP CC agents require the ability to parse XML PIDF bodies
+ * in incoming PUBLISH requests
+ */
+ if (ast_cc_agent_register(&sip_cc_agent_callbacks)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ }
+ if (ast_cc_monitor_register(&sip_cc_monitor_callbacks)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ if (!(sip_monitor_instances = ao2_container_alloc(37, sip_monitor_instance_hash_fn, sip_monitor_instance_cmp_fn))) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
/* And start the monitor for the first time */
restart_monitor();
@@ -25433,6 +27238,7 @@ static int unload_module(void)
clear_realm_authentication(authl);
+ destroy_escs();
if (default_tls_cfg.certfile)
ast_free(default_tls_cfg.certfile);
@@ -25464,6 +27270,8 @@ static int unload_module(void)
ast_context_destroy(con, "SIP");
ast_unload_realtime("sipregs");
ast_unload_realtime("sippeers");
+ ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
+ ast_cc_agent_unregister(&sip_cc_agent_callbacks);
sip_unregister_tests();
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index 8e4aa3391..f8b1ede45 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -169,6 +169,14 @@ static int analog_get_callerid(struct analog_pvt *p, char *name, char *number, e
return -1;
}
+static const char *analog_get_orig_dialstring(struct analog_pvt *p)
+{
+ if (p->calls->get_orig_dialstring) {
+ return p->calls->get_orig_dialstring(p->chan_pvt);
+ }
+ return "";
+}
+
static int analog_get_event(struct analog_pvt *p)
{
if (p->calls->get_event) {
@@ -934,6 +942,24 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int
ast_setstate(ast, AST_STATE_RINGING);
index = analog_get_index(ast, p, 0);
if (index > -1) {
+ struct ast_cc_config_params *cc_params;
+
+ /* This is where the initial ringing frame is queued for an analog call.
+ * As such, this is a great time to offer CCNR to the caller if it's available.
+ */
+ cc_params = ast_channel_get_cc_config_params(p->subs[index].owner);
+ if (cc_params) {
+ switch (ast_get_cc_monitor_policy(cc_params)) {
+ case AST_CC_MONITOR_NEVER:
+ break;
+ case AST_CC_MONITOR_NATIVE:
+ case AST_CC_MONITOR_ALWAYS:
+ case AST_CC_MONITOR_GENERIC:
+ ast_queue_cc_frame(p->subs[index].owner, AST_CC_GENERIC_MONITOR_TYPE,
+ analog_get_orig_dialstring(p), AST_CC_CCNR, NULL);
+ break;
+ }
+ }
ast_queue_control(p->subs[index].owner, AST_CONTROL_RINGING);
}
break;
diff --git a/channels/sig_analog.h b/channels/sig_analog.h
index 33b642289..57fc5c1f2 100644
--- a/channels/sig_analog.h
+++ b/channels/sig_analog.h
@@ -213,6 +213,8 @@ struct analog_callback {
void (* const cancel_cidspill)(void *pvt);
int (* const confmute)(void *pvt, int mute);
void (* const set_pulsedial)(void *pvt, int flag);
+
+ const char *(* const get_orig_dialstring)(void *pvt);
};
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index fd3b4111e..5749e89b3 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -55,11 +55,32 @@
/* define this to send PRI user-user information elements */
#undef SUPPORT_USERUSER
-#if 0
-#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)
-#else
-#define DEFAULT_PRI_DEBUG 0
-#endif
+#if defined(HAVE_PRI_CCSS)
+struct sig_pri_cc_agent_prv {
+ /*! Asterisk span D channel control structure. */
+ struct sig_pri_pri *pri;
+ /*! CC id value to use with libpri. -1 if invalid. */
+ long cc_id;
+ /*! TRUE if CC has been requested and we are waiting for the response. */
+ unsigned char cc_request_response_pending;
+};
+
+struct sig_pri_cc_monitor_instance {
+ /*! \brief Asterisk span D channel control structure. */
+ struct sig_pri_pri *pri;
+ /*! CC id value to use with libpri. (-1 if already canceled). */
+ long cc_id;
+ /*! CC core id value. */
+ int core_id;
+ /*! Device name(Channel name less sequence number) */
+ char name[1];
+};
+
+/*! Upper level agent/monitor type name. */
+static const char *sig_pri_cc_type_name;
+/*! Container of sig_pri monitor instances. */
+static struct ao2_container *sig_pri_cc_monitors;
+#endif /* defined(HAVE_PRI_CCSS) */
static int pri_matchdigittimeout = 3000;
@@ -120,6 +141,45 @@ static void sig_pri_set_digital(struct sig_pri_chan *p, int flag)
p->calls->set_digital(p->chan_pvt, flag);
}
+static const char *sig_pri_get_orig_dialstring(struct sig_pri_chan *p)
+{
+ if (p->calls->get_orig_dialstring) {
+ return p->calls->get_orig_dialstring(p->chan_pvt);
+ }
+ ast_log(LOG_ERROR, "get_orig_dialstring callback not defined\n");
+ return "";
+}
+
+#if defined(HAVE_PRI_CCSS)
+static void sig_pri_make_cc_dialstring(struct sig_pri_chan *p, char *buf, size_t buf_size)
+{
+ if (p->calls->make_cc_dialstring) {
+ p->calls->make_cc_dialstring(p->chan_pvt, buf, buf_size);
+ } else {
+ ast_log(LOG_ERROR, "make_cc_dialstring callback not defined\n");
+ buf[0] = '\0';
+ }
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \internal
+ * \brief Reevaluate the PRI span device state.
+ * \since 1.8
+ *
+ * \param pri Asterisk D channel control structure.
+ *
+ * \return Nothing
+ *
+ * \note Assumes the pri->lock is already obtained.
+ */
+static void sig_pri_span_devstate_changed(struct sig_pri_pri *pri)
+{
+ if (pri->calls->update_span_devstate) {
+ pri->calls->update_span_devstate(pri);
+ }
+}
+
/*!
* \internal
* \brief Set the caller id information in the parent module.
@@ -733,6 +793,12 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s
pbx_builtin_setvar_helper(c, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability));
sig_pri_set_digital(p, 1);
}
+ if (p->pri && !pri_grab(p, p->pri)) {
+ sig_pri_span_devstate_changed(p->pri);
+ pri_rel(p->pri);
+ } else {
+ ast_log(LOG_WARNING, "Failed to grab PRI!\n");
+ }
return c;
}
@@ -1476,6 +1542,615 @@ static void sig_pri_lock_owner(struct sig_pri_pri *pri, int chanpos)
}
}
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Compare the CC agent private data by libpri cc_id.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \return values are a combination of enum _cb_results.
+ */
+static int sig_pri_cc_agent_cmp_cc_id(void *obj, void *arg, int flags)
+{
+ struct ast_cc_agent *agent_1 = obj;
+ struct sig_pri_cc_agent_prv *agent_prv_1 = agent_1->private_data;
+ struct sig_pri_cc_agent_prv *agent_prv_2 = arg;
+
+ return (agent_prv_1 && agent_prv_1->pri == agent_prv_2->pri
+ && agent_prv_1->cc_id == agent_prv_2->cc_id) ? CMP_MATCH | CMP_STOP : 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Find the CC agent by libpri cc_id.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID to find.
+ *
+ * \note
+ * Since agents are refcounted, and this function returns
+ * a reference to the agent, it is imperative that you decrement
+ * the refcount of the agent once you have finished using it.
+ *
+ * \retval agent on success.
+ * \retval NULL not found.
+ */
+static struct ast_cc_agent *sig_pri_find_cc_agent_by_cc_id(struct sig_pri_pri *pri, long cc_id)
+{
+ struct sig_pri_cc_agent_prv finder = {
+ .pri = pri,
+ .cc_id = cc_id,
+ };
+
+ return ast_cc_agent_callback(0, sig_pri_cc_agent_cmp_cc_id, &finder,
+ sig_pri_cc_type_name);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Compare the CC monitor instance by libpri cc_id.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \return values are a combination of enum _cb_results.
+ */
+static int sig_pri_cc_monitor_cmp_cc_id(void *obj, void *arg, int flags)
+{
+ struct sig_pri_cc_monitor_instance *monitor_1 = obj;
+ struct sig_pri_cc_monitor_instance *monitor_2 = arg;
+
+ return (monitor_1->pri == monitor_2->pri
+ && monitor_1->cc_id == monitor_2->cc_id) ? CMP_MATCH | CMP_STOP : 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Find the CC monitor instance by libpri cc_id.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID to find.
+ *
+ * \note
+ * Since monitor_instances are refcounted, and this function returns
+ * a reference to the instance, it is imperative that you decrement
+ * the refcount of the instance once you have finished using it.
+ *
+ * \retval monitor_instance on success.
+ * \retval NULL not found.
+ */
+static struct sig_pri_cc_monitor_instance *sig_pri_find_cc_monitor_by_cc_id(struct sig_pri_pri *pri, long cc_id)
+{
+ struct sig_pri_cc_monitor_instance finder = {
+ .pri = pri,
+ .cc_id = cc_id,
+ };
+
+ return ao2_callback(sig_pri_cc_monitors, 0, sig_pri_cc_monitor_cmp_cc_id, &finder);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Destroy the given monitor instance.
+ * \since 1.8
+ *
+ * \param data Monitor instance to destroy.
+ *
+ * \return Nothing
+ */
+static void sig_pri_cc_monitor_instance_destroy(void *data)
+{
+ struct sig_pri_cc_monitor_instance *monitor_instance = data;
+
+ if (monitor_instance->cc_id != -1) {
+ ast_mutex_lock(&monitor_instance->pri->lock);
+ pri_cc_cancel(monitor_instance->pri->pri, monitor_instance->cc_id);
+ ast_mutex_unlock(&monitor_instance->pri->lock);
+ }
+ monitor_instance->pri->calls->module_unref();
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Construct a new monitor instance.
+ * \since 1.8
+ *
+ * \param core_id CC core ID.
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID.
+ * \param device_name Name of device (Asterisk channel name less sequence number).
+ *
+ * \note
+ * Since monitor_instances are refcounted, and this function returns
+ * a reference to the instance, it is imperative that you decrement
+ * the refcount of the instance once you have finished using it.
+ *
+ * \retval monitor_instance on success.
+ * \retval NULL on error.
+ */
+static struct sig_pri_cc_monitor_instance *sig_pri_cc_monitor_instance_init(int core_id, struct sig_pri_pri *pri, long cc_id, const char *device_name)
+{
+ struct sig_pri_cc_monitor_instance *monitor_instance;
+
+ if (!pri->calls->module_ref || !pri->calls->module_unref) {
+ return NULL;
+ }
+
+ monitor_instance = ao2_alloc(sizeof(*monitor_instance) + strlen(device_name),
+ sig_pri_cc_monitor_instance_destroy);
+ if (!monitor_instance) {
+ return NULL;
+ }
+
+ monitor_instance->cc_id = cc_id;
+ monitor_instance->pri = pri;
+ monitor_instance->core_id = core_id;
+ strcpy(monitor_instance->name, device_name);
+
+ pri->calls->module_ref();
+
+ ao2_link(sig_pri_cc_monitors, monitor_instance);
+ return monitor_instance;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Announce to the CC core that protocol CC monitor is available for this call.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param chanpos Channel position in the span.
+ * \param cc_id CC record ID.
+ * \param service CCBS/CCNR indication.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ * \note Assumes the sig_pri_lock_owner(pri, chanpos) is already obtained.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int sig_pri_cc_available(struct sig_pri_pri *pri, int chanpos, long cc_id, enum ast_cc_service_type service)
+{
+ struct sig_pri_chan *pvt;
+ struct ast_cc_config_params *cc_params;
+ struct sig_pri_cc_monitor_instance *monitor;
+ enum ast_cc_monitor_policies monitor_policy;
+ int core_id;
+ int res;
+ char device_name[AST_CHANNEL_NAME];
+ char dialstring[AST_CHANNEL_NAME];
+
+ pvt = pri->pvts[chanpos];
+
+ core_id = ast_cc_get_current_core_id(pvt->owner);
+ if (core_id == -1) {
+ return -1;
+ }
+
+ cc_params = ast_channel_get_cc_config_params(pvt->owner);
+ if (!cc_params) {
+ return -1;
+ }
+
+ res = -1;
+ monitor_policy = ast_get_cc_monitor_policy(cc_params);
+ switch (monitor_policy) {
+ case AST_CC_MONITOR_NEVER:
+ /* CCSS is not enabled. */
+ break;
+ case AST_CC_MONITOR_NATIVE:
+ case AST_CC_MONITOR_ALWAYS:
+ /*
+ * If it is AST_CC_MONITOR_ALWAYS and native fails we will attempt the fallback
+ * later in the call to sig_pri_cc_generic_check().
+ */
+ ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name));
+ sig_pri_make_cc_dialstring(pvt, dialstring, sizeof(dialstring));
+ monitor = sig_pri_cc_monitor_instance_init(core_id, pri, cc_id, device_name);
+ if (!monitor) {
+ break;
+ }
+ res = ast_queue_cc_frame(pvt->owner, sig_pri_cc_type_name, dialstring, service,
+ monitor);
+ if (res) {
+ monitor->cc_id = -1;
+ ao2_unlink(sig_pri_cc_monitors, monitor);
+ ao2_ref(monitor, -1);
+ }
+ break;
+ case AST_CC_MONITOR_GENERIC:
+ ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pvt), service, NULL);
+ /* Say it failed to force caller to cancel native CC. */
+ break;
+ }
+ return res;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \internal
+ * \brief Check if generic CC monitor is needed and request it.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param chanpos Channel position in the span.
+ * \param service CCBS/CCNR indication.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_cc_generic_check(struct sig_pri_pri *pri, int chanpos, enum ast_cc_service_type service)
+{
+ struct ast_channel *owner;
+ struct ast_cc_config_params *cc_params;
+#if defined(HAVE_PRI_CCSS)
+ struct ast_cc_monitor *monitor;
+ char device_name[AST_CHANNEL_NAME];
+#endif /* defined(HAVE_PRI_CCSS) */
+ enum ast_cc_monitor_policies monitor_policy;
+ int core_id;
+
+ if (!pri->pvts[chanpos]->outgoing) {
+ /* This is not an outgoing call so it cannot be CC monitor. */
+ return;
+ }
+
+ sig_pri_lock_owner(pri, chanpos);
+ owner = pri->pvts[chanpos]->owner;
+ if (!owner) {
+ return;
+ }
+ core_id = ast_cc_get_current_core_id(owner);
+ if (core_id == -1) {
+ /* No CC core setup */
+ goto done;
+ }
+
+ cc_params = ast_channel_get_cc_config_params(owner);
+ if (!cc_params) {
+ /* Could not get CC config parameters. */
+ goto done;
+ }
+
+#if defined(HAVE_PRI_CCSS)
+ ast_channel_get_device_name(owner, device_name, sizeof(device_name));
+ monitor = ast_cc_get_monitor_by_recall_core_id(core_id, device_name);
+ if (monitor) {
+ /* CC monitor is already present so no need for generic CC. */
+ ao2_ref(monitor, -1);
+ goto done;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+
+ monitor_policy = ast_get_cc_monitor_policy(cc_params);
+ switch (monitor_policy) {
+ case AST_CC_MONITOR_NEVER:
+ /* CCSS is not enabled. */
+ break;
+ case AST_CC_MONITOR_NATIVE:
+ if (pri->sig == SIG_BRI_PTMP && pri->nodetype == PRI_NETWORK) {
+ /* Request generic CC monitor. */
+ ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL);
+ }
+ break;
+ case AST_CC_MONITOR_ALWAYS:
+ if (pri->sig == SIG_BRI_PTMP && pri->nodetype != PRI_NETWORK) {
+ /*
+ * Cannot monitor PTMP TE side since this is not defined.
+ * We are playing the roll of a phone in this case and
+ * a phone cannot monitor a party over the network without
+ * protocol help.
+ */
+ break;
+ }
+ /*
+ * We are either falling back or this is a PTMP NT span.
+ * Request generic CC monitor.
+ */
+ ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL);
+ break;
+ case AST_CC_MONITOR_GENERIC:
+ if (pri->sig == SIG_BRI_PTMP && pri->nodetype == PRI_NETWORK) {
+ /* Request generic CC monitor. */
+ ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL);
+ }
+ break;
+ }
+
+done:
+ ast_channel_unlock(owner);
+}
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief The CC link canceled the CC instance.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID.
+ * \param is_agent TRUE if the cc_id is for an agent.
+ *
+ * \return Nothing
+ */
+static void sig_pri_cc_link_canceled(struct sig_pri_pri *pri, long cc_id, int is_agent)
+{
+ if (is_agent) {
+ struct ast_cc_agent *agent;
+
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, cc_id);
+ if (!agent) {
+ return;
+ }
+ ast_cc_failed(agent->core_id, "%s agent got canceled by link",
+ sig_pri_cc_type_name);
+ ao2_ref(agent, -1);
+ } else {
+ struct sig_pri_cc_monitor_instance *monitor;
+
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri, cc_id);
+ if (!monitor) {
+ return;
+ }
+ monitor->cc_id = -1;
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s monitor got canceled by link", sig_pri_cc_type_name);
+ ao2_ref(monitor, -1);
+ }
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \internal
+ * \brief TRUE if PRI event came in on a CIS call.
+ * \since 1.8
+ *
+ * \param channel PRI encoded span/channel
+ *
+ * \retval non-zero if CIS call.
+ */
+static int sig_pri_is_cis_call(int channel)
+{
+ return channel != -1 && (channel & PRI_CIS_CALL);
+}
+
+/*!
+ * \internal
+ * \brief Handle the CIS associated PRI subcommand events.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param event_id PRI event id
+ * \param subcmds Subcommands to process if any. (Could be NULL).
+ * \param call_rsp libpri opaque call structure to send any responses toward.
+ * Could be NULL either because it is not available or the call is for the
+ * dummy call reference. However, this should not be NULL in the cases that
+ * need to use the pointer to send a response message back.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_handle_cis_subcmds(struct sig_pri_pri *pri, int event_id,
+ const struct pri_subcommands *subcmds, q931_call *call_rsp)
+{
+ int index;
+#if defined(HAVE_PRI_CCSS)
+ struct ast_cc_agent *agent;
+ struct sig_pri_cc_agent_prv *agent_prv;
+ struct sig_pri_cc_monitor_instance *monitor;
+#endif /* defined(HAVE_PRI_CCSS) */
+
+ if (!subcmds) {
+ return;
+ }
+ for (index = 0; index < subcmds->counter_subcmd; ++index) {
+ const struct pri_subcommand *subcmd = &subcmds->subcmd[index];
+
+ switch (subcmd->cmd) {
+#if defined(STATUS_REQUEST_PLACE_HOLDER)
+ case PRI_SUBCMD_STATUS_REQ:
+ case PRI_SUBCMD_STATUS_REQ_RSP:
+ /* Ignore for now. */
+ break;
+#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_REQ:
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_request.cc_id);
+ if (!agent) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
+ break;
+ }
+ if (!ast_cc_request_is_within_limits()) {
+ if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id,
+ 5/* queue_full */)) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
+ }
+ ast_cc_failed(agent->core_id, "%s agent system CC queue full",
+ sig_pri_cc_type_name);
+ ao2_ref(agent, -1);
+ break;
+ }
+ agent_prv = agent->private_data;
+ agent_prv->cc_request_response_pending = 1;
+ if (ast_cc_agent_accept_request(agent->core_id,
+ "%s caller accepted CC offer.", sig_pri_cc_type_name)) {
+ agent_prv->cc_request_response_pending = 0;
+ if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id,
+ 2/* short_term_denial */)) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
+ }
+ ast_cc_failed(agent->core_id, "%s agent CC core request accept failed",
+ sig_pri_cc_type_name);
+ }
+ ao2_ref(agent, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_REQ_RSP:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_request_rsp.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request_rsp.cc_id);
+ break;
+ }
+ switch (subcmd->u.cc_request_rsp.status) {
+ case 0:/* success */
+ ast_cc_monitor_request_acked(monitor->core_id,
+ "%s far end accepted CC request", sig_pri_cc_type_name);
+ break;
+ case 1:/* timeout */
+ ast_verb(2, "core_id:%d %s CC request timeout\n", monitor->core_id,
+ sig_pri_cc_type_name);
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request timeout", sig_pri_cc_type_name);
+ break;
+ case 2:/* error */
+ ast_verb(2, "core_id:%d %s CC request error: %s\n", monitor->core_id,
+ sig_pri_cc_type_name,
+ pri_facility_error2str(subcmd->u.cc_request_rsp.fail_code));
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request error", sig_pri_cc_type_name);
+ break;
+ case 3:/* reject */
+ ast_verb(2, "core_id:%d %s CC request reject: %s\n", monitor->core_id,
+ sig_pri_cc_type_name,
+ pri_facility_reject2str(subcmd->u.cc_request_rsp.fail_code));
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request reject", sig_pri_cc_type_name);
+ break;
+ default:
+ ast_verb(2, "core_id:%d %s CC request unknown status %d\n",
+ monitor->core_id, sig_pri_cc_type_name,
+ subcmd->u.cc_request_rsp.status);
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request unknown status", sig_pri_cc_type_name);
+ break;
+ }
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_REMOTE_USER_FREE:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_remote_user_free.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_remote_user_free.cc_id);
+ break;
+ }
+ ast_cc_monitor_callee_available(monitor->core_id,
+ "%s callee has become available", sig_pri_cc_type_name);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_B_FREE:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_b_free.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_b_free.cc_id);
+ break;
+ }
+ ast_cc_monitor_party_b_free(monitor->core_id);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STATUS_REQ:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_status_req.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_status_req.cc_id);
+ break;
+ }
+ ast_cc_monitor_status_request(monitor->core_id);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STATUS_REQ_RSP:
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_status_req_rsp.cc_id);
+ if (!agent) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_status_req_rsp.cc_id);
+ break;
+ }
+ ast_cc_agent_status_response(agent->core_id,
+ subcmd->u.cc_status_req_rsp.status ? AST_DEVICE_INUSE
+ : AST_DEVICE_NOT_INUSE);
+ ao2_ref(agent, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STATUS:
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_status.cc_id);
+ if (!agent) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_status.cc_id);
+ break;
+ }
+ if (subcmd->u.cc_status.status) {
+ ast_cc_agent_caller_busy(agent->core_id, "%s agent caller is busy",
+ sig_pri_cc_type_name);
+ } else {
+ ast_cc_agent_caller_available(agent->core_id,
+ "%s agent caller is available", sig_pri_cc_type_name);
+ }
+ ao2_ref(agent, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_CANCEL:
+ sig_pri_cc_link_canceled(pri, subcmd->u.cc_cancel.cc_id,
+ subcmd->u.cc_cancel.is_agent);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STOP_ALERTING:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_stop_alerting.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_stop_alerting.cc_id);
+ break;
+ }
+ ast_cc_monitor_stop_ringing(monitor->core_id);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+ default:
+ ast_debug(2,
+ "Unknown CIS subcommand(%d) in %s event on span %d.\n",
+ subcmd->cmd, pri_event2str(event_id), pri->span);
+ break;
+ }
+ }
+}
+
/*!
* \internal
* \brief Handle the call associated PRI subcommand events.
@@ -1647,6 +2322,63 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
}
break;
#endif /* defined(HAVE_PRI_CALL_REROUTING) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_AVAILABLE:
+ sig_pri_lock_owner(pri, chanpos);
+ owner = pri->pvts[chanpos]->owner;
+ if (owner) {
+ enum ast_cc_service_type service;
+
+ switch (event_id) {
+ case PRI_EVENT_RINGING:
+ service = AST_CC_CCNR;
+ break;
+ case PRI_EVENT_HANGUP_REQ:
+ /* We will assume that the cause was busy/congestion. */
+ service = AST_CC_CCBS;
+ break;
+ default:
+ service = AST_CC_NONE;
+ break;
+ }
+ if (service == AST_CC_NONE
+ || sig_pri_cc_available(pri, chanpos, subcmd->u.cc_available.cc_id,
+ service)) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_available.cc_id);
+ }
+ ast_channel_unlock(owner);
+ } else {
+ /* No asterisk channel. */
+ pri_cc_cancel(pri->pri, subcmd->u.cc_available.cc_id);
+ }
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_CALL:
+ sig_pri_lock_owner(pri, chanpos);
+ owner = pri->pvts[chanpos]->owner;
+ if (owner) {
+ struct ast_cc_agent *agent;
+
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_call.cc_id);
+ if (agent) {
+ ast_setup_cc_recall_datastore(owner, agent->core_id);
+ ast_cc_agent_set_interfaces_chanvar(owner);
+ ast_cc_agent_recalling(agent->core_id,
+ "%s caller is attempting recall", sig_pri_cc_type_name);
+ ao2_ref(agent, -1);
+ }
+
+ ast_channel_unlock(owner);
+ }
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_CANCEL:
+ sig_pri_cc_link_canceled(pri, subcmd->u.cc_cancel.cc_id,
+ subcmd->u.cc_cancel.is_agent);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
default:
ast_debug(2,
"Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n",
@@ -1793,6 +2525,7 @@ static int sig_pri_handle_hold(struct sig_pri_pri *pri, pri_event *ev)
f.subclass.integer = AST_CONTROL_HOLD;
ast_queue_frame(owner, &f);
+ sig_pri_span_devstate_changed(pri);
retval = 0;
}
@@ -1866,6 +2599,7 @@ static void sig_pri_handle_retrieve(struct sig_pri_pri *pri, pri_event *ev)
pri_queue_frame(pri->pvts[chanpos], &f, pri);
}
sig_pri_unlock_private(pri->pvts[chanpos]);
+ sig_pri_span_devstate_changed(pri);
pri_retrieve_ack(pri->pri, ev->retrieve.call,
PVT_TO_CHANNEL(pri->pvts[chanpos]));
}
@@ -2094,10 +2828,12 @@ static void *pri_dchannel(void *vpri)
}
pri->resetting = 0;
/* Take the channels from inalarm condition */
- for (i = 0; i < pri->numchans; i++)
+ for (i = 0; i < pri->numchans; i++) {
if (pri->pvts[i]) {
pri->pvts[i]->inalarm = 0;
}
+ }
+ sig_pri_span_devstate_changed(pri);
break;
case PRI_EVENT_DCHAN_DOWN:
pri_find_dchan(pri);
@@ -2128,6 +2864,7 @@ static void *pri_dchannel(void *vpri)
p->inalarm = 1;
}
}
+ sig_pri_span_devstate_changed(pri);
}
break;
case PRI_EVENT_RESTART:
@@ -2180,6 +2917,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_KEYPAD_DIGIT:
+ if (sig_pri_is_cis_call(e->digit.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->digit.subcmds,
+ e->digit.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->digit.channel, e->digit.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "KEYPAD_DIGITs received on unconfigured channel %d/%d span %d\n",
@@ -2210,6 +2952,11 @@ static void *pri_dchannel(void *vpri)
break;
case PRI_EVENT_INFO_RECEIVED:
+ if (sig_pri_is_cis_call(e->ring.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds,
+ e->ring.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "INFO received on unconfigured channel %d/%d span %d\n",
@@ -2262,6 +3009,8 @@ static void *pri_dchannel(void *vpri)
snprintf(db_answer, sizeof(db_answer), "%s:%u",
SRVST_TYPE_OOS, *why);
ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+ } else {
+ sig_pri_span_devstate_changed(pri);
}
break;
case 2: /* out-of-service */
@@ -2271,6 +3020,7 @@ static void *pri_dchannel(void *vpri)
snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS,
*why);
ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+ sig_pri_span_devstate_changed(pri);
break;
default:
ast_log(LOG_ERROR, "Huh? changestatus is: %d\n", e->service.changestatus);
@@ -2301,7 +3051,12 @@ static void *pri_dchannel(void *vpri)
pri_destroycall(pri->pri, e->ring.call);
break;
}
- if (e->ring.channel == -1)
+ if (sig_pri_is_cis_call(e->ring.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds,
+ e->ring.call);
+ break;
+ }
+ if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF)
chanpos = pri_find_empty_chan(pri, 1);
else
chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
@@ -2644,6 +3399,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_RINGING:
+ if (sig_pri_is_cis_call(e->ringing.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->ringing.subcmds,
+ e->ringing.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->ringing.channel, e->ringing.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d/%d span %d\n",
@@ -2658,6 +3418,7 @@ static void *pri_dchannel(void *vpri)
sig_pri_handle_subcmds(pri, chanpos, e->e, e->ringing.channel,
e->ringing.subcmds, e->ringing.call);
+ sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCNR);
sig_pri_set_echocanceller(pri->pvts[chanpos], 1);
pri_queue_control(pri->pvts[chanpos], AST_CONTROL_RINGING, pri);
pri->pvts[chanpos]->alerting = 1;
@@ -2681,7 +3442,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_PROGRESS:
- /* Get chan value if e->e is not PRI_EVNT_RINGING */
+ if (sig_pri_is_cis_call(e->proceeding.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->proceeding.subcmds,
+ e->proceeding.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call);
if (chanpos > -1) {
sig_pri_lock_private(pri->pvts[chanpos]);
@@ -2731,6 +3496,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_PROCEEDING:
+ if (sig_pri_is_cis_call(e->proceeding.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->proceeding.subcmds,
+ e->proceeding.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call);
if (chanpos > -1) {
sig_pri_lock_private(pri->pvts[chanpos]);
@@ -2760,6 +3530,17 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_FACILITY:
+ if (!e->facility.call || sig_pri_is_cis_call(e->facility.channel)) {
+ /* Event came in on the dummy channel or a CIS call. */
+#if defined(HAVE_PRI_CALL_REROUTING)
+ sig_pri_handle_cis_subcmds(pri, e->e, e->facility.subcmds,
+ e->facility.subcall);
+#else
+ sig_pri_handle_cis_subcmds(pri, e->e, e->facility.subcmds,
+ e->facility.call);
+#endif /* !defined(HAVE_PRI_CALL_REROUTING) */
+ break;
+ }
chanpos = pri_find_principle(pri, e->facility.channel, e->facility.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Facility requested on unconfigured channel %d/%d span %d\n",
@@ -2783,6 +3564,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_ANSWER:
+ if (sig_pri_is_cis_call(e->answer.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->answer.subcmds,
+ e->answer.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->answer.channel, e->answer.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n",
@@ -2821,6 +3607,12 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_HANGUP:
+ if (sig_pri_is_cis_call(e->hangup.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
+ e->hangup.call);
+ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause);
+ break;
+ }
chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Hangup requested on unconfigured channel %d/%d span %d\n",
@@ -2834,6 +3626,14 @@ static void *pri_dchannel(void *vpri)
if (!pri->pvts[chanpos]->alreadyhungup) {
/* we're calling here dahdi_hangup so once we get there we need to clear p->call after calling pri_hangup */
pri->pvts[chanpos]->alreadyhungup = 1;
+ switch (e->hangup.cause) {
+ case PRI_CAUSE_USER_BUSY:
+ case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS);
+ break;
+ default:
+ break;
+ }
if (pri->pvts[chanpos]->owner) {
/* Queue a BUSY instead of a hangup if our cause is appropriate */
pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
@@ -2900,6 +3700,12 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_HANGUP_REQ:
+ if (sig_pri_is_cis_call(e->hangup.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
+ e->hangup.call);
+ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause);
+ break;
+ }
chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n",
@@ -2922,6 +3728,14 @@ static void *pri_dchannel(void *vpri)
sig_pri_lock_private(pri->pvts[chanpos]);
}
#endif /* defined(HAVE_PRI_CALL_HOLD) */
+ switch (e->hangup.cause) {
+ case PRI_CAUSE_USER_BUSY:
+ case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS);
+ break;
+ default:
+ break;
+ }
if (pri->pvts[chanpos]->owner) {
pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
switch (pri->pvts[chanpos]->owner->_state) {
@@ -2984,6 +3798,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_HANGUP_ACK:
+ if (sig_pri_is_cis_call(e->hangup.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
+ e->hangup.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Hangup ACK requested on unconfigured channel number %d/%d span %d\n",
@@ -3067,6 +3886,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_SETUP_ACK:
+ if (sig_pri_is_cis_call(e->setup_ack.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->setup_ack.subcmds,
+ e->setup_ack.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->setup_ack.channel, e->setup_ack.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Received SETUP_ACKNOWLEDGE on unconfigured channel %d/%d span %d\n",
@@ -3090,6 +3914,15 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_NOTIFY:
+ if (sig_pri_is_cis_call(e->notify.channel)) {
+#if defined(HAVE_PRI_CALL_HOLD)
+ sig_pri_handle_cis_subcmds(pri, e->e, e->notify.subcmds,
+ e->notify.call);
+#else
+ sig_pri_handle_cis_subcmds(pri, e->e, e->notify.subcmds, NULL);
+#endif /* !defined(HAVE_PRI_CALL_HOLD) */
+ break;
+ }
#if defined(HAVE_PRI_CALL_HOLD)
chanpos = pri_find_principle(pri, e->notify.channel, e->notify.call);
#else
@@ -3130,6 +3963,7 @@ static void *pri_dchannel(void *vpri)
break;
#if defined(HAVE_PRI_CALL_HOLD)
case PRI_EVENT_HOLD:
+ /* We should not be getting any CIS calls with this message type. */
if (sig_pri_handle_hold(pri, e)) {
pri_hold_rej(pri->pri, e->hold.call,
PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
@@ -3150,6 +3984,7 @@ static void *pri_dchannel(void *vpri)
#endif /* defined(HAVE_PRI_CALL_HOLD) */
#if defined(HAVE_PRI_CALL_HOLD)
case PRI_EVENT_RETRIEVE:
+ /* We should not be getting any CIS calls with this message type. */
sig_pri_handle_retrieve(pri, e);
break;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
@@ -3189,7 +4024,7 @@ void sig_pri_init_pri(struct sig_pri_pri *pri)
int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
{
- int res = 0;
+ int res;
#ifdef SUPPORT_USERUSER
const char *useruser = pbx_builtin_getvar_helper(ast, "USERUSERINFO");
#endif
@@ -3213,47 +4048,43 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
p->exten[0] = '\0';
sig_pri_set_dialing(p, 0);
- if (!p->call) {
- res = 0;
- goto exit;
- }
-
/* Make sure we have a call (or REALLY have a call in the case of a PRI) */
if (!pri_grab(p, p->pri)) {
- if (p->alreadyhungup) {
- ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n");
+ if (p->call) {
+ if (p->alreadyhungup) {
+ ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n");
#ifdef SUPPORT_USERUSER
- pri_call_set_useruser(p->call, useruser);
+ pri_call_set_useruser(p->call, useruser);
#endif
- pri_hangup(p->pri->pri, p->call, -1);
- p->call = NULL;
- } else {
- const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE");
- int icause = ast->hangupcause ? ast->hangupcause : -1;
- ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n");
+ pri_hangup(p->pri->pri, p->call, -1);
+ p->call = NULL;
+ } else {
+ const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE");
+ int icause = ast->hangupcause ? ast->hangupcause : -1;
+ ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n");
#ifdef SUPPORT_USERUSER
- pri_call_set_useruser(p->call, useruser);
+ pri_call_set_useruser(p->call, useruser);
#endif
- p->alreadyhungup = 1;
- if (cause) {
- if (atoi(cause))
- icause = atoi(cause);
+ p->alreadyhungup = 1;
+ if (cause) {
+ if (atoi(cause))
+ icause = atoi(cause);
+ }
+ pri_hangup(p->pri->pri, p->call, icause);
}
- pri_hangup(p->pri->pri, p->call, icause);
}
- if (res < 0)
- ast_log(LOG_WARNING, "pri_disconnect failed\n");
+ sig_pri_span_devstate_changed(p->pri);
pri_rel(p->pri);
+ res = 0;
} else {
ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span);
res = -1;
}
-exit:
ast->tech_pvt = NULL;
return res;
}
@@ -3356,6 +4187,7 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
#ifdef SUPPORT_USERUSER
const char *useruser;
#endif
+ int core_id;
int pridialplan;
int dp_strip;
int prilocaldialplan;
@@ -3672,7 +4504,41 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
pri_sr_set_useruser(sr, useruser);
#endif
- if (pri_setup(p->pri->pri, p->call, sr)) {
+#if defined(HAVE_PRI_CCSS)
+ if (ast_cc_is_recall(ast, &core_id, sig_pri_cc_type_name)) {
+ struct ast_cc_monitor *monitor;
+ char device_name[AST_CHANNEL_NAME];
+
+ /* This is a CC recall call. */
+ ast_channel_get_device_name(ast, device_name, sizeof(device_name));
+ monitor = ast_cc_get_monitor_by_recall_core_id(core_id, device_name);
+ if (monitor) {
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor->private_data;
+
+ /* If this fails then we have monitor instance ambiguity. */
+ ast_assert(p->pri == instance->pri);
+
+ if (pri_cc_call(p->pri->pri, instance->cc_id, p->call, sr)) {
+ /* The CC recall call failed for some reason. */
+ ast_log(LOG_WARNING, "Unable to setup CC recall call to device %s\n",
+ device_name);
+ ao2_ref(monitor, -1);
+ pri_rel(p->pri);
+ pri_sr_free(sr);
+ return -1;
+ }
+ ao2_ref(monitor, -1);
+ } else {
+ core_id = -1;
+ }
+ } else
+#endif /* defined(HAVE_PRI_CCSS) */
+ {
+ core_id = -1;
+ }
+ if (core_id == -1 && pri_setup(p->pri->pri, p->call, sr)) {
ast_log(LOG_WARNING, "Unable to setup call to %s (using %s)\n",
c + p->stripmsd + dp_strip, dialplan2str(p->pri->dialplan));
pri_rel(p->pri);
@@ -3935,12 +4801,6 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
#ifdef HAVE_PRI_INBANDDISCONNECT
pri_set_inbanddisconnect(pri->dchans[i], pri->inbanddisconnect);
#endif
-#if defined(HAVE_PRI_CALL_HOLD)
- pri_hold_enable(pri->dchans[i], 1);
-#endif /* defined(HAVE_PRI_CALL_HOLD) */
-#if defined(HAVE_PRI_CALL_REROUTING)
- pri_reroute_enable(pri->dchans[i], 1);
-#endif /* defined(HAVE_PRI_CALL_REROUTING) */
/* Enslave to master if appropriate */
if (i)
pri_enslave(pri->dchans[0], pri->dchans[i]);
@@ -3951,7 +4811,7 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
ast_log(LOG_ERROR, "Unable to create PRI structure\n");
return -1;
}
- pri_set_debug(pri->dchans[i], DEFAULT_PRI_DEBUG);
+ pri_set_debug(pri->dchans[i], SIG_PRI_DEBUG_DEFAULT);
pri_set_nsf(pri->dchans[i], pri->nsf);
#ifdef PRI_GETSET_TIMERS
for (x = 0; x < PRI_MAX_TIMERS; x++) {
@@ -3960,8 +4820,23 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
}
#endif
}
+
/* Assume primary is the one we use */
pri->pri = pri->dchans[0];
+
+#if defined(HAVE_PRI_CALL_HOLD)
+ pri_hold_enable(pri->pri, 1);
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
+#if defined(HAVE_PRI_CALL_REROUTING)
+ pri_reroute_enable(pri->pri, 1);
+#endif /* defined(HAVE_PRI_CALL_REROUTING) */
+#if defined(HAVE_PRI_CCSS)
+ pri_cc_enable(pri->pri, 1);
+ pri_cc_recall_mode(pri->pri, pri->cc_ptmp_recall_mode);
+ pri_cc_retain_signaling_req(pri->pri, pri->cc_qsig_signaling_link_req);
+ pri_cc_retain_signaling_rsp(pri->pri, pri->cc_qsig_signaling_link_rsp);
+#endif /* defined(HAVE_PRI_CCSS) */
+
pri->resetpos = -1;
if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) {
for (i = 0; i < NUM_DCHANS; i++) {
@@ -4160,4 +5035,601 @@ void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str
}
}
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief PRI CC agent initialization.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ * \param pvt_chan Original channel the agent will attempt to recall.
+ *
+ * \details
+ * This callback is called when the CC core is initialized. Agents should allocate
+ * any private data necessary for the call and assign it to the private_data
+ * on the agent. Additionally, if any ast_cc_agent_flags are pertinent to the
+ * specific agent type, they should be set in this function as well.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = ast_calloc(1, sizeof(*cc_pvt));
+ if (!cc_pvt) {
+ return -1;
+ }
+
+ ast_mutex_lock(&pvt_chan->pri->lock);
+ cc_pvt->pri = pvt_chan->pri;
+ cc_pvt->cc_id = pri_cc_available(pvt_chan->pri->pri, pvt_chan->call);
+ ast_mutex_unlock(&pvt_chan->pri->lock);
+ if (cc_pvt->cc_id == -1) {
+ ast_free(cc_pvt);
+ return -1;
+ }
+ agent->private_data = cc_pvt;
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Start the offer timer.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This is called by the core when the caller hangs up after
+ * a call for which CC may be requested. The agent should
+ * begin the timer as configured.
+ *
+ * The primary reason why this functionality is left to
+ * the specific agent implementations is due to the differing
+ * use of schedulers throughout the code. Some channel drivers
+ * may already have a scheduler context they wish to use, and
+ * amongst those, some may use the ast_sched API while others
+ * may use the ast_sched_thread API, which are incompatible.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent)
+{
+ /* libpri maintains it's own offer timer in the form of T_RETENTION. */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Stop the offer timer.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This callback is called by the CC core when the caller
+ * has requested CC.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
+{
+ /* libpri maintains it's own offer timer in the form of T_RETENTION. */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Acknowledge CC request.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * When the core receives knowledge that a called
+ * party has accepted a CC request, it will call
+ * this callback.
+ *
+ * The duty of this is to accept a CC request from
+ * the caller by acknowledging receipt of that request.
+ *
+ * \return Nothing
+ */
+void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+ int res;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ if (cc_pvt->cc_request_response_pending) {
+ cc_pvt->cc_request_response_pending = 0;
+ res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 0/* success */);
+ } else {
+ res = 0;
+ }
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ if (res) {
+ ast_cc_failed(agent->core_id, "%s agent failed to send the CC request ack.",
+ sig_pri_cc_type_name);
+ }
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Request the status of the agent's device.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * Asynchronous request for the status of any caller
+ * which may be a valid caller for the CC transaction.
+ * Status responses should be made using the
+ * ast_cc_status_response function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_status_req(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Request for an agent's phone to stop ringing.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The usefulness of this is quite limited. The only specific
+ * known case for this is if Asterisk requests CC over an ISDN
+ * PTMP link as the TE side. If other phones are in the same
+ * recall group as the Asterisk server, and one of those phones
+ * picks up the recall notice, then Asterisk will receive a
+ * "stop ringing" notification from the NT side of the PTMP
+ * link. This indication needs to be passed to the phone
+ * on the other side of the Asterisk server which originally
+ * placed the call so that it will stop ringing. Since the
+ * phone may be of any type, it is necessary to have a callback
+ * that the core can know about.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_stop_alerting(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Let the caller know that the callee has become free
+ * but that the caller cannot attempt to call back because
+ * he is either busy or there is congestion on his line.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This is something that really only affects a scenario where
+ * a phone places a call over ISDN PTMP to Asterisk, who then
+ * connects over PTMP again to the ISDN network. For most agent
+ * types, there is no need to implement this callback at all
+ * because they don't really need to actually do anything in
+ * this situation. If you're having trouble understanding what
+ * the purpose of this callback is, then you can be safe simply
+ * not implementing it.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_b_free(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Begin monitoring a busy device.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this callback if the callee becomes
+ * available but the caller has reported that he is busy.
+ * The agent should begin monitoring the caller's device.
+ * When the caller becomes available again, the agent should
+ * call ast_cc_agent_caller_available.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_start_monitoring(struct ast_cc_agent *agent)
+{
+ /* libpri already knows when and how it needs to monitor Party A. */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Alert the caller that it is time to try recalling.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function when it receives notice
+ * that a monitored party has become available.
+ *
+ * The agent's job is to send a message to the caller to
+ * notify it of such a change. If the agent is able to
+ * discern that the caller is currently unavailable, then
+ * the agent should react by calling the ast_cc_caller_unavailable
+ * function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_callee_available(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_remote_user_free(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Destroy private data on the agent.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function upon completion
+ * or failure of CC.
+ *
+ * \note
+ * The agent private_data pointer may be NULL if the agent
+ * constructor failed.
+ *
+ * \return Nothing
+ */
+void sig_pri_cc_agent_destructor(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+ int res;
+
+ cc_pvt = agent->private_data;
+ if (!cc_pvt) {
+ /* The agent constructor probably failed. */
+ return;
+ }
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ res = -1;
+ if (cc_pvt->cc_request_response_pending) {
+ res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 2/* short_term_denial */);
+ }
+ if (res) {
+ pri_cc_cancel(cc_pvt->pri->pri, cc_pvt->cc_id);
+ }
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ ast_free(cc_pvt);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Return the hash value of the given CC monitor instance object.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param flags flags from ao2_callback(). Ignored at the moment.
+ *
+ * \retval core_id
+ */
+static int sig_pri_cc_monitor_instance_hash_fn(const void *obj, const int flags)
+{
+ const struct sig_pri_cc_monitor_instance *monitor_instance = obj;
+
+ return monitor_instance->core_id;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Compere the monitor instance core_id key value.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \return values are a combination of enum _cb_results.
+ */
+static int sig_pri_cc_monitor_instance_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct sig_pri_cc_monitor_instance *monitor_1 = obj;
+ struct sig_pri_cc_monitor_instance *monitor_2 = arg;
+
+ return monitor_1->core_id == monitor_2->core_id ? CMP_MATCH | CMP_STOP : 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Request CCSS.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ * \param available_timer_id Where to put the available timer scheduler id.
+ * Will never be NULL for a device monitor.
+ *
+ * \details
+ * Perform whatever steps are necessary in order to request CC.
+ * In addition, the monitor implementation is responsible for
+ * starting the available timer in this callback. The scheduler
+ * ID for the callback must be stored in the parent_link's child_avail_id
+ * field.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_req_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+ int cc_mode;
+ int res;
+
+ switch (monitor->service_offered) {
+ case AST_CC_CCBS:
+ cc_mode = 0;/* CCBS */
+ break;
+ case AST_CC_CCNR:
+ cc_mode = 1;/* CCNR */
+ break;
+ default:
+ /* CC service not supported by ISDN. */
+ return -1;
+ }
+
+ instance = monitor->private_data;
+
+ /* libpri handles it's own available timer. */
+ ast_mutex_lock(&instance->pri->lock);
+ res = pri_cc_req(instance->pri->pri, instance->cc_id, cc_mode);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return res;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Suspend monitoring.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ *
+ * \details
+ * Implementers must perform the necessary steps to suspend
+ * monitoring.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_suspend(struct ast_cc_monitor *monitor)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor->private_data;
+ ast_mutex_lock(&instance->pri->lock);
+ pri_cc_status(instance->pri->pri, instance->cc_id, 1/* busy */);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Unsuspend monitoring.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ *
+ * \details
+ * Perform the necessary steps to unsuspend monitoring.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_unsuspend(struct ast_cc_monitor *monitor)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor->private_data;
+ ast_mutex_lock(&instance->pri->lock);
+ pri_cc_status(instance->pri->pri, instance->cc_id, 0/* free */);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Status response to an ast_cc_monitor_status_request().
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ * \param devstate Current status of a Party A device.
+ *
+ * \details
+ * Alert a monitor as to the status of the agent for which
+ * the monitor had previously requested a status request.
+ *
+ * \note Zero or more responses may come as a result.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_status_rsp(struct ast_cc_monitor *monitor, enum ast_device_state devstate)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+ int cc_status;
+
+ switch (devstate) {
+ case AST_DEVICE_UNKNOWN:
+ case AST_DEVICE_NOT_INUSE:
+ cc_status = 0;/* free */
+ break;
+ case AST_DEVICE_BUSY:
+ case AST_DEVICE_INUSE:
+ cc_status = 1;/* busy */
+ break;
+ default:
+ /* Don't know how to interpret this device state into free/busy status. */
+ return 0;
+ }
+ instance = monitor->private_data;
+ ast_mutex_lock(&instance->pri->lock);
+ pri_cc_status_req_rsp(instance->pri->pri, instance->cc_id, cc_status);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Cancel the running available timer.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ * \param sched_id Available timer scheduler id to cancel.
+ * Will never be NULL for a device monitor.
+ *
+ * \details
+ * In most cases, this function will likely consist of just a
+ * call to AST_SCHED_DEL. It might have been possible to do this
+ * within the core, but unfortunately the mixture of sched_thread
+ * and sched usage in Asterisk prevents such usage.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id)
+{
+ /*
+ * libpri maintains it's own available timer as one of:
+ * T_CCBS2/T_CCBS5/T_CCBS6/QSIG_CCBS_T2
+ * T_CCNR2/T_CCNR5/T_CCNR6/QSIG_CCNR_T2
+ */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Destroy PRI private data on the monitor.
+ * \since 1.8
+ *
+ * \param monitor_pvt CC device monitor private data pointer.
+ *
+ * \details
+ * Implementers of this callback are responsible for destroying
+ * all heap-allocated data in the monitor's private_data pointer, including
+ * the private_data itself.
+ */
+void sig_pri_cc_monitor_destructor(void *monitor_pvt)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor_pvt;
+ if (!instance) {
+ return;
+ }
+ ao2_unlink(sig_pri_cc_monitors, instance);
+ ao2_ref(instance, -1);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \brief Load the sig_pri submodule.
+ * \since 1.8
+ *
+ * \param cc_type_name CC type name to use when looking up agent/monitor.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_load(const char *cc_type_name)
+{
+#if defined(HAVE_PRI_CCSS)
+ sig_pri_cc_type_name = cc_type_name;
+ sig_pri_cc_monitors = ao2_container_alloc(37, sig_pri_cc_monitor_instance_hash_fn,
+ sig_pri_cc_monitor_instance_cmp_fn);
+ if (!sig_pri_cc_monitors) {
+ return -1;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+ return 0;
+}
+
+/*!
+ * \brief Unload the sig_pri submodule.
+ * \since 1.8
+ *
+ * \return Nothing
+ */
+void sig_pri_unload(void)
+{
+#if defined(HAVE_PRI_CCSS)
+ if (sig_pri_cc_monitors) {
+ ao2_ref(sig_pri_cc_monitors, -1);
+ sig_pri_cc_monitors = NULL;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+}
+
#endif /* HAVE_PRI */
diff --git a/channels/sig_pri.h b/channels/sig_pri.h
index 0bccd6ab0..7ea92d752 100644
--- a/channels/sig_pri.h
+++ b/channels/sig_pri.h
@@ -27,8 +27,44 @@
#include "asterisk/channel.h"
#include "asterisk/frame.h"
+#include "asterisk/ccss.h"
#include <libpri.h>
#include <dahdi/user.h>
+#if defined(PRI_SUBCMD_CC_AVAILABLE)
+/* BUGBUG the HAVE_PRI_CCSS line is to be removed when the CCSS branch is merged to trunk and the configure script is updated. */
+#define HAVE_PRI_CCSS 1
+#endif /* defined(PRI_SUBCMD_CC_AVAILABLE) */
+
+#if defined(HAVE_PRI_CCSS)
+/*! PRI debug message flags when normal PRI debugging is turned on at the command line. */
+#define SIG_PRI_DEBUG_NORMAL \
+ (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE \
+ | PRI_DEBUG_CC)
+
+/*! PRI debug message flags when intense PRI debugging is turned on at the command line. */
+#define SIG_PRI_DEBUG_INTENSE \
+ (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE \
+ | PRI_DEBUG_CC | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP)
+
+#else
+
+/*! PRI debug message flags when normal PRI debugging is turned on at the command line. */
+#define SIG_PRI_DEBUG_NORMAL \
+ (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE)
+
+/*! PRI debug message flags when intense PRI debugging is turned on at the command line. */
+#define SIG_PRI_DEBUG_INTENSE \
+ (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE \
+ | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP)
+#endif /* !defined(HAVE_PRI_CCSS) */
+
+#if 0
+/*! PRI debug message flags set on initial startup. */
+#define SIG_PRI_DEBUG_DEFAULT SIG_PRI_DEBUG_NORMAL
+#else
+/*! PRI debug message flags set on initial startup. */
+#define SIG_PRI_DEBUG_DEFAULT 0
+#endif
enum sig_pri_tone {
SIG_PRI_TONE_RINGTONE = 0,
@@ -78,6 +114,14 @@ struct sig_pri_callback {
void (* const set_rdnis)(void *pvt, const char *rdnis);
void (* const queue_control)(void *pvt, int subclass);
int (* const new_nobch_intf)(struct sig_pri_pri *pri);
+ const char *(* const get_orig_dialstring)(void *pvt);
+ void (* const make_cc_dialstring)(void *pvt, char *buf, size_t buf_size);
+ void (* const update_span_devstate)(struct sig_pri_pri *pri);
+
+ /*! Reference the parent module. */
+ void (*module_ref)(void);
+ /*! Unreference the parent module. */
+ void (*module_unref)(void);
};
#define NUM_DCHANS 4 /*!< No more than 4 d-channels */
@@ -194,6 +238,7 @@ struct sig_pri_chan {
struct sig_pri_pri {
/* Should be set by user */
+ struct ast_cc_config_params *cc_params; /*!< CC config parameters for each new call. */
int pritimers[PRI_MAX_TIMERS];
int overlapdial; /*!< In overlap dialing mode */
int qsigchannelmapping; /*!< QSIG channel mapping type */
@@ -229,6 +274,11 @@ struct sig_pri_pri {
int switchtype; /*!< Type of switch to emulate */
int nsf; /*!< Network-Specific Facilities */
int trunkgroup; /*!< What our trunkgroup is */
+#if defined(HAVE_PRI_CCSS)
+ int cc_ptmp_recall_mode; /*!< CC PTMP recall mode. globalRecall(0), specificRecall(1) */
+ int cc_qsig_signaling_link_req; /*!< CC Q.SIG signaling link retention (Party A) release(0), retain(1), do-not-care(2) */
+ int cc_qsig_signaling_link_rsp; /*!< CC Q.SIG signaling link retention (Party B) release(0), retain(1) */
+#endif /* defined(HAVE_PRI_CCSS) */
int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */
int debug; /*!< set to true if to dump PRI event info (tested but never set) */
@@ -257,6 +307,37 @@ struct sig_pri_pri {
ast_mutex_t lock; /*!< libpri access Mutex */
time_t lastreset; /*!< time when unused channels were last reset */
struct sig_pri_callback *calls;
+ /*!
+ * \brief Congestion device state of the span.
+ * \details
+ * AST_DEVICE_NOT_INUSE - Span does not have all B channels in use.
+ * AST_DEVICE_BUSY - All B channels are in use.
+ * AST_DEVICE_UNAVAILABLE - Span is in alarm.
+ * \note
+ * Device name: DAHDI/I<span>/congestion
+ */
+ int congestion_devstate;
+#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
+ /*! \todo An ISDN span threshold device state could be useful in determining how often a span utilization goes over a configurable threshold. */
+ /*!
+ * \brief User threshold device state of the span.
+ * \details
+ * AST_DEVICE_NOT_INUSE - There are no B channels in use.
+ * AST_DEVICE_INUSE - The number of B channels in use is less than
+ * the configured threshold but not zero.
+ * AST_DEVICE_BUSY - The number of B channels in use meets or exceeds
+ * the configured threshold.
+ * AST_DEVICE_UNAVAILABLE - Span is in alarm.
+ * \note
+ * Device name: DAHDI/I<span>/threshold
+ */
+ int threshold_devstate;
+ /*!
+ * \brief Number of B channels in use to consider the span in a busy state.
+ * \note Setting the threshold to zero is interpreted as all B channels.
+ */
+ int user_busy_threshold;
+#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
};
void sig_pri_extract_called_num_subaddr(struct sig_pri_chan *p, const char *rdest, char *called, size_t called_buff_size);
@@ -304,4 +385,25 @@ int pri_maintenance_bservice(struct pri *pri, struct sig_pri_chan *p, int change
void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_pri_chan *pchan);
+int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan);
+int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent);
+int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent);
+void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent);
+int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent);
+int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent);
+int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent);
+int sig_pri_cc_agent_start_monitoring(struct ast_cc_agent *agent);
+int sig_pri_cc_agent_callee_available(struct ast_cc_agent *agent);
+void sig_pri_cc_agent_destructor(struct ast_cc_agent *agent);
+
+int sig_pri_cc_monitor_req_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
+int sig_pri_cc_monitor_suspend(struct ast_cc_monitor *monitor);
+int sig_pri_cc_monitor_unsuspend(struct ast_cc_monitor *monitor);
+int sig_pri_cc_monitor_status_rsp(struct ast_cc_monitor *monitor, enum ast_device_state devstate);
+int sig_pri_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
+void sig_pri_cc_monitor_destructor(void *monitor_pvt);
+
+int sig_pri_load(const char *cc_type_name);
+void sig_pri_unload(void);
+
#endif /* _SIG_PRI_H */
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index ce87f0f23..1d900eb58 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -153,7 +153,7 @@
* \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have
* allowsubscribe and allowrefer on in sip.conf.
*/
-#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO"
+#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH"
/*! \brief SIP Extensions we support
* \note This should be generated based on the previous array
@@ -239,6 +239,7 @@
*/
/*@{*/
#define SIP_OUTGOING (1 << 0) /*!< D: Direction of the last transaction in this dialog */
+#define SIP_OFFER_CC (1 << 1) /*!< D: Offer CC on subsequent responses */
#define SIP_RINGING (1 << 2) /*!< D: Have sent 180 ringing */
#define SIP_PROGRESS_SENT (1 << 3) /*!< D: Have sent 183 message progress */
#define SIP_NEEDREINVITE (1 << 4) /*!< D: Do we need to send another reinvite? */
@@ -415,7 +416,8 @@ enum subscriptiontype {
DIALOG_INFO_XML,
CPIM_PIDF_XML,
PIDF_XML,
- MWI_NOTIFICATION
+ MWI_NOTIFICATION,
+ CALL_COMPLETION,
};
/*! \brief The number of media types in enum \ref media_type below. */
@@ -930,6 +932,7 @@ struct sip_pvt {
AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */
AST_STRING_FIELD(parkinglot); /*!< Parkinglot */
AST_STRING_FIELD(engine); /*!< RTP engine to use */
+ AST_STRING_FIELD(dialstring); /*!< The dialstring used to call this SIP endpoint */
);
char via[128]; /*!< Via: header */
struct sip_socket socket; /*!< The socket used for this dialog */
@@ -1066,6 +1069,8 @@ struct sip_pvt {
* The large-scale changes would be a good idea for implementing during an SDP rewrite.
*/
struct offered_media offered_media[OFFERED_MEDIA_COUNT];
+ struct ast_cc_config_params *cc_params;
+ struct sip_epa_entry *epa_entry;
};
/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
@@ -1197,6 +1202,7 @@ struct sip_peer {
/*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */
enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
unsigned int disallowed_methods;
+ struct ast_cc_config_params *cc_params;
};
/*!
@@ -1286,4 +1292,359 @@ struct sip_subscription_mwi {
struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for subscription */
struct sockaddr_in us; /*!< Who the server thinks we are */
};
+
+/*!
+ * SIP PUBLISH support!
+ * PUBLISH support was added to chan_sip due to its use in the call-completion
+ * event package. In order to suspend and unsuspend monitoring of a called party,
+ * a PUBLISH message must be sent. Rather than try to hack in PUBLISH transmission
+ * and reception solely for the purposes of handling call-completion-related messages,
+ * an effort has been made to create a generic framework for handling PUBLISH messages.
+ *
+ * There are two main components to the effort, the event publication agent (EPA) and
+ * the event state compositor (ESC). Both of these terms appear in RFC 3903, and the
+ * implementation in Asterisk conforms to the defintions there. An EPA is a UAC that
+ * transmits PUBLISH requests. An ESC is a UAS that receives PUBLISH requests and
+ * acts appropriately based on the content of those requests.
+ *
+ * ESC:
+ * The main structure in chan_sip is the event_state_compositor. There is an
+ * event_state_compositor structure for each event package supported (as of Nov 2009
+ * this is only the call-completion package). The structure contains data which is
+ * intrinsic to the event package itself, such as the name of the package and a set
+ * of callbacks for handling incoming PUBLISH requests. In addition, the
+ * event_state_compositor struct contains an ao2_container of sip_esc_entries.
+ *
+ * A sip_esc_entry corresponds to an entity which has sent a PUBLISH to Asterisk. We are
+ * able to match the incoming PUBLISH to a sip_esc_entry using the Sip-If-Match header
+ * of the message. Of course, if none is present, then a new sip_esc_entry will be created.
+ *
+ * Once it is determined what type of PUBLISH request has come in (from RFC 3903, it may
+ * be an initial, modify, refresh, or remove), then the event package-specific callbacks
+ * may be called. If your event package doesn't need to take any specific action for a
+ * specific PUBLISH type, it is perfectly safe to not define the callback at all. The callback
+ * only needs to take care of application-specific information. If there is a problem, it is
+ * up to the callback to take care of sending an appropriate 4xx or 5xx response code. In such
+ * a case, the callback should return -1. This will tell the function that called the handler
+ * that an appropriate error response has been sent. If the callback returns 0, however, then
+ * the caller of the callback will generate a new entity tag and send a 200 OK response.
+ *
+ * ESC entries are reference-counted, however as an implementor of a specific event package,
+ * this should be transparent, since the reference counts are handled by the general ESC
+ * framework.
+ *
+ * EPA:
+ * The event publication agent in chan_sip is structured quite a bit differently than the
+ * ESC. With an ESC, an appropriate entry has to be found based on the contents of an incoming
+ * PUBLISH message. With an EPA, the application interested in sending the PUBLISH can maintain
+ * a reference to the appropriate EPA entry instead. Similarly, when matching a PUBLISH response
+ * to an appropriate EPA entry, the sip_pvt can maintain a reference to the corresponding
+ * EPA entry. The result of this train of thought is that there is no compelling reason to
+ * maintain a container of these entries.
+ *
+ * Instead, there is only the sip_epa_entry structure. Every sip_epa_entry has an entity tag
+ * that it maintains so that subsequent PUBLISH requests will be identifiable by the ESC on
+ * the far end. In addition, there is a static_data field which contains information that is
+ * common to all sip_epa_entries for a specific event package. This static data includes the
+ * name of the event package and callbacks for handling specific responses for outgoing PUBLISHes.
+ * Also, there is a field for pointing to instance-specific data. This can include the current
+ * published state or other identifying information that is specific to an instance of an EPA
+ * entry of a particular event package.
+ *
+ * When an application wishes to send a PUBLISH request, it simply will call create_epa_entry,
+ * followed by transmit_publish in order to send the PUBLISH. That's all that is necessary.
+ * Like with ESC entries, sip_epa_entries are reference counted. Unlike ESC entries, though,
+ * sip_epa_entries reference counts have to be maintained to some degree by the application making
+ * use of the sip_epa_entry. The application will acquire a reference to the EPA entry when it
+ * calls create_epa_entry. When the application has finished using the EPA entry (which may not
+ * be until after several PUBLISH transactions have taken place) it must use ao2_ref to decrease
+ * the reference count by 1.
+ */
+
+/*!
+ * \brief The states that can be represented in a SIP call-completion PUBLISH
+ */
+enum sip_cc_publish_state {
+ /*! Closed, i.e. unavailable */
+ CC_CLOSED,
+ /*! Open, i.e. available */
+ CC_OPEN,
+};
+
+/*!
+ * \brief The states that can be represented in a SIP call-completion NOTIFY
+ */
+enum sip_cc_notify_state {
+ /*! Queued, i.e. unavailable */
+ CC_QUEUED,
+ /*! Ready, i.e. available */
+ CC_READY,
+};
+
+/*!
+ * \brief The types of PUBLISH messages defined in RFC 3903
+ */
+enum sip_publish_type {
+ /*!
+ * \brief Unknown
+ *
+ * \details
+ * This actually is not defined in RFC 3903. We use this as a constant
+ * to indicate that an incoming PUBLISH does not fit into any of the
+ * other categories and is thus invalid.
+ */
+ SIP_PUBLISH_UNKNOWN,
+ /*!
+ * \brief Initial
+ *
+ * \details
+ * The first PUBLISH sent. This will contain a non-zero Expires header
+ * as well as a body that indicates the current state of the endpoint
+ * that has sent the message. The initial PUBLISH is the only type
+ * of PUBLISH to not contain a Sip-If-Match header in it.
+ */
+ SIP_PUBLISH_INITIAL,
+ /*!
+ * \brief Refresh
+ *
+ * \details
+ * Used to keep a published state from expiring. This will contain a
+ * non-zero Expires header but no body since its purpose is not to
+ * update state.
+ */
+ SIP_PUBLISH_REFRESH,
+ /*!
+ * \brief Modify
+ *
+ * \details
+ * Used to change state from its previous value. This will contain
+ * a body updating the published state. May or may not contain an
+ * Expires header.
+ */
+ SIP_PUBLISH_MODIFY,
+ /*!
+ * \brief Remove
+ *
+ * \details
+ * Used to remove published state from an ESC. This will contain
+ * an Expires header set to 0 and likely no body.
+ */
+ SIP_PUBLISH_REMOVE,
+};
+
+/*!
+ * Data which is the same for all instances of an EPA for a
+ * particular event package
+ */
+struct epa_static_data {
+ /*! The event type */
+ enum subscriptiontype event;
+ /*!
+ * The name of the event as it would
+ * appear in a SIP message
+ */
+ const char *name;
+ /*!
+ * The callback called when a 200 OK is received on an outbound PUBLISH
+ */
+ void (*handle_ok)(struct sip_pvt *, struct sip_request *, struct sip_epa_entry *);
+ /*!
+ * The callback called when an error response is received on an outbound PUBLISH
+ */
+ void (*handle_error)(struct sip_pvt *, const int resp, struct sip_request *, struct sip_epa_entry *);
+ /*!
+ * Destructor to call to clean up instance data
+ */
+ void (*destructor)(void *instance_data);
+};
+
+/*!
+ * \brief backend for an event publication agent
+ */
+struct epa_backend {
+ const struct epa_static_data *static_data;
+ AST_LIST_ENTRY(epa_backend) next;
+};
+
+struct sip_epa_entry {
+ /*!
+ * When we are going to send a publish, we need to
+ * know the type of PUBLISH to send.
+ */
+ enum sip_publish_type publish_type;
+ /*!
+ * When we send a PUBLISH, we have to be
+ * sure to include the entity tag that we
+ * received in the previous response.
+ */
+ char entity_tag[SIPBUFSIZE];
+ /*!
+ * The destination to which this EPA should send
+ * PUBLISHes. This may be the name of a SIP peer
+ * or a hostname.
+ */
+ char destination[SIPBUFSIZE];
+ /*!
+ * The body of the most recently-sent PUBLISH message.
+ * This is useful for situations such as authentication,
+ * in which we must send a message identical to the
+ * one previously sent
+ */
+ char body[SIPBUFSIZE];
+ /*!
+ * Every event package has some constant data and
+ * callbacks that all instances will share. This
+ * data resides in this field.
+ */
+ const struct epa_static_data *static_data;
+ /*!
+ * In addition to the static data that all instances
+ * of sip_epa_entry will have, each instance will
+ * require its own instance-specific data.
+ */
+ void *instance_data;
+};
+
+/*!
+ * \brief Instance data for a Call completion EPA entry
+ */
+struct cc_epa_entry {
+ /*!
+ * The core ID of the CC transaction
+ * for which this EPA entry belongs. This
+ * essentially acts as a unique identifier
+ * for the entry and is used in the hash
+ * and comparison functions
+ */
+ int core_id;
+ /*!
+ * We keep the last known state of the
+ * device in question handy in case
+ * it needs to be known by a third party.
+ * Also, in the case where for some reason
+ * we get asked to transmit state that we
+ * already sent, we can just ignore the
+ * request.
+ */
+ enum sip_cc_publish_state current_state;
+};
+
+struct event_state_compositor;
+
+/*!
+ * \brief common ESC items for all event types
+ *
+ * The entity_id field serves as a means by which
+ * A specific entry may be found.
+ */
+struct sip_esc_entry {
+ /*!
+ * The name of the party who
+ * sent us the PUBLISH. This will more
+ * than likely correspond to a peer name.
+ *
+ * This field's utility isn't really that
+ * great. It's mainly just a user-recognizable
+ * handle that can be printed in debug messages.
+ */
+ const char *device_name;
+ /*!
+ * The event package for which this esc_entry
+ * exists. Most of the time this isn't really
+ * necessary since you'll have easy access to the
+ * ESC which contains this entry. However, in
+ * some circumstances, we won't have the ESC
+ * available.
+ */
+ const char *event;
+ /*!
+ * The entity ID used when corresponding
+ * with the EPA on the other side. As the
+ * ESC, we generate an entity ID for each
+ * received PUBLISH and store it in this
+ * structure.
+ */
+ char entity_tag[30];
+ /*!
+ * The ID for the scheduler. We schedule
+ * destruction of a sip_esc_entry when we
+ * receive a PUBLISH. The destruction is
+ * scheduled for the duration received in
+ * the Expires header.
+ */
+ int sched_id;
+ /*!
+ * Each ESC entry will be for a specific
+ * event type. Those entries will need to
+ * carry data which is intrinsic to the
+ * ESC entry but which is specific to
+ * the event package
+ */
+ void *event_specific_data;
+};
+
+typedef int (* const esc_publish_callback)(struct sip_pvt *, struct sip_request *, struct event_state_compositor *, struct sip_esc_entry *);
+
+/*!
+ * \brief Callbacks for SIP ESCs
+ *
+ * \details
+ * The names of the callbacks are self-explanatory. The
+ * corresponding handler is called whenever the specific
+ * type of PUBLISH is received.
+ */
+struct sip_esc_publish_callbacks {
+ const esc_publish_callback initial_handler;
+ const esc_publish_callback refresh_handler;
+ const esc_publish_callback modify_handler;
+ const esc_publish_callback remove_handler;
+};
+
+struct sip_cc_agent_pvt {
+ int offer_timer_id;
+ /* A copy of the original call's Call-ID.
+ * We use this as a search key when attempting
+ * to find a particular sip_pvt.
+ */
+ char original_callid[SIPBUFSIZE];
+ /* A copy of the exten called originally.
+ * We use this to set the proper extension
+ * to dial during the recall since the incoming
+ * request URI is one that was generated just
+ * for the recall
+ */
+ char original_exten[SIPBUFSIZE];
+ /* A reference to the dialog which we will
+ * be sending a NOTIFY on when it comes time
+ * to send one
+ */
+ struct sip_pvt *subscribe_pvt;
+ /* When we send a NOTIFY, we include a URI
+ * that should be used by the caller when he
+ * wishes to send a PUBLISH or INVITE to us.
+ * We store that URI here.
+ */
+ char notify_uri[SIPBUFSIZE];
+ /* When we advertise call completion to a caller,
+ * we provide a URI for the caller to use when
+ * he sends us a SUBSCRIBE. We store it for matching
+ * purposes when we receive the SUBSCRIBE from the
+ * caller.
+ */
+ char subscribe_uri[SIPBUFSIZE];
+ char is_available;
+};
+
+struct sip_monitor_instance {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(subscribe_uri);
+ AST_STRING_FIELD(notify_uri);
+ AST_STRING_FIELD(peername);
+ AST_STRING_FIELD(device_name);
+ );
+ int core_id;
+ struct sip_pvt *subscription_pvt;
+ struct sip_epa_entry *suspension_entry;
+};
+
#endif
diff --git a/configs/ccss.conf.sample b/configs/ccss.conf.sample
new file mode 100644
index 000000000..420e4367b
--- /dev/null
+++ b/configs/ccss.conf.sample
@@ -0,0 +1,150 @@
+[general]
+; There is only a single option that may be defined in this file.
+; The cc_max_requests option is a global limit on the number of
+; CC requests that may be in the Asterisk system at any time.
+;
+;cc_max_requests = 20
+;
+;
+;============================================
+; PLEASE READ THIS!!!
+; The options described below should NOT be
+; set in this file. Rather, they should be
+; set per-device in a channel driver
+; configuration file.
+; PLEASE READ THIS!!!
+;===========================================
+;
+;---------------------------------------------------------------------
+; Timers
+;---------------------------------------------------------------------
+;There are three configurable timers for all types of CC: the
+;cc_offer_timer, the ccbs_available_timer, and the ccnr_available_timer.
+;In addition, when using a generic agent, there is a fourth timer,
+;the cc_recall_timer. All timers are configured in seconds, and the
+;values shown below are the defaults.
+;
+;When a caller is offered CCBS or CCNR, the cc_offer_timer will
+;be started. If the caller does not request CC before the
+;cc_offer_timer expires, then the caller will be unable to request
+;CC for this call.
+;
+;cc_offer_timer = 20
+;
+;Once a caller has requested CC, then either the ccbs_available_timer
+;or the ccnr_available_timer will run, depending on the service
+;requested. The reason why there are two separate timers for CCBS
+;and CCNR is that it is reasonable to want to have a shorter timeout
+;configured for CCBS than for CCNR. If the available timer expires
+;before the called party becomes available, then the CC attempt
+;will have failed and monitoring of the called party will stop.
+;
+;ccbs_available_timer = 4800
+;ccnr_available_timer = 7200
+;
+; When using a generic agent, the original caller is called back
+; when one of the original called parties becomes available. The
+; cc_recall_timer tells Asterisk how long it should let the original
+; caller's phone ring before giving up. Please note that this parameter
+; only affects operation when using a generic agent.
+;
+;cc_recall_timer = 20
+;---------------------------------------------------------------------
+; Policies
+;---------------------------------------------------------------------
+; Policy settings tell Asterisk how to behave and what sort of
+; resources to allocate in order to facilitate CC. There are two
+; settings to control the actions Asterisk will take.
+;
+; The cc_agent_policy describes the behavior that Asterisk will
+; take when communicating with the caller during CC. There are
+; three possible options.
+;
+;never: Never offer CC to the caller. Setting the cc_agent_policy
+; to this value is the way to disable CC for a call.
+;
+;generic: A generic CC agent is one which uses no protocol-specific
+; mechanisms to offer CC to the caller. Instead, the caller
+; requests CC using a dialplan function. Due to internal
+; restrictions, you should only use a generic CC agent on
+; phones (i.e. not "trunks"). If you are using phones which
+; do not support a protocol-specific method of using CC, then
+; generic CC agents are what you should use.
+;
+;native: A native CC agent is one which uses protocol-specific
+; signaling to offer CC to the caller and accept CC requests
+; from the caller. The supported protocols for native CC
+; agents are SIP, ISDN ETSI PTP, ISDN ETSI PTMP, and Q.SIG
+;cc_agent_policy=never
+;
+; The cc_monitor_policy describes the behavior that Asterisk will
+; take when communicating with the called party during CC. There
+; are four possible options.
+;
+;never: Analogous to the cc_agent_policy setting. We will never
+; attempt to request CC services on this interface.
+;
+;generic: Analogous to the cc_agent_policy setting. We will monitor
+; the called party's progress using protocol-agnostic
+; capabilities. Like with generic CC agents, generic CC
+; monitors should only be used for phones.
+;
+;native: Analogous to the cc_agent_policy setting. We will use
+; protocol-specific methods to request CC from this interface
+; and to monitor the interface for availability.
+;
+;accept: If an interface is set to "accept," then we will accept
+; protocol-specific CC offers from the caller and use
+; a native CC monitor for the remainder of the CC transaction.
+; However, if the interface does not offer protocol-specific
+; CC, then we will fall back to using a generic CC monitor
+; instead. This is a good setting to use for phones for which
+; you do not know if they support protocol-specific CC
+; methodologies.
+;cc_monitor_policy=never
+;
+;
+;---------------------------------------------------------------------
+; Limits
+;---------------------------------------------------------------------
+;
+; The use of CC requires Asterisk to potentially use more memory than
+; some administrators would like. As such, it is a good idea to limit
+; the number of CC requests that can be in the system at a given time.
+; The values shown below are the defaults.
+;
+; The cc_max_agents setting limits the number of outstanding CC
+; requests a caller may have at any given time. Please note that due
+; to implementation restrictions, this setting is ignored when using
+; generic CC agents. Generic CC agents may only have one outstanding
+; CC request.
+;
+;cc_max_agents = 5
+;
+; The cc_max_monitors setting limits the number of outstanding CC
+; requests can be made to a specific interface at a given time.
+;
+;cc_max_monitors = 5
+;
+;---------------------------------------------------------------------
+; Other
+;---------------------------------------------------------------------
+;
+; When using a generic CC agent, the caller who requested CC will be
+; called back when a called party becomes available. When the caller
+; answers his phone, the administrator may opt to have a macro run.
+; What this macro does is up to the administrator. By default there
+; is no callback macro configured.
+;
+;cc_callback_macro=
+;
+; When using an ISDN phone and a generic CC agent, Asterisk is unable
+; to determine the dialstring that should be used when calling back
+; the original caller. Furthermore, if you desire to use any dialstring-
+; specific options, such as distinctive ring, you must set this
+; configuration option. For non-ISDN phones, it is not necessary to
+; set this, since Asterisk can determine the dialstring to use since
+; it is identical to the name of the calling device. By default, there
+; is no cc_agent_dialstring set.
+;
+;cc_agent_dialstring=
diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample
index fb0d06931..30229b5fc 100644
--- a/configs/chan_dahdi.conf.sample
+++ b/configs/chan_dahdi.conf.sample
@@ -85,10 +85,11 @@
;service_message_support=yes
; Enable service message support for channel. Must be set after switchtype.
;
-; PRI Reverse Charging Indication: Indicate to the called party that the
-; call will be reverse charged. To enable, prefix the dialed number with one
-; of the following letters:
-; C - Reverse Charge Indication Requested
+; Dialing options for ISDN (i.e., Dial(DAHDI/g1/exten/options)):
+; R Reverse Charge Indication
+; Indicate to the called party that the call will be reverse charged.
+; K(n) Keypad digits n
+; Send out the specified digits as keypad digits.
;
; PRI Dialplan: The ISDN-level Type Of Number (TON) or numbering plan, used for
; the dialed number. For most installations, leaving this as 'unknown' (the
@@ -236,9 +237,52 @@
; May vary in other ISDN standards (Q.931 1993 : 90000 ms)
; T313: Wait for CONNECT acknowledge, CPE side only (default 3000 ms)
;
+; T-RESPONSE: Maximum time to wait for a typical APDU response. (default 4000 ms)
+; This is an implementation timer when the standard does not specify one.
+; T-ACTIVATE: Request supervision timeout. (default 10000 ms)
+; T-RETENTION: Maximum time to wait for user A to activate call-completion. (default 30000 ms)
+; Used by ETSI PTP, ETSI PTMP, and Q.SIG as the cc_offer_timer.
+; T-CCBS1: T-STATUS timer equivalent for CC user A status. (default 4000 ms)
+; T-CCBS2: Maximum time the CCBS service will be active (default 45 min in ms)
+; T-CCBS3: Maximum time to wait for user A to respond to user B availability. (default 20000 ms)
+; T-CCBS5: Network B CCBS supervision timeout. (default 60 min in ms)
+; T-CCBS6: Network A CCBS supervision timeout. (default 60 min in ms)
+; T-CCNR2: Maximum time the CCNR service will be active (default 180 min in ms)
+; T-CCNR5: Network B CCNR supervision timeout. (default 195 min in ms)
+; T-CCNR6: Network A CCNR supervision timeout. (default 195 min in ms)
+; CC-T1: Q.SIG CC request supervision timeout. (default 30000 ms)
+; CCBS-T2: Q.SIG CCBS supervision timeout. (default 60 min in ms)
+; CCNR-T2: Q.SIG CCNR supervision timeout. (default 195 min in ms)
+; CC-T3: Q.SIG CC Maximum time to wait for user A to respond to user B availability. (default 30000 ms)
+;
;pritimer => t200,1000
;pritimer => t313,4000
;
+; CC PTMP recall mode:
+; specific - Only the CC original party A can participate in the CC callback
+; global - Other compatible endpoints on the PTMP line can be party A in the CC callback
+;
+; cc_ptmp_recall_mode cannot be changed on a reload.
+;
+;cc_ptmp_recall_mode = specific
+;
+; CC Q.SIG Party A (requester) retain signaling link option
+; retain Require that the signaling link be retained.
+; release Request that the signaling link be released.
+; do_not_care The responder is free to choose if the signaling link will be retained.
+;
+;cc_qsig_signaling_link_req = retain
+;
+; CC Q.SIG Party B (responder) retain signaling link option
+; retain Prefer that the signaling link be retained.
+; release Prefer that the signaling link be released.
+;
+;cc_qsig_signaling_link_rsp = retain
+;
+; See ccss.conf.sample for more options. The timers described by ccss.conf.sample
+; are not used by ISDN for the native protocol since they are defined by the
+; standards and set by pritimer above.
+;
; To enable transmission of facility-based ISDN supplementary services (such
; as caller name from CPE over facility), enable this option.
; Cannot be changed on a reload.
@@ -267,6 +311,10 @@
; fxo_ks: FXO (Kewl Start)
; pri_cpe: PRI signalling, CPE side
; pri_net: PRI signalling, Network side
+; bri_cpe: BRI PTP signalling, CPE side
+; bri_net: BRI PTP signalling, Network side
+; bri_cpe_ptmp: BRI PTMP signalling, CPE side
+; bri_net_ptmp: BRI PTMP signalling, Network side
; sf: SF (Inband Tone) Signalling
; sf_w: SF Wink
; sf_featd: SF Feature Group D (The fake, Adtran style, DTMF)
diff --git a/configs/manager.conf.sample b/configs/manager.conf.sample
index 229db2dac..078d17932 100644
--- a/configs/manager.conf.sample
+++ b/configs/manager.conf.sample
@@ -84,6 +84,7 @@ bindaddr = 0.0.0.0
; Write authorization permits you to send commands and get back responses. The
; following classes exist:
;
+; all - All event classes below (including any we may have missed).
; system - General information about the system and ability to run system
; management commands, such as Shutdown, Restart, and Reload.
; call - Information about channels and ability to set information in a
@@ -100,6 +101,8 @@ bindaddr = 0.0.0.0
; cdr - Output of cdr_manager, if loaded. Read-only.
; dialplan - Receive NewExten and VarSet events. Read-only.
; originate - Permission to originate new calls. Write-only.
+; agi - Output AGI commands executed. Input AGI command to execute.
+; cc - Call Completion events. Read-only.
;
;read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
;write = system,call,agent,user,config,command,reporting,originate
diff --git a/configure.ac b/configure.ac
index 7a4cbb8eb..9e774683b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -335,6 +335,7 @@ AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
AST_EXT_LIB_SETUP([POPT], [popt], [popt])
AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
+AST_EXT_LIB_SETUP_DEPENDENT([PRI_CCSS], [ISDN PRI call completion supplementary service], [PRI], [pri])
AST_EXT_LIB_SETUP_DEPENDENT([PRI_SUBADDR], [ISDN PRI subaddressing], [PRI], [pri])
AST_EXT_LIB_SETUP_DEPENDENT([PRI_CALL_HOLD], [ISDN PRI call hold], [PRI], [pri])
AST_EXT_LIB_SETUP_DEPENDENT([PRI_CALL_REROUTING], [ISDN PRI call rerouting and call deflection], [PRI], [pri])
@@ -1543,6 +1544,7 @@ AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
AST_EXT_LIB_CHECK([PORTAUDIO], [portaudio], [Pa_GetDeviceCount], [portaudio.h])
AST_EXT_LIB_CHECK([PRI], [pri], [pri_connected_line_update], [libpri.h])
+AST_EXT_LIB_CHECK([PRI_CCSS], [pri], [pri_cc_enable], [libpri.h])
AST_EXT_LIB_CHECK([PRI_SUBADDR], [pri], [pri_sr_set_called_subaddress], [libpri.h])
AST_EXT_LIB_CHECK([PRI_CALL_HOLD], [pri], [pri_hold_enable], [libpri.h])
AST_EXT_LIB_CHECK([PRI_CALL_REROUTING], [pri], [pri_reroute_enable], [libpri.h])
diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex
index 8097d14ed..0427389b1 100644
--- a/doc/tex/asterisk.tex
+++ b/doc/tex/asterisk.tex
@@ -147,6 +147,9 @@ reference purposes.
\chapter{Security Framework}
\input{security-events.tex}
+\chapter{Call Completion Supplementary Services}
+ \input{ccss.tex}
+
\chapter{Development}
\section{Backtrace}
\input{backtrace.tex}
diff --git a/doc/tex/ccss.tex b/doc/tex/ccss.tex
new file mode 100644
index 000000000..cfe07cbe0
--- /dev/null
+++ b/doc/tex/ccss.tex
@@ -0,0 +1,414 @@
+\section{Introduction}
+
+ A new feature for Asterisk 1.8 is Call Completion Supplementary
+Services. This document aims to explain the system and how to use it.
+In addition, this document examines some potential troublesome points
+which administrators may come across during their deployment of the
+feature.
+
+\section{What is CCSS?}
+
+ Call Completion Supplementary Services (often abbreviated "CCSS" or
+simply "CC") allow for a caller to let Asterisk automatically alert him
+when a called party has become available, given that a previous call to
+that party failed for some reason. The two services offered are Call
+Completion on Busy Subscriber (CCBS) and Call Completion on No Response
+(CCNR).
+ To illustrate, let's say that Alice attempts to call Bob. Bob is
+currently on a phone call with Carol, though, so Alice hears a busy
+signal. In this situation, assuming that Asterisk has been configured
+to allow for such activity, Alice would be able to request CCBS. Once
+Bob has finished his phone call, Alice will be alerted. Alice can then
+attempt to call Bob again.
+
+\section{Glossary of Terms}
+
+ In this document, we will use some terms which may require
+clarification. Most of these terms are specific to Asterisk, and are by
+no means standard.
+
+\begin{itemize}
+\item CCBS: Call Completion on Busy Subscriber. When a call fails because the
+recipient's phone is busy, the caller will have the opportunity to
+request CCBS. When the recipient's phone is no longer busy, the caller
+will be alerted. The means by which the caller is alerted is dependent
+upon the type of agent used by the caller.
+
+\item CCNR: Call Completion on No Response. When a call fails because the
+recipient does not answer the phone, the caller will have the opportun-
+ity to request CCNR. When the recipient's phone becomes busy and then
+is no longer busy, the caller will be alerted. The means by which the
+caller is alerted is dependent upon the type of the agent used by the
+caller.
+
+\item Agent: The agent is the entity within Asterisk that communicates with
+and acts on behalf of the calling party.
+
+\item Monitor: The monitor is the entity within Asterisk that communicates
+with and monitors the status of the called party.
+
+\item Generic Agent: A generic agent is an agent that uses protocol-agnostic
+methods to communicate with the caller. Generic agents should only be
+used for phones, and never should be used for "trunks."
+
+\item Generic Monitor: A generic monitor is a monitor that uses protocol-
+agnostic methods to monitor the status of the called party. Like with
+generic agents, generic monitors should only be used for phones.
+
+\item Native Agent: The opposite of a generic agent. A native agent uses
+protocol-specific messages to communicate with the calling party.
+Native agents may be used for both phones and trunks, but it must be
+known ahead of time that the device with which Asterisk is communica-
+ting supports the necessary signaling.
+
+\item Native Monitor: The opposite of a generic monitor. A native monitor
+uses protocol-specific messages to subscribe to and receive notifica-
+tion of the status of the called party. Native monitors may be used
+for both phones and trunks, but it must be known ahead of time that
+the device with which Asterisk is communicating supports the
+necessary signaling.
+
+\item Offer: An offer of CC refers to the notification received by the caller
+that he may request CC.
+
+\item Request: When the caller decides that he would like to subscribe to CC,
+he will make a request for CC. Furthermore, the term may refer to any
+outstanding requests made by callers.
+
+\item Recall: When the caller attempts to call the recipient after being
+alerted that the recipient is available, this action is referred to
+as a "recall."
+\end{itemize}
+
+\section{The CC Process}
+
+\subsection{The Initial Call}
+
+ The only requirement for the use of CC is to configure an agent for
+the caller and a monitor for at least one recipient of the call.
+This is controlled using the cc\_agent\_policy for the caller and the
+cc\_monitor\_policy for the recipient. For more information about these
+configuration settings, see configs/samples/ccss.conf.sample. If the
+agent for the caller is set to something other than "never" and at
+least one recipient has his monitor set to something other than
+"never," then CC will be offered to the caller at the end of the
+call.
+
+ Once the initial call has been hung up, the configured
+cc\_offer\_timer for the caller will be started. If the caller wishes to
+request CC for the previous call, he must do so before the timer
+expires.
+
+\subsection{Requesting CC}
+
+ Requesting CC is done differently depending on the type of agent
+the caller is using.
+
+ With generic agents, the CallCompletionRequest application must be
+called in order to request CC. There are two different ways in which
+this may be called. It may either be called before the caller hangs up
+during the initial call, or the caller may hang up from the initial
+call and dial an extension which calls the CallCompletionRequest
+application. If the second method is used, then the caller will
+have until the cc\_offer\_timer expires to request CC.
+
+ With native agents, the method for requesting CC is dependent upon
+the technology being used, coupled with the make of equipment. It may
+be possible to request CC using a programmable key on a phone or by
+clicking a button on a console. If you are using equipment which can
+natively support CC but do not know the means by which to request it,
+then contact the equipment manufacturer for more information.
+
+\subsection{Cancelling CC}
+
+ CC may be canceled after it has been requested. The method by which
+this is accomplished differs based on the type of agent the calling
+party uses.
+
+ When using a generic agent, the dialplan application
+CallRequestCancel is used to cancel CC. When using a native monitor,
+the method by which CC is cancelled depends on the protocol used.
+Likely, this will be done using a button on a phone.
+
+ Keep in mind that if CC is cancelled, it cannot be un-cancelled.
+
+\subsection{Monitoring the Called Party}
+
+ Once the caller has requested CC, then Asterisk's job is to monitor
+the progress of the called parties. It is at this point that Asterisk
+allocates the necessary resources to monitor the called parties.
+
+ A generic monitor uses Asterisk's device state subsystem in order
+to determine when the called party has become available. For both CCBS
+and CCNR, Asterisk simply waits for the phone's state to change to
+a "not in use" state from a different state. Once this happens, then
+Asterisk will consider the called party to be available and will alert
+the caller.
+
+ A native monitor relies on the network to send a protocol-specific
+message when the called party has become available. When Asterisk
+receives such a message, it will consider the called party to be
+available and will alert the caller.
+
+ Note that since a single caller may dial multiple parties, a monitor
+is used for each called party. It is within reason that different called
+parties will use different types of monitors for the same CC request.
+
+\subsection{Alerting the Caller}
+
+ Once Asterisk has determined that the called party has become available
+the time comes for Asterisk to alert the caller that the called party has
+become available. The method by which this is done differs based on the
+type of agent in use.
+
+ If a generic agent is used, then Asterisk will originate a call to
+the calling party. Upon answering the call, if a callback macro has
+been configured, then that macro will be executed on the calling
+party's channel. After the macro has completed, an outbound call
+will be issued to the parties involved in the original call.
+
+ If a native agent is used, then Asterisk will send an appropriate
+notification message to the calling party to alert it that it may now
+attempt its recall. How this is presented to the caller is dependent
+upon the protocol and equipment that the caller is using. It is
+possible that the calling party's phone will ring and a recall will
+be triggered upon answering the phone, or it may be that the user
+has a specific button that he may press to initiate a recall.
+
+\subsection{If the Caller is unavailable}
+
+ When the called party has become available, it is possible that
+when Asterisk attempts to alert the calling party of the called party's
+availability, the calling party itself will have become unavailable.
+If this is the case, then Asterisk will suspend monitoring of the
+called party and will instead monitor the availability of the calling
+party. The monitoring procedure for the calling party is the same
+as is used in the section "Monitoring the Called Party." In other
+words, the method by which the calling party is monitored is dependent
+upon the type of agent used by the caller.
+
+ Once Asterisk has determined that the calling party has become
+available again, Asterisk will then move back to the process used
+in the section "Monitoring the Called Party."
+
+\subsection{The CC recall}
+
+ The calling party will make its recall to the same extension
+that was dialed. Asterisk will provide a channel variable,
+CC\_INTERFACES, to be used as an argument to the Dial application
+for CC recalls. It is strongly recommended that you use this
+channel variable during a CC recall. Listed are two reasons:
+
+\begin{itemize}
+\item The dialplan may be written in such a way that the dialed
+destintations are dynamically generated. With such a dialplan, it
+cannot be guaranteed that the same interfaces will be recalled.
+\item For calling destinations with native CC monitors, it may be
+necessary to dial a special string in order to notify the channel
+driver that the number being dialed is actually part of a CC recall.
+\end{itemize}
+
+ Note that even if your call gets routed through local channels,
+the CC\_INTERFACES variable will be populated with the appropriate
+values for that specific extension.
+ When the called parties are dialed, it is expected that a called
+party will answer, since Asterisk had previously determined that the
+party was available. However, it is possible that the called party
+may choose not to respond to the call, or he could have become busy
+again. In such a situation, the calling party must re-request CC if
+he wishes to still be alerted when the calling party has become
+available.
+
+\section{Miscellaneous Information and Tips}
+
+\begin{itemize}
+\item Be aware when using a generic agent that the max\_cc\_agents
+configuration parameter is ignored. The main driving reason for
+this is that the mechanism for cancelling CC when using a generic
+agent would become much more potentially confusing to execute. By
+limiting a calling party to having a single request, there is only
+ever a single request to be cancelled, making the process simple.
+
+\item Keep in mind that no matter what CC agent type is being used,
+a CC request can only be made for the latest call issued.
+
+\item If available timers are running on multiple called parties,
+it is possible that one of the timers may expire before the others
+do. If such a situation occurs, then the interface on which the
+timer expired will cease to be monitored. If, though, one of the
+other called parties becomes available before his available timer
+expires, the called party whose available timer had previously
+expired will still be included in the CC\_INTERFACES channel
+variable on the recall.
+
+\item It is strongly recommended that lots of thought is placed
+into the settings of the CC timers. Our general recommendation is
+that timers for phones should be set shorter than those for trunks.
+The reason for this is that it makes it less likely for a link in
+the middle of a network to cause CC to fail.
+
+\item CC can potentially be a memory hog if used irresponsibly. The
+following are recommendations to help curb the amount of resources
+required by the CC engine. First, limit the maximum number of
+CC requests in the system using the cc\_max\_requests option in
+ccss.conf. Second, set the cc\_offer\_timer low for your callers. Since
+it is likely that most calls will not result in a CC request, it is
+a good idea to set this value to something low so that information
+for calls does not stick around in memory for long. The final thing
+that can be done is to conditionally set the cc\_agent\_policy to
+"never" using the CALLCOMPLETION dialplan function. By doing this,
+no CC information will be kept around after the call completes.
+
+\item It is possible to request CCNR on answered calls. The reason
+for this is that it is impossible to know whether a call that is
+answered has actually been answered by a person or by something
+such as voicemail or some other IVR.
+
+\item Not all channel drivers have had the ability to set CC config
+parameters in their configuration files added yet. At the time of
+this writing (2009 Oct), only chan\_sip has had this ability added, with
+short-term plans to add this to chan\_dahdi as well. It is
+possible to set CC configuration parameters for other channel types,
+though. For these channel types, the setting of the parameters can
+only be accomplished using the CALLCOMPLETION dialplan function.
+
+\item It is documented in many places that generic agents and monitors
+can only be used for phones. In most cases, however, Asterisk has no
+way of distinguishing between a phone and a trunk itself. The result
+is that Asterisk will happily let you violate the advice given and
+allow you to set up a trunk with a generic monitor or agent. While this
+will not cause anything catastrophic to occur, the behavior will most
+definitely not be what you want.
+
+\item At the time of this writing (2009 Oct), Asterisk is the only
+known SIP stack to write an implementation of
+draft-ietf-bliss-call-completion-04. As a result, it is recommended
+that for your SIP phones, use a generic agent and monitor. For SIP
+trunks, you will only be able to use CC if the other end is
+terminated by another Asterisk server running version 1.8 or later.
+
+\item If the Dial application is called multiple times by a single
+extension, CC will only be offered to the caller for the parties called
+by the first instantiation of Dial.
+
+\item If a phone forwards a call, then CC may only be requested for
+the phone that executed the call forward. CC may not be requested
+for the phone to which the call was forwarded.
+
+\item CC is currently only supported by the Dial application. Queue,
+Followme, and Page do not support CC because it is not particularly
+useful for those applications.
+
+\item Generic CC relies heavily on accurate device state reporting. In
+particular, when using SIP phones it is vital to be sure that device
+state is updated properly when using them. In order to facilitate proper
+device state handling, be sure to set callcounter=yes for all peers and
+to set limitonpeers=yes in the general section of sip.conf
+
+\item When using SIP CC (i.e. native CC over SIP), it is important that
+your minexpiry and maxexpiry values allow for available timers to run
+as little or as long as they are configured. When an Asterisk server
+requests call completion over SIP, it sends a SUBSCRIBE message with
+an Expires header set to the number of seconds that the available
+timer should run. If the Asterisk server that receives this SUBSCRIBE
+has a maxexpiry set lower than what is in the received Expires header,
+then the available timer will only run for maxexpiry seconds.
+
+\item As with all Asterisk components, CC is not perfect. If you should
+find a bug or wish to enhance the feature, please open an issue on
+https://issues.asterisk.org. If writing an enhancement, please be sure
+to include a patch for the enhancement, or else the issue will be
+closed.
+
+\end{itemize}
+
+\section{Simple Example of generic call completion}
+
+The following is an incredibly bare-bones example sip.conf
+and dialplan to show basic usage of generic call completion.
+It is likely that if you have a more complex setup, you will
+need to make use of items like the CALLCOMPLETION dialplan
+function or the CC\_INTERFACES channel variable.
+
+First, let's establish a very simple sip.conf to use for this
+
+\begin{verbatim}
+[Mark]
+context=phone_calls
+cc_agent_policy=generic
+cc_monitor_policy=generic
+;We will accept defaults for the rest of the cc parameters
+;We also are not concerned with other SIP details for this
+;example
+
+[Richard]
+context=phone_calls
+cc_agent_policy=generic
+cc_monitor_policy=generic
+\end{verbatim}
+
+Now, let's write a simple dialplan
+
+\begin{verbatim}
+[phone_calls]
+
+exten => 1000,1,Dial(SIP/Mark,20)
+exten => 1000,n,Hangup
+
+exten => 2000,1,Dial(SIP/Richard,20)
+exten => 2000,n,Hangup
+
+exten => 30,1,CallCompletionRequest
+exten => 30,n,Hangup
+
+exten => 31,1,CallCompletionCancel
+exten => 31,n,Hangup
+\end{verbatim}
+
+\begin{itemize}
+\item Scenario 1:
+Mark picks up his phone and dials Richard by dialing 2000. Richard is
+currently on a call, so Mark hears a busy signal. Mark then hangs up,
+picks up the phone and dials 30 to call the CallCompletionRequest
+application. After some time, Richard finishes his call and hangs up.
+Mark is automatically called back by Asterisk. When Mark picks up his
+phone, Asterisk will dial extension 2000 for him.
+
+\item Scenario 2:
+Richard picks up his phone and dials Mark by dialing 1000. Mark has stepped
+away from his desk, and so he is unable to answer the phone within the
+20 second dial timeout. Richard hangs up, picks the phone back up and then
+dials 30 to request call completion. Mark gets back to his desk and dials
+somebody's number. When Mark finishes the call, Asterisk detects that Mark's
+phone has had some activity and has become available again and rings Richard's
+phone. Once Richard picks up, Asterisk automatically dials exteision 1000 for
+him.
+
+\item Scenario 3:
+Much like scenario 1, Mark calls Richard and Richard is busy. Mark hangs up,
+picks the phone back up and then dials 30 to request call completion. After
+a little while, Mark realizes he doesn't actually need to talk to Richard, so
+he dials 31 to cancel call completion. When Richard becomes free, Mark will
+not automatically be redialed by Asterisk.
+
+\item Scenario 4:
+Richard calls Mark, but Mark is busy. About thirty seconds later, Richard decides
+that he should perhaps request call completion. However, since Richard's phone
+has the default cc\_offer\_timer of 20 seconds, he has run out of time to
+request call completion. He instead must attempt to dial Mark again manually. If
+Mark is still busy, Richard can attempt to request call completion on this second
+call instead.
+
+\item Scenario 5:
+Mark calls Richard, and Richard is busy. Mark requests call completion. Richard
+does not finish his current call for another 2 hours (7200 seconds). Since Mark
+has the default ccbs\_available\_timer of 4800 seconds set, Mark will not be
+automatically recalled by Asterisk when Richard finishes his call.
+
+\item Scenario 6:
+Mark calls Richard, and Richard does not respond within the 20 second dial timeout.
+Mark requests call completion. Richard does not use his phone again for another
+4 hours (144000 seconds). Since Mark has the default ccnr\_available\_timer
+of 7200 seconds set, Mark will not be automatically recalled by Asterisk when
+Richard finishes his call.
+\end{itemize}
diff --git a/funcs/func_callcompletion.c b/funcs/func_callcompletion.c
new file mode 100644
index 000000000..191667bb0
--- /dev/null
+++ b/funcs/func_callcompletion.c
@@ -0,0 +1,114 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2010, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Call Completion Supplementary Services implementation
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/ccss.h"
+#include "asterisk/pbx.h"
+
+/*** DOCUMENTATION
+ <function name="CALLCOMPLETION" language="en_US">
+ <synopsis>
+ Get or set a call completion configuration parameter for a channel.
+ </synopsis>
+ <syntax>
+ <parameter name="option" required="true">
+ <para>The allowable options are:</para>
+ <enumlist>
+ <enum name="cc_agent_policy" />
+ <enum name="cc_monitor_policy" />
+ <enum name="cc_offer_timer" />
+ <enum name="ccnr_available_timer" />
+ <enum name="ccbs_available_timer" />
+ <enum name="cc_recall_timer" />
+ <enum name="cc_max_agents" />
+ <enum name="cc_max_monitors" />
+ <enum name="cc_callback_macro" />
+ <enum name="cc_agent_dialstring" />
+ </enumlist>
+ </parameter>
+ </syntax>
+ <description>
+ <para>The CALLCOMPLETION function can be used to get or set a call
+ completion configuration parameter for a channel. Note that setting
+ a configuration parameter will only change the parameter for the
+ duration of the call.</para>
+ </description>
+ </function>
+ ***/
+
+static int acf_cc_read(struct ast_channel *chan, const char *name, char *data,
+ char *buf, size_t buf_len)
+{
+ struct ast_cc_config_params *cc_params;
+ int res;
+
+ ast_channel_lock(chan);
+ if (!(cc_params = ast_channel_get_cc_config_params(chan))) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ res = ast_cc_get_param(cc_params, data, buf, buf_len);
+ ast_channel_unlock(chan);
+ return res;
+}
+
+static int acf_cc_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ struct ast_cc_config_params *cc_params;
+ int res;
+
+ ast_channel_lock(chan);
+ if (!(cc_params = ast_channel_get_cc_config_params(chan))) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ res = ast_cc_set_param(cc_params, data, value);
+ ast_channel_unlock(chan);
+ return res;
+}
+
+static struct ast_custom_function cc_function = {
+ .name = "CALLCOMPLETION",
+ .read = acf_cc_read,
+ .write = acf_cc_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&cc_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&cc_function) == 0 ? AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Control Configuration Function");
diff --git a/include/asterisk/ccss.h b/include/asterisk/ccss.h
new file mode 100644
index 000000000..c2d7ec850
--- /dev/null
+++ b/include/asterisk/ccss.h
@@ -0,0 +1,1582 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2010, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Call Completion Supplementary Services API
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#ifndef _ASTERISK_CCSS_H
+#define _ASTERISK_CCSS_H
+
+#include "asterisk.h"
+
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+
+enum ast_cc_service_type {
+ /* No Service available/requested */
+ AST_CC_NONE,
+ /* Call Completion Busy Subscriber */
+ AST_CC_CCBS,
+ /* Call Completion No Response */
+ AST_CC_CCNR,
+ /* Call Completion Not Logged In (currently SIP only) */
+ AST_CC_CCNL,
+};
+
+/*!
+ * \since 1.8
+ * \brief The various possibilities for cc_agent_policy values
+ */
+enum ast_cc_agent_policies {
+ /*! Never offer CCSS to the caller */
+ AST_CC_AGENT_NEVER,
+ /*! Offer CCSS using native signaling */
+ AST_CC_AGENT_NATIVE,
+ /*! Use generic agent for caller */
+ AST_CC_AGENT_GENERIC,
+};
+
+/*!
+ * \brief agent flags that can alter core behavior
+ */
+enum ast_cc_agent_flags {
+ /* Some agent types allow for a caller to
+ * request CC without reaching the CC_CALLER_OFFERED
+ * state. In other words, the caller can request
+ * CC while he is still on the phone from the failed
+ * call. The generic agent is an agent which allows
+ * for this behavior.
+ */
+ AST_CC_AGENT_SKIP_OFFER = (1 << 0),
+};
+
+/*!
+ * \since 1.8
+ * \brief The various possibilities for cc_monitor_policy values
+ */
+enum ast_cc_monitor_policies {
+ /*! Never accept CCSS offers from callee */
+ AST_CC_MONITOR_NEVER,
+ /* CCSS only available if callee offers it through signaling */
+ AST_CC_MONITOR_NATIVE,
+ /*! Always use CCSS generic monitor for callee
+ * Note that if callee offers CCSS natively, we still
+ * will use a generic CCSS monitor if this is set
+ */
+ AST_CC_MONITOR_GENERIC,
+ /*! Accept native CCSS offers, but if no offer is present,
+ * use a generic CCSS monitor
+ */
+ AST_CC_MONITOR_ALWAYS,
+};
+
+/* Forward declaration. Struct is in main/ccss.c */
+struct ast_cc_config_params;
+
+/*!
+ * \since 1.8
+ * \brief Queue an AST_CONTROL_CC frame
+ *
+ * \note
+ * Since this function calls ast_queue_frame, the channel will be
+ * locked during the course of this function.
+ *
+ * \param chan The channel onto which to queue the frame
+ * \param monitor_type The type of monitor to use when CC is requested
+ * \param dialstring The dial string used to call the device
+ * \param service The type of CC service the device is willing to offer
+ * \param private_data If a native monitor is being used, and some channel-driver-specific private
+ * data has been allocated, then this parameter should contain a pointer to that data. If using a generic
+ * monitor, this parameter should remain NULL. Note that if this function should fail at some point,
+ * it is the responsibility of the caller to free the private data upon return.
+ * \retval 0 Success
+ * \retval -1 Error
+ */
+int ast_queue_cc_frame(struct ast_channel *chan, const char * const monitor_type,
+ const char * const dialstring, enum ast_cc_service_type service, void *private_data);
+
+/*!
+ * \brief Allocate and initialize an ast_cc_config_params structure
+ *
+ * \note
+ * Reasonable default values are chosen for the parameters upon allocation.
+ *
+ * \retval NULL Unable to allocate the structure
+ * \retval non-NULL A pointer to the newly allocated and initialized structure
+ */
+struct ast_cc_config_params *__ast_cc_config_params_init(const char *file, int line, const char *function);
+
+/*!
+ * \brief Allocate and initialize an ast_cc_config_params structure
+ *
+ * \note
+ * Reasonable default values are chosen for the parameters upon allocation.
+ *
+ * \retval NULL Unable to allocate the structure
+ * \retval non-NULL A pointer to the newly allocated and initialized structure
+ */
+#define ast_cc_config_params_init() __ast_cc_config_params_init(__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+/*!
+ * \brief Free memory from CCSS configuration params
+ *
+ * \note
+ * Just a call to ast_free for now...
+ *
+ * \param params Pointer to structure whose memory we need to free
+ * \retval void
+ */
+void ast_cc_config_params_destroy(struct ast_cc_config_params *params);
+
+/*!
+ * \brief set a CCSS configuration parameter, given its name
+ *
+ * \note
+ * Useful when parsing config files when used in conjunction
+ * with ast_ccss_is_cc_config_param.
+ *
+ * \param params The parameter structure to set the value on
+ * \param name The name of the cc parameter
+ * \param value The value of the parameter
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_cc_set_param(struct ast_cc_config_params *params, const char * const name,
+ const char * value);
+
+/*!
+ * \brief get a CCSS configuration parameter, given its name
+ *
+ * \note
+ * Useful when reading input as a string, like from dialplan or
+ * manager.
+ *
+ * \param params The CCSS configuration from which to get the value
+ * \param name The name of the CCSS parameter we want
+ * \param buf A preallocated buffer to hold the value
+ * \param buf_len The size of buf
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_cc_get_param(struct ast_cc_config_params *params, const char * const name,
+ char *buf, size_t buf_len);
+
+/*!
+ * \since 1.8
+ * \brief Is this a CCSS configuration parameter?
+ * \param name Name of configuration option being parsed.
+ * \retval 1 Yes, this is a CCSS configuration parameter.
+ * \retval 0 No, this is not a CCSS configuration parameter.
+ */
+int ast_cc_is_config_param(const char * const name);
+
+/*!
+ * \since 1.8
+ * \brief copy CCSS configuration parameters from one structure to another
+ *
+ * \details
+ * For now, this is a simple memcpy, but this function is necessary since
+ * the size of an ast_cc_config_params structure is unknown outside of
+ * main/ccss.c. Also, this allows for easier expansion of the function in
+ * case it becomes more complex than just a memcpy.
+ *
+ * \param src The structure from which data is copied
+ * \param dest The structure to which data is copied
+ * \retval -1 Copy failed (no way for this to happen yet)
+ * \retval 0 Copy succeeded
+ */
+void ast_cc_copy_config_params(struct ast_cc_config_params *dest, const struct ast_cc_config_params *src);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_agent_policy
+ * \param config The configuration to retrieve the policy from
+ * \return The current cc_agent_policy for this configuration
+ */
+enum ast_cc_agent_policies ast_get_cc_agent_policy(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_agent_policy
+ * \param config The configuration to set the cc_agent_policy on
+ * \param value The new cc_agent_policy we want to change to
+ * \retval 0 Success
+ * \retval -1 Failure (likely due to bad input)
+ */
+int ast_set_cc_agent_policy(struct ast_cc_config_params *config, enum ast_cc_agent_policies value);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_monitor_policy
+ * \param config The configuration to retrieve the cc_monitor_policy from
+ * \return The cc_monitor_policy retrieved from the configuration
+ */
+enum ast_cc_monitor_policies ast_get_cc_monitor_policy(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_monitor_policy
+ * \param config The configuration to set the cc_monitor_policy on
+ * \param value The new cc_monitor_policy we want to change to
+ * \retval 0 Success
+ * \retval -1 Failure (likely due to bad input)
+ */
+int ast_set_cc_monitor_policy(struct ast_cc_config_params *config, enum ast_cc_monitor_policies value);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_offer_timer
+ * \param config The configuration to retrieve the cc_offer_timer from
+ * \return The cc_offer_timer from this configuration
+ */
+unsigned int ast_get_cc_offer_timer(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_offer_timer
+ * \param config The configuration to set the cc_offer_timer on
+ * \param value The new cc_offer_timer we want to change to
+ * \retval void
+ */
+void ast_set_cc_offer_timer(struct ast_cc_config_params *config, unsigned int value);
+
+/*!
+ * \since 1.8
+ * \brief Get the ccnr_available_timer
+ * \param config The configuration to retrieve the ccnr_available_timer from
+ * \return The ccnr_available_timer from this configuration
+ */
+unsigned int ast_get_ccnr_available_timer(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the ccnr_available_timer
+ * \param config The configuration to set the ccnr_available_timer on
+ * \param value The new ccnr_available_timer we want to change to
+ * \retval void
+ */
+void ast_set_ccnr_available_timer(struct ast_cc_config_params *config, unsigned int value);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_recall_timer
+ * \param config The configuration to retrieve the cc_recall_timer from
+ * \return The cc_recall_timer from this configuration
+ */
+unsigned int ast_get_cc_recall_timer(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_recall_timer
+ * \param config The configuration to set the cc_recall_timer on
+ * \param value The new cc_recall_timer we want to change to
+ * \retval void
+ */
+void ast_set_cc_recall_timer(struct ast_cc_config_params *config, unsigned int value);
+
+/*!
+ * \since 1.8
+ * \brief Get the ccbs_available_timer
+ * \param config The configuration to retrieve the ccbs_available_timer from
+ * \return The ccbs_available_timer from this configuration
+ */
+unsigned int ast_get_ccbs_available_timer(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the ccbs_available_timer
+ * \param config The configuration to set the ccbs_available_timer on
+ * \param value The new ccbs_available_timer we want to change to
+ * \retval void
+ */
+void ast_set_ccbs_available_timer(struct ast_cc_config_params *config, unsigned int value);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_agent_dialstring
+ * \param config The configuration to retrieve the cc_agent_dialstring from
+ * \return The cc_agent_dialstring from this configuration
+ */
+const char *ast_get_cc_agent_dialstring(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_agent_dialstring
+ * \param config The configuration to set the cc_agent_dialstring on
+ * \param value The new cc_agent_dialstring we want to change to
+ * \retval void
+ */
+void ast_set_cc_agent_dialstring(struct ast_cc_config_params *config, const char *const value);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_max_agents
+ * \param config The configuration to retrieve the cc_max_agents from
+ * \return The cc_max_agents from this configuration
+ */
+unsigned int ast_get_cc_max_agents(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_max_agents
+ * \param config The configuration to set the cc_max_agents on
+ * \param value The new cc_max_agents we want to change to
+ * \retval void
+ */
+void ast_set_cc_max_agents(struct ast_cc_config_params *config, unsigned int value);
+
+/*!
+ * \since 1.8
+ * \brief Get the cc_max_monitors
+ * \param config The configuration to retrieve the cc_max_monitors from
+ * \return The cc_max_monitors from this configuration
+ */
+unsigned int ast_get_cc_max_monitors(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the cc_max_monitors
+ * \param config The configuration to set the cc_max_monitors on
+ * \param value The new cc_max_monitors we want to change to
+ * \retval void
+ */
+void ast_set_cc_max_monitors(struct ast_cc_config_params *config, unsigned int value);
+
+/*!
+ * \since 1.8
+ * \brief Get the name of the callback_macro
+ * \param config The configuration to retrieve the callback_macro from
+ * \return The callback_macro name
+ */
+const char *ast_get_cc_callback_macro(struct ast_cc_config_params *config);
+
+/*!
+ * \since 1.8
+ * \brief Set the callback_macro name
+ * \param config The configuration to set the callback_macro on
+ * \param value The new callback macro we want to change to
+ * \retval void
+ */
+void ast_set_cc_callback_macro(struct ast_cc_config_params *config, const char * const value);
+
+/* END CONFIGURATION FUNCTIONS */
+
+/* BEGIN AGENT/MONITOR REGISTRATION API */
+
+struct ast_cc_monitor_callbacks;
+
+/*!
+ * \since 1.8
+ * \brief Register a set of monitor callbacks with the core
+ *
+ * \details
+ * This is made so that at monitor creation time, the proper callbacks
+ * may be installed and the proper .init callback may be called for the
+ * monitor to establish private data.
+ *
+ * \param callbacks The callbacks used by the monitor implementation
+ * \retval 0 Successfully registered
+ * \retval -1 Failure to register
+ */
+int ast_cc_monitor_register(const struct ast_cc_monitor_callbacks *callbacks);
+
+/*!
+ * \since 1.8
+ * \brief Unregister a set of monitor callbacks with the core
+ *
+ * \details
+ * If a module which makes use of a CC monitor is unloaded, then it may
+ * unregister its monitor callbacks with the core.
+ *
+ * \param callbacks The callbacks used by the monitor implementation
+ * \retval 0 Successfully unregistered
+ * \retval -1 Failure to unregister
+ */
+void ast_cc_monitor_unregister(const struct ast_cc_monitor_callbacks *callbacks);
+
+struct ast_cc_agent_callbacks;
+
+/*!
+ * \since 1.8
+ * \brief Register a set of agent callbacks with the core
+ *
+ * \details
+ * This is made so that at agent creation time, the proper callbacks
+ * may be installed and the proper .init callback may be called for the
+ * monitor to establish private data.
+ *
+ * \param callbacks The callbacks used by the agent implementation
+ * \retval 0 Successfully registered
+ * \retval -1 Failure to register
+ */
+int ast_cc_agent_register(const struct ast_cc_agent_callbacks *callbacks);
+
+/*!
+ * \since 1.8
+ * \brief Unregister a set of agent callbacks with the core
+ *
+ * \details
+ * If a module which makes use of a CC agent is unloaded, then it may
+ * unregister its agent callbacks with the core.
+ *
+ * \param callbacks The callbacks used by the agent implementation
+ * \retval 0 Successfully unregistered
+ * \retval -1 Failure to unregister
+ */
+void ast_cc_agent_unregister(const struct ast_cc_agent_callbacks *callbacks);
+
+/* END AGENT/MONITOR REGISTRATION API */
+
+/* BEGIN SECTION ON MONITORS AND MONITOR CALLBACKS */
+
+/*!
+ * It is recommended that monitors use a pointer to
+ * an ast_cc_monitor_callbacks::type when creating
+ * an AST_CONTROL_CC frame. Since the generic monitor
+ * callbacks are opaque and channel drivers will wish
+ * to use that, this string is made globally available
+ * for all to use
+ */
+#define AST_CC_GENERIC_MONITOR_TYPE "generic"
+
+/*!
+ * Used to determine which type
+ * of monitor an ast_cc_device_monitor
+ * is.
+ */
+enum ast_cc_monitor_class {
+ AST_CC_DEVICE_MONITOR,
+ AST_CC_EXTENSION_MONITOR,
+};
+
+/*!
+ * \internal
+ * \brief An item in a CC interface tree.
+ *
+ * These are the individual items in an interface tree.
+ * The key difference between this structure and the ast_cc_interface
+ * is that this structure contains data which is intrinsic to the item's
+ * placement in the tree, such as who its parent is.
+ */
+struct ast_cc_monitor {
+ /*!
+ * Information regarding the interface.
+ */
+ struct ast_cc_interface *interface;
+ /*!
+ * Every interface has an id that uniquely identifies it. It is
+ * formed by incrementing a counter.
+ */
+ unsigned int id;
+ /*!
+ * The ID of this monitor's parent. If this monitor is at the
+ * top of the tree, then his parent will be 0.
+ */
+ unsigned int parent_id;
+ /*!
+ * The instance of the CC core to which this monitor belongs
+ */
+ int core_id;
+ /*!
+ * The type of call completion service offered by a device.
+ */
+ enum ast_cc_service_type service_offered;
+ /*!
+ * \brief Name that should be used to recall specified interface
+ *
+ * \details
+ * When issuing a CC recall, some technologies will require
+ * that a name other than the device name is dialed. For instance,
+ * with SIP, a specific URI will be used which chan_sip will be able
+ * to recognize as being a CC recall. Similarly, ISDN will need a specific
+ * dial string to know that the call is a recall.
+ */
+ char *dialstring;
+ /*!
+ * The ID of the available timer used by the current monitor
+ */
+ int available_timer_id;
+ /*!
+ * Monitor callbacks
+ */
+ const struct ast_cc_monitor_callbacks *callbacks;
+ /*!
+ * \brief Data that is private to a monitor technology
+ *
+ * Most channel drivers that implement CC monitors will have to
+ * allocate data that the CC core does not care about but which
+ * is vital to the operation of the monitor. This data is stored
+ * in this pointer so that the channel driver may use it as
+ * needed
+ */
+ void *private_data;
+ AST_LIST_ENTRY(ast_cc_monitor) next;
+};
+
+/*!
+ * \brief Callbacks defined by CC monitors
+ *
+ * \note
+ * Every callback is called with the list of monitors locked. There
+ * are several public API calls that also will try to lock this lock.
+ * These public functions have a note in their doxygen stating so.
+ * As such, pay attention to the lock order you establish in these callbacks
+ * to ensure that you do not violate the lock order when calling
+ * the functions in this file with lock order notices.
+ */
+struct ast_cc_monitor_callbacks {
+ /*!
+ * \brief Type of monitor the callbacks belong to.
+ *
+ * \note
+ * Examples include "generic" and "SIP"
+ */
+ const char *type;
+ /*!
+ * \brief Request CCSS.
+ *
+ * \param monitor CC core monitor control.
+ * \param available_timer_id The scheduler ID for the available timer.
+ * Will never be NULL for a device monitor.
+ *
+ * \details
+ * Perform whatever steps are necessary in order to request CC.
+ * In addition, the monitor implementation is responsible for
+ * starting the available timer in this callback.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+ int (*request_cc)(struct ast_cc_monitor *monitor, int *available_timer_id);
+ /*!
+ * \brief Suspend monitoring.
+ *
+ * \param monitor CC core monitor control.
+ *
+ * \details
+ * Implementers must perform the necessary steps to suspend
+ * monitoring.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+ int (*suspend)(struct ast_cc_monitor *monitor);
+ /*!
+ * \brief Status response to an ast_cc_monitor_status_request().
+ *
+ * \param monitor CC core monitor control.
+ * \param devstate Current status of a Party A device.
+ *
+ * \details
+ * Alert a monitor as to the status of the agent for which
+ * the monitor had previously requested a status request.
+ *
+ * \note Zero or more responses may come as a result.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+ int (*status_response)(struct ast_cc_monitor *monitor, enum ast_device_state devstate);
+ /*!
+ * \brief Unsuspend monitoring.
+ *
+ * \param monitor CC core monitor control.
+ *
+ * \details
+ * Perform the necessary steps to unsuspend monitoring.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+ int (*unsuspend)(struct ast_cc_monitor *monitor);
+ /*!
+ * \brief Cancel the running available timer.
+ *
+ * \param monitor CC core monitor control.
+ * \param sched_id Available timer scheduler id to cancel.
+ * Will never be NULL for a device monitor.
+ *
+ * \details
+ * In most cases, this function will likely consist of just a
+ * call to AST_SCHED_DEL. It might have been possible to do this
+ * within the core, but unfortunately the mixture of sched_thread
+ * and sched usage in Asterisk prevents such usage.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+ int (*cancel_available_timer)(struct ast_cc_monitor *monitor, int *sched_id);
+ /*!
+ * \brief Destroy private data on the monitor.
+ *
+ * \param private_data The private data pointer from the monitor.
+ *
+ * \details
+ * Implementers of this callback are responsible for destroying
+ * all heap-allocated data in the monitor's private_data pointer, including
+ * the private_data itself.
+ */
+ void (*destructor)(void *private_data);
+};
+
+/*!
+ * \since 1.8
+ * \brief Scheduler callback for available timer expiration
+ *
+ * \note
+ * When arming the available timer from within a device monitor, you MUST
+ * use this function as the callback for the scheduler.
+ *
+ * \param data A reference to the CC monitor on which the timer was running.
+ */
+int ast_cc_available_timer_expire(const void *data);
+
+/* END SECTION ON MONITORS AND MONITOR CALLBACKS */
+
+/* BEGIN API FOR IN-CALL CC HANDLING */
+
+/*!
+ * \since 1.8
+ *
+ * \brief Mark the channel to ignore further CC activity.
+ *
+ * \details
+ * When a CC-capable application, such as Dial, has finished
+ * with all CC processing for a channel and knows that any further
+ * CC processing should be ignored, this function should be called.
+ *
+ * \param chan The channel for which further CC processing should be ignored.
+ * \retval void
+ */
+void ast_ignore_cc(struct ast_channel *chan);
+
+/*!
+ * \since 1.8
+ *
+ * \brief Properly react to a CC control frame.
+ *
+ * \details
+ * When a CC-capable application, such as Dial, receives a frame
+ * of type AST_CONTROL_CC, then it may call this function in order
+ * to have the device which sent the frame added to the tree of interfaces
+ * which is kept on the inbound channel.
+ *
+ * \param inbound The inbound channel
+ * \param outbound The outbound channel (The one from which the CC frame was read)
+ * \param frame_data The ast_frame's data.ptr field.
+ * \retval void
+ */
+void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data);
+
+/*!
+ * \since 1.8
+ *
+ * \brief Start the CC process on a call.
+ *
+ * \details
+ * Whenever a CC-capable application, such as Dial, wishes to
+ * engage in CC activity, it initiates the process by calling this
+ * function. If the CC core should discover that a previous application
+ * has called ast_ignore_cc on this channel or a "parent" channel, then
+ * the value of the ignore_cc integer passed in will be set nonzero.
+ *
+ * The ignore_cc parameter is a convenience parameter. It can save an
+ * application the trouble of trying to call CC APIs when it knows that
+ * it should just ignore further attempts at CC actions.
+ *
+ * \param chan The inbound channel calling the CC-capable application.
+ * \param[out] ignore_cc Will be set non-zero if no further CC actions need to be taken
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_cc_call_init(struct ast_channel *chan, int *ignore_cc);
+
+/*!
+ * \since 1.8
+ *
+ * \brief Add a child dialstring to an extension monitor
+ *
+ * Whenever we request a channel, the parent extension monitor needs
+ * to store the dialstring of the device requested. The reason is so
+ * that we can call the device back during the recall even if we are
+ * not monitoring the device.
+ *
+ * \param incoming The caller's channel
+ * \param dialstring The dialstring used when requesting the outbound channel
+ * \param device_name The device name associated with the requested outbound channel
+ * \retval void
+ */
+void ast_cc_extension_monitor_add_dialstring(struct ast_channel *incoming, const char * const dialstring, const char * const device_name);
+
+/*!
+ * \since 1.8
+ * \brief Check if the incoming CC request is within the bounds
+ * set by the cc_max_requests configuration option
+ *
+ * \details
+ * It is recommended that an entity which receives an incoming
+ * CC request calls this function before calling
+ * ast_cc_agent_accept_request. This way, immediate feedback can be
+ * given to the caller about why his request was rejected.
+ *
+ * If this is not called and a state change to CC_CALLER_REQUESTED
+ * is made, then the core will still not allow for the request
+ * to succeed. However, if done this way, it may not be obvious
+ * to the requestor why the request failed.
+ *
+ * \retval 0 Not within the limits. Fail.
+ * \retval non-zero Within the limits. Success.
+ */
+int ast_cc_request_is_within_limits(void);
+
+/*!
+ * \since 1.8
+ * \brief Get the core id for the current call
+ *
+ * \details
+ * The main use of this function is for channel drivers
+ * who queue an AST_CONTROL_CC frame. A channel driver may
+ * call this function in order to get the core_id for what
+ * may become a CC request. This way, when monitor functions
+ * are called which use a core_id as a means of identification,
+ * the channel driver will have saved this information.
+ *
+ * The channel given to this function may be an inbound or outbound
+ * channel. Both will have the necessary info on it.
+ *
+ * \param chan The channel from which to get the core_id.
+ * \retval core_id on success
+ * \retval -1 Failure
+ */
+int ast_cc_get_current_core_id(struct ast_channel *chan);
+
+/* END API FOR IN-CALL CC HANDLING */
+
+/*!
+ * \brief Structure with information about an outbound interface
+ *
+ * \details
+ * This structure is first created when an outbound interface indicates that
+ * it is capable of accepting a CC request. It is stored in a "tree" on a datastore on
+ * the caller's channel. Once an agent structure is created, the agent gains
+ * a reference to the tree of interfaces. If CC is requested, then the
+ * interface tree on the agent is converted into a tree of monitors. Each
+ * monitor will contain a pointer to an individual ast_cc_interface. Finally,
+ * the tree of interfaces is also present on a second datastore during a
+ * CC recall so that the CC_INTERFACES channel variable may be properly
+ * populated.
+ */
+struct ast_cc_interface {
+ /* What class of monitor is being offered here
+ */
+ enum ast_cc_monitor_class monitor_class;
+ /*!
+ * \brief The type of monitor that should be used for this interface
+ *
+ * \details
+ * This will be something like "extension" "generic" or "SIP".
+ * This should point to a static const char *, so there is
+ * no reason to make a new copy.
+ */
+ const char *monitor_type;
+ /*!
+ * The configuration parameters used for this interface
+ */
+ struct ast_cc_config_params *config_params;
+ /* The name of the interface/extension. local channels will
+ * have 'exten@context' for a name. Other channel types will
+ * have 'tech/device' for a name.
+ */
+ char device_name[1];
+};
+
+/* BEGIN STRUCTURES FOR AGENTS */
+
+struct ast_cc_agent {
+ /*!
+ * Which instance of the core state machine does this
+ * agent pertain to?
+ */
+ unsigned int core_id;
+ /*!
+ * Callback functions needed for specific agent
+ * implementations
+ */
+ const struct ast_cc_agent_callbacks *callbacks;
+ /*!
+ * Configuration parameters that affect this
+ * agent's operation.
+ */
+ struct ast_cc_config_params *cc_params;
+ /*!
+ * \brief Flags for agent operation
+ *
+ * \details
+ * There are some attributes of certain agent types
+ * that can alter the behavior of certain CC functions.
+ * For a list of these flags, see the ast_cc_agent_flags
+ * enum
+ */
+ unsigned int flags;
+ /*! Data specific to agent implementation */
+ void *private_data;
+ /*! The name of the device which this agent
+ * represents/communicates with
+ */
+ char device_name[1];
+};
+
+struct ast_cc_agent_callbacks {
+ /*!
+ * \brief Type of agent the callbacks belong to.
+ *
+ * \note
+ * Examples are "SIP" "ISDN" and "generic"
+ */
+ const char *type;
+ /*!
+ * \brief CC agent initialization.
+ *
+ * \param agent CC core agent control.
+ * \param chan Original channel the agent will attempt to recall.
+ *
+ * \details
+ * This callback is called when the CC core
+ * is initialized. Agents should allocate
+ * any private data necessary for the
+ * call and assign it to the private_data
+ * on the agent. Additionally, if any ast_cc_agent_flags
+ * are pertinent to the specific agent type, they should
+ * be set in this function as well.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*init)(struct ast_cc_agent *agent, struct ast_channel *chan);
+ /*!
+ * \brief Start the offer timer.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This is called by the core when the caller hangs up after
+ * a call for which CC may be requested. The agent should
+ * begin the timer as configured.
+ *
+ * The primary reason why this functionality is left to
+ * the specific agent implementations is due to the differing
+ * use of schedulers throughout the code. Some channel drivers
+ * may already have a scheduler context they wish to use, and
+ * amongst those, some may use the ast_sched API while others
+ * may use the ast_sched_thread API, which are incompatible.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*start_offer_timer)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Stop the offer timer.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This callback is called by the CC core when the caller
+ * has requested CC.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*stop_offer_timer)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Acknowledge CC request.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * When the core receives knowledge that a called
+ * party has accepted a CC request, it will call
+ * this callback.
+ *
+ * The duty of this is to accept a CC request from
+ * the caller by acknowledging receipt of that request.
+ */
+ void (*ack)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Request the status of the agent's device.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * Asynchronous request for the status of any caller
+ * which may be a valid caller for the CC transaction.
+ * Status responses should be made using the
+ * ast_cc_status_response function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*status_request)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Request for an agent's phone to stop ringing.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The usefulness of this is quite limited. The only specific
+ * known case for this is if Asterisk requests CC over an ISDN
+ * PTMP link as the TE side. If other phones are in the same
+ * recall group as the Asterisk server, and one of those phones
+ * picks up the recall notice, then Asterisk will receive a
+ * "stop ringing" notification from the NT side of the PTMP
+ * link. This indication needs to be passed to the phone
+ * on the other side of the Asterisk server which originally
+ * placed the call so that it will stop ringing. Since the
+ * phone may be of any type, it is necessary to have a callback
+ * that the core can know about.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*stop_ringing)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Let the caller know that the callee has become free
+ * but that the caller cannot attempt to call back because
+ * he is either busy or there is congestion on his line.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This is something that really only affects a scenario where
+ * a phone places a call over ISDN PTMP to Asterisk, who then
+ * connects over PTMP again to the ISDN network. For most agent
+ * types, there is no need to implement this callback at all
+ * because they don't really need to actually do anything in
+ * this situation. If you're having trouble understanding what
+ * the purpose of this callback is, then you can be safe simply
+ * not implementing it.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*party_b_free)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Begin monitoring a busy device.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this callback if the callee becomes
+ * available but the caller has reported that he is busy.
+ * The agent should begin monitoring the caller's device.
+ * When the caller becomes available again, the agent should
+ * call ast_cc_agent_caller_available.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*start_monitoring)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Alert the caller that it is time to try recalling.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function when it receives notice
+ * that a monitored party has become available.
+ *
+ * The agent's job is to send a message to the caller to
+ * notify it of such a change. If the agent is able to
+ * discern that the caller is currently unavailable, then
+ * the agent should react by calling the ast_cc_caller_unavailable
+ * function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+ int (*callee_available)(struct ast_cc_agent *agent);
+ /*!
+ * \brief Destroy private data on the agent.
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function upon completion
+ * or failure of CC.
+ *
+ * \note
+ * The agent private_data pointer may be NULL if the agent
+ * constructor failed.
+ */
+ void (*destructor)(struct ast_cc_agent *agent);
+};
+
+/*!
+ * \brief Call a callback on all agents of a specific type
+ *
+ * \details
+ * Since the container of CC core instances is private, and so
+ * are the items which the container contains, we have to provide
+ * an ao2_callback-like method so that a specific agent may be
+ * found or so that an operation can be made on all agents of
+ * a particular type. The first three arguments should be familiar
+ * to anyone who has used ao2_callback. The final argument is the
+ * type of agent you wish to have the callback called on.
+ *
+ * \note Since agents are refcounted, and this function returns
+ * a reference to the agent, it is imperative that you decrement
+ * the refcount of the agent once you have finished using it.
+ *
+ * \param flags astobj2 search flags
+ * \param function an ao2 callback function to call
+ * \param arg the argument to the callback function
+ * \param type The type of agents to call the callback on
+ */
+struct ast_cc_agent *ast_cc_agent_callback(int flags, ao2_callback_fn *function, void *arg, const char * const type);
+
+/* END STRUCTURES FOR AGENTS */
+
+/* BEGIN STATE CHANGE API */
+
+/*!
+ * \since 1.8
+ * \brief Offer CC to a caller
+ *
+ * \details
+ * This function is called from ast_hangup if the caller is
+ * eligible to be offered call completion service.
+ *
+ * \param caller_chan The calling channel
+ * \retval -1 Error
+ * \retval 0 Success
+ */
+int ast_cc_offer(struct ast_channel *caller_chan);
+
+/*!
+ * \since 1.8
+ * \brief Accept inbound CC request
+ *
+ * \details
+ * When a caller requests CC, this function should be called to let
+ * the core know that the request has been accepted.
+ *
+ * \param core_id core_id of the CC transaction
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_agent_accept_request(int core_id, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Indicate that an outbound entity has accepted our CC request
+ *
+ * \details
+ * When we receive confirmation that an outbound device has accepted the
+ * CC request we sent it, this function must be called.
+ *
+ * \param core_id core_id of the CC transaction
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_monitor_request_acked(int core_id, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Indicate that the caller is busy
+ *
+ * \details
+ * When the callee makes it known that he is available, the core
+ * will let the caller's channel driver know that it may attempt
+ * to let the caller know to attempt a recall. If the channel
+ * driver can detect, though, that the caller is busy, then
+ * the channel driver should call this function to let the CC
+ * core know.
+ *
+ * \param core_id core_id of the CC transaction
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_agent_caller_busy(int core_id, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Indicate that a previously unavailable caller has become available
+ *
+ * \details
+ * If a monitor is suspended due to a caller becoming unavailable, then this
+ * function should be called to indicate that the caller has become available.
+ *
+ * \param core_id core_id of the CC transaction
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_agent_caller_available(int core_id, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Tell the CC core that a caller is currently recalling
+ *
+ * \details
+ * The main purpose of this is so that the core can alert the monitor
+ * to stop its available timer since the caller has begun its recall
+ * phase.
+ *
+ * \param core_id core_id of the CC transaction
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_agent_recalling(int core_id, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Indicate recall has been acknowledged
+ *
+ * \details
+ * When we receive confirmation that an endpoint has responded to our
+ * CC recall, we call this function.
+ *
+ * \param chan The inbound channel making the CC recall
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_completed(struct ast_channel *chan, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Indicate failure has occurred
+ *
+ * \details
+ * If at any point a failure occurs, this is the function to call
+ * so that the core can initiate cleanup procedures.
+ *
+ * \param core_id core_id of the CC transaction
+ * \param debug optional string to print for debugging purposes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_failed(int core_id, const char * const debug, ...);
+
+/*!
+ * \since 1.8
+ * \brief Indicate that a failure has occurred on a specific monitor
+ *
+ * \details
+ * If a monitor should detect that a failure has occurred when communicating
+ * with its endpoint, then ast_cc_monitor_failed should be called. The big
+ * difference between ast_cc_monitor_failed and ast_cc_failed is that ast_cc_failed
+ * indicates a global failure for a CC transaction, where as ast_cc_monitor_failed
+ * is localized to a particular monitor. When ast_cc_failed is called, the entire
+ * CC transaction is torn down. When ast_cc_monitor_failed is called, only the
+ * monitor on which the failure occurred is pruned from the tree of monitors.
+ *
+ * If there are no more devices left to monitor when this function is called,
+ * then the core will fail the CC transaction globally.
+ *
+ * \param core_id The core ID for the CC transaction
+ * \param monitor_name The name of the monitor on which the failure occurred
+ * \param debug A debug message to print to the CC log
+ * \return void
+ */
+int __attribute__((format(printf, 3, 4))) ast_cc_monitor_failed(int core_id, const char * const monitor_name, const char * const debug, ...);
+
+/* END STATE CHANGE API */
+
+/*!
+ * The following are all functions which are required due to the unique
+ * case where Asterisk is acting as the NT side of an ISDN PTMP
+ * connection to the caller and as the TE side of an ISDN PTMP connection
+ * to the callee. In such a case, there are several times where the
+ * PTMP monitor needs information from the agent in order to formulate
+ * the appropriate messages to send.
+ */
+
+/*!
+ * \brief Request the status of a caller or callers.
+ *
+ * \details
+ * When an ISDN PTMP monitor senses that the callee has become
+ * available, it needs to know the current status of the caller
+ * in order to determine the appropriate response to send to
+ * the caller. In order to do this, the monitor calls this function.
+ * Responses will arrive asynchronously.
+ *
+ * \note Zero or more responses may come as a result.
+ *
+ * \param core_id The core ID of the CC transaction
+ *
+ * \retval 0 Successfully requested status
+ * \retval -1 Failed to request status
+ */
+int ast_cc_monitor_status_request(int core_id);
+
+/*!
+ * \brief Response with a caller's current status
+ *
+ * \details
+ * When an ISDN PTMP monitor requests the caller's status, the
+ * agent must respond to the request using this function. For
+ * simplicity it is recommended that the devstate parameter
+ * be one of AST_DEVICE_INUSE or AST_DEVICE_NOT_INUSE.
+ *
+ * \param core_id The core ID of the CC transaction
+ * \param devstate The current state of the caller to which the agent pertains
+ * \retval 0 Successfully responded with our status
+ * \retval -1 Failed to respond with our status
+ */
+int ast_cc_agent_status_response(int core_id, enum ast_device_state devstate);
+
+/*!
+ * \brief Alert a caller to stop ringing
+ *
+ * \details
+ * When an ISDN PTMP monitor becomes available, it is assumed
+ * that the agent will then cause the caller's phone to ring. In
+ * some cases, this is literally what happens. In other cases, it may
+ * be that the caller gets a visible indication on his phone that he
+ * may attempt to recall the callee. If multiple callers are recalled
+ * (since it may be possible to have a group of callers configured as
+ * a single party A), and one of those callers picks up his phone, then
+ * the ISDN PTMP monitor will alert the other callers to stop ringing.
+ * The agent's stop_ringing callback will be called, and it is up to the
+ * agent's driver to send an appropriate message to make his caller
+ * stop ringing.
+ *
+ * \param core_id The core ID of the CC transaction
+ * \retval 0 Successfully requested for the phone to stop ringing
+ * \retval -1 Could not request for the phone to stop ringing
+ */
+int ast_cc_monitor_stop_ringing(int core_id);
+
+/*!
+ * \brief Alert a caller that though the callee has become free, the caller
+ * himself is not and may not call back.
+ *
+ * \details
+ * When an ISDN PTMP monitor senses that his monitored party has become
+ * available, he will request the status of the called party. If he determines
+ * that the caller is currently not available, then he will call this function
+ * so that an appropriate message is sent to the caller.
+ *
+ * Yes, you just read that correctly. The callee asks the caller what his
+ * current status is, and if the caller is currently unavailable, the monitor
+ * must send him a message anyway. WTF?
+ *
+ * This function results in the agent's party_b_free callback being called.
+ * It is most likely that you will not need to actually implement the
+ * party_b_free callback in an agent because it is not likely that you will
+ * need to or even want to send a caller a message indicating the callee's
+ * status if the caller himself is not also free.
+ *
+ * \param core_id The core ID of the CC transaction
+ * \retval 0 Successfully alerted the core that party B is free
+ * \retval -1 Could not alert the core that party B is free
+ */
+int ast_cc_monitor_party_b_free(int core_id);
+
+/* BEGIN API FOR USE WITH/BY MONITORS */
+
+/*!
+ * \since 1.8
+ * \brief Return the number of outstanding CC requests to a specific device
+ *
+ * \note
+ * This function will lock the list of monitors stored on every instance of
+ * the CC core. Callers of this function should be aware of this and avoid
+ * any potential lock ordering problems.
+ *
+ * \param name The name of the monitored device
+ * \param type The type of the monitored device (e.g. "generic")
+ * \return The number of CC requests for the monitor
+ */
+int ast_cc_monitor_count(const char * const name, const char * const type);
+
+/*!
+ * \since 1.8
+ * \brief Alert the core that a device being monitored has become available.
+ *
+ * \note
+ * The code in the core will take care of making sure that the information gets passed
+ * up the ladder correctly.
+ *
+ * \param core_id The core ID of the corresponding CC transaction
+ * \retval 0 Request successfully queued
+ * \retval -1 Request could not be queued
+ */
+int __attribute__((format(printf, 2, 3))) ast_cc_monitor_callee_available(const int core_id, const char * const debug, ...);
+
+/* END API FOR USE WITH/BY MONITORS */
+
+/* BEGIN API TO BE USED ON CC RECALL */
+
+/*!
+ * \since 1.8
+ * \brief Set up a CC recall datastore on a channel
+ *
+ * \details
+ * Implementers of protocol-specific CC agents will need to call this
+ * function in order for the channel to have the necessary interfaces
+ * to recall.
+ *
+ * This function must be called by the implementer once it has been detected
+ * that an inbound call is a cc_recall. After allocating the channel, call this
+ * function, followed by ast_cc_set_cc_interfaces_chanvar. While it would be nice to
+ * be able to have the core do this automatically, it just cannot be done given
+ * the current architecture.
+ */
+int ast_setup_cc_recall_datastore(struct ast_channel *chan, const int core_id);
+
+/*!
+ * \since 1.8
+ * \brief Decide if a call to a particular channel is a CC recall
+ *
+ * \details
+ * When a CC recall happens, it is important on the called side to
+ * know that the call is a CC recall and not a normal call. This function
+ * will determine first if the call in question is a CC recall. Then it
+ * will determine based on the chan parameter if the channel is being
+ * called is being recalled.
+ *
+ * As a quick example, let's say a call is placed to SIP/1000 and SIP/1000
+ * is currently on the phone. The caller requests CCBS. SIP/1000 finishes
+ * his call, and so the caller attempts to recall. Now, the dialplan
+ * administrator has set up this second call so that not only is SIP/1000
+ * called, but also SIP/2000 is called. If SIP/1000's channel were passed
+ * to this function, the return value would be non-zero, but if SIP/2000's
+ * channel were passed into this function, then the return would be 0 since
+ * SIP/2000 was not one of the original devices dialed.
+ *
+ * \note
+ * This function may be called on a calling channel as well to
+ * determine if it is part of a CC recall.
+ *
+ * \note
+ * This function will lock the channel as well as the list of monitors
+ * on the channel datastore, though the locks are not held at the same time. Be
+ * sure that you have no potential lock order issues here.
+ *
+ * \param chan The channel to check
+ * \param core_id[out] If this is a valid CC recall, the core_id of the failed call
+ * will be placed in this output parameter
+ * \param monitor_type Clarify which type of monitor type we are looking for if this
+ * is happening on a called channel. For incoming channels, this parameter is not used.
+ * \retval 0 Either this is not a recall or it is but this channel is not part of the recall
+ * \retval non-zero This is a recall and the channel in question is directly involved.
+ */
+int ast_cc_is_recall(struct ast_channel *chan, int *core_id, const char * const monitor_type);
+
+/*!
+ * \since 1.8
+ * \brief Get the associated monitor given the device name and core_id
+ *
+ * \details
+ * The function ast_cc_is_recall is helpful for determining if a call to
+ * a specific channel is a recall. However, once you have determined that
+ * this is a recall, you will most likely need access to the private data
+ * within the associated monitor. This function is what one uses to get
+ * that monitor.
+ *
+ * \note
+ * This function locks the list of monitors that correspond to the core_id
+ * passed in. Be sure that you have no potential lock order issues when
+ * calling this function.
+ *
+ * \param core_id The core ID to which this recall corresponds. This likely will
+ * have been obtained using the ast_cc_is_recall function
+ * \param device_name Which device to find the monitor for.
+ *
+ * \retval NULL Appropriate monitor does not exist
+ * \retval non-NULL The monitor to use for this recall
+ */
+struct ast_cc_monitor *ast_cc_get_monitor_by_recall_core_id(const int core_id, const char * const device_name);
+
+/*!
+ * \since 1.8
+ * \brief Set the first level CC_INTERFACES channel variable for a channel.
+ *
+ * \note
+ * Implementers of protocol-specific CC agents should call this function after
+ * calling ast_setup_cc_recall_datastore.
+ *
+ * \note
+ * This function will lock the channel as well as the list of monitors stored
+ * on the channel's CC recall datastore, though neither are held at the same
+ * time. Callers of this function should be aware of potential lock ordering
+ * problems that may arise.
+ *
+ * \details
+ * The CC_INTERFACES channel variable will have the interfaces that should be
+ * called back for a specific PBX instance.
+ *
+ * \param chan The channel to set the CC_INTERFACES variable on
+ */
+int ast_cc_agent_set_interfaces_chanvar(struct ast_channel *chan);
+
+/*!
+ * \since 1.8
+ * \brief Set the CC_INTERFACES channel variable for a channel using an
+ * extension@context as a starting point
+ *
+ * \details
+ * The CC_INTERFACES channel variable will have the interfaces that should be
+ * called back for a specific PBX instance. This version of the function is used
+ * mainly by chan_local, wherein we need to set CC_INTERFACES based on an extension
+ * and context that appear in the middle of the tree of dialed interfaces
+ *
+ * \note
+ * This function will lock the channel as well as the list of monitors stored
+ * on the channel's CC recall datastore, though neither are held at the same
+ * time. Callers of this function should be aware of potential lock ordering
+ * problems that may arise.
+ *
+ * \param chan The channel to set the CC_INTERFACES variable on
+ * \param extension The name of the extension for which we're setting the variable.
+ * This should be in the form of "exten@context"
+ */
+int ast_set_cc_interfaces_chanvar(struct ast_channel *chan, const char * const extension);
+
+/*!
+ * \since 1.8
+ * \brief Make CCBS available in the case that ast_call fails
+ *
+ * In some situations, notably if a call-limit is reached in SIP, ast_call will fail
+ * due to Asterisk's knowing that the desired device is currently busy. In such a situation,
+ * CCBS should be made available to the caller.
+ *
+ * One caveat is that this may only be used if generic monitoring is being used. The reason
+ * is that since Asterisk determined that the device was busy without actually placing a call to it,
+ * the far end will have no idea what call we are requesting call completion for if we were to send
+ * a call completion request.
+ */
+void ast_cc_call_failed(struct ast_channel *incoming, struct ast_channel *outgoing, const char * const dialstring);
+
+/*!
+ * \since 1.8
+ * \brief Callback made from ast_cc_callback for certain channel types
+ *
+ * \param inbound Incoming asterisk channel.
+ * \param cc_params The CC configuration parameters for the outbound target
+ * \param monitor_type The type of monitor to use when CC is requested
+ * \param device_name The name of the outbound target device.
+ * \param dialstring The dial string used when calling this specific interface
+ * \param private_data If a native monitor is being used, and some channel-driver-specific private
+ * data has been allocated, then this parameter should contain a pointer to that data. If using a generic
+ * monitor, this parameter should remain NULL. Note that if this function should fail at some point,
+ * it is the responsibility of the caller to free the private data upon return.
+ *
+ * \details
+ * For channel types that fail ast_request when the device is busy, we call into the
+ * channel driver with ast_cc_callback. This is the callback that is called in that
+ * case for each device found which could have been returned by ast_request.
+ *
+ * This function creates a CC control frame payload, simulating the act of reading
+ * it from the nonexistent outgoing channel's frame queue. We then handle this
+ * simulated frame just as we would a normal CC frame which had actually been queued
+ * by the channel driver.
+ */
+void ast_cc_busy_interface(struct ast_channel *inbound, struct ast_cc_config_params *cc_params,
+ const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data);
+
+/*!
+ * \since 1.8
+ * \brief Create a CC Control frame
+ *
+ * \details
+ * chan_dahdi is weird. It doesn't seem to actually queue frames when it needs to tell
+ * an application something. Instead it wakes up, tells the application that it has data
+ * ready, and then based on set flags, creates the proper frame type. For chan_dahdi, we
+ * provide this function. It provides us the data we need, and we'll make its frame for it.
+ *
+ * \param chan A channel involved in the call. What we want is on a datastore on both incoming and outgoing so either may be provided
+ * \param cc_params The CC configuration parameters for the outbound target
+ * \param monitor_type The type of monitor to use when CC is requested
+ * \param device_name The name of the outbound target device.
+ * \param dialstring The dial string used when calling this specific interface
+ * \param service What kind of CC service is being offered. (CCBS/CCNR/etc...)
+ * \param private_data If a native monitor is being used, and some channel-driver-specific private
+ * data has been allocated, then this parameter should contain a pointer to that data. If using a generic
+ * monitor, this parameter should remain NULL. Note that if this function should fail at some point,
+ * it is the responsibility of the caller to free the private data upon return.
+ * \param[out] frame. The frame we will be returning to the caller. It is vital that ast_frame_free be called on this frame since the
+ * payload will be allocated on the heap.
+ * \retval -1 Failure. At some point there was a failure. Do not attempt to use the frame in this case.
+ * \retval 0 Success
+ */
+int ast_cc_build_frame(struct ast_channel *chan, struct ast_cc_config_params *cc_params,
+ const char *monitor_type, const char * const device_name,
+ const char * const dialstring, enum ast_cc_service_type service, void *private_data,
+ struct ast_frame *frame);
+
+
+/*!
+ * \brief Callback made from ast_cc_callback for certain channel types
+ * \since 1.8
+ *
+ * \param chan A channel involved in the call. What we want is on a datastore on both incoming and outgoing so either may be provided
+ * \param cc_params The CC configuration parameters for the outbound target
+ * \param monitor_type The type of monitor to use when CC is requested
+ * \param device_name The name of the outbound target device.
+ * \param dialstring The dial string used when calling this specific interface
+ * \param private_data If a native monitor is being used, and some channel-driver-specific private
+ * data has been allocated, then this parameter should contain a pointer to that data. If using a generic
+ * monitor, this parameter should remain NULL. Note that if this function should fail at some point,
+ * it is the responsibility of the caller to free the private data upon return.
+ *
+ * \details
+ * For channel types that fail ast_request when the device is busy, we call into the
+ * channel driver with ast_cc_callback. This is the callback that is called in that
+ * case for each device found which could have been returned by ast_request.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_cc_callback_fn)(struct ast_channel *chan, struct ast_cc_config_params *cc_params,
+ const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data);
+
+/*!
+ * \since 1.8
+ * \brief Run a callback for potential matching destinations.
+ *
+ * \note
+ * See the explanation in ast_channel_tech::cc_callback for more
+ * details.
+ *
+ * \param tech Channel technology to use
+ * \param dest Channel/group/peer or whatever the specific technology uses
+ * \param callback Function to call when a target is reached
+ * \retval Always 0, I guess.
+ */
+int ast_cc_callback(struct ast_channel *inbound, const char * const tech, const char * const dest, ast_cc_callback_fn callback);
+
+/*!
+ * \since 1.8
+ * \brief Initialize CCSS
+ *
+ * Performs startup routines necessary for CC operation.
+ *
+ * \retval 0 Success
+ * \retval nonzero Failure
+ */
+int ast_cc_init(void);
+
+#endif /* _ASTERISK_CCSS_H */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index a32486a39..119dc42e8 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -148,6 +148,8 @@ extern "C" {
#include "asterisk/linkedlists.h"
#include "asterisk/stringfields.h"
#include "asterisk/datastore.h"
+#include "asterisk/channelstate.h"
+#include "asterisk/ccss.h"
#define DATASTORE_INHERIT_FOREVER INT_MAX
@@ -507,6 +509,29 @@ struct ast_channel_tech {
/*! \brief Get the unique identifier for the PVT, i.e. SIP call-ID for SIP */
const char * (* get_pvt_uniqueid)(struct ast_channel *chan);
+
+ /*! \brief Call a function with cc parameters as a function parameter
+ *
+ * \details
+ * This is a highly specialized callback that is not likely to be needed in many
+ * channel drivers. When dealing with a busy channel, for instance, most channel
+ * drivers will successfully return a channel to the requester. Once called, the channel
+ * can then queue a busy frame when it receives an appropriate message from the far end.
+ * In such a case, the channel driver has the opportunity to also queue a CC frame.
+ * The parameters for the CC channel can be retrieved from the channel structure.
+ *
+ * For other channel drivers, notably those that deal with "dumb" phones, the channel
+ * driver will not return a channel when one is requested. In such a scenario, there is never
+ * an opportunity for the channel driver to queue a CC frame since the channel is never
+ * called. Furthermore, it is not possible to retrieve the CC configuration parameters
+ * for the desired channel because no channel is ever allocated or returned to the
+ * requester. In such a case, call completion may still be a viable option. What we do is
+ * pass the same string that the requester used originally to request the channel to the
+ * channel driver. The channel driver can then find any potential channels/devices that
+ * match the input and return call the designated callback with the device's call completion
+ * parameters as a parameter.
+ */
+ int (* cc_callback)(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback);
};
struct ast_epoll_data;
@@ -535,27 +560,6 @@ enum ast_channel_adsicpe {
};
/*!
- * \brief ast_channel states
- *
- * \note Bits 0-15 of state are reserved for the state (up/down) of the line
- * Bits 16-32 of state are reserved for flags
- */
-enum ast_channel_state {
- AST_STATE_DOWN, /*!< Channel is down and available */
- AST_STATE_RESERVED, /*!< Channel is down, but reserved */
- AST_STATE_OFFHOOK, /*!< Channel is off hook */
- AST_STATE_DIALING, /*!< Digits (or equivalent) have been dialed */
- AST_STATE_RING, /*!< Line is ringing */
- AST_STATE_RINGING, /*!< Remote end is ringing */
- AST_STATE_UP, /*!< Line is up */
- AST_STATE_BUSY, /*!< Line is busy */
- AST_STATE_DIALING_OFFHOOK, /*!< Digits (or equivalent) have been dialed while offhook */
- AST_STATE_PRERING, /*!< Channel has detected an incoming call and is waiting for ring */
-
- AST_STATE_MUTE = (1 << 16), /*!< Do not transmit voice data */
-};
-
-/*!
* \brief Possible T38 states on channels
*/
enum ast_t38_state {
@@ -950,9 +954,6 @@ int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore
*/
struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid);
-/*! \brief Change the state of a channel */
-int ast_setstate(struct ast_channel *chan, enum ast_channel_state);
-
/*!
* \brief Create a channel structure
* \since 1.8
@@ -2756,6 +2757,95 @@ void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct
* '0'
*/
int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const void *connected_info, int caller, int frame);
+
+#include "asterisk/ccss.h"
+
+/*!
+ * \since 1.8
+ * \brief Set up datastore with CCSS parameters for a channel
+ *
+ * \note
+ * If base_params is NULL, the channel will get the default
+ * values for all CCSS parameters.
+ *
+ * \details
+ * This function makes use of datastore operations on the channel, so
+ * it is important to lock the channel before calling this function.
+ *
+ * \param chan The channel to create the datastore on
+ * \param base_params CCSS parameters we wish to copy into the channel
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_channel_cc_params_init(struct ast_channel *chan,
+ const struct ast_cc_config_params *base_params);
+
+/*!
+ * \since 1.8
+ * \brief Get the CCSS parameters from a channel
+ *
+ * \details
+ * This function makes use of datastore operations on the channel, so
+ * it is important to lock the channel before calling this function.
+ *
+ * \param chan Channel to retrieve parameters from
+ * \retval NULL Failure
+ * \retval non-NULL The parameters desired
+ */
+struct ast_cc_config_params *ast_channel_get_cc_config_params(struct ast_channel *chan);
+
+
+/*!
+ * \since 1.8
+ * \brief Get a device name given its channel structure
+ *
+ * \details
+ * A common practice in Asterisk is to determine the device being talked
+ * to by dissecting the channel name. For certain channel types, this is not
+ * accurate. For instance, an ISDN channel is named based on what B channel is
+ * used, not the device being communicated with.
+ *
+ * This function interfaces with a channel tech's queryoption callback to
+ * retrieve the name of the device being communicated with. If the channel does not
+ * implement this specific option, then the traditional method of using the channel
+ * name is used instead.
+ *
+ * \param chan The channel to retrieve the information from
+ * \param device_name[out] The buffer to place the device's name into
+ * \param name_buffer_length The allocated space for the device_name
+ * \return 0 always
+ */
+int ast_channel_get_device_name(struct ast_channel *chan, char *device_name, size_t name_buffer_length);
+
+/*!
+ * \since 1.8
+ * \brief Find the appropriate CC agent type to use given a channel
+ *
+ * \details
+ * During call completion, we will need to create a call completion agent structure. To
+ * figure out the type of agent to construct, we need to ask the channel driver for the
+ * appropriate type.
+ *
+ * Prior to adding this function, the call completion core attempted to figure this
+ * out for itself by stripping the technology off the channel's name. However, in the
+ * case of chan_dahdi, there are multiple agent types registered, and so simply searching
+ * for an agent type called "DAHDI" is not possible. In a case where multiple agent types
+ * are defined, the channel driver must have a queryoption callback defined in its
+ * channel_tech, and the queryoption callback must handle AST_OPTION_CC_AGENT_TYPE
+ *
+ * If a channel driver does not have a queryoption callback or if the queryoption callback
+ * does not handle AST_OPTION_CC_AGENT_TYPE, then the old behavior of using the technology
+ * portion of the channel name is used instead. This is perfectly suitable for channel drivers
+ * whose channel technologies are a one-to-one match with the agent types defined within.
+ *
+ * Note that this function is only called when the agent policy on a given channel is set
+ * to "native." Generic agents' type can be determined automatically by the core.
+ *
+ * \param chan The channel for which we wish to retrieve the agent type
+ * \param[out] agent_type The type of agent the channel driver wants us to use
+ * \param size The size of the buffer to write to
+ */
+int ast_channel_get_cc_agent_type(struct ast_channel *chan, char *agent_type, size_t size);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/channelstate.h b/include/asterisk/channelstate.h
new file mode 100644
index 000000000..f5f7392dd
--- /dev/null
+++ b/include/asterisk/channelstate.h
@@ -0,0 +1,53 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Channel states
+ * \par See also:
+ * \arg \ref Def_Channel
+ * \arg \ref channel_drivers
+ */
+
+#ifndef __AST_CHANNELSTATE_H__
+#define __AST_CHANNELSTATE_H__
+
+#include "asterisk.h"
+
+/*!
+ * \brief ast_channel states
+ *
+ * \note Bits 0-15 of state are reserved for the state (up/down) of the line
+ * Bits 16-32 of state are reserved for flags
+ */
+enum ast_channel_state {
+ AST_STATE_DOWN, /*!< Channel is down and available */
+ AST_STATE_RESERVED, /*!< Channel is down, but reserved */
+ AST_STATE_OFFHOOK, /*!< Channel is off hook */
+ AST_STATE_DIALING, /*!< Digits (or equivalent) have been dialed */
+ AST_STATE_RING, /*!< Line is ringing */
+ AST_STATE_RINGING, /*!< Remote end is ringing */
+ AST_STATE_UP, /*!< Line is up */
+ AST_STATE_BUSY, /*!< Line is busy */
+ AST_STATE_DIALING_OFFHOOK, /*!< Digits (or equivalent) have been dialed while offhook */
+ AST_STATE_PRERING, /*!< Channel has detected an incoming call and is waiting for ring */
+
+ AST_STATE_MUTE = (1 << 16), /*!< Do not transmit voice data */
+};
+
+/*! \brief Change the state of a channel */
+int ast_setstate(struct ast_channel *chan, enum ast_channel_state);
+
+#endif /* __AST_CHANNELSTATE_H__ */
diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h
index 4c516870e..2a53ebb46 100644
--- a/include/asterisk/devicestate.h
+++ b/include/asterisk/devicestate.h
@@ -37,7 +37,7 @@
#ifndef _ASTERISK_DEVICESTATE_H
#define _ASTERISK_DEVICESTATE_H
-#include "asterisk/channel.h"
+#include "asterisk/channelstate.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 30119d49b..68a0c7eb6 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -324,7 +324,8 @@ enum ast_control_frame_type {
AST_CONTROL_CONNECTED_LINE = 22,/*!< Indicate connected line has changed */
AST_CONTROL_REDIRECTING = 23, /*!< Indicate redirecting id has changed */
AST_CONTROL_T38_PARAMETERS = 24, /*! T38 state change request/notification with parameters */
- AST_CONTROL_SRCCHANGE = 25, /*!< Media source has changed and requires a new RTP SSRC */
+ AST_CONTROL_CC = 25, /*!< Indication that Call completion service is possible */
+ AST_CONTROL_SRCCHANGE = 26, /*!< Media source has changed and requires a new RTP SSRC */
};
enum ast_control_t38 {
@@ -433,6 +434,12 @@ enum ast_control_transfer {
/*! Get or set the fax tone detection state of the channel */
#define AST_OPTION_FAX_DETECT 15
+/*! Get the device name from the channel */
+#define AST_OPTION_DEVICE_NAME 16
+
+/*! Get the CC agent type from the channel */
+#define AST_OPTION_CC_AGENT_TYPE 17
+
struct oprmode {
struct ast_channel *peer;
int mode;
diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h
index 91c43d3fe..dd7c160f4 100644
--- a/include/asterisk/manager.h
+++ b/include/asterisk/manager.h
@@ -82,6 +82,7 @@
#define EVENT_FLAG_ORIGINATE (1 << 12) /* Originate a call to an extension */
#define EVENT_FLAG_AGI (1 << 13) /* AGI events */
#define EVENT_FLAG_HOOKRESPONSE (1 << 14) /* Hook Response */
+#define EVENT_FLAG_CC (1 << 15) /* Call Completion events */
/*@} */
/*! \brief Export manager structures */
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index 770f4d2f5..e7b809d4c 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -70,6 +70,7 @@ extern "C" {
#endif
#include "asterisk/astobj2.h"
+#include "asterisk/frame.h"
/* Maximum number of payloads supported */
#define AST_RTP_MAX_PT 256
diff --git a/include/asterisk/xml.h b/include/asterisk/xml.h
index 5c78cc9c0..2c30986cc 100644
--- a/include/asterisk/xml.h
+++ b/include/asterisk/xml.h
@@ -45,6 +45,14 @@ int ast_xml_finish(void);
*/
struct ast_xml_doc *ast_xml_open(char *filename);
+/*! \brief Open an XML document that resides in memory.
+ * \param buffer The address where the document is stored
+ * \size The number of bytes in the document
+ * \retval NULL on error.
+ * \retval The ast_xml_doc reference to the open document.
+ */
+struct ast_xml_doc *ast_xml_read_memory(char *buffer, size_t size);
+
/*! \brief Close an already open document and free the used
* structure.
* \retval doc The document reference.
@@ -90,6 +98,8 @@ const char *ast_xml_get_attribute(struct ast_xml_node *node, const char *attrnam
* \retval The node on success.
*/
struct ast_xml_node *ast_xml_find_element(struct ast_xml_node *root_node, const char *name, const char *attrname, const char *attrvalue);
+struct ast_xml_ns *ast_xml_find_namespace(struct ast_xml_doc *doc, struct ast_xml_node *node, const char *ns_name);
+const char *ast_xml_get_ns_href(struct ast_xml_ns *ns);
/*! \brief Get an element content string.
* \param node Node from where to get the string.
diff --git a/main/asterisk.c b/main/asterisk.c
index 8f557f736..57ccc3f63 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -140,6 +140,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/buildinfo.h"
#include "asterisk/xmldoc.h"
#include "asterisk/poll-compat.h"
+#include "asterisk/ccss.h"
#include "asterisk/test.h"
#include "../defaults.h"
@@ -3684,6 +3685,11 @@ int main(int argc, char *argv[])
exit(1);
}
+ if (ast_cc_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
if ((moduleresult = load_modules(0))) { /* Load modules */
printf("%s", term_quit());
exit(moduleresult == -2 ? 2 : 1);
diff --git a/main/ccss.c b/main/ccss.c
new file mode 100644
index 000000000..4e616011b
--- /dev/null
+++ b/main/ccss.c
@@ -0,0 +1,4157 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2010, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Call Completion Supplementary Services implementation
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/astobj2.h"
+#include "asterisk/strings.h"
+#include "asterisk/ccss.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/taskprocessor.h"
+#include "asterisk/event.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/causes.h"
+
+/*** DOCUMENTATION
+ <application name="CallCompletionRequest" language="en_US">
+ <synopsis>
+ Request call completion service for previous call
+ </synopsis>
+ <syntax />
+ <description>
+ <para>Request call completion service for a previously failed
+ call attempt.</para>
+ </description>
+ </application>
+ <application name="CallCompletionCancel" language="en_US">
+ <synopsis>
+ Cancel call completion service
+ </synopsis>
+ <syntax />
+ <description>
+ <para>Cancel a Call Completion Request.</para>
+ </description>
+ </application>
+ ***/
+
+/* These are some file-scoped variables. It would be
+ * nice to define them closer to their first usage, but since
+ * they are used in many places throughout the file, defining
+ * them here at the top is easiest.
+ */
+
+/*!
+ * The sched_thread ID used for all generic CC timeouts
+ */
+static struct ast_sched_thread *cc_sched_thread;
+/*!
+ * Counter used to create core IDs for CC calls. Each new
+ * core ID is created by atomically adding 1 to the core_id_counter
+ */
+static int core_id_counter;
+/*!
+ * Taskprocessor from which all CC agent and monitor callbacks
+ * are called.
+ */
+static struct ast_taskprocessor *cc_core_taskprocessor;
+/*!
+ * Name printed on all CC log messages.
+ */
+static const char *CC_LOGGER_LEVEL_NAME = "CC";
+/*!
+ * Logger level registered by the CC core.
+ */
+static int cc_logger_level;
+/*!
+ * Parsed configuration value for cc_max_requests
+ */
+static unsigned int global_cc_max_requests;
+/*!
+ * The current number of CC requests in the system
+ */
+static int cc_request_count;
+
+#define cc_ref(obj, debug) ({ao2_t_ref((obj), +1, (debug)); (obj);})
+#define cc_unref(obj, debug) ({ao2_t_ref((obj), -1, (debug)); NULL;})
+
+/*!
+ * \since 1.8
+ * \internal
+ * \brief A structure for holding the configuration parameters
+ * relating to CCSS
+ */
+struct ast_cc_config_params {
+ enum ast_cc_agent_policies cc_agent_policy;
+ enum ast_cc_monitor_policies cc_monitor_policy;
+ unsigned int cc_offer_timer;
+ unsigned int ccnr_available_timer;
+ unsigned int ccbs_available_timer;
+ unsigned int cc_recall_timer;
+ unsigned int cc_max_agents;
+ unsigned int cc_max_monitors;
+ char cc_callback_macro[AST_MAX_EXTENSION];
+ char cc_agent_dialstring[AST_MAX_EXTENSION];
+};
+
+/*!
+ * \since 1.8
+ * \brief The states used in the CCSS core state machine
+ *
+ * For more information, see doc/CCSS_architecture.pdf
+ */
+enum cc_state {
+ /*! Entered when it is determined that CCSS may be used for the call */
+ CC_AVAILABLE,
+ /*! Entered when a CCSS agent has offered CCSS to a caller */
+ CC_CALLER_OFFERED,
+ /*! Entered when a CCSS agent confirms that a caller has
+ * requested CCSS */
+ CC_CALLER_REQUESTED,
+ /*! Entered when a CCSS monitor confirms acknowledgment of an
+ * outbound CCSS request */
+ CC_ACTIVE,
+ /*! Entered when a CCSS monitor alerts the core that the called party
+ * has become available */
+ CC_CALLEE_READY,
+ /*! Entered when a CCSS agent alerts the core that the calling party
+ * may not be recalled because he is unavailable
+ */
+ CC_CALLER_BUSY,
+ /*! Entered when a CCSS agent alerts the core that the calling party
+ * is attempting to recall the called party
+ */
+ CC_RECALLING,
+ /*! Entered when an application alerts the core that the calling party's
+ * recall attempt has had a call progress response indicated
+ */
+ CC_COMPLETE,
+ /*! Entered any time that something goes wrong during the process, thus
+ * resulting in the failure of the attempted CCSS transaction. Note also
+ * that cancellations of CC are treated as failures.
+ */
+ CC_FAILED,
+};
+
+/*!
+ * \brief The payload for an AST_CONTROL_CC frame
+ *
+ * \details
+ * This contains all the necessary data regarding
+ * a called device so that the CC core will be able
+ * to allocate the proper monitoring resources.
+ */
+struct cc_control_payload {
+ /*!
+ * \brief The type of monitor to allocate.
+ *
+ * \details
+ * The type of monitor to allocate. This is a string which corresponds
+ * to a set of monitor callbacks registered. Examples include "generic"
+ * and "SIP"
+ *
+ * \note This really should be an array of characters in case this payload
+ * is sent accross an IAX2 link. However, this would not make too much sense
+ * given this type may not be recognized by the other end.
+ * Protection may be necessary to prevent it from being transmitted.
+ *
+ * In addition the following other problems are also possible:
+ * 1) Endian issues with the integers/enums stored in the config_params.
+ * 2) Alignment padding issues for the element types.
+ */
+ const char *monitor_type;
+ /*!
+ * \brief Private data allocated by the callee
+ *
+ * \details
+ * All channel drivers that monitor endpoints will need to allocate
+ * data that is not usable by the CC core. In most cases, some or all
+ * of this data is allocated at the time that the channel driver offers
+ * CC to the caller. There are many opportunities for failures to occur
+ * between when a channel driver offers CC and when a monitor is actually
+ * allocated to watch the endpoint. For this reason, the channel driver
+ * must give the core a pointer to the private data that was allocated so
+ * that the core can call back into the channel driver to destroy it if
+ * a failure occurs. If no private data has been allocated at the time that
+ * CC is offered, then it is perfectly acceptable to pass NULL for this
+ * field.
+ */
+ void *private_data;
+ /*!
+ * \brief Service offered by the endpoint
+ *
+ * \details
+ * This indicates the type of call completion service offered by the
+ * endpoint. This data is not crucial to the machinations of the CC core,
+ * but it is helpful for debugging purposes.
+ */
+ enum ast_cc_service_type service;
+ /*!
+ * \brief Configuration parameters used by this endpoint
+ *
+ * \details
+ * Each time an endpoint offers call completion, it must provide its call
+ * completion configuration parameters. This is because settings may be different
+ * depending on the circumstances.
+ */
+ struct ast_cc_config_params config_params;
+ /*!
+ * \brief ID of parent extension
+ *
+ * \details
+ * This is the only datum that the CC core derives on its own and is not
+ * provided by the offerer of CC. This provides the core with information on
+ * which extension monitor is the most immediate parent of this device.
+ */
+ int parent_interface_id;
+ /*!
+ * \brief Name of device to be monitored
+ *
+ * \details
+ * The device name by which this monitored endpoint will be referred in the
+ * CC core. It is highly recommended that this device name is derived by using
+ * the function ast_channel_get_device_name.
+ */
+ char device_name[AST_CHANNEL_NAME];
+ /*!
+ * \brief Recall dialstring
+ *
+ * \details
+ * Certain channel drivers (DAHDI in particular) will require that a special
+ * dialstring be used to indicate that the outgoing call is to interpreted as
+ * a CC recall. If the channel driver has such a requirement, then this is
+ * where that special recall dialstring is placed. If no special dialstring
+ * is to be used, then the channel driver must provide the original dialstring
+ * used to call this endpoint.
+ */
+ char dialstring[AST_CHANNEL_NAME];
+};
+
+/*!
+ * \brief The "tree" of interfaces that is dialed.
+ *
+ * \details
+ * Though this is a linked list, it is logically treated
+ * as a tree of monitors. Each monitor has an id and a parent_id
+ * associated with it. The id is a unique ID for that monitor, and
+ * the parent_id is the unique ID of the monitor's parent in the
+ * tree. The tree is structured such that all of a parent's children
+ * will appear after the parent in the tree. However, it cannot be
+ * guaranteed exactly where after the parent the children are.
+ *
+ * The tree is reference counted since several threads may need
+ * to use it, and it may last beyond the lifetime of a single
+ * thread.
+ */
+AST_LIST_HEAD(cc_monitor_tree, ast_cc_monitor);
+
+static const int CC_CORE_INSTANCES_BUCKETS = 17;
+static struct ao2_container *cc_core_instances;
+
+struct cc_core_instance {
+ /*!
+ * Unique identifier for this instance of the CC core.
+ */
+ int core_id;
+ /*!
+ * The current state for this instance of the CC core.
+ */
+ enum cc_state current_state;
+ /*!
+ * The CC agent in use for this call
+ */
+ struct ast_cc_agent *agent;
+ /*!
+ * Reference to the monitor tree formed during the initial call
+ */
+ struct cc_monitor_tree *monitors;
+};
+
+/*!
+ * \internal
+ * \brief Request that the core change states
+ * \param state The state to which we wish to change
+ * \param core_id The unique identifier for this instance of the CCSS core state machine
+ * \param debug Optional message explaining the reason for the state change
+ * \param ap varargs list
+ * \retval 0 State change successfully queued
+ * \retval -1 Unable to queue state change request
+ */
+static int __attribute__((format(printf, 3, 0))) cc_request_state_change(enum cc_state state, const int core_id, const char *debug, va_list ap);
+
+/*!
+ * \internal
+ * \brief create a new instance of the CC core and an agent for the calling channel
+ *
+ * This function will check to make sure that the incoming channel
+ * is allowed to request CC by making sure that the incoming channel
+ * has not exceeded its maximum number of allowed agents.
+ *
+ * Should that check pass, the core instance is created, and then the
+ * agent for the channel.
+ *
+ * \param caller_chan The incoming channel for this particular call
+ * \param called_tree A reference to the tree of called devices. The agent
+ * will gain a reference to this tree as well
+ * \param core_id The core_id that this core_instance will assume
+ * \retval NULL Failed to create the core instance either due to memory allocation
+ * errors or due to the agent count for the caller being too high
+ * \retval non-NULL A reference to the newly created cc_core_instance
+ */
+static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller_chan,
+ struct cc_monitor_tree *called_tree, const int core_id, struct cc_control_payload *cc_data);
+
+static const struct {
+ enum ast_cc_service_type service;
+ const char *service_string;
+} cc_service_to_string_map[] = {
+ {AST_CC_NONE, "NONE"},
+ {AST_CC_CCBS, "CCBS"},
+ {AST_CC_CCNR, "CCNR"},
+ {AST_CC_CCNL, "CCNL"},
+};
+
+static const struct {
+ enum cc_state state;
+ const char *state_string;
+} cc_state_to_string_map[] = {
+ {CC_AVAILABLE, "CC is available"},
+ {CC_CALLER_OFFERED, "CC offered to caller"},
+ {CC_CALLER_REQUESTED, "CC requested by caller"},
+ {CC_ACTIVE, "CC accepted by callee"},
+ {CC_CALLEE_READY, "Callee has become available"},
+ {CC_CALLER_BUSY, "Callee was ready, but caller is now unavailable"},
+ {CC_RECALLING, "Caller is attempting to recall"},
+ {CC_COMPLETE, "Recall complete"},
+ {CC_FAILED, "CC has failed"},
+};
+
+static const char *cc_state_to_string(enum cc_state state)
+{
+ return cc_state_to_string_map[state].state_string;
+}
+
+static const char *cc_service_to_string(enum ast_cc_service_type service)
+{
+ return cc_service_to_string_map[service].service_string;
+}
+
+static int cc_core_instance_hash_fn(const void *obj, const int flags)
+{
+ const struct cc_core_instance *core_instance = obj;
+ return core_instance->core_id;
+}
+
+static int cc_core_instance_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct cc_core_instance *core_instance1 = obj;
+ struct cc_core_instance *core_instance2 = arg;
+
+ return core_instance1->core_id == core_instance2->core_id ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static struct cc_core_instance *find_cc_core_instance(const int core_id)
+{
+ struct cc_core_instance finder = {.core_id = core_id,};
+
+ return ao2_t_find(cc_core_instances, &finder, OBJ_POINTER, "Finding a core_instance");
+}
+
+struct cc_callback_helper {
+ ao2_callback_fn *function;
+ void *args;
+ const char *type;
+};
+
+static int cc_agent_callback_helper(void *obj, void *args, int flags)
+{
+ struct cc_core_instance *core_instance = obj;
+ struct cc_callback_helper *helper = args;
+
+ if (strcmp(core_instance->agent->callbacks->type, helper->type)) {
+ return 0;
+ }
+
+ return helper->function(core_instance->agent, helper->args, flags);
+}
+
+struct ast_cc_agent *ast_cc_agent_callback(int flags, ao2_callback_fn *function, void *args, const char * const type)
+{
+ struct cc_callback_helper helper = {.function = function, .args = args, .type = type};
+ struct cc_core_instance *core_instance;
+ if ((core_instance = ao2_t_callback(cc_core_instances, flags, cc_agent_callback_helper, &helper,
+ "Calling provided agent callback function"))) {
+ struct ast_cc_agent *agent = cc_ref(core_instance->agent, "An outside entity needs the agent");
+ cc_unref(core_instance, "agent callback done with the core_instance");
+ return agent;
+ }
+ return NULL;
+}
+
+enum match_flags {
+ /* Only match agents that have not yet
+ * made a CC request
+ */
+ MATCH_NO_REQUEST = (1 << 0),
+ /* Only match agents that have made
+ * a CC request
+ */
+ MATCH_REQUEST = (1 << 1),
+};
+
+/* ao2_callbacks for cc_core_instances */
+
+/*!
+ * \internal
+ * \brief find a core instance based on its agent
+ *
+ * The match flags tell whether we wish to find core instances
+ * that have a monitor or core instances that do not. Core instances
+ * with no monitor are core instances for which a caller has not yet
+ * requested CC. Core instances with a monitor are ones for which the
+ * caller has requested CC.
+ */
+static int match_agent(void *obj, void *arg, void *data, int flags)
+{
+ struct cc_core_instance *core_instance = obj;
+ const char *name = arg;
+ unsigned long match_flags = *(unsigned long *)data;
+ int possible_match = 0;
+
+ if ((match_flags & MATCH_NO_REQUEST) && core_instance->current_state < CC_CALLER_REQUESTED) {
+ possible_match = 1;
+ }
+
+ if ((match_flags & MATCH_REQUEST) && core_instance->current_state >= CC_CALLER_REQUESTED) {
+ possible_match = 1;
+ }
+
+ if (!possible_match) {
+ return 0;
+ }
+
+ if (!strcmp(core_instance->agent->device_name, name)) {
+ return CMP_MATCH | CMP_STOP;
+ }
+ return 0;
+}
+
+struct count_agents_cb_data {
+ int count;
+ int core_id_exception;
+};
+
+/*!
+ * \internal
+ * \brief Count the number of agents a specific interface is using
+ *
+ * We're only concerned with the number of agents that have requested
+ * CC, so we restrict our search to core instances which have a non-NULL
+ * monitor pointer
+ */
+static int count_agents_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct cc_core_instance *core_instance = obj;
+ const char *name = arg;
+ struct count_agents_cb_data *cb_data = data;
+
+ if (cb_data->core_id_exception == core_instance->core_id) {
+ ast_log_dynamic_level(cc_logger_level, "Found agent with core_id %d but not counting it toward total\n", core_instance->core_id);
+ return 0;
+ }
+
+ if (core_instance->current_state >= CC_CALLER_REQUESTED && !strcmp(core_instance->agent->device_name, name)) {
+ cb_data->count++;
+ }
+ return 0;
+}
+
+static const unsigned int CC_OFFER_TIMER_DEFAULT = 20u;
+static const unsigned int CCNR_AVAILABLE_TIMER_DEFAULT = 7200u;
+static const unsigned int CCBS_AVAILABLE_TIMER_DEFAULT = 4800u;
+static const unsigned int CC_RECALL_TIMER_DEFAULT = 20u;
+static const unsigned int CC_MAX_AGENTS_DEFAULT = 5u;
+static const unsigned int CC_MAX_MONITORS_DEFAULT = 5u;
+static const unsigned int GLOBAL_CC_MAX_REQUESTS_DEFAULT = 20u;
+
+struct ast_cc_config_params *__ast_cc_config_params_init(const char *file, int line, const char *function)
+{
+#if defined(__AST_DEBUG_MALLOC)
+ struct ast_cc_config_params *params = __ast_calloc(1, sizeof(*params), file, line, function);
+#else
+ struct ast_cc_config_params *params = ast_calloc(1, sizeof(*params));
+#endif
+
+ if (!params) {
+ return NULL;
+ }
+
+ /* Yeah, I could use the get/set functions, but what's the point since
+ * I have direct access to the structure fields in this file.
+ */
+ params->cc_agent_policy = AST_CC_AGENT_NEVER;
+ params->cc_monitor_policy = AST_CC_MONITOR_NEVER;
+ params->cc_offer_timer = CC_OFFER_TIMER_DEFAULT;
+ params->ccnr_available_timer = CCNR_AVAILABLE_TIMER_DEFAULT;
+ params->ccbs_available_timer = CCBS_AVAILABLE_TIMER_DEFAULT;
+ params->cc_recall_timer = CC_RECALL_TIMER_DEFAULT;
+ params->cc_max_agents = CC_MAX_AGENTS_DEFAULT;
+ params->cc_max_monitors = CC_MAX_MONITORS_DEFAULT;
+ /* No need to set cc_callback_macro since calloc will 0 it out anyway */
+ return params;
+}
+
+void ast_cc_config_params_destroy(struct ast_cc_config_params *params)
+{
+ ast_free(params);
+}
+
+static enum ast_cc_agent_policies str_to_agent_policy(const char * const value)
+{
+ if (!strcasecmp(value, "never")) {
+ return AST_CC_AGENT_NEVER;
+ } else if (!strcasecmp(value, "native")) {
+ return AST_CC_AGENT_NATIVE;
+ } else if (!strcasecmp(value, "generic")) {
+ return AST_CC_AGENT_GENERIC;
+ } else {
+ ast_log(LOG_WARNING, "%s is an invalid value for cc_agent_policy. Switching to 'never'\n", value);
+ return AST_CC_AGENT_NEVER;
+ }
+}
+
+static enum ast_cc_monitor_policies str_to_monitor_policy(const char * const value)
+{
+ if (!strcasecmp(value, "never")) {
+ return AST_CC_MONITOR_NEVER;
+ } else if (!strcasecmp(value, "native")) {
+ return AST_CC_MONITOR_NATIVE;
+ } else if (!strcasecmp(value, "generic")) {
+ return AST_CC_MONITOR_GENERIC;
+ } else if (!strcasecmp(value, "always")) {
+ return AST_CC_MONITOR_ALWAYS;
+ } else {
+ ast_log(LOG_WARNING, "%s is an invalid value for cc_monitor_policy. Switching to 'never'\n", value);
+ return AST_CC_MONITOR_NEVER;
+ }
+}
+
+static const char *agent_policy_to_str(enum ast_cc_agent_policies policy)
+{
+ switch (policy) {
+ case AST_CC_AGENT_NEVER:
+ return "never";
+ case AST_CC_AGENT_NATIVE:
+ return "native";
+ case AST_CC_AGENT_GENERIC:
+ return "generic";
+ default:
+ /* This should never happen... */
+ return "";
+ }
+}
+
+static const char *monitor_policy_to_str(enum ast_cc_monitor_policies policy)
+{
+ switch (policy) {
+ case AST_CC_MONITOR_NEVER:
+ return "never";
+ case AST_CC_MONITOR_NATIVE:
+ return "native";
+ case AST_CC_MONITOR_GENERIC:
+ return "generic";
+ case AST_CC_MONITOR_ALWAYS:
+ return "always";
+ default:
+ /* This should never happen... */
+ return "";
+ }
+}
+int ast_cc_get_param(struct ast_cc_config_params *params, const char * const name,
+ char *buf, size_t buf_len)
+{
+ const char *value = NULL;
+ if (!strcasecmp(name, "cc_callback_macro")) {
+ value = ast_get_cc_callback_macro(params);
+ } else if (!strcasecmp(name, "cc_agent_policy")) {
+ value = agent_policy_to_str(ast_get_cc_agent_policy(params));
+ } else if (!strcasecmp(name, "cc_monitor_policy")) {
+ value = monitor_policy_to_str(ast_get_cc_monitor_policy(params));
+ } else if (!strcasecmp(name, "cc_agent_dialstring")) {
+ value = ast_get_cc_agent_dialstring(params);
+ }
+
+ if (!ast_strlen_zero(value)) {
+ ast_copy_string(buf, value, buf_len);
+ return 0;
+ }
+
+ /* The rest of these are all ints of some sort and require some
+ * snprintf-itude
+ */
+
+ if (!strcasecmp(name, "cc_offer_timer")) {
+ snprintf(buf, buf_len, "%u", ast_get_cc_offer_timer(params));
+ } else if (!strcasecmp(name, "ccnr_available_timer")) {
+ snprintf(buf, buf_len, "%u", ast_get_ccnr_available_timer(params));
+ } else if (!strcasecmp(name, "ccbs_available_timer")) {
+ snprintf(buf, buf_len, "%u", ast_get_ccbs_available_timer(params));
+ } else if (!strcasecmp(name, "cc_max_agents")) {
+ snprintf(buf, buf_len, "%u", ast_get_cc_max_agents(params));
+ } else if (!strcasecmp(name, "cc_max_monitors")) {
+ snprintf(buf, buf_len, "%u", ast_get_cc_max_monitors(params));
+ } else if (!strcasecmp(name, "cc_recall_timer")) {
+ snprintf(buf, buf_len, "%u", ast_get_cc_recall_timer(params));
+ } else {
+ ast_log(LOG_WARNING, "%s is not a valid CC parameter. Ignoring.\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ast_cc_set_param(struct ast_cc_config_params *params, const char * const name,
+ const char * const value)
+{
+ unsigned int value_as_uint;
+ if (!strcasecmp(name, "cc_agent_policy")) {
+ return ast_set_cc_agent_policy(params, str_to_agent_policy(value));
+ } else if (!strcasecmp(name, "cc_monitor_policy")) {
+ return ast_set_cc_monitor_policy(params, str_to_monitor_policy(value));
+ } else if (!strcasecmp(name, "cc_agent_dialstring")) {
+ ast_set_cc_agent_dialstring(params, value);
+ } else if (!strcasecmp(name, "cc_callback_macro")) {
+ ast_set_cc_callback_macro(params, value);
+ return 0;
+ }
+
+ if (!sscanf(value, "%30u", &value_as_uint) == 1) {
+ return -1;
+ }
+
+ if (!strcasecmp(name, "cc_offer_timer")) {
+ ast_set_cc_offer_timer(params, value_as_uint);
+ } else if (!strcasecmp(name, "ccnr_available_timer")) {
+ ast_set_ccnr_available_timer(params, value_as_uint);
+ } else if (!strcasecmp(name, "ccbs_available_timer")) {
+ ast_set_ccbs_available_timer(params, value_as_uint);
+ } else if (!strcasecmp(name, "cc_max_agents")) {
+ ast_set_cc_max_agents(params, value_as_uint);
+ } else if (!strcasecmp(name, "cc_max_monitors")) {
+ ast_set_cc_max_monitors(params, value_as_uint);
+ } else if (!strcasecmp(name, "cc_recall_timer")) {
+ ast_set_cc_recall_timer(params, value_as_uint);
+ } else {
+ ast_log(LOG_WARNING, "%s is not a valid CC parameter. Ignoring.\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ast_cc_is_config_param(const char * const name)
+{
+ return (!strcasecmp(name, "cc_agent_policy") ||
+ !strcasecmp(name, "cc_monitor_policy") ||
+ !strcasecmp(name, "cc_offer_timer") ||
+ !strcasecmp(name, "ccnr_available_timer") ||
+ !strcasecmp(name, "ccbs_available_timer") ||
+ !strcasecmp(name, "cc_max_agents") ||
+ !strcasecmp(name, "cc_max_monitors") ||
+ !strcasecmp(name, "cc_callback_macro") ||
+ !strcasecmp(name, "cc_agent_dialstring") ||
+ !strcasecmp(name, "cc_recall_timer"));
+}
+
+void ast_cc_copy_config_params(struct ast_cc_config_params *dest, const struct ast_cc_config_params *src)
+{
+ *dest = *src;
+}
+
+enum ast_cc_agent_policies ast_get_cc_agent_policy(struct ast_cc_config_params *config)
+{
+ return config->cc_agent_policy;
+}
+
+int ast_set_cc_agent_policy(struct ast_cc_config_params *config, enum ast_cc_agent_policies value)
+{
+ /* Screw C and its weak type checking for making me have to do this
+ * validation at runtime.
+ */
+ if (value < AST_CC_AGENT_NEVER || value > AST_CC_AGENT_GENERIC) {
+ return -1;
+ }
+ config->cc_agent_policy = value;
+ return 0;
+}
+
+enum ast_cc_monitor_policies ast_get_cc_monitor_policy(struct ast_cc_config_params *config)
+{
+ return config->cc_monitor_policy;
+}
+
+int ast_set_cc_monitor_policy(struct ast_cc_config_params *config, enum ast_cc_monitor_policies value)
+{
+ /* Screw C and its weak type checking for making me have to do this
+ * validation at runtime.
+ */
+ if (value < AST_CC_MONITOR_NEVER || value > AST_CC_MONITOR_ALWAYS) {
+ return -1;
+ }
+ config->cc_monitor_policy = value;
+ return 0;
+}
+
+unsigned int ast_get_cc_offer_timer(struct ast_cc_config_params *config)
+{
+ return config->cc_offer_timer;
+}
+
+void ast_set_cc_offer_timer(struct ast_cc_config_params *config, unsigned int value)
+{
+ /* 0 is an unreasonable value for any timer. Stick with the default */
+ if (value == 0) {
+ ast_log(LOG_WARNING, "0 is an invalid value for cc_offer_timer. Retaining value as %u\n", config->cc_offer_timer);
+ return;
+ }
+ config->cc_offer_timer = value;
+}
+
+unsigned int ast_get_ccnr_available_timer(struct ast_cc_config_params *config)
+{
+ return config->ccnr_available_timer;
+}
+
+void ast_set_ccnr_available_timer(struct ast_cc_config_params *config, unsigned int value)
+{
+ /* 0 is an unreasonable value for any timer. Stick with the default */
+ if (value == 0) {
+ ast_log(LOG_WARNING, "0 is an invalid value for ccnr_available_timer. Retaining value as %u\n", config->ccnr_available_timer);
+ return;
+ }
+ config->ccnr_available_timer = value;
+}
+
+unsigned int ast_get_cc_recall_timer(struct ast_cc_config_params *config)
+{
+ return config->cc_recall_timer;
+}
+
+void ast_set_cc_recall_timer(struct ast_cc_config_params *config, unsigned int value)
+{
+ /* 0 is an unreasonable value for any timer. Stick with the default */
+ if (value == 0) {
+ ast_log(LOG_WARNING, "0 is an invalid value for ccnr_available_timer. Retaining value as %u\n", config->cc_recall_timer);
+ return;
+ }
+ config->cc_recall_timer = value;
+}
+
+unsigned int ast_get_ccbs_available_timer(struct ast_cc_config_params *config)
+{
+ return config->ccbs_available_timer;
+}
+
+void ast_set_ccbs_available_timer(struct ast_cc_config_params *config, unsigned int value)
+{
+ /* 0 is an unreasonable value for any timer. Stick with the default */
+ if (value == 0) {
+ ast_log(LOG_WARNING, "0 is an invalid value for ccbs_available_timer. Retaining value as %u\n", config->ccbs_available_timer);
+ return;
+ }
+ config->ccbs_available_timer = value;
+}
+
+const char *ast_get_cc_agent_dialstring(struct ast_cc_config_params *config)
+{
+ return config->cc_agent_dialstring;
+}
+
+void ast_set_cc_agent_dialstring(struct ast_cc_config_params *config, const char *const value)
+{
+ if (ast_strlen_zero(value)) {
+ config->cc_agent_dialstring[0] = '\0';
+ } else {
+ ast_copy_string(config->cc_agent_dialstring, value, sizeof(config->cc_agent_dialstring));
+ }
+}
+
+unsigned int ast_get_cc_max_agents(struct ast_cc_config_params *config)
+{
+ return config->cc_max_agents;
+}
+
+void ast_set_cc_max_agents(struct ast_cc_config_params *config, unsigned int value)
+{
+ config->cc_max_agents = value;
+}
+
+unsigned int ast_get_cc_max_monitors(struct ast_cc_config_params *config)
+{
+ return config->cc_max_monitors;
+}
+
+void ast_set_cc_max_monitors(struct ast_cc_config_params *config, unsigned int value)
+{
+ config->cc_max_monitors = value;
+}
+
+const char *ast_get_cc_callback_macro(struct ast_cc_config_params *config)
+{
+ return config->cc_callback_macro;
+}
+
+void ast_set_cc_callback_macro(struct ast_cc_config_params *config, const char * const value)
+{
+ if (ast_strlen_zero(value)) {
+ config->cc_callback_macro[0] = '\0';
+ } else {
+ ast_copy_string(config->cc_callback_macro, value, sizeof(config->cc_callback_macro));
+ }
+}
+
+struct cc_monitor_backend {
+ AST_LIST_ENTRY(cc_monitor_backend) next;
+ const struct ast_cc_monitor_callbacks *callbacks;
+};
+
+AST_RWLIST_HEAD_STATIC(cc_monitor_backends, cc_monitor_backend);
+
+int ast_cc_monitor_register(const struct ast_cc_monitor_callbacks *callbacks)
+{
+ struct cc_monitor_backend *backend = ast_calloc(1, sizeof(*backend));
+
+ if (!backend) {
+ return -1;
+ }
+
+ backend->callbacks = callbacks;
+
+ AST_RWLIST_WRLOCK(&cc_monitor_backends);
+ AST_RWLIST_INSERT_TAIL(&cc_monitor_backends, backend, next);
+ AST_RWLIST_UNLOCK(&cc_monitor_backends);
+ return 0;
+}
+
+static const struct ast_cc_monitor_callbacks *find_monitor_callbacks(const char * const type)
+{
+ struct cc_monitor_backend *backend;
+ const struct ast_cc_monitor_callbacks *callbacks = NULL;
+
+ AST_RWLIST_RDLOCK(&cc_monitor_backends);
+ AST_RWLIST_TRAVERSE(&cc_monitor_backends, backend, next) {
+ if (!strcmp(backend->callbacks->type, type)) {
+ ast_log_dynamic_level(cc_logger_level, "Returning monitor backend %s\n", backend->callbacks->type);
+ callbacks = backend->callbacks;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&cc_monitor_backends);
+ return callbacks;
+}
+
+void ast_cc_monitor_unregister(const struct ast_cc_monitor_callbacks *callbacks)
+{
+ struct cc_monitor_backend *backend;
+ AST_RWLIST_WRLOCK(&cc_monitor_backends);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&cc_monitor_backends, backend, next) {
+ if (backend->callbacks == callbacks) {
+ AST_RWLIST_REMOVE_CURRENT(next);
+ ast_free(backend);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&cc_monitor_backends);
+}
+
+struct cc_agent_backend {
+ AST_LIST_ENTRY(cc_agent_backend) next;
+ const struct ast_cc_agent_callbacks *callbacks;
+};
+
+AST_RWLIST_HEAD_STATIC(cc_agent_backends, cc_agent_backend);
+
+int ast_cc_agent_register(const struct ast_cc_agent_callbacks *callbacks)
+{
+ struct cc_agent_backend *backend = ast_calloc(1, sizeof(*backend));
+
+ if (!backend) {
+ return -1;
+ }
+
+ backend->callbacks = callbacks;
+ AST_RWLIST_WRLOCK(&cc_agent_backends);
+ AST_RWLIST_INSERT_TAIL(&cc_agent_backends, backend, next);
+ AST_RWLIST_UNLOCK(&cc_agent_backends);
+ return 0;
+}
+
+void ast_cc_agent_unregister(const struct ast_cc_agent_callbacks *callbacks)
+{
+ struct cc_agent_backend *backend;
+ AST_RWLIST_WRLOCK(&cc_agent_backends);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&cc_agent_backends, backend, next) {
+ if (backend->callbacks == callbacks) {
+ AST_RWLIST_REMOVE_CURRENT(next);
+ ast_free(backend);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&cc_agent_backends);
+}
+
+static const struct ast_cc_agent_callbacks *find_agent_callbacks(struct ast_channel *chan)
+{
+ struct cc_agent_backend *backend;
+ const struct ast_cc_agent_callbacks *callbacks = NULL;
+ struct ast_cc_config_params *cc_params;
+ char type[32];
+
+ cc_params = ast_channel_get_cc_config_params(chan);
+ if (!cc_params) {
+ return NULL;
+ }
+ switch (ast_get_cc_agent_policy(cc_params)) {
+ case AST_CC_AGENT_GENERIC:
+ ast_copy_string(type, "generic", sizeof(type));
+ break;
+ case AST_CC_AGENT_NATIVE:
+ ast_channel_get_cc_agent_type(chan, type, sizeof(type));
+ break;
+ default:
+ ast_log_dynamic_level(cc_logger_level, "Not returning agent callbacks since this channel is configured not to have a CC agent\n");
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&cc_agent_backends);
+ AST_RWLIST_TRAVERSE(&cc_agent_backends, backend, next) {
+ if (!strcmp(backend->callbacks->type, type)) {
+ ast_log_dynamic_level(cc_logger_level, "Returning agent backend %s\n", backend->callbacks->type);
+ callbacks = backend->callbacks;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&cc_agent_backends);
+ return callbacks;
+}
+
+static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
+static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor);
+static int cc_generic_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate);
+static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor);
+static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
+static void cc_generic_monitor_destructor(void *private_data);
+
+static struct ast_cc_monitor_callbacks generic_monitor_cbs = {
+ .type = "generic",
+ .request_cc = cc_generic_monitor_request_cc,
+ .suspend = cc_generic_monitor_suspend,
+ .status_response = cc_generic_monitor_status_response,
+ .unsuspend = cc_generic_monitor_unsuspend,
+ .cancel_available_timer = cc_generic_monitor_cancel_available_timer,
+ .destructor = cc_generic_monitor_destructor,
+};
+
+struct ao2_container *generic_monitors;
+
+struct generic_monitor_instance {
+ int core_id;
+ int is_suspended;
+ int monitoring;
+ AST_LIST_ENTRY(generic_monitor_instance) next;
+};
+
+struct generic_monitor_instance_list {
+ const char *device_name;
+ enum ast_device_state current_state;
+ struct ast_event_sub *sub;
+ AST_LIST_HEAD_NOLOCK(, generic_monitor_instance) list;
+};
+
+/*!
+ * \brief private data for generic device monitor
+ */
+struct generic_monitor_pvt {
+ /*!
+ * We need the device name during destruction so we
+ * can find the appropriate item to destroy.
+ */
+ const char *device_name;
+ /*!
+ * We need the core ID for similar reasons. Once we
+ * find the appropriate item in our ao2_container, we
+ * need to remove the appropriate cc_monitor from the
+ * list of monitors.
+ */
+ int core_id;
+};
+
+static int generic_monitor_hash_fn(const void *obj, const int flags)
+{
+ const struct generic_monitor_instance_list *generic_list = obj;
+ return ast_str_hash(generic_list->device_name);
+}
+
+static int generic_monitor_cmp_fn(void *obj, void *arg, int flags)
+{
+ const struct generic_monitor_instance_list *generic_list1 = obj;
+ const struct generic_monitor_instance_list *generic_list2 = arg;
+
+ return !strcmp(generic_list1->device_name, generic_list2->device_name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static struct generic_monitor_instance_list *find_generic_monitor_instance_list(const char * const device_name)
+{
+ struct generic_monitor_instance_list finder = {.device_name = device_name};
+
+ return ao2_t_find(generic_monitors, &finder, OBJ_POINTER, "Finding generic monitor instance list");
+}
+
+static void generic_monitor_instance_list_destructor(void *obj)
+{
+ struct generic_monitor_instance_list *generic_list = obj;
+ struct generic_monitor_instance *generic_instance;
+
+ generic_list->sub = ast_event_unsubscribe(generic_list->sub);
+ while ((generic_instance = AST_LIST_REMOVE_HEAD(&generic_list->list, next))) {
+ ast_free(generic_instance);
+ }
+ ast_free((char *)generic_list->device_name);
+}
+
+static void generic_monitor_devstate_cb(const struct ast_event *event, void *userdata);
+static struct generic_monitor_instance_list *create_new_generic_list(struct ast_cc_monitor *monitor)
+{
+ struct generic_monitor_instance_list *generic_list = ao2_t_alloc(sizeof(*generic_list),
+ generic_monitor_instance_list_destructor, "allocate generic monitor instance list");
+
+ if (!generic_list) {
+ return NULL;
+ }
+
+ if (!(generic_list->device_name = ast_strdup(monitor->interface->device_name))) {
+ cc_unref(generic_list, "Failed to strdup the monitor's device name");
+ return NULL;
+ }
+
+ if (!(generic_list->sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, generic_monitor_devstate_cb,
+ "Requesting CC", NULL, AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
+ monitor->interface->device_name, AST_EVENT_IE_END))) {
+ cc_unref(generic_list, "Failed to subscribe to device state");
+ return NULL;
+ }
+ generic_list->current_state = ast_device_state(monitor->interface->device_name);
+ ao2_t_link(generic_monitors, generic_list, "linking new generic monitor instance list");
+ return generic_list;
+}
+
+struct generic_tp_cb_data {
+ const char *device_name;
+ enum ast_device_state new_state;
+};
+
+static int generic_monitor_devstate_tp_cb(void *data)
+{
+ struct generic_tp_cb_data *gtcd = data;
+ enum ast_device_state new_state = gtcd->new_state;
+ enum ast_device_state previous_state = gtcd->new_state;
+ const char *monitor_name = gtcd->device_name;
+ struct generic_monitor_instance_list *generic_list;
+ struct generic_monitor_instance *generic_instance;
+
+ if (!(generic_list = find_generic_monitor_instance_list(monitor_name))) {
+ /* The most likely cause for this is that we destroyed the monitor in the
+ * time between subscribing to its device state and the time this executes.
+ * Not really a big deal.
+ */
+ ast_free((char *) gtcd->device_name);
+ ast_free(gtcd);
+ return 0;
+ }
+
+ if (generic_list->current_state == new_state) {
+ /* The device state hasn't actually changed, so we don't really care */
+ cc_unref(generic_list, "Kill reference of generic list in devstate taskprocessor callback");
+ ast_free((char *) gtcd->device_name);
+ ast_free(gtcd);
+ return 0;
+ }
+
+ previous_state = generic_list->current_state;
+ generic_list->current_state = new_state;
+
+ if ((new_state == AST_DEVICE_NOT_INUSE || new_state == AST_DEVICE_UNKNOWN) &&
+ (previous_state == AST_DEVICE_INUSE || previous_state == AST_DEVICE_UNAVAILABLE ||
+ previous_state == AST_DEVICE_BUSY)) {
+ AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) {
+ if (!generic_instance->is_suspended && generic_instance->monitoring) {
+ generic_instance->monitoring = 0;
+ ast_cc_monitor_callee_available(generic_instance->core_id, "Generic monitored party has become available");
+ break;
+ }
+ }
+ }
+ cc_unref(generic_list, "Kill reference of generic list in devstate taskprocessor callback");
+ ast_free((char *) gtcd->device_name);
+ ast_free(gtcd);
+ return 0;
+}
+
+static void generic_monitor_devstate_cb(const struct ast_event *event, void *userdata)
+{
+ /* Wow, it's cool that we've picked up on a state change, but we really want
+ * the actual work to be done in the core's taskprocessor execution thread
+ * so that all monitor operations can be serialized. Locks?! We don't need
+ * no steenkin' locks!
+ */
+ struct generic_tp_cb_data *gtcd = ast_calloc(1, sizeof(*gtcd));
+
+ if (!gtcd) {
+ return;
+ }
+
+ if (!(gtcd->device_name = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE)))) {
+ ast_free(gtcd);
+ return;
+ }
+ gtcd->new_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+
+ if (ast_taskprocessor_push(cc_core_taskprocessor, generic_monitor_devstate_tp_cb, gtcd)) {
+ ast_free((char *)gtcd->device_name);
+ ast_free(gtcd);
+ }
+}
+
+int ast_cc_available_timer_expire(const void *data)
+{
+ struct ast_cc_monitor *monitor = (struct ast_cc_monitor *) data;
+ int res;
+ monitor->available_timer_id = -1;
+ res = ast_cc_monitor_failed(monitor->core_id, monitor->interface->device_name, "Available timer expired for monitor");
+ cc_unref(monitor, "Unref reference from scheduler\n");
+ return res;
+}
+
+static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
+{
+ struct generic_monitor_instance_list *generic_list;
+ struct generic_monitor_instance *generic_instance;
+ struct generic_monitor_pvt *gen_mon_pvt;
+ enum ast_cc_service_type service = monitor->service_offered;
+ int when;
+
+ /* First things first. Native channel drivers will have their private data allocated
+ * at the time that they tell the core that they can offer CC. Generic is quite a bit
+ * different, and we wait until this point to allocate our private data.
+ */
+ if (!(gen_mon_pvt = ast_calloc(1, sizeof(*gen_mon_pvt)))) {
+ return -1;
+ }
+
+ if (!(gen_mon_pvt->device_name = ast_strdup(monitor->interface->device_name))) {
+ ast_free(gen_mon_pvt);
+ return -1;
+ }
+
+ gen_mon_pvt->core_id = monitor->core_id;
+
+ monitor->private_data = gen_mon_pvt;
+
+ if (!(generic_list = find_generic_monitor_instance_list(monitor->interface->device_name))) {
+ if (!(generic_list = create_new_generic_list(monitor))) {
+ return -1;
+ }
+ }
+
+ if (!(generic_instance = ast_calloc(1, sizeof(*generic_instance)))) {
+ /* The generic monitor destructor will take care of the appropriate
+ * deallocations
+ */
+ cc_unref(generic_list, "Generic monitor instance failed to allocate");
+ return -1;
+ }
+ generic_instance->core_id = monitor->core_id;
+ generic_instance->monitoring = 1;
+ AST_LIST_INSERT_TAIL(&generic_list->list, generic_instance, next);
+ when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
+ ast_get_ccnr_available_timer(monitor->interface->config_params);
+
+ *available_timer_id = ast_sched_thread_add(cc_sched_thread, when * 1000,
+ ast_cc_available_timer_expire, cc_ref(monitor, "Give the scheduler a monitor reference"));
+ if (*available_timer_id == -1) {
+ cc_unref(monitor, "Failed to schedule available timer. (monitor)");
+ cc_unref(generic_list, "Failed to schedule available timer. (generic_list)");
+ return -1;
+ }
+ ast_cc_monitor_request_acked(monitor->core_id, "Generic monitor for %s subscribed to device state.",
+ monitor->interface->device_name);
+ cc_unref(generic_list, "Finished with monitor instance reference in request cc callback");
+ return 0;
+}
+
+static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor)
+{
+ struct generic_monitor_instance_list *generic_list;
+ struct generic_monitor_instance *generic_instance;
+ enum ast_device_state state = ast_device_state(monitor->interface->device_name);
+
+ if (!(generic_list = find_generic_monitor_instance_list(monitor->interface->device_name))) {
+ return -1;
+ }
+
+ /* First we need to mark this particular monitor as being suspended. */
+ AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) {
+ if (generic_instance->core_id == monitor->core_id) {
+ generic_instance->is_suspended = 1;
+ break;
+ }
+ }
+
+ /* If the device being suspended is currently in use, then we don't need to
+ * take any further actions
+ */
+ if (state != AST_DEVICE_NOT_INUSE && state != AST_DEVICE_UNKNOWN) {
+ cc_unref(generic_list, "Device is in use. Nothing to do. Unref generic list.");
+ return 0;
+ }
+
+ /* If the device is not in use, though, then it may be possible to report the
+ * device's availability using a different monitor which is monitoring the
+ * same device
+ */
+
+ AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) {
+ if (!generic_instance->is_suspended) {
+ ast_cc_monitor_callee_available(generic_instance->core_id, "Generic monitored party has become available");
+ break;
+ }
+ }
+ cc_unref(generic_list, "Done with generic list in suspend callback");
+ return 0;
+}
+
+static int cc_generic_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate)
+{
+ /* The generic monitor will never issue a status request of the other side's agent.
+ * If this somehow gets called, something really fishy is going on.
+ */
+ ast_log(LOG_WARNING, "Why has a generic monitor's status_response callback been called? CoreID is %d\n", monitor->core_id);
+ return 0;
+}
+
+static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor)
+{
+ struct generic_monitor_instance *generic_instance;
+ struct generic_monitor_instance_list *generic_list = find_generic_monitor_instance_list(monitor->interface->device_name);
+ enum ast_device_state state = ast_device_state(monitor->interface->device_name);
+
+ if (!generic_list) {
+ return -1;
+ }
+ /* If the device is currently available, we can immediately announce
+ * its availability
+ */
+ if (state == AST_DEVICE_NOT_INUSE || state == AST_DEVICE_UNKNOWN) {
+ ast_cc_monitor_callee_available(monitor->core_id, "Generic monitored party has become available");
+ }
+
+ /* In addition, we need to mark this generic_monitor_instance as not being suspended anymore */
+ AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) {
+ if (generic_instance->core_id == monitor->core_id) {
+ generic_instance->is_suspended = 0;
+ generic_instance->monitoring = 1;
+ break;
+ }
+ }
+ cc_unref(generic_list, "Done with generic list in cc_generic_monitor_unsuspend");
+ return 0;
+}
+
+static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id)
+{
+ ast_assert(sched_id != NULL);
+
+ if (*sched_id == -1) {
+ return 0;
+ }
+
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Canceling generic monitor available timer for monitor %s\n",
+ monitor->core_id, monitor->interface->device_name);
+ if (!ast_sched_thread_del(cc_sched_thread, *sched_id)) {
+ cc_unref(monitor, "Remove scheduler's reference to the monitor");
+ }
+ *sched_id = -1;
+ return 0;
+}
+
+static void cc_generic_monitor_destructor(void *private_data)
+{
+ struct generic_monitor_pvt *gen_mon_pvt = private_data;
+ struct generic_monitor_instance_list *generic_list;
+ struct generic_monitor_instance *generic_instance;
+
+ if (!private_data) {
+ /* If the private data is NULL, that means that the monitor hasn't even
+ * been created yet, but that the destructor was called. While this sort
+ * of behavior is useful for native monitors, with a generic one, there is
+ * nothing in particular to do.
+ */
+ return;
+ }
+
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Destroying generic monitor %s\n",
+ gen_mon_pvt->core_id, gen_mon_pvt->device_name);
+
+ if (!(generic_list = find_generic_monitor_instance_list(gen_mon_pvt->device_name))) {
+ /* If there's no generic list, that means that the monitor is being destroyed
+ * before we actually got to request CC. Not a biggie. Same in the situation
+ * below if the list traversal should complete without finding an entry.
+ */
+ ast_free((char *)gen_mon_pvt->device_name);
+ ast_free(gen_mon_pvt);
+ return;
+ }
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&generic_list->list, generic_instance, next) {
+ if (generic_instance->core_id == gen_mon_pvt->core_id) {
+ AST_LIST_REMOVE_CURRENT(next);
+ ast_free(generic_instance);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (AST_LIST_EMPTY(&generic_list->list)) {
+ /* No more monitors with this device name exist. Time to unlink this
+ * list from the container
+ */
+ ao2_t_unlink(generic_monitors, generic_list, "Generic list is empty. Unlink it from the container");
+ }
+ cc_unref(generic_list, "Done with generic list in generic monitor destructor");
+ ast_free((char *)gen_mon_pvt->device_name);
+ ast_free(gen_mon_pvt);
+}
+
+static void cc_interface_destroy(void *data)
+{
+ struct ast_cc_interface *interface = data;
+ ast_log_dynamic_level(cc_logger_level, "Destroying cc interface %s\n", interface->device_name);
+ ast_cc_config_params_destroy(interface->config_params);
+}
+
+/*!
+ * \brief Data regarding an extension monitor's child's dialstrings
+ *
+ * \details
+ * In developing CCSS, we had most aspects of its operation finished,
+ * but there was one looming problem that we had failed to get right.
+ * In our design document, we stated that when a CC recall occurs, all
+ * endpoints that had been dialed originally would be called back.
+ * Unfortunately, our implementation only allowed for devices which had
+ * active monitors to inhabit the CC_INTERFACES channel variable, thus
+ * making the automated recall only call monitored devices.
+ *
+ * Devices that were not CC-capable, or devices which failed CC at some
+ * point during the process would not make it into the CC_INTERFACES
+ * channel variable. This struct is meant as a remedy for the problem.
+ */
+struct extension_child_dialstring {
+ /*!
+ * \brief the original dialstring used to call a particular device
+ *
+ * \details
+ * When someone dials a particular endpoint, the dialstring used in
+ * the dialplan is copied into this buffer. What's important here is
+ * that this is the ORIGINAL dialstring, not the dialstring saved on
+ * a device monitor. The dialstring on a device monitor is what should
+ * be used when recalling that device. The two dialstrings may not be
+ * the same.
+ *
+ * By keeping a copy of the original dialstring used, we can fall back
+ * to using it if the device either does not ever offer CC or if the
+ * device at some point fails for some reason, such as a timer expiration.
+ */
+ char original_dialstring[AST_CHANNEL_NAME];
+ /*!
+ * \brief The name of the device being dialed
+ *
+ * \details
+ * This serves mainly as a key when searching for a particular dialstring.
+ * For instance, let's say that we have called device SIP/400@somepeer. This
+ * device offers call completion, but then due to some unforeseen circumstance,
+ * this device backs out and makes CC unavailable. When that happens, we need
+ * to find the dialstring that corresponds to that device, and we use the
+ * stored device name as a way to find it.
+ *
+ * Note that there is one particular case where the device name stored here
+ * will be empty. This is the case where we fail to request a channel, but we
+ * still can make use of generic call completion. In such a case, since we never
+ * were able to request the channel, we can't find what its device name is. In
+ * this case, however, it is not important because the dialstring is guaranteed
+ * to be the same both here and in the device monitor.
+ */
+ char device_name[AST_CHANNEL_NAME];
+ /*!
+ * \brief Is this structure valid for use in CC_INTERFACES?
+ *
+ * \details
+ * When this structure is first created, all information stored here is planned
+ * to be used, so we set the is_valid flag. However, if a device offers call
+ * completion, it will potentially have its own dialstring to use for the recall,
+ * so we find this structure and clear the is_valid flag. By clearing the is_valid
+ * flag, we won't try to populate the CC_INTERFACES variable with the dialstring
+ * stored in this struct. Now, if later, the device which had offered CC should fail,
+ * perhaps due to a timer expiration, then we need to re-set the is_valid flag. This
+ * way, we still will end up placing a call to the device again, and the dialstring
+ * used will be the same as was originally used.
+ */
+ int is_valid;
+ AST_LIST_ENTRY(extension_child_dialstring) next;
+};
+
+/*!
+ * \brief Private data for an extension monitor
+ */
+struct extension_monitor_pvt {
+ AST_LIST_HEAD_NOLOCK(, extension_child_dialstring) child_dialstrings;
+};
+
+static void cc_extension_monitor_destructor(void *private_data)
+{
+ struct extension_monitor_pvt *extension_pvt = private_data;
+ struct extension_child_dialstring *child_dialstring;
+
+ /* This shouldn't be possible, but I'm paranoid */
+ if (!extension_pvt) {
+ return;
+ }
+
+ while ((child_dialstring = AST_LIST_REMOVE_HEAD(&extension_pvt->child_dialstrings, next))) {
+ ast_free(child_dialstring);
+ }
+ ast_free(extension_pvt);
+}
+
+static void cc_monitor_destroy(void *data)
+{
+ struct ast_cc_monitor *monitor = data;
+ /* During the monitor creation process, it is possible for this
+ * function to be called prior to when callbacks are assigned
+ * to the monitor. Also, extension monitors do not have callbacks
+ * assigned to them, so we wouldn't want to segfault when we try
+ * to destroy one of them.
+ */
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Calling destructor for monitor %s\n",
+ monitor->core_id, monitor->interface->device_name);
+ if (monitor->interface->monitor_class == AST_CC_EXTENSION_MONITOR) {
+ cc_extension_monitor_destructor(monitor->private_data);
+ }
+ if (monitor->callbacks) {
+ monitor->callbacks->destructor(monitor->private_data);
+ }
+ cc_unref(monitor->interface, "Unreffing tree's reference to interface");
+ ast_free(monitor->dialstring);
+}
+
+static void cc_interface_tree_destroy(void *data)
+{
+ struct cc_monitor_tree *cc_interface_tree = data;
+ struct ast_cc_monitor *monitor;
+ while ((monitor = AST_LIST_REMOVE_HEAD(cc_interface_tree, next))) {
+ if (monitor->callbacks) {
+ monitor->callbacks->cancel_available_timer(monitor, &monitor->available_timer_id);
+ }
+ cc_unref(monitor, "Destroying all monitors");
+ }
+ AST_LIST_HEAD_DESTROY(cc_interface_tree);
+}
+
+/*!
+ * This counter is used for assigning unique ids
+ * to CC-enabled dialed interfaces.
+ */
+static int dialed_cc_interface_counter;
+
+/*!
+ * \internal
+ * \brief data stored in CC datastore
+ *
+ * The datastore creates a list of interfaces that were
+ * dialed, including both extensions and devices. In addition
+ * to the intrinsic data of the tree, some extra information
+ * is needed for use by app_dial.
+ */
+struct dialed_cc_interfaces {
+ /*!
+ * This value serves a dual-purpose. When dial starts, if the
+ * dialed_cc_interfaces datastore currently exists on the calling
+ * channel, then the dial_parent_id will serve as a means of
+ * letting the new extension cc_monitor we create know
+ * who his parent is. This value will be the extension
+ * cc_monitor that dialed the local channel that resulted
+ * in the new Dial app being called.
+ *
+ * In addition, once an extension cc_monitor is created,
+ * the dial_parent_id will be changed to the id of that newly
+ * created interface. This way, device interfaces created from
+ * receiving AST_CONTROL_CC frames can use this field to determine
+ * who their parent extension interface should be.
+ */
+ unsigned int dial_parent_id;
+ /*!
+ * Identifier for the potential CC request that may be made
+ * based on this call. Even though an instance of the core may
+ * not be made (since the caller may not request CC), we allocate
+ * a new core_id at the beginning of the call so that recipient
+ * channel drivers can have the information handy just in case
+ * the caller does end up requesting CC.
+ */
+ int core_id;
+ /*!
+ * When a new Dial application is started, and the datastore
+ * already exists on the channel, we can determine if we
+ * should be adding any new interface information to tree.
+ */
+ char ignore;
+ /*!
+ * When it comes time to offer CC to the caller, we only want to offer
+ * it to the original incoming channel. For nested Dials and outbound
+ * channels, it is incorrect to attempt such a thing. This flag indicates
+ * if the channel to which this datastore is attached may be legally
+ * offered CC when the call is finished.
+ */
+ char is_original_caller;
+ /*!
+ * Reference-counted "tree" of interfaces.
+ */
+ struct cc_monitor_tree *interface_tree;
+};
+
+/*!
+ * \internal
+ * \brief Destructor function for cc_interfaces datastore
+ *
+ * This function will free the actual datastore and drop
+ * the refcount for the monitor tree by one. In cases
+ * where CC can actually be used, this unref will not
+ * result in the destruction of the monitor tree, because
+ * the CC core will still have a reference.
+ *
+ * \param data The dialed_cc_interfaces struct to destroy
+ */
+static void dialed_cc_interfaces_destroy(void *data)
+{
+ struct dialed_cc_interfaces *cc_interfaces = data;
+ cc_unref(cc_interfaces->interface_tree, "Unref dial's ref to monitor tree");
+ ast_free(cc_interfaces);
+}
+
+/*!
+ * \internal
+ * \brief Duplicate callback for cc_interfaces datastore
+ *
+ * Integers are copied by value, but the monitor tree
+ * is done via a shallow copy and a bump of the refcount.
+ * This way, sub-Dials will be appending interfaces onto
+ * the same list as this call to Dial.
+ *
+ * \param data The old dialed_cc_interfaces we want to copy
+ * \retval NULL Could not allocate memory for new dialed_cc_interfaces
+ * \retval non-NULL The new copy of the dialed_cc_interfaces
+ */
+static void *dialed_cc_interfaces_duplicate(void *data)
+{
+ struct dialed_cc_interfaces *old_cc_interfaces = data;
+ struct dialed_cc_interfaces *new_cc_interfaces = ast_calloc(1, sizeof(*new_cc_interfaces));
+ if (!new_cc_interfaces) {
+ return NULL;
+ }
+ new_cc_interfaces->ignore = old_cc_interfaces->ignore;
+ new_cc_interfaces->dial_parent_id = old_cc_interfaces->dial_parent_id;
+ new_cc_interfaces->is_original_caller = 0;
+ cc_ref(old_cc_interfaces->interface_tree, "New ref due to duplication of monitor tree");
+ new_cc_interfaces->core_id = old_cc_interfaces->core_id;
+ new_cc_interfaces->interface_tree = old_cc_interfaces->interface_tree;
+ return new_cc_interfaces;
+}
+
+/*!
+ * \internal
+ * \brief information regarding the dialed_cc_interfaces datastore
+ *
+ * The dialed_cc_interfaces datastore is responsible for keeping track
+ * of what CC-enabled interfaces have been dialed by the caller. For
+ * more information regarding the actual structure of the tree, see
+ * the documentation provided in include/asterisk/ccss.h
+ */
+static const struct ast_datastore_info dialed_cc_interfaces_info = {
+ .type = "Dial CC Interfaces",
+ .duplicate = dialed_cc_interfaces_duplicate,
+ .destroy = dialed_cc_interfaces_destroy,
+};
+
+static struct extension_monitor_pvt *extension_monitor_pvt_init(void)
+{
+ struct extension_monitor_pvt *ext_pvt = ast_calloc(1, sizeof(*ext_pvt));
+ if (!ext_pvt) {
+ return NULL;
+ }
+ AST_LIST_HEAD_INIT_NOLOCK(&ext_pvt->child_dialstrings);
+ return ext_pvt;
+}
+
+void ast_cc_extension_monitor_add_dialstring(struct ast_channel *incoming, const char * const dialstring, const char * const device_name)
+{
+ struct ast_datastore *cc_datastore;
+ struct dialed_cc_interfaces *cc_interfaces;
+ struct ast_cc_monitor *monitor;
+ struct extension_monitor_pvt *extension_pvt;
+ struct extension_child_dialstring *child_dialstring;
+ struct cc_monitor_tree *interface_tree;
+ int id;
+
+ ast_channel_lock(incoming);
+ if (!(cc_datastore = ast_channel_datastore_find(incoming, &dialed_cc_interfaces_info, NULL))) {
+ ast_channel_unlock(incoming);
+ return;
+ }
+
+ cc_interfaces = cc_datastore->data;
+ interface_tree = cc_interfaces->interface_tree;
+ id = cc_interfaces->dial_parent_id;
+ ast_channel_unlock(incoming);
+
+ AST_LIST_LOCK(interface_tree);
+ AST_LIST_TRAVERSE(interface_tree, monitor, next) {
+ if (monitor->id == id) {
+ break;
+ }
+ }
+
+ if (!monitor) {
+ AST_LIST_UNLOCK(interface_tree);
+ return;
+ }
+
+ extension_pvt = monitor->private_data;
+ if (!(child_dialstring = ast_calloc(1, sizeof(*child_dialstring)))) {
+ AST_LIST_UNLOCK(interface_tree);
+ return;
+ }
+ ast_copy_string(child_dialstring->original_dialstring, dialstring, sizeof(child_dialstring->original_dialstring));
+ ast_copy_string(child_dialstring->device_name, device_name, sizeof(child_dialstring->device_name));
+ child_dialstring->is_valid = 1;
+ AST_LIST_INSERT_TAIL(&extension_pvt->child_dialstrings, child_dialstring, next);
+ AST_LIST_UNLOCK(interface_tree);
+}
+
+static void cc_extension_monitor_change_is_valid(struct cc_core_instance *core_instance, unsigned int parent_id, const char * const device_name, int is_valid)
+{
+ struct ast_cc_monitor *monitor_iter;
+ struct extension_monitor_pvt *extension_pvt;
+ struct extension_child_dialstring *child_dialstring;
+
+ AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->id == parent_id) {
+ break;
+ }
+ }
+
+ if (!monitor_iter) {
+ return;
+ }
+ extension_pvt = monitor_iter->private_data;
+
+ AST_LIST_TRAVERSE(&extension_pvt->child_dialstrings, child_dialstring, next) {
+ if (!strcmp(child_dialstring->device_name, device_name)) {
+ child_dialstring->is_valid = is_valid;
+ break;
+ }
+ }
+}
+
+/*!
+ * \internal
+ * \brief Allocate and initialize an "extension" interface for CC purposes
+ *
+ * When app_dial starts, this function is called in order to set up the
+ * information about the extension in which this Dial is occurring. Any
+ * devices dialed will have this particular cc_monitor as a parent.
+ *
+ * \param exten Extension from which Dial is occurring
+ * \param context Context to which exten belongs
+ * \param parent_id What should we set the parent_id of this interface to?
+ * \retval NULL Memory allocation failure
+ * \retval non-NULL The newly-created cc_monitor for the extension
+ */
+static struct ast_cc_monitor *cc_extension_monitor_init(const char * const exten, const char * const context, const unsigned int parent_id)
+{
+ struct ast_str *str = ast_str_alloca(2 * AST_MAX_EXTENSION);
+ struct ast_cc_interface *cc_interface;
+ struct ast_cc_monitor *monitor;
+
+ ast_str_set(&str, 0, "%s@%s", exten, context);
+
+ if (!(cc_interface = ao2_t_alloc(sizeof(*cc_interface) + ast_str_strlen(str), cc_interface_destroy,
+ "Allocating new ast_cc_interface"))) {
+ return NULL;
+ }
+
+ if (!(monitor = ao2_t_alloc(sizeof(*monitor), cc_monitor_destroy, "Allocating new ast_cc_monitor"))) {
+ cc_unref(cc_interface, "failed to allocate the monitor, so unref the interface");
+ return NULL;
+ }
+
+ if (!(monitor->private_data = extension_monitor_pvt_init())) {
+ cc_unref(monitor, "Failed to initialize extension monitor private data. uref monitor");
+ cc_unref(cc_interface, "Failed to initialize extension monitor private data. unref cc_interface");
+ }
+
+ monitor->id = ast_atomic_fetchadd_int(&dialed_cc_interface_counter, +1);
+ monitor->parent_id = parent_id;
+ cc_interface->monitor_type = "extension";
+ cc_interface->monitor_class = AST_CC_EXTENSION_MONITOR;
+ strcpy(cc_interface->device_name, ast_str_buffer(str));
+ monitor->interface = cc_interface;
+ ast_log_dynamic_level(cc_logger_level, "Created an extension cc interface for '%s' with id %d and parent %d\n", cc_interface->device_name, monitor->id, monitor->parent_id);
+ return monitor;
+}
+
+/*!
+ * \internal
+ * \brief allocate dialed_cc_interfaces datastore and initialize fields
+ *
+ * This function is called when Situation 1 occurs in ast_cc_call_init.
+ * See that function for more information on what Situation 1 is.
+ *
+ * In this particular case, we have to do a lot of memory allocation in order
+ * to create the datastore, the data for the datastore, the tree of interfaces
+ * that we'll be adding to, and the initial extension interface for this Dial
+ * attempt.
+ *
+ * \param chan The channel onto which the datastore should be added.
+ * \retval -1 An error occurred
+ * \retval 0 Success
+ */
+static int cc_interfaces_datastore_init(struct ast_channel *chan) {
+ struct dialed_cc_interfaces *interfaces;
+ struct ast_cc_monitor *monitor;
+ struct ast_datastore *dial_cc_datastore;
+
+ /*XXX This may be a bit controversial. In an attempt to not allocate
+ * extra resources, I make sure that a future request will be within
+ * limits. The problem here is that it is reasonable to think that
+ * even if we're not within the limits at this point, we may be by
+ * the time the requestor will have made his request. This may be
+ * deleted at some point.
+ */
+ if (!ast_cc_request_is_within_limits()) {
+ return 0;
+ }
+
+ if (!(interfaces = ast_calloc(1, sizeof(*interfaces)))) {
+ return -1;
+ }
+
+ if (!(monitor = cc_extension_monitor_init(S_OR(chan->macroexten, chan->exten), S_OR(chan->macrocontext, chan->context), 0))) {
+ ast_free(interfaces);
+ return -1;
+ }
+
+ if (!(dial_cc_datastore = ast_datastore_alloc(&dialed_cc_interfaces_info, NULL))) {
+ cc_unref(monitor, "Could not allocate the dialed interfaces datastore. Unreffing monitor");
+ ast_free(interfaces);
+ return -1;
+ }
+
+ if (!(interfaces->interface_tree = ao2_t_alloc(sizeof(*interfaces->interface_tree), cc_interface_tree_destroy,
+ "Allocate monitor tree"))) {
+ ast_datastore_free(dial_cc_datastore);
+ cc_unref(monitor, "Could not allocate monitor tree on dialed interfaces datastore. Unreffing monitor");
+ ast_free(interfaces);
+ return -1;
+ }
+
+ /* Finally, all that allocation is done... */
+ AST_LIST_HEAD_INIT(interfaces->interface_tree);
+ AST_LIST_INSERT_TAIL(interfaces->interface_tree, monitor, next);
+ cc_ref(monitor, "List's reference to extension monitor");
+ dial_cc_datastore->data = interfaces;
+ dial_cc_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+ interfaces->dial_parent_id = monitor->id;
+ interfaces->core_id = monitor->core_id = ast_atomic_fetchadd_int(&core_id_counter, +1);
+ interfaces->is_original_caller = 1;
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, dial_cc_datastore);
+ ast_channel_unlock(chan);
+ cc_unref(monitor, "Unreffing allocation's reference");
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Call a monitor's destructor before the monitor has been allocated
+ * \since 1.8
+ *
+ * \param monitor_type The type of monitor callbacks to use when calling the destructor
+ * \param private_data Data allocated by a channel driver that must be freed
+ *
+ * \details
+ * I'll admit, this is a bit evil.
+ *
+ * When a channel driver determines that it can offer a call completion service to
+ * a caller, it is very likely that the channel driver will need to allocate some
+ * data so that when the time comes to request CC, the channel driver will have the
+ * necessary data at hand.
+ *
+ * The problem is that there are many places where failures may occur before the monitor
+ * has been properly allocated and had its callbacks assigned to it. If one of these
+ * failures should occur, then we still need to let the channel driver know that it
+ * must destroy the data that it allocated.
+ *
+ * \return Nothing
+ */
+static void call_destructor_with_no_monitor(const char * const monitor_type, void *private_data)
+{
+ const struct ast_cc_monitor_callbacks *monitor_callbacks = find_monitor_callbacks(monitor_type);
+
+ if (!monitor_callbacks) {
+ return;
+ }
+
+ monitor_callbacks->destructor(private_data);
+}
+
+/*!
+ * \internal
+ * \brief Allocate and intitialize a device cc_monitor
+ *
+ * For all intents and purposes, this is the same as
+ * cc_extension_monitor_init, except that there is only
+ * a single parameter used for naming the interface.
+ *
+ * This function is called when handling AST_CONTROL_CC frames.
+ * The device has reported that CC is possible, so we add it
+ * to the interface_tree.
+ *
+ * Note that it is not necessarily erroneous to add the same
+ * device to the tree twice. If the same device is called by
+ * two different extension during the same call, then
+ * that is a legitimate situation. Of course, I'm pretty sure
+ * the dialed_interfaces global datastore will not allow that
+ * to happen anyway.
+ *
+ * \param device_name The name of the device being added to the tree
+ * \param dialstring The dialstring used to dial the device being added
+ * \param parent_id The parent of this new tree node.
+ * \retval NULL Memory allocation failure
+ * \retval non-NULL The new ast_cc_interface created.
+ */
+static struct ast_cc_monitor *cc_device_monitor_init(const char * const device_name, const char * const dialstring, const struct cc_control_payload *cc_data, int core_id)
+{
+ struct ast_cc_interface *cc_interface;
+ struct ast_cc_monitor *monitor;
+ size_t device_name_len = strlen(device_name);
+ int parent_id = cc_data->parent_interface_id;
+
+ if (!(cc_interface = ao2_t_alloc(sizeof(*cc_interface) + device_name_len, cc_interface_destroy,
+ "Allocating new ast_cc_interface"))) {
+ return NULL;
+ }
+
+ if (!(cc_interface->config_params = ast_cc_config_params_init())) {
+ cc_unref(cc_interface, "Failed to allocate config params, unref interface");
+ return NULL;
+ }
+
+ if (!(monitor = ao2_t_alloc(sizeof(*monitor), cc_monitor_destroy, "Allocating new ast_cc_monitor"))) {
+ cc_unref(cc_interface, "Failed to allocate monitor, unref interface");
+ return NULL;
+ }
+
+ if (!(monitor->dialstring = ast_strdup(dialstring))) {
+ cc_unref(monitor, "Failed to copy dialable name. Unref monitor");
+ cc_unref(cc_interface, "Failed to copy dialable name");
+ return NULL;
+ }
+
+ if (!(monitor->callbacks = find_monitor_callbacks(cc_data->monitor_type))) {
+ cc_unref(monitor, "Failed to find monitor callbacks. Unref monitor");
+ cc_unref(cc_interface, "Failed to find monitor callbacks");
+ return NULL;
+ }
+
+ strcpy(cc_interface->device_name, device_name);
+ monitor->id = ast_atomic_fetchadd_int(&dialed_cc_interface_counter, +1);
+ monitor->parent_id = parent_id;
+ monitor->core_id = core_id;
+ monitor->service_offered = cc_data->service;
+ monitor->private_data = cc_data->private_data;
+ cc_interface->monitor_type = cc_data->monitor_type;
+ cc_interface->monitor_class = AST_CC_DEVICE_MONITOR;
+ monitor->interface = cc_interface;
+ monitor->available_timer_id = -1;
+ ast_cc_copy_config_params(cc_interface->config_params, &cc_data->config_params);
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Created a device cc interface for '%s' with id %d and parent %d\n",
+ monitor->core_id, cc_interface->device_name, monitor->id, monitor->parent_id);
+ return monitor;
+}
+
+/*!
+ * \details
+ * Unless we are ignoring CC for some reason, we will always
+ * call this function when we read an AST_CONTROL_CC frame
+ * from an outbound channel.
+ *
+ * This function will call cc_device_monitor_init to
+ * create the new cc_monitor for the device from which
+ * we read the frame. In addition, the new device will be added
+ * to the monitor tree on the dialed_cc_interfaces datastore
+ * on the inbound channel.
+ *
+ * If this is the first AST_CONTROL_CC frame that we have handled
+ * for this call, then we will also initialize the CC core for
+ * this call.
+ */
+void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data)
+{
+ char *device_name;
+ char *dialstring;
+ struct ast_cc_monitor *monitor;
+ struct ast_datastore *cc_datastore;
+ struct dialed_cc_interfaces *cc_interfaces;
+ struct cc_control_payload *cc_data = frame_data;
+ struct cc_core_instance *core_instance;
+
+ device_name = cc_data->device_name;
+ dialstring = cc_data->dialstring;
+
+ ast_channel_lock(inbound);
+ if (!(cc_datastore = ast_channel_datastore_find(inbound, &dialed_cc_interfaces_info, NULL))) {
+ ast_log(LOG_WARNING, "Unable to retrieve CC datastore while processing CC frame from '%s'. CC services will be unavailable.\n", device_name);
+ ast_channel_unlock(inbound);
+ call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
+ return;
+ }
+
+ cc_interfaces = cc_datastore->data;
+
+ if (cc_interfaces->ignore) {
+ ast_channel_unlock(inbound);
+ call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
+ return;
+ }
+
+ if (!cc_interfaces->is_original_caller) {
+ /* If the is_original_caller is not set on the *inbound* channel, then
+ * it must be a local channel. As such, we do not want to create a core instance
+ * or an agent for the local channel. Instead, we want to pass this along to the
+ * other side of the local channel so that the original caller can benefit.
+ */
+ ast_channel_unlock(inbound);
+ ast_indicate_data(inbound, AST_CONTROL_CC, cc_data, sizeof(*cc_data));
+ return;
+ }
+
+ core_instance = find_cc_core_instance(cc_interfaces->core_id);
+ if (!core_instance) {
+ core_instance = cc_core_init_instance(inbound, cc_interfaces->interface_tree,
+ cc_interfaces->core_id, cc_data);
+ if (!core_instance) {
+ cc_interfaces->ignore = 1;
+ ast_channel_unlock(inbound);
+ call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
+ return;
+ }
+ }
+
+ ast_channel_unlock(inbound);
+
+ /* Yeah this kind of sucks, but luckily most people
+ * aren't dialing thousands of interfaces on every call
+ *
+ * This traversal helps us to not create duplicate monitors in
+ * case a device queues multiple CC control frames.
+ */
+ AST_LIST_LOCK(cc_interfaces->interface_tree);
+ AST_LIST_TRAVERSE(cc_interfaces->interface_tree, monitor, next) {
+ if (!strcmp(monitor->interface->device_name, device_name)) {
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Device %s sent us multiple CC control frames. Ignoring those beyond the first.\n",
+ core_instance->core_id, device_name);
+ AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+ cc_unref(core_instance, "Returning early from ast_handle_cc_control_frame. Unref core_instance");
+ call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
+ return;
+ }
+ }
+ AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+
+ if (!(monitor = cc_device_monitor_init(device_name, dialstring, cc_data, core_instance->core_id))) {
+ ast_log(LOG_WARNING, "Unable to create CC device interface for '%s'. CC services will be unavailable on this interface.\n", device_name);
+ cc_unref(core_instance, "Returning early from ast_handle_cc_control_frame. Unref core_instance");
+ call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
+ return;
+ }
+
+ AST_LIST_LOCK(cc_interfaces->interface_tree);
+ cc_ref(monitor, "monitor tree's reference to the monitor");
+ AST_LIST_INSERT_TAIL(cc_interfaces->interface_tree, monitor, next);
+ AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+
+ cc_extension_monitor_change_is_valid(core_instance, monitor->parent_id, monitor->interface->device_name, 0);
+
+ manager_event(EVENT_FLAG_CC, "CCAvailable",
+ "CoreID: %d\r\n"
+ "Callee: %s\r\n"
+ "Service: %s\r\n",
+ cc_interfaces->core_id, device_name, cc_service_to_string(cc_data->service)
+ );
+
+ cc_unref(core_instance, "Done with core_instance after handling CC control frame");
+ cc_unref(monitor, "Unref reference from allocating monitor");
+}
+
+int ast_cc_call_init(struct ast_channel *chan, int *ignore_cc)
+{
+ /* There are three situations to deal with here:
+ *
+ * 1. The channel does not have a dialed_cc_interfaces datastore on
+ * it. This means that this is the first time that Dial has
+ * been called. We need to create/initialize the datastore.
+ *
+ * 2. The channel does have a cc_interface datastore on it and
+ * the "ignore" indicator is 0. This means that a Local channel
+ * was called by a "parent" dial. We can check the datastore's
+ * parent field to see who the root of this particular dial tree
+ * is.
+ *
+ * 3. The channel does have a cc_interface datastore on it and
+ * the "ignore" indicator is 1. This means that a second Dial call
+ * is being made from an extension. In this case, we do not
+ * want to make any additions/modifications to the datastore. We
+ * will instead set a flag to indicate that CCSS is completely
+ * disabled for this Dial attempt.
+ */
+
+ struct ast_datastore *cc_interfaces_datastore;
+ struct dialed_cc_interfaces *interfaces;
+ struct ast_cc_monitor *monitor;
+ struct ast_cc_config_params *cc_params;
+
+ ast_channel_lock(chan);
+
+ cc_params = ast_channel_get_cc_config_params(chan);
+ if (!cc_params) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ if (ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_NEVER) {
+ /* We can't offer CC to this caller anyway, so don't bother with CC on this call
+ */
+ *ignore_cc = 1;
+ ast_channel_unlock(chan);
+ ast_log_dynamic_level(cc_logger_level, "Agent policy for %s is 'never'. CC not possible\n", chan->name);
+ return 0;
+ }
+
+ if (!(cc_interfaces_datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
+ /* Situation 1 has occurred */
+ ast_channel_unlock(chan);
+ return cc_interfaces_datastore_init(chan);
+ }
+ interfaces = cc_interfaces_datastore->data;
+ ast_channel_unlock(chan);
+
+ if (interfaces->ignore) {
+ /* Situation 3 has occurred */
+ *ignore_cc = 1;
+ ast_log_dynamic_level(cc_logger_level, "Datastore is present with ignore flag set. Ignoring CC offers on this call\n");
+ return 0;
+ }
+
+ /* Situation 2 has occurred */
+ if (!(monitor = cc_extension_monitor_init(S_OR(chan->macroexten, chan->exten),
+ S_OR(chan->macrocontext, chan->context), interfaces->dial_parent_id))) {
+ return -1;
+ }
+ monitor->core_id = interfaces->core_id;
+ AST_LIST_LOCK(interfaces->interface_tree);
+ cc_ref(monitor, "monitor tree's reference to the monitor");
+ AST_LIST_INSERT_TAIL(interfaces->interface_tree, monitor, next);
+ AST_LIST_UNLOCK(interfaces->interface_tree);
+ interfaces->dial_parent_id = monitor->id;
+ cc_unref(monitor, "Unref monitor's allocation reference");
+ return 0;
+}
+
+int ast_cc_request_is_within_limits(void)
+{
+ return cc_request_count < global_cc_max_requests;
+}
+
+int ast_cc_get_current_core_id(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+ struct dialed_cc_interfaces *cc_interfaces;
+ int core_id_return;
+
+ ast_channel_lock(chan);
+ if (!(datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ cc_interfaces = datastore->data;
+ core_id_return = cc_interfaces->ignore ? -1 : cc_interfaces->core_id;
+ ast_channel_unlock(chan);
+ return core_id_return;
+
+}
+
+static long count_agents(const char * const caller, const int core_id_exception)
+{
+ struct count_agents_cb_data data = {.core_id_exception = core_id_exception,};
+
+ ao2_t_callback_data(cc_core_instances, OBJ_NODATA, count_agents_cb, (char *)caller, &data, "Counting agents");
+ ast_log_dynamic_level(cc_logger_level, "Counted %d agents\n", data.count);
+ return data.count;
+}
+
+static void kill_duplicate_offers(char *caller)
+{
+ unsigned long match_flags = MATCH_NO_REQUEST;
+ ao2_t_callback_data(cc_core_instances, OBJ_UNLINK | OBJ_NODATA, match_agent, caller, &match_flags, "Killing duplicate offers");
+}
+
+static void check_callback_sanity(const struct ast_cc_agent_callbacks *callbacks)
+{
+ ast_assert(callbacks->init != NULL);
+ ast_assert(callbacks->start_offer_timer != NULL);
+ ast_assert(callbacks->stop_offer_timer != NULL);
+ ast_assert(callbacks->ack != NULL);
+ ast_assert(callbacks->status_request != NULL);
+ ast_assert(callbacks->start_monitoring != NULL);
+ ast_assert(callbacks->callee_available != NULL);
+ ast_assert(callbacks->destructor != NULL);
+}
+
+static void agent_destroy(void *data)
+{
+ struct ast_cc_agent *agent = data;
+
+ if (agent->callbacks) {
+ agent->callbacks->destructor(agent);
+ }
+ ast_cc_config_params_destroy(agent->cc_params);
+}
+
+static struct ast_cc_agent *cc_agent_init(struct ast_channel *caller_chan,
+ const char * const caller_name, const int core_id,
+ struct cc_monitor_tree *interface_tree)
+{
+ struct ast_cc_agent *agent;
+ struct ast_cc_config_params *cc_params;
+
+ if (!(agent = ao2_t_alloc(sizeof(*agent) + strlen(caller_name), agent_destroy,
+ "Allocating new ast_cc_agent"))) {
+ return NULL;
+ }
+
+ agent->core_id = core_id;
+ strcpy(agent->device_name, caller_name);
+
+ cc_params = ast_channel_get_cc_config_params(caller_chan);
+ if (!cc_params) {
+ cc_unref(agent, "Could not get channel config params.");
+ return NULL;
+ }
+ if (!(agent->cc_params = ast_cc_config_params_init())) {
+ cc_unref(agent, "Could not init agent config params.");
+ return NULL;
+ }
+ ast_cc_copy_config_params(agent->cc_params, cc_params);
+
+ if (!(agent->callbacks = find_agent_callbacks(caller_chan))) {
+ cc_unref(agent, "Could not find agent callbacks.");
+ return NULL;
+ }
+ check_callback_sanity(agent->callbacks);
+
+ if (agent->callbacks->init(agent, caller_chan)) {
+ cc_unref(agent, "Agent init callback failed.");
+ return NULL;
+ }
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Created an agent for caller %s\n",
+ agent->core_id, agent->device_name);
+ return agent;
+}
+
+/* Generic agent callbacks */
+static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
+static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent);
+static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent);
+static void cc_generic_agent_ack(struct ast_cc_agent *agent);
+static int cc_generic_agent_status_request(struct ast_cc_agent *agent);
+static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent);
+static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent);
+static int cc_generic_agent_recall(struct ast_cc_agent *agent);
+static void cc_generic_agent_destructor(struct ast_cc_agent *agent);
+
+static struct ast_cc_agent_callbacks generic_agent_callbacks = {
+ .type = "generic",
+ .init = cc_generic_agent_init,
+ .start_offer_timer = cc_generic_agent_start_offer_timer,
+ .stop_offer_timer = cc_generic_agent_stop_offer_timer,
+ .ack = cc_generic_agent_ack,
+ .status_request = cc_generic_agent_status_request,
+ .stop_ringing = cc_generic_agent_stop_ringing,
+ .start_monitoring = cc_generic_agent_start_monitoring,
+ .callee_available = cc_generic_agent_recall,
+ .destructor = cc_generic_agent_destructor,
+};
+
+struct cc_generic_agent_pvt {
+ /*!
+ * Subscription to device state
+ *
+ * Used in the CC_CALLER_BUSY state. The
+ * generic agent will subscribe to the
+ * device state of the caller in order to
+ * determine when we may move on
+ */
+ struct ast_event_sub *sub;
+ /*!
+ * Scheduler id of offer timer.
+ */
+ int offer_timer_id;
+ /*!
+ * Caller ID number
+ *
+ * When we re-call the caller, we need
+ * to provide this information to
+ * ast_request_and_dial so that the
+ * information will be present in the
+ * call to the callee
+ */
+ char cid_num[AST_CHANNEL_NAME];
+ /*!
+ * Caller ID name
+ *
+ * See the description of cid_num.
+ * The same applies here, except this
+ * is the caller's name.
+ */
+ char cid_name[AST_CHANNEL_NAME];
+ /*!
+ * Extension dialed
+ *
+ * The original extension dialed. This is used
+ * so that when performing a recall, we can
+ * call the proper extension.
+ */
+ char exten[AST_CHANNEL_NAME];
+ /*!
+ * Context dialed
+ *
+ * The original context dialed. This is used
+ * so that when performaing a recall, we can
+ * call into the proper context
+ */
+ char context[AST_CHANNEL_NAME];
+};
+
+static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan)
+{
+ struct cc_generic_agent_pvt *generic_pvt = ast_calloc(1, sizeof(*generic_pvt));
+
+ if (!generic_pvt) {
+ return -1;
+ }
+
+ generic_pvt->offer_timer_id = -1;
+ ast_copy_string(generic_pvt->cid_num, chan->cid.cid_num, sizeof(generic_pvt->cid_num));
+ ast_copy_string(generic_pvt->cid_name, chan->cid.cid_name, sizeof(generic_pvt->cid_name));
+ ast_copy_string(generic_pvt->exten, S_OR(chan->macroexten, chan->exten), sizeof(generic_pvt->exten));
+ ast_copy_string(generic_pvt->context, S_OR(chan->macrocontext, chan->context), sizeof(generic_pvt->context));
+ agent->private_data = generic_pvt;
+ ast_set_flag(agent, AST_CC_AGENT_SKIP_OFFER);
+ return 0;
+}
+
+static int offer_timer_expire(const void *data)
+{
+ const struct ast_cc_agent *agent = data;
+ struct cc_generic_agent_pvt *agent_pvt = agent->private_data;
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Queuing change request because offer timer has expired.\n",
+ agent->core_id);
+ agent_pvt->offer_timer_id = -1;
+ ast_cc_failed(agent->core_id, "Generic agent %s offer timer expired", agent->device_name);
+ cc_unref((struct ast_cc_agent *)agent, "Remove scheduler's reference to the agent");
+ return 0;
+}
+
+static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent)
+{
+ int when;
+ int sched_id;
+ struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
+
+ ast_assert(cc_sched_thread != NULL);
+ ast_assert(agent->cc_params != NULL);
+
+ when = ast_get_cc_offer_timer(agent->cc_params) * 1000;
+ ast_log_dynamic_level(cc_logger_level, "Core %d: About to schedule offer timer expiration for %d ms\n",
+ agent->core_id, when);
+ if ((sched_id = ast_sched_thread_add(cc_sched_thread, when, offer_timer_expire, cc_ref(agent, "Give scheduler an agent ref"))) == -1) {
+ return -1;
+ }
+ generic_pvt->offer_timer_id = sched_id;
+ return 0;
+}
+
+static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent)
+{
+ struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
+
+ if (generic_pvt->offer_timer_id != -1) {
+ if (!ast_sched_thread_del(cc_sched_thread, generic_pvt->offer_timer_id)) {
+ cc_unref(agent, "Remove scheduler's reference to the agent");
+ }
+ generic_pvt->offer_timer_id = -1;
+ }
+ return 0;
+}
+
+static void cc_generic_agent_ack(struct ast_cc_agent *agent)
+{
+ /* The generic agent doesn't have to do anything special to
+ * acknowledge a CC request. Just return.
+ */
+ return;
+}
+
+static int cc_generic_agent_status_request(struct ast_cc_agent *agent)
+{
+ ast_cc_agent_status_response(agent->core_id, ast_device_state(agent->device_name));
+ return 0;
+}
+
+static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent)
+{
+ struct ast_channel *recall_chan = ast_channel_get_by_name_prefix(agent->device_name, strlen(agent->device_name));
+
+ if (!recall_chan) {
+ return 0;
+ }
+
+ ast_softhangup(recall_chan, AST_SOFTHANGUP_EXPLICIT);
+ return 0;
+}
+
+static int generic_agent_devstate_unsubscribe(void *data)
+{
+ struct ast_cc_agent *agent = data;
+ struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
+
+ if (generic_pvt->sub != NULL) {
+ generic_pvt->sub = ast_event_unsubscribe(generic_pvt->sub);
+ }
+ cc_unref(agent, "Done unsubscribing from devstate");
+ return 0;
+}
+
+static void generic_agent_devstate_cb(const struct ast_event *event, void *userdata)
+{
+ struct ast_cc_agent *agent = userdata;
+
+ /* We can't unsubscribe from device state events here because it causes a deadlock */
+ if (ast_taskprocessor_push(cc_core_taskprocessor, generic_agent_devstate_unsubscribe,
+ cc_ref(agent, "ref agent for device state unsubscription"))) {
+ cc_unref(agent, "Unref agent unsubscribing from devstate failed");
+ }
+ ast_cc_agent_caller_available(agent->core_id, "%s is no longer busy", agent->device_name);
+}
+
+static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent)
+{
+ struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
+ struct ast_str *str = ast_str_alloca(128);
+
+ ast_assert(generic_pvt->sub == NULL);
+ ast_str_set(&str, 0, "Starting to monitor %s device state since it is busy\n", agent->device_name);
+
+ if (!(generic_pvt->sub = ast_event_subscribe(
+ AST_EVENT_DEVICE_STATE, generic_agent_devstate_cb, ast_str_buffer(str), agent,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, agent->device_name,
+ AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, AST_DEVICE_NOT_INUSE,
+ AST_EVENT_IE_END))) {
+ return -1;
+ }
+ return 0;
+}
+
+static void *generic_recall(void *data)
+{
+ struct ast_cc_agent *agent = data;
+ struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
+ const char *interface = S_OR(ast_get_cc_agent_dialstring(agent->cc_params), ast_strdupa(agent->device_name));
+ const char *tech;
+ char *target;
+ int reason;
+ struct ast_channel *chan;
+ const char *callback_macro = ast_get_cc_callback_macro(agent->cc_params);
+ unsigned int recall_timer = ast_get_cc_recall_timer(agent->cc_params) * 1000;
+
+ tech = interface;
+ if ((target = strchr(interface, '/'))) {
+ *target++ = '\0';
+ }
+ if (!(chan = ast_request_and_dial(tech, AST_FORMAT_SLINEAR, NULL, target, recall_timer, &reason, generic_pvt->cid_num, generic_pvt->cid_name))) {
+ /* Hmm, no channel. Sucks for you, bud.
+ */
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Failed to call back %s for reason %d\n",
+ agent->core_id, agent->device_name, reason);
+ ast_cc_failed(agent->core_id, "Failed to call back device %s/%s", tech, target);
+ return NULL;
+ }
+ if (!ast_strlen_zero(callback_macro)) {
+ ast_log_dynamic_level(cc_logger_level, "Core %d: There's a callback macro configured for agent %s\n",
+ agent->core_id, agent->device_name);
+ if (ast_app_run_macro(NULL, chan, callback_macro, NULL)) {
+ ast_cc_failed(agent->core_id, "Callback macro to %s failed. Maybe a hangup?", agent->device_name);
+ ast_hangup(chan);
+ return NULL;
+ }
+ }
+ /* We have a channel. It's time now to set up the datastore of recalled CC interfaces.
+ * This will be a common task for all recall functions. If it were possible, I'd have
+ * the core do it automatically, but alas I cannot. Instead, I will provide a public
+ * function to do so.
+ */
+ ast_setup_cc_recall_datastore(chan, agent->core_id);
+ ast_cc_agent_set_interfaces_chanvar(chan);
+
+ ast_copy_string(chan->exten, generic_pvt->exten, sizeof(chan->exten));
+ ast_copy_string(chan->context, generic_pvt->context, sizeof(chan->context));
+ chan->priority = 1;
+ ast_cc_agent_recalling(agent->core_id, "Generic agent %s is recalling", agent->device_name);
+ ast_pbx_start(chan);
+ return NULL;
+}
+
+static int cc_generic_agent_recall(struct ast_cc_agent *agent)
+{
+ pthread_t clotho;
+ enum ast_device_state current_state = ast_device_state(agent->device_name);
+
+ if (current_state != AST_DEVICE_NOT_INUSE && current_state != AST_DEVICE_UNKNOWN) {
+ /* We can't try to contact the device right now because he's not available
+ * Let the core know he's busy.
+ */
+ ast_cc_agent_caller_busy(agent->core_id, "Generic agent caller %s is busy", agent->device_name);
+ return 0;
+ }
+ ast_pthread_create_detached_background(&clotho, NULL, generic_recall, agent);
+ return 0;
+}
+
+static void cc_generic_agent_destructor(struct ast_cc_agent *agent)
+{
+ struct cc_generic_agent_pvt *agent_pvt = agent->private_data;
+
+ if (!agent_pvt) {
+ /* The agent constructor probably failed. */
+ return;
+ }
+
+ cc_generic_agent_stop_offer_timer(agent);
+ if (agent_pvt->sub) {
+ agent_pvt->sub = ast_event_unsubscribe(agent_pvt->sub);
+ }
+
+ ast_free(agent_pvt);
+}
+
+static void cc_core_instance_destructor(void *data)
+{
+ struct cc_core_instance *core_instance = data;
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Destroying core instance\n", core_instance->core_id);
+ if (core_instance->agent) {
+ cc_unref(core_instance->agent, "Core instance is done with the agent now");
+ }
+ if (core_instance->monitors) {
+ core_instance->monitors = cc_unref(core_instance->monitors, "Core instance is done with interface list");
+ }
+}
+
+static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller_chan,
+ struct cc_monitor_tree *called_tree, const int core_id, struct cc_control_payload *cc_data)
+{
+ char caller[AST_CHANNEL_NAME];
+ struct cc_core_instance *core_instance;
+ struct ast_cc_config_params *cc_params;
+ long agent_count;
+ int recall_core_id;
+
+ ast_channel_get_device_name(caller_chan, caller, sizeof(caller));
+ cc_params = ast_channel_get_cc_config_params(caller_chan);
+ if (!cc_params) {
+ ast_log_dynamic_level(cc_logger_level, "Could not get CC parameters for %s\n",
+ caller);
+ return NULL;
+ }
+ /* First, we need to kill off other pending CC offers from caller. If the caller is going
+ * to request a CC service, it may only be for the latest call he made.
+ */
+ if (ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_GENERIC) {
+ kill_duplicate_offers(caller);
+ }
+
+ ast_cc_is_recall(caller_chan, &recall_core_id, NULL);
+ agent_count = count_agents(caller, recall_core_id);
+ if (agent_count >= ast_get_cc_max_agents(cc_params)) {
+ ast_log_dynamic_level(cc_logger_level, "Caller %s already has the maximum number of agents configured\n", caller);
+ return NULL;
+ }
+
+ /* Generic agents can only have a single outstanding CC request per caller. */
+ if (agent_count > 0 && ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_GENERIC) {
+ ast_log_dynamic_level(cc_logger_level, "Generic agents can only have a single outstanding request\n");
+ return NULL;
+ }
+
+ /* Next, we need to create the core instance for this call */
+ if (!(core_instance = ao2_t_alloc(sizeof(*core_instance), cc_core_instance_destructor, "Creating core instance for CC"))) {
+ return NULL;
+ }
+
+ core_instance->core_id = core_id;
+ if (!(core_instance->agent = cc_agent_init(caller_chan, caller, core_instance->core_id, called_tree))) {
+ cc_unref(core_instance, "Couldn't allocate agent, unref core_instance");
+ return NULL;
+ }
+
+ core_instance->monitors = cc_ref(called_tree, "Core instance getting ref to monitor tree");
+
+ ao2_t_link(cc_core_instances, core_instance, "Link core instance into container");
+
+ return core_instance;
+}
+
+struct cc_state_change_args {
+ enum cc_state state;
+ int core_id;
+ char debug[1];
+};
+
+static int is_state_change_valid(enum cc_state current_state, const enum cc_state new_state, struct ast_cc_agent *agent)
+{
+ int is_valid = 0;
+ switch (new_state) {
+ case CC_AVAILABLE:
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Asked to change to state %d? That should never happen.\n",
+ agent->core_id, new_state);
+ break;
+ case CC_CALLER_OFFERED:
+ if (current_state == CC_AVAILABLE) {
+ is_valid = 1;
+ }
+ break;
+ case CC_CALLER_REQUESTED:
+ if (current_state == CC_CALLER_OFFERED ||
+ (current_state == CC_AVAILABLE && ast_test_flag(agent, AST_CC_AGENT_SKIP_OFFER))) {
+ is_valid = 1;
+ }
+ break;
+ case CC_ACTIVE:
+ if (current_state == CC_CALLER_REQUESTED || current_state == CC_CALLER_BUSY) {
+ is_valid = 1;
+ }
+ break;
+ case CC_CALLEE_READY:
+ if (current_state == CC_ACTIVE) {
+ is_valid = 1;
+ }
+ break;
+ case CC_CALLER_BUSY:
+ if (current_state == CC_CALLEE_READY) {
+ is_valid = 1;
+ }
+ break;
+ case CC_RECALLING:
+ if (current_state == CC_CALLEE_READY) {
+ is_valid = 1;
+ }
+ break;
+ case CC_COMPLETE:
+ if (current_state == CC_RECALLING) {
+ is_valid = 1;
+ }
+ break;
+ case CC_FAILED:
+ is_valid = 1;
+ break;
+ default:
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Asked to change to unknown state %d\n",
+ agent->core_id, new_state);
+ break;
+ }
+
+ return is_valid;
+}
+
+static int cc_available(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ /* This should never happen... */
+ ast_log(LOG_WARNING, "Someone requested to change to CC_AVAILABLE? Ignoring.\n");
+ return -1;
+}
+
+static int cc_caller_offered(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ if (core_instance->agent->callbacks->start_offer_timer(core_instance->agent)) {
+ ast_cc_failed(core_instance->core_id, "Failed to start the offer timer for %s\n",
+ core_instance->agent->device_name);
+ return -1;
+ }
+ manager_event(EVENT_FLAG_CC, "CCOfferTimerStart",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n"
+ "Expires: %u\r\n",
+ core_instance->core_id, core_instance->agent->device_name, core_instance->agent->cc_params->cc_offer_timer);
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Started the offer timer for the agent %s!\n",
+ core_instance->core_id, core_instance->agent->device_name);
+ return 0;
+}
+
+/*!
+ * \brief check if the core instance has any device monitors
+ *
+ * In any case where we end up removing a device monitor from the
+ * list of device monitors, it is important to see what the state
+ * of the list is afterwards. If we find that we only have extension
+ * monitors left, then no devices are actually being monitored.
+ * In such a case, we need to declare that CC has failed for this
+ * call. This function helps those cases to determine if they should
+ * declare failure.
+ *
+ * \param core_instance The core instance we are checking for the existence
+ * of device monitors
+ * \retval 0 No device monitors exist on this core_instance
+ * \retval 1 There is still at least 1 device monitor remaining
+ */
+static int has_device_monitors(struct cc_core_instance *core_instance)
+{
+ struct ast_cc_monitor *iter;
+ int res = 0;
+
+ AST_LIST_TRAVERSE(core_instance->monitors, iter, next) {
+ if (iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ res = 1;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static void request_cc(struct cc_core_instance *core_instance)
+{
+ struct ast_cc_monitor *monitor_iter;
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ if (monitor_iter->callbacks->request_cc(monitor_iter, &monitor_iter->available_timer_id)) {
+ AST_LIST_REMOVE_CURRENT(next);
+ cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id,
+ monitor_iter->interface->device_name, 1);
+ cc_unref(monitor_iter, "request_cc failed. Unref list's reference to monitor");
+ } else {
+ manager_event(EVENT_FLAG_CC, "CCRequested",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n"
+ "Callee: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name, monitor_iter->interface->device_name);
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!has_device_monitors(core_instance)) {
+ ast_cc_failed(core_instance->core_id, "All device monitors failed to request CC");
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+}
+
+static int cc_caller_requested(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ if (!ast_cc_request_is_within_limits()) {
+ ast_log(LOG_WARNING, "Cannot request CC since there is no more room for requests\n");
+ ast_cc_failed(core_instance->core_id, "Too many requests in the system");
+ return -1;
+ }
+ core_instance->agent->callbacks->stop_offer_timer(core_instance->agent);
+ request_cc(core_instance);
+ return 0;
+}
+
+static void unsuspend(struct cc_core_instance *core_instance)
+{
+ struct ast_cc_monitor *monitor_iter;
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ if (monitor_iter->callbacks->unsuspend(monitor_iter)) {
+ AST_LIST_REMOVE_CURRENT(next);
+ cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id,
+ monitor_iter->interface->device_name, 1);
+ cc_unref(monitor_iter, "unsuspend failed. Unref list's reference to monitor");
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!has_device_monitors(core_instance)) {
+ ast_cc_failed(core_instance->core_id, "All device monitors failed to unsuspend CC");
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+}
+
+static int cc_active(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ /* Either
+ * 1. Callee accepted CC request, call agent's ack callback.
+ * 2. Caller became available, call agent's stop_monitoring callback and
+ * call monitor's unsuspend callback.
+ */
+ if (previous_state == CC_CALLER_REQUESTED) {
+ core_instance->agent->callbacks->ack(core_instance->agent);
+ manager_event(EVENT_FLAG_CC, "CCRequestAcknowledged",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name);
+ } else if (previous_state == CC_CALLER_BUSY) {
+ manager_event(EVENT_FLAG_CC, "CCCallerStopMonitoring",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name);
+ unsuspend(core_instance);
+ }
+ /* Not possible for previous_state to be anything else due to the is_state_change_valid check at the beginning */
+ return 0;
+}
+
+static int cc_callee_ready(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ core_instance->agent->callbacks->callee_available(core_instance->agent);
+ return 0;
+}
+
+static void suspend(struct cc_core_instance *core_instance)
+{
+ struct ast_cc_monitor *monitor_iter;
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ if (monitor_iter->callbacks->suspend(monitor_iter)) {
+ AST_LIST_REMOVE_CURRENT(next);
+ cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id,
+ monitor_iter->interface->device_name, 1);
+ cc_unref(monitor_iter, "suspend failed. Unref list's reference to monitor");
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!has_device_monitors(core_instance)) {
+ ast_cc_failed(core_instance->core_id, "All device monitors failed to suspend CC");
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+}
+
+static int cc_caller_busy(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ /* Callee was available, but caller was busy, call agent's begin_monitoring callback
+ * and call monitor's suspend callback.
+ */
+ suspend(core_instance);
+ core_instance->agent->callbacks->start_monitoring(core_instance->agent);
+ manager_event(EVENT_FLAG_CC, "CCCallerStartMonitoring",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name);
+ return 0;
+}
+
+static void cancel_available_timer(struct cc_core_instance *core_instance)
+{
+ struct ast_cc_monitor *monitor_iter;
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ if (monitor_iter->callbacks->cancel_available_timer(monitor_iter, &monitor_iter->available_timer_id)) {
+ AST_LIST_REMOVE_CURRENT(next);
+ cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id,
+ monitor_iter->interface->device_name, 1);
+ cc_unref(monitor_iter, "cancel_available_timer failed. Unref list's reference to monitor");
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!has_device_monitors(core_instance)) {
+ ast_cc_failed(core_instance->core_id, "All device monitors failed to cancel their available timers");
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+}
+
+static int cc_recalling(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ /* Both caller and callee are available, call agent's recall callback
+ */
+ cancel_available_timer(core_instance);
+ manager_event(EVENT_FLAG_CC, "CCCallerRecalling",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name);
+ return 0;
+}
+
+static int cc_complete(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ /* Recall has made progress, call agent and monitor destructor functions
+ */
+ manager_event(EVENT_FLAG_CC, "CCRecallComplete",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name);
+ ao2_t_unlink(cc_core_instances, core_instance, "Unlink core instance since CC recall has completed");
+ return 0;
+}
+
+static int cc_failed(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
+{
+ /* Something along the way failed, call agent and monitor destructor functions
+ */
+ manager_event(EVENT_FLAG_CC, "CCFailure",
+ "CoreID: %d\r\n"
+ "Caller: %s\r\n"
+ "Reason: %s\r\n",
+ core_instance->core_id, core_instance->agent->device_name, args->debug);
+ ao2_t_unlink(cc_core_instances, core_instance, "Unlink core instance since CC failed");
+ return 0;
+}
+
+static int (* const state_change_funcs [])(struct cc_core_instance *, struct cc_state_change_args *, enum cc_state previous_state) = {
+ [CC_AVAILABLE] = cc_available,
+ [CC_CALLER_OFFERED] = cc_caller_offered,
+ [CC_CALLER_REQUESTED] = cc_caller_requested,
+ [CC_ACTIVE] = cc_active,
+ [CC_CALLEE_READY] = cc_callee_ready,
+ [CC_CALLER_BUSY] = cc_caller_busy,
+ [CC_RECALLING] = cc_recalling,
+ [CC_COMPLETE] = cc_complete,
+ [CC_FAILED] = cc_failed,
+};
+
+static int cc_do_state_change(void *datap)
+{
+ struct cc_state_change_args *args = datap;
+ struct cc_core_instance *core_instance;
+ enum cc_state previous_state;
+ int res;
+
+ ast_log_dynamic_level(cc_logger_level, "Core %d: State change to %d requested. Reason: %s\n",
+ args->core_id, args->state, args->debug);
+
+ if (!(core_instance = find_cc_core_instance(args->core_id))) {
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Unable to find core instance.\n", args->core_id);
+ ast_free(args);
+ return -1;
+ }
+
+ if (!is_state_change_valid(core_instance->current_state, args->state, core_instance->agent)) {
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Invalid state change requested. Cannot go from %s to %s\n",
+ args->core_id, cc_state_to_string(core_instance->current_state), cc_state_to_string(args->state));
+ ast_free(args);
+ cc_unref(core_instance, "Unref core instance from when it was found earlier");
+ return -1;
+ }
+
+ /* We can change to the new state now. */
+ previous_state = core_instance->current_state;
+ core_instance->current_state = args->state;
+ res = state_change_funcs[core_instance->current_state](core_instance, args, previous_state);
+
+ ast_free(args);
+ cc_unref(core_instance, "Unref since state change has completed"); /* From ao2_find */
+ return res;
+}
+
+static int cc_request_state_change(enum cc_state state, const int core_id, const char *debug, va_list ap)
+{
+ int res;
+ int debuglen;
+ char dummy[1];
+ va_list aq;
+ struct cc_state_change_args *args;
+ /* This initial call to vsnprintf is simply to find what the
+ * size of the string needs to be
+ */
+ va_copy(aq, ap);
+ /* We add 1 to the result since vsnprintf's return does not
+ * include the terminating null byte
+ */
+ debuglen = vsnprintf(dummy, sizeof(dummy), debug, aq) + 1;
+ va_end(aq);
+
+ if (!(args = ast_calloc(1, sizeof(*args) + debuglen))) {
+ return -1;
+ }
+
+ args->state = state;
+ args->core_id = core_id;
+ vsnprintf(args->debug, debuglen, debug, ap);
+
+ res = ast_taskprocessor_push(cc_core_taskprocessor, cc_do_state_change, args);
+ if (res) {
+ ast_free(args);
+ }
+ return res;
+}
+
+struct cc_recall_ds_data {
+ int core_id;
+ char ignore;
+ char nested;
+ struct cc_monitor_tree *interface_tree;
+};
+
+static void *cc_recall_ds_duplicate(void *data)
+{
+ struct cc_recall_ds_data *old_data = data;
+ struct cc_recall_ds_data *new_data = ast_calloc(1, sizeof(*new_data));
+
+ if (!new_data) {
+ return NULL;
+ }
+ new_data->interface_tree = cc_ref(old_data->interface_tree, "Bump refcount of monitor tree for recall datastore duplicate");
+ new_data->core_id = old_data->core_id;
+ new_data->nested = 1;
+ return new_data;
+}
+
+static void cc_recall_ds_destroy(void *data)
+{
+ struct cc_recall_ds_data *recall_data = data;
+ recall_data->interface_tree = cc_unref(recall_data->interface_tree, "Unref recall monitor tree");
+ ast_free(recall_data);
+}
+
+static struct ast_datastore_info recall_ds_info = {
+ .type = "cc_recall",
+ .duplicate = cc_recall_ds_duplicate,
+ .destroy = cc_recall_ds_destroy,
+};
+
+int ast_setup_cc_recall_datastore(struct ast_channel *chan, const int core_id)
+{
+ struct ast_datastore *recall_datastore = ast_datastore_alloc(&recall_ds_info, NULL);
+ struct cc_recall_ds_data *recall_data;
+ struct cc_core_instance *core_instance;
+
+ if (!recall_datastore) {
+ return -1;
+ }
+
+ if (!(recall_data = ast_calloc(1, sizeof(*recall_data)))) {
+ ast_datastore_free(recall_datastore);
+ return -1;
+ }
+
+ if (!(core_instance = find_cc_core_instance(core_id))) {
+ ast_free(recall_data);
+ ast_datastore_free(recall_datastore);
+ return -1;
+ }
+
+ recall_data->interface_tree = cc_ref(core_instance->monitors,
+ "Bump refcount for monitor tree for recall datastore");
+ recall_data->core_id = core_id;
+ recall_datastore->data = recall_data;
+ recall_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, recall_datastore);
+ ast_channel_unlock(chan);
+ cc_unref(core_instance, "Recall datastore set up. No need for core_instance ref");
+ return 0;
+}
+
+int ast_cc_is_recall(struct ast_channel *chan, int *core_id, const char * const monitor_type)
+{
+ struct ast_datastore *recall_datastore;
+ struct cc_recall_ds_data *recall_data;
+ struct cc_monitor_tree *interface_tree;
+ char device_name[AST_CHANNEL_NAME];
+ struct ast_cc_monitor *device_monitor;
+ int core_id_candidate;
+
+ ast_assert(core_id != NULL);
+
+ *core_id = -1;
+
+ ast_channel_lock(chan);
+ if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) {
+ /* Obviously not a recall if the datastore isn't present */
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ recall_data = recall_datastore->data;
+
+ if (recall_data->ignore) {
+ /* Though this is a recall, the call to this particular interface is not part of the
+ * recall either because this is a call forward or because this is not the first
+ * invocation of Dial during this call
+ */
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ if (!recall_data->nested) {
+ /* If the nested flag is not set, then this means that
+ * the channel passed to this function is the caller making
+ * the recall. This means that we shouldn't look through
+ * the monitor tree for the channel because it shouldn't be
+ * there. However, this is a recall though, so return true.
+ */
+ *core_id = recall_data->core_id;
+ ast_channel_unlock(chan);
+ return 1;
+ }
+
+ if (ast_strlen_zero(monitor_type)) {
+ /* If someone passed a NULL or empty monitor type, then it is clear
+ * the channel they passed in was an incoming channel, and so searching
+ * the list of dialed interfaces is not going to be helpful. Just return
+ * false immediately.
+ */
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ interface_tree = recall_data->interface_tree;
+ ast_channel_get_device_name(chan, device_name, sizeof(device_name));
+ /* We grab the value of the recall_data->core_id so that we
+ * can unlock the channel before we start looking through the
+ * interface list. That way we don't have to worry about a possible
+ * clash between the channel lock and the monitor tree lock.
+ */
+ core_id_candidate = recall_data->core_id;
+ ast_channel_unlock(chan);
+
+ /*
+ * Now we need to find out if the channel device name
+ * is in the list of interfaces in the called tree.
+ */
+ AST_LIST_LOCK(interface_tree);
+ AST_LIST_TRAVERSE(interface_tree, device_monitor, next) {
+ if (!strcmp(device_monitor->interface->device_name, device_name) &&
+ !strcmp(device_monitor->interface->monitor_type, monitor_type)) {
+ /* BOOM! Device is in the tree! We have a winner! */
+ *core_id = core_id_candidate;
+ AST_LIST_UNLOCK(interface_tree);
+ return 1;
+ }
+ }
+ AST_LIST_UNLOCK(interface_tree);
+ return 0;
+}
+
+struct ast_cc_monitor *ast_cc_get_monitor_by_recall_core_id(const int core_id, const char * const device_name)
+{
+ struct cc_core_instance *core_instance = find_cc_core_instance(core_id);
+ struct ast_cc_monitor *monitor_iter;
+
+ if (!core_instance) {
+ return NULL;
+ }
+
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) {
+ if (!strcmp(monitor_iter->interface->device_name, device_name)) {
+ /* Found a monitor. */
+ cc_ref(monitor_iter, "Hand the requester of the monitor a reference");
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+ cc_unref(core_instance, "Done with core instance ref in ast_cc_get_monitor_by_recall_core_id");
+ return monitor_iter;
+}
+
+/*!
+ * \internal
+ * \brief uniquely append a dialstring to our CC_INTERFACES chanvar string.
+ *
+ * We will only append a string if it has not already appeared in our channel
+ * variable earlier. We ensure that we don't erroneously match substrings by
+ * adding an ampersand to the end of our potential dialstring and searching for
+ * it plus the ampersand in our variable.
+ *
+ * It's important to note that once we have built the full CC_INTERFACES string,
+ * there will be an extra ampersand at the end which must be stripped off by
+ * the caller of this function.
+ *
+ * \param str An ast_str holding what we will add to CC_INTERFACES
+ * \param dialstring A new dialstring to add
+ * \retval void
+ */
+static void cc_unique_append(struct ast_str *str, const char * const dialstring)
+{
+ char dialstring_search[AST_CHANNEL_NAME];
+
+ snprintf(dialstring_search, sizeof(dialstring_search), "%s%c", dialstring, '&');
+ if (strstr(ast_str_buffer(str), dialstring_search)) {
+ return;
+ }
+ ast_str_append(&str, 0, "%s", dialstring_search);
+}
+
+/*!
+ * \internal
+ * \brief Build the CC_INTERFACES channel variable
+ *
+ * The method used is to traverse the child dialstrings in the
+ * passed-in extension monitor, adding any that have the is_valid
+ * flag set. Then, traverse the monitors, finding all children
+ * of the starting extension monitor and adding their dialstrings
+ * as well.
+ *
+ * \param starting_point The extension monitor that is the parent to all
+ * monitors whose dialstrings should be added to CC_INTERFACES
+ * \param str Where we will store CC_INTERFACES
+ * \retval void
+ */
+static void build_cc_interfaces_chanvar(struct ast_cc_monitor *starting_point, struct ast_str *str)
+{
+ struct extension_monitor_pvt *extension_pvt;
+ struct extension_child_dialstring *child_dialstring;
+ struct ast_cc_monitor *monitor_iter = starting_point;
+ int top_level_id = starting_point->id;
+
+ /* First we need to take all of the is_valid child_dialstrings from
+ * the extension monitor we found and add them to the CC_INTERFACES
+ * chanvar
+ */
+ extension_pvt = starting_point->private_data;
+ AST_LIST_TRAVERSE(&extension_pvt->child_dialstrings, child_dialstring, next) {
+ if (child_dialstring->is_valid) {
+ cc_unique_append(str, child_dialstring->original_dialstring);
+ }
+ }
+
+ /* And now we get the dialstrings from each of the device monitors */
+ while ((monitor_iter = AST_LIST_NEXT(monitor_iter, next))) {
+ if (monitor_iter->parent_id == top_level_id) {
+ cc_unique_append(str, monitor_iter->dialstring);
+ }
+ }
+
+ /* str will have an extra '&' tacked onto the end of it, so we need
+ * to get rid of that.
+ */
+ ast_str_truncate(str, ast_str_strlen(str) - 1);
+}
+
+int ast_cc_agent_set_interfaces_chanvar(struct ast_channel *chan)
+{
+ struct ast_datastore *recall_datastore;
+ struct cc_monitor_tree *interface_tree;
+ struct ast_cc_monitor *monitor;
+ struct cc_recall_ds_data *recall_data;
+ struct ast_str *str = ast_str_create(64);
+ int core_id;
+
+ if (!str) {
+ return -1;
+ }
+
+ ast_channel_lock(chan);
+ if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) {
+ ast_channel_unlock(chan);
+ ast_free(str);
+ return -1;
+ }
+ recall_data = recall_datastore->data;
+ interface_tree = recall_data->interface_tree;
+ core_id = recall_data->core_id;
+ ast_channel_unlock(chan);
+
+ AST_LIST_LOCK(interface_tree);
+ monitor = AST_LIST_FIRST(interface_tree);
+ build_cc_interfaces_chanvar(monitor, str);
+ AST_LIST_UNLOCK(interface_tree);
+
+ pbx_builtin_setvar_helper(chan, "CC_INTERFACES", ast_str_buffer(str));
+ ast_log_dynamic_level(cc_logger_level, "Core %d: CC_INTERFACES set to %s\n",
+ core_id, ast_str_buffer(str));
+
+ ast_free(str);
+ return 0;
+}
+
+int ast_set_cc_interfaces_chanvar(struct ast_channel *chan, const char * const extension)
+{
+ struct ast_datastore *recall_datastore;
+ struct cc_monitor_tree *interface_tree;
+ struct ast_cc_monitor *monitor_iter;
+ struct cc_recall_ds_data *recall_data;
+ struct ast_str *str = ast_str_create(64);
+ int core_id;
+
+ if (!str) {
+ return -1;
+ }
+
+ ast_channel_lock(chan);
+ if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) {
+ ast_channel_unlock(chan);
+ ast_free(str);
+ return -1;
+ }
+ recall_data = recall_datastore->data;
+ interface_tree = recall_data->interface_tree;
+ core_id = recall_data->core_id;
+ ast_channel_unlock(chan);
+
+ AST_LIST_LOCK(interface_tree);
+ AST_LIST_TRAVERSE(interface_tree, monitor_iter, next) {
+ if (!strcmp(monitor_iter->interface->device_name, extension)) {
+ break;
+ }
+ }
+
+ if (!monitor_iter) {
+ /* We couldn't find this extension. This may be because
+ * we have been directed into an unexpected extension because
+ * the admin has changed a CC_INTERFACES variable at some point.
+ */
+ AST_LIST_UNLOCK(interface_tree);
+ ast_free(str);
+ return -1;
+ }
+
+ build_cc_interfaces_chanvar(monitor_iter, str);
+ AST_LIST_UNLOCK(interface_tree);
+
+ pbx_builtin_setvar_helper(chan, "CC_INTERFACES", ast_str_buffer(str));
+ ast_log_dynamic_level(cc_logger_level, "Core %d: CC_INTERFACES set to %s\n",
+ core_id, ast_str_buffer(str));
+
+ ast_free(str);
+ return 0;
+}
+
+void ast_ignore_cc(struct ast_channel *chan)
+{
+ struct ast_datastore *cc_datastore;
+ struct ast_datastore *cc_recall_datastore;
+ struct dialed_cc_interfaces *cc_interfaces;
+ struct cc_recall_ds_data *recall_cc_data;
+
+ ast_channel_lock(chan);
+ if ((cc_datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
+ cc_interfaces = cc_datastore->data;
+ cc_interfaces->ignore = 1;
+ }
+
+ if ((cc_recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) {
+ recall_cc_data = cc_recall_datastore->data;
+ recall_cc_data->ignore = 1;
+ }
+ ast_channel_unlock(chan);
+}
+
+static __attribute__((format(printf, 2, 3))) int cc_offer(const int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_CALLER_OFFERED, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_offer(struct ast_channel *caller_chan)
+{
+ int core_id;
+ int res = -1;
+ struct ast_datastore *datastore;
+ struct dialed_cc_interfaces *cc_interfaces;
+ char cc_is_offerable;
+
+ ast_channel_lock(caller_chan);
+ if (!(datastore = ast_channel_datastore_find(caller_chan, &dialed_cc_interfaces_info, NULL))) {
+ ast_channel_unlock(caller_chan);
+ return res;
+ }
+
+ cc_interfaces = datastore->data;
+ cc_is_offerable = cc_interfaces->is_original_caller;
+ core_id = cc_interfaces->core_id;
+ ast_channel_unlock(caller_chan);
+
+ if (cc_is_offerable) {
+ res = cc_offer(core_id, "CC offered to caller %s", caller_chan->name);
+ }
+ return res;
+}
+
+int ast_cc_agent_accept_request(int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_CALLER_REQUESTED, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_monitor_request_acked(int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_ACTIVE, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_monitor_callee_available(const int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_CALLEE_READY, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_agent_caller_busy(int core_id, const char * debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_CALLER_BUSY, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_agent_caller_available(int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_ACTIVE, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_agent_recalling(int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_RECALLING, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_completed(struct ast_channel *chan, const char * const debug, ...)
+{
+ struct ast_datastore *recall_datastore;
+ struct cc_recall_ds_data *recall_data;
+ int core_id;
+ va_list ap;
+ int res;
+
+ ast_channel_lock(chan);
+ if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) {
+ /* Silly! Why did you call this function if there's no recall DS? */
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ recall_data = recall_datastore->data;
+ if (recall_data->nested || recall_data->ignore) {
+ /* If this is being called from a nested Dial, it is too
+ * early to determine if the recall has actually completed.
+ * The outermost dial is the only one with the authority to
+ * declare the recall to be complete.
+ *
+ * Similarly, if this function has been called when the
+ * recall has progressed beyond the first dial, this is not
+ * a legitimate time to declare the recall to be done. In fact,
+ * that should have been done already.
+ */
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ core_id = recall_data->core_id;
+ ast_channel_unlock(chan);
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_COMPLETE, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+int ast_cc_failed(int core_id, const char * const debug, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, debug);
+ res = cc_request_state_change(CC_FAILED, core_id, debug, ap);
+ va_end(ap);
+ return res;
+}
+
+struct ast_cc_monitor_failure_data {
+ const char *device_name;
+ char *debug;
+ int core_id;
+};
+
+static int cc_monitor_failed(void *data)
+{
+ struct ast_cc_monitor_failure_data *failure_data = data;
+ struct cc_core_instance *core_instance;
+ struct ast_cc_monitor *monitor_iter;
+
+ core_instance = find_cc_core_instance(failure_data->core_id);
+ if (!core_instance) {
+ /* Core instance no longer exists or invalid core_id. */
+ ast_log_dynamic_level(cc_logger_level,
+ "Core %d: Could not find core instance for device %s '%s'\n",
+ failure_data->core_id, failure_data->device_name, failure_data->debug);
+ ast_free((char *) failure_data->device_name);
+ ast_free((char *) failure_data->debug);
+ ast_free(failure_data);
+ return -1;
+ }
+
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ if (!strcmp(monitor_iter->interface->device_name, failure_data->device_name)) {
+ AST_LIST_REMOVE_CURRENT(next);
+ cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id,
+ monitor_iter->interface->device_name, 1);
+ monitor_iter->callbacks->cancel_available_timer(monitor_iter, &monitor_iter->available_timer_id);
+ manager_event(EVENT_FLAG_CC, "CCMonitorFailed",
+ "CoreID: %d\r\n"
+ "Callee: %s\r\n",
+ monitor_iter->core_id, monitor_iter->interface->device_name);
+ cc_unref(monitor_iter, "Monitor reported failure. Unref list's reference.");
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!has_device_monitors(core_instance)) {
+ ast_cc_failed(core_instance->core_id, "All monitors have failed\n");
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+ cc_unref(core_instance, "Finished with core_instance in cc_monitor_failed\n");
+
+ ast_free((char *) failure_data->device_name);
+ ast_free((char *) failure_data->debug);
+ ast_free(failure_data);
+ return 0;
+}
+
+int ast_cc_monitor_failed(int core_id, const char *const monitor_name, const char * const debug, ...)
+{
+ struct ast_cc_monitor_failure_data *failure_data;
+ int res;
+ va_list ap;
+
+ if (!(failure_data = ast_calloc(1, sizeof(*failure_data)))) {
+ return -1;
+ }
+
+ if (!(failure_data->device_name = ast_strdup(monitor_name))) {
+ ast_free(failure_data);
+ return -1;
+ }
+
+ va_start(ap, debug);
+ if (ast_vasprintf(&failure_data->debug, debug, ap) == -1) {
+ va_end(ap);
+ ast_free((char *)failure_data->device_name);
+ ast_free(failure_data);
+ return -1;
+ }
+ va_end(ap);
+
+ failure_data->core_id = core_id;
+
+ res = ast_taskprocessor_push(cc_core_taskprocessor, cc_monitor_failed, failure_data);
+ if (res) {
+ ast_free((char *)failure_data->device_name);
+ ast_free((char *)failure_data->debug);
+ ast_free(failure_data);
+ }
+ return res;
+}
+
+static int cc_status_request(void *data)
+{
+ struct cc_core_instance *core_instance= data;
+ int res;
+
+ res = core_instance->agent->callbacks->status_request(core_instance->agent);
+ cc_unref(core_instance, "Status request finished. Unref core instance");
+ return res;
+}
+
+int ast_cc_monitor_status_request(int core_id)
+{
+ int res;
+ struct cc_core_instance *core_instance = find_cc_core_instance(core_id);
+
+ if (!core_instance) {
+ return -1;
+ }
+
+ res = ast_taskprocessor_push(cc_core_taskprocessor, cc_status_request, core_instance);
+ if (res) {
+ cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed");
+ }
+ return res;
+}
+
+static int cc_stop_ringing(void *data)
+{
+ struct cc_core_instance *core_instance = data;
+ int res = 0;
+
+ if (core_instance->agent->callbacks->stop_ringing) {
+ res = core_instance->agent->callbacks->stop_ringing(core_instance->agent);
+ }
+ /* If an agent is being asked to stop ringing, then he needs to be prepared if for
+ * whatever reason he needs to be called back again. The proper state to be in to
+ * detect such a circumstance is the CC_ACTIVE state.
+ *
+ * We get to this state using the slightly unintuitive method of calling
+ * ast_cc_monitor_request_acked because it gets us to the proper state.
+ */
+ ast_cc_monitor_request_acked(core_instance->core_id, "Agent %s asked to stop ringing. Be prepared to be recalled again.",
+ core_instance->agent->device_name);
+ cc_unref(core_instance, "Stop ringing finished. Unref core_instance");
+ return res;
+}
+
+int ast_cc_monitor_stop_ringing(int core_id)
+{
+ int res;
+ struct cc_core_instance *core_instance = find_cc_core_instance(core_id);
+
+ if (!core_instance) {
+ return -1;
+ }
+
+ res = ast_taskprocessor_push(cc_core_taskprocessor, cc_stop_ringing, core_instance);
+ if (res) {
+ cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed");
+ }
+ return res;
+}
+
+static int cc_party_b_free(void *data)
+{
+ struct cc_core_instance *core_instance = data;
+ int res = 0;
+
+ if (core_instance->agent->callbacks->party_b_free) {
+ res = core_instance->agent->callbacks->party_b_free(core_instance->agent);
+ }
+ cc_unref(core_instance, "Party B free finished. Unref core_instance");
+ return res;
+}
+
+int ast_cc_monitor_party_b_free(int core_id)
+{
+ int res;
+ struct cc_core_instance *core_instance = find_cc_core_instance(core_id);
+
+ if (!core_instance) {
+ return -1;
+ }
+
+ res = ast_taskprocessor_push(cc_core_taskprocessor, cc_party_b_free, core_instance);
+ if (res) {
+ cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed");
+ }
+ return res;
+}
+
+struct cc_status_response_args {
+ struct cc_core_instance *core_instance;
+ enum ast_device_state devstate;
+};
+
+static int cc_status_response(void *data)
+{
+ struct cc_status_response_args *args = data;
+ struct cc_core_instance *core_instance = args->core_instance;
+ struct ast_cc_monitor *monitor_iter;
+ enum ast_device_state devstate = args->devstate;
+
+ ast_free(args);
+
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) {
+ if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR &&
+ monitor_iter->callbacks->status_response) {
+ monitor_iter->callbacks->status_response(monitor_iter, devstate);
+ }
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+ cc_unref(core_instance, "Status response finished. Unref core instance");
+ return 0;
+}
+
+int ast_cc_agent_status_response(int core_id, enum ast_device_state devstate)
+{
+ struct cc_status_response_args *args;
+ struct cc_core_instance *core_instance;
+ int res;
+
+ args = ast_calloc(1, sizeof(*args));
+ if (!args) {
+ return -1;
+ }
+
+ core_instance = find_cc_core_instance(core_id);
+ if (!core_instance) {
+ ast_free(args);
+ return -1;
+ }
+
+ args->core_instance = core_instance;
+ args->devstate = devstate;
+
+ res = ast_taskprocessor_push(cc_core_taskprocessor, cc_status_response, args);
+ if (res) {
+ cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed");
+ ast_free(args);
+ }
+ return res;
+}
+
+static int cc_build_payload(struct ast_channel *chan, struct ast_cc_config_params *cc_params,
+ const char *monitor_type, const char * const device_name, const char * dialstring,
+ enum ast_cc_service_type service, void *private_data, struct cc_control_payload *payload)
+{
+ struct ast_datastore *datastore;
+ struct dialed_cc_interfaces *cc_interfaces;
+ int dial_parent_id;
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL);
+ if (!datastore) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ cc_interfaces = datastore->data;
+ dial_parent_id = cc_interfaces->dial_parent_id;
+ ast_channel_unlock(chan);
+
+ payload->monitor_type = monitor_type;
+ payload->private_data = private_data;
+ payload->service = service;
+ ast_cc_copy_config_params(&payload->config_params, cc_params);
+ payload->parent_interface_id = dial_parent_id;
+ ast_copy_string(payload->device_name, device_name, sizeof(payload->device_name));
+ ast_copy_string(payload->dialstring, dialstring, sizeof(payload->dialstring));
+ return 0;
+}
+
+int ast_queue_cc_frame(struct ast_channel *chan, const char *monitor_type,
+ const char * const dialstring, enum ast_cc_service_type service, void *private_data)
+{
+ struct ast_frame frame = {0,};
+ char device_name[AST_CHANNEL_NAME];
+ int retval;
+ struct ast_cc_config_params *cc_params;
+
+ cc_params = ast_channel_get_cc_config_params(chan);
+ if (!cc_params) {
+ return -1;
+ }
+ ast_channel_get_device_name(chan, device_name, sizeof(device_name));
+ if (ast_cc_monitor_count(device_name, monitor_type) >= ast_get_cc_max_monitors(cc_params)) {
+ ast_log(LOG_NOTICE, "Not queuing a CC frame for device %s since it already has its maximum monitors allocated\n", device_name);
+ return -1;
+ }
+
+ if (ast_cc_build_frame(chan, cc_params, monitor_type, device_name, dialstring, service, private_data, &frame)) {
+ /* Frame building failed. We can't use this. */
+ return -1;
+ }
+ retval = ast_queue_frame(chan, &frame);
+ ast_frfree(&frame);
+ return retval;
+}
+
+int ast_cc_build_frame(struct ast_channel *chan, struct ast_cc_config_params *cc_params,
+ const char *monitor_type, const char * const device_name,
+ const char * const dialstring, enum ast_cc_service_type service, void *private_data,
+ struct ast_frame *frame)
+{
+ struct cc_control_payload *payload = ast_calloc(1, sizeof(*payload));
+
+ if (!payload) {
+ return -1;
+ }
+ if (cc_build_payload(chan, cc_params, monitor_type, device_name, dialstring, service, private_data, payload)) {
+ /* Something screwed up, we can't make a frame with this */
+ ast_free(payload);
+ return -1;
+ }
+ frame->frametype = AST_FRAME_CONTROL;
+ frame->subclass.integer = AST_CONTROL_CC;
+ frame->data.ptr = payload;
+ frame->datalen = sizeof(*payload);
+ frame->mallocd = AST_MALLOCD_DATA;
+ return 0;
+}
+
+void ast_cc_call_failed(struct ast_channel *incoming, struct ast_channel *outgoing, const char * const dialstring)
+{
+ char device_name[AST_CHANNEL_NAME];
+ struct cc_control_payload payload;
+ struct ast_cc_config_params *cc_params;
+
+ if (outgoing->hangupcause != AST_CAUSE_BUSY && outgoing->hangupcause != AST_CAUSE_CONGESTION) {
+ /* It doesn't make sense to try to offer CCBS to the caller if the reason for ast_call
+ * failing is something other than busy or congestion
+ */
+ return;
+ }
+
+ cc_params = ast_channel_get_cc_config_params(outgoing);
+ if (!cc_params) {
+ return;
+ }
+ if (ast_get_cc_monitor_policy(cc_params) != AST_CC_MONITOR_GENERIC) {
+ /* This sort of CCBS only works if using generic CC. For native, we would end up sending
+ * a CC request for a non-existent call. The far end will reject this every time
+ */
+ return;
+ }
+
+ ast_channel_get_device_name(outgoing, device_name, sizeof(device_name));
+ if (cc_build_payload(outgoing, cc_params, AST_CC_GENERIC_MONITOR_TYPE, device_name,
+ dialstring, AST_CC_CCBS, NULL, &payload)) {
+ /* Something screwed up, we can't make a frame with this */
+ return;
+ }
+ ast_handle_cc_control_frame(incoming, outgoing, &payload);
+}
+
+void ast_cc_busy_interface(struct ast_channel *inbound, struct ast_cc_config_params *cc_params,
+ const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data)
+{
+ struct cc_control_payload payload;
+ if (cc_build_payload(inbound, cc_params, monitor_type, device_name, dialstring, AST_CC_CCBS, private_data, &payload)) {
+ /* Something screwed up. Don't try to handle this payload */
+ call_destructor_with_no_monitor(monitor_type, private_data);
+ return;
+ }
+ ast_handle_cc_control_frame(inbound, NULL, &payload);
+}
+
+int ast_cc_callback(struct ast_channel *inbound, const char * const tech, const char * const dest, ast_cc_callback_fn callback)
+{
+ const struct ast_channel_tech *chantech = ast_get_channel_tech(tech);
+
+ if (chantech && chantech->cc_callback) {
+ chantech->cc_callback(inbound, dest, callback);
+ }
+
+ return 0;
+}
+
+static const char *ccreq_app = "CallCompletionRequest";
+
+static int ccreq_exec(struct ast_channel *chan, const char *data)
+{
+ struct cc_core_instance *core_instance;
+ char device_name[AST_CHANNEL_NAME];
+ unsigned long match_flags;
+ int res;
+
+ ast_channel_get_device_name(chan, device_name, sizeof(device_name));
+
+ match_flags = MATCH_NO_REQUEST;
+ if (!(core_instance = ao2_t_callback_data(cc_core_instances, 0, match_agent, device_name, &match_flags, "Find core instance for CallCompletionRequest"))) {
+ ast_log_dynamic_level(cc_logger_level, "Couldn't find a core instance for caller %s\n", device_name);
+ return -1;
+ }
+
+ ast_log_dynamic_level(cc_logger_level, "Core %d: Found core_instance for caller %s\n",
+ core_instance->core_id, device_name);
+
+ if (strcmp(core_instance->agent->callbacks->type, "generic")) {
+ ast_log_dynamic_level(cc_logger_level, "Core %d: CallCompletionRequest is only for generic agent types.\n",
+ core_instance->core_id);
+ pbx_builtin_setvar_helper(chan, "CC_REQUEST_RESULT", "FAIL");
+ cc_unref(core_instance, "Unref core_instance since CallCompletionRequest was called with native agent");
+ return 0;
+ }
+
+ if (!ast_cc_request_is_within_limits()) {
+ ast_log_dynamic_level(cc_logger_level, "Core %d: CallCompletionRequest failed. Too many requests in the system\n",
+ core_instance->core_id);
+ ast_cc_failed(core_instance->core_id, "Too many CC requests\n");
+ pbx_builtin_setvar_helper(chan, "CC_REQUEST_RESULT", "FAIL");
+ cc_unref(core_instance, "Unref core_instance since too many CC requests");
+ return 0;
+ }
+
+ res = ast_cc_agent_accept_request(core_instance->core_id, "CallCompletionRequest called by caller %s for core_id %d", device_name, core_instance->core_id);
+ pbx_builtin_setvar_helper(chan, "CC_REQUEST_RESULT", res ? "FAIL" : "SUCCESS");
+ cc_unref(core_instance, "Done with CallCompletionRequest");
+ return res;
+}
+
+static const char *cccancel_app = "CallCompletionCancel";
+
+static int cccancel_exec(struct ast_channel *chan, const char *data)
+{
+ struct cc_core_instance *core_instance;
+ char device_name[AST_CHANNEL_NAME];
+ unsigned long match_flags;
+ int res;
+
+ ast_channel_get_device_name(chan, device_name, sizeof(device_name));
+
+ match_flags = MATCH_REQUEST;
+ if (!(core_instance = ao2_t_callback_data(cc_core_instances, 0, match_agent, device_name, &match_flags, "Find core instance for CallCompletionCancel"))) {
+ ast_log(LOG_WARNING, "Cannot find CC transaction to cancel for caller %s\n", device_name);
+ return -1;
+ }
+
+ if (strcmp(core_instance->agent->callbacks->type, "generic")) {
+ ast_log(LOG_WARNING, "CallCompletionCancel may only be used for calles with a generic agent\n");
+ cc_unref(core_instance, "Unref core instance found during CallCompletionCancel");
+ return -1;
+ }
+ res = ast_cc_failed(core_instance->core_id, "Call completion request Cancelled for core ID %d by caller %s",
+ core_instance->core_id, device_name);
+ cc_unref(core_instance, "Unref core instance found during CallCompletionCancel");
+ return res;
+}
+
+struct count_monitors_cb_data {
+ const char *device_name;
+ const char *monitor_type;
+ int count;
+};
+
+static int count_monitors_cb(void *obj, void *arg, int flags)
+{
+ struct cc_core_instance *core_instance = obj;
+ struct count_monitors_cb_data *cb_data = arg;
+ const char *device_name = cb_data->device_name;
+ const char *monitor_type = cb_data->monitor_type;
+ struct ast_cc_monitor *monitor_iter;
+
+ AST_LIST_LOCK(core_instance->monitors);
+ AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) {
+ if (!strcmp(monitor_iter->interface->device_name, device_name) &&
+ !strcmp(monitor_iter->interface->monitor_type, monitor_type)) {
+ cb_data->count++;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(core_instance->monitors);
+ return 0;
+}
+
+int ast_cc_monitor_count(const char * const name, const char * const type)
+{
+ struct count_monitors_cb_data data = {.device_name = name, .monitor_type = type,};
+
+ ao2_t_callback(cc_core_instances, OBJ_NODATA, count_monitors_cb, &data, "Counting agents");
+ ast_log_dynamic_level(cc_logger_level, "Counted %d monitors\n", data.count);
+ return data.count;
+}
+
+static void initialize_cc_max_requests(void)
+{
+ struct ast_config *cc_config;
+ const char *cc_max_requests_str;
+ struct ast_flags config_flags = {0,};
+ char *endptr;
+
+ cc_config = ast_config_load2("ccss.conf", "ccss", config_flags);
+ if (!cc_config || cc_config == CONFIG_STATUS_FILEINVALID) {
+ ast_log(LOG_WARNING, "Could not find valid ccss.conf file. Using cc_max_requests default\n");
+ global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
+ return;
+ }
+
+ if (!(cc_max_requests_str = ast_variable_retrieve(cc_config, "general", "cc_max_requests"))) {
+ ast_config_destroy(cc_config);
+ ast_log(LOG_WARNING, "No cc_max_requests defined. Using default\n");
+ global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
+ return;
+ }
+
+ global_cc_max_requests = strtol(cc_max_requests_str, &endptr, 10);
+
+ if (!ast_strlen_zero(endptr)) {
+ ast_log(LOG_WARNING, "Invalid input given for cc_max_requests. Using default\n");
+ global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
+ }
+
+ ast_config_destroy(cc_config);
+ return;
+}
+
+static void cc_cli_print_monitor_stats(struct ast_cc_monitor *monitor, int fd, int parent_id)
+{
+ struct ast_cc_monitor *child_monitor_iter = monitor;
+ if (!monitor) {
+ return;
+ }
+
+ ast_cli(fd, "\t\t|-->%s", monitor->interface->device_name);
+ if (monitor->interface->monitor_class == AST_CC_DEVICE_MONITOR) {
+ ast_cli(fd, "(%s)", cc_service_to_string(monitor->service_offered));
+ }
+ ast_cli(fd, "\n");
+
+ while ((child_monitor_iter = AST_LIST_NEXT(child_monitor_iter, next))) {
+ if (child_monitor_iter->parent_id == monitor->id) {
+ cc_cli_print_monitor_stats(child_monitor_iter, fd, child_monitor_iter->id);
+ }
+ }
+}
+
+static int print_stats_cb(void *obj, void *arg, int flags)
+{
+ int *cli_fd = arg;
+ struct cc_core_instance *core_instance = obj;
+
+ ast_cli(*cli_fd, "%d\t\t%s\t\t%s\n", core_instance->core_id, core_instance->agent->device_name,
+ cc_state_to_string(core_instance->current_state));
+ AST_LIST_LOCK(core_instance->monitors);
+ cc_cli_print_monitor_stats(AST_LIST_FIRST(core_instance->monitors), *cli_fd, 0);
+ AST_LIST_UNLOCK(core_instance->monitors);
+ return 0;
+}
+
+static int cc_cli_output_status(void *data)
+{
+ int *cli_fd = data;
+ int count = ao2_container_count(cc_core_instances);
+
+ if (!count) {
+ ast_cli(*cli_fd, "There are currently no active call completion transactions\n");
+ } else {
+ ast_cli(*cli_fd, "%d Call completion transactions\n", count);
+ ast_cli(*cli_fd, "Core ID\t\tCaller\t\t\t\tStatus\n");
+ ast_cli(*cli_fd, "----------------------------------------------------------------------------\n");
+ ao2_t_callback(cc_core_instances, OBJ_NODATA, print_stats_cb, cli_fd, "Printing stats to CLI");
+ }
+ ast_free(cli_fd);
+ return 0;
+}
+
+static char *handle_cc_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int *cli_fd;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cc report status";
+ e->usage =
+ "Usage: cc report status\n"
+ " Report the current status of any ongoing CC transactions\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ cli_fd = ast_malloc(sizeof(*cli_fd));
+ if (!cli_fd) {
+ return CLI_FAILURE;
+ }
+
+ *cli_fd = a->fd;
+
+ if (ast_taskprocessor_push(cc_core_taskprocessor, cc_cli_output_status, cli_fd)) {
+ ast_free(cli_fd);
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+static int kill_cores(void *obj, void *arg, int flags)
+{
+ int *core_id = arg;
+ struct cc_core_instance *core_instance = obj;
+
+ if (!core_id || (core_instance->core_id == *core_id)) {
+ ast_cc_failed(core_instance->core_id, "CC transaction canceled administratively\n");
+ }
+ return 0;
+}
+
+static char *complete_core_id(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ int wordlen = strlen(word);
+ char *ret = NULL;
+ struct ao2_iterator core_iter = ao2_iterator_init(cc_core_instances, 0);
+ struct cc_core_instance *core_instance;
+
+ for (; (core_instance = ao2_t_iterator_next(&core_iter, "Next core instance"));
+ cc_unref(core_instance, "CLI tab completion iteration")) {
+ char core_id_str[20];
+ snprintf(core_id_str, sizeof(core_id_str), "%d", core_instance->core_id);
+ if (!strncmp(word, core_id_str, wordlen) && ++which > state) {
+ ret = ast_strdup(core_id_str);
+ cc_unref(core_instance, "Found a matching core ID for CLI tab-completion");
+ break;
+ }
+ }
+ ao2_iterator_destroy(&core_iter);
+
+ return ret;
+}
+
+static char *handle_cc_kill(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ static const char * const option[] = { "core", "all", NULL };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cc cancel";
+ e->usage =
+ "Usage: cc cancel can be used in two ways.\n"
+ " 1. 'cc cancel core [core ID]' will cancel the CC transaction with\n"
+ " core ID equal to the specified core ID.\n"
+ " 2. 'cc cancel all' will cancel all active CC transactions.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 2) {
+ return ast_cli_complete(a->word, option, a->n);
+ }
+ if (a->pos == 3) {
+ return complete_core_id(a->line, a->word, a->pos, a->n);
+ }
+ return NULL;
+ }
+
+ if (a->argc == 4) {
+ int core_id;
+ char *endptr;
+ if (strcasecmp(a->argv[2], "core")) {
+ return CLI_SHOWUSAGE;
+ }
+ core_id = strtol(a->argv[3], &endptr, 10);
+ if ((errno != 0 && core_id == 0) || (endptr == a->argv[3])) {
+ return CLI_SHOWUSAGE;
+ }
+ ao2_t_callback(cc_core_instances, OBJ_NODATA, kill_cores, &core_id, "CLI Killing Core Id");
+ } else if (a->argc == 3) {
+ if (strcasecmp(a->argv[2], "all")) {
+ return CLI_SHOWUSAGE;
+ }
+ ao2_t_callback(cc_core_instances, OBJ_NODATA, kill_cores, NULL, "CLI Killing all CC cores");
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cc_cli[] = {
+ AST_CLI_DEFINE(handle_cc_status, "Reports CC stats"),
+ AST_CLI_DEFINE(handle_cc_kill, "Kill a CC transaction"),
+};
+
+int ast_cc_init(void)
+{
+ int res;
+
+ if (!(cc_core_instances = ao2_t_container_alloc(CC_CORE_INSTANCES_BUCKETS,
+ cc_core_instance_hash_fn, cc_core_instance_cmp_fn,
+ "Create core instance container"))) {
+ return -1;
+ }
+ if (!(generic_monitors = ao2_t_container_alloc(CC_CORE_INSTANCES_BUCKETS,
+ generic_monitor_hash_fn, generic_monitor_cmp_fn,
+ "Create generic monitor container"))) {
+ return -1;
+ }
+ if (!(cc_core_taskprocessor = ast_taskprocessor_get("CCSS core", TPS_REF_DEFAULT))) {
+ return -1;
+ }
+ if (!(cc_sched_thread = ast_sched_thread_create())) {
+ return -1;
+ }
+ res = ast_register_application2(ccreq_app, ccreq_exec, NULL, NULL, NULL);
+ res |= ast_register_application2(cccancel_app, cccancel_exec, NULL, NULL, NULL);
+ res |= ast_cc_monitor_register(&generic_monitor_cbs);
+ res |= ast_cc_agent_register(&generic_agent_callbacks);
+ ast_cli_register_multiple(cc_cli, ARRAY_LEN(cc_cli));
+ cc_logger_level = ast_logger_register_level(CC_LOGGER_LEVEL_NAME);
+ dialed_cc_interface_counter = 1;
+ initialize_cc_max_requests();
+ return res;
+}
diff --git a/main/channel.c b/main/channel.c
index 5be973a49..03c657cff 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -2239,6 +2239,7 @@ int ast_hangup(struct ast_channel *chan)
}
ast_channel_unlock(chan);
+ ast_cc_offer(chan);
ast_manager_event(chan, EVENT_FLAG_CALL, "Hangup",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@@ -3611,6 +3612,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
case AST_CONTROL_TRANSFER:
case AST_CONTROL_T38_PARAMETERS:
case _XXX_AST_CONTROL_T38:
+ case AST_CONTROL_CC:
break;
case AST_CONTROL_CONGESTION:
@@ -3754,6 +3756,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
case AST_CONTROL_TRANSFER:
case AST_CONTROL_CONNECTED_LINE:
case AST_CONTROL_REDIRECTING:
+ case AST_CONTROL_CC:
/* Nothing left to do for these. */
res = 0;
break;
@@ -4477,6 +4480,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, format_t format, co
case AST_CONTROL_SRCCHANGE:
case AST_CONTROL_CONNECTED_LINE:
case AST_CONTROL_REDIRECTING:
+ case AST_CONTROL_CC:
case -1: /* Ignore -- just stopping indications */
break;
@@ -7444,6 +7448,107 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc
return retval;
}
+static void *channel_cc_params_copy(void *data)
+{
+ const struct ast_cc_config_params *src = data;
+ struct ast_cc_config_params *dest = ast_cc_config_params_init();
+ if (!dest) {
+ return NULL;
+ }
+ ast_cc_copy_config_params(dest, src);
+ return dest;
+}
+
+static void channel_cc_params_destroy(void *data)
+{
+ struct ast_cc_config_params *cc_params = data;
+ ast_cc_config_params_destroy(cc_params);
+}
+
+static const struct ast_datastore_info cc_channel_datastore_info = {
+ .type = "Call Completion",
+ .duplicate = channel_cc_params_copy,
+ .destroy = channel_cc_params_destroy,
+};
+
+int ast_channel_cc_params_init(struct ast_channel *chan,
+ const struct ast_cc_config_params *base_params)
+{
+ struct ast_cc_config_params *cc_params;
+ struct ast_datastore *cc_datastore;
+
+ if (!(cc_params = ast_cc_config_params_init())) {
+ return -1;
+ }
+
+ if (!(cc_datastore = ast_datastore_alloc(&cc_channel_datastore_info, NULL))) {
+ ast_cc_config_params_destroy(cc_params);
+ return -1;
+ }
+
+ if (base_params) {
+ ast_cc_copy_config_params(cc_params, base_params);
+ }
+ cc_datastore->data = cc_params;
+ ast_channel_datastore_add(chan, cc_datastore);
+ return 0;
+}
+
+struct ast_cc_config_params *ast_channel_get_cc_config_params(struct ast_channel *chan)
+{
+ struct ast_datastore *cc_datastore;
+
+ if (!(cc_datastore = ast_channel_datastore_find(chan, &cc_channel_datastore_info, NULL))) {
+ /* If we can't find the datastore, it almost definitely means that the channel type being
+ * used has not had its driver modified to parse CC config parameters. The best action
+ * to take here is to create the parameters on the spot with the defaults set.
+ */
+ if (ast_channel_cc_params_init(chan, NULL)) {
+ return NULL;
+ }
+ if (!(cc_datastore = ast_channel_datastore_find(chan, &cc_channel_datastore_info, NULL))) {
+ /* Should be impossible */
+ return NULL;
+ }
+ }
+
+ ast_assert(cc_datastore->data != NULL);
+ return cc_datastore->data;
+}
+
+int ast_channel_get_device_name(struct ast_channel *chan, char *device_name, size_t name_buffer_length)
+{
+ int len = name_buffer_length;
+ char *dash;
+ if (!ast_channel_queryoption(chan, AST_OPTION_DEVICE_NAME, device_name, &len, 0)) {
+ return 0;
+ }
+
+ /* Dang. Do it the old-fashioned way */
+ ast_copy_string(device_name, chan->name, name_buffer_length);
+ if ((dash = strrchr(device_name, '-'))) {
+ *dash = '\0';
+ }
+
+ return 0;
+}
+
+int ast_channel_get_cc_agent_type(struct ast_channel *chan, char *agent_type, size_t size)
+{
+ int len = size;
+ char *slash;
+
+ if (!ast_channel_queryoption(chan, AST_OPTION_CC_AGENT_TYPE, agent_type, &len, 0)) {
+ return 0;
+ }
+
+ ast_copy_string(agent_type, chan->name, size);
+ if ((slash = strchr(agent_type, '/'))) {
+ *slash = '\0';
+ }
+ return 0;
+}
+
/* DO NOT PUT ADDITIONAL FUNCTIONS BELOW THIS BOUNDARY
*
* ONLY FUNCTIONS FOR PROVIDING BACKWARDS ABI COMPATIBILITY BELONG HERE
diff --git a/main/manager.c b/main/manager.c
index f811987f7..0456990b4 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -980,6 +980,7 @@ static const struct permalias {
{ EVENT_FLAG_DIALPLAN, "dialplan" },
{ EVENT_FLAG_ORIGINATE, "originate" },
{ EVENT_FLAG_AGI, "agi" },
+ { EVENT_FLAG_CC, "cc" },
{ INT_MAX, "all" },
{ 0, "none" },
};
diff --git a/main/xml.c b/main/xml.c
index 36e7dd812..0f93abc54 100644
--- a/main/xml.c
+++ b/main/xml.c
@@ -23,6 +23,7 @@
#include "asterisk.h"
#include "asterisk/xml.h"
+#include "asterisk/logger.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -67,6 +68,25 @@ struct ast_xml_doc *ast_xml_open(char *filename)
return (struct ast_xml_doc *) doc;
}
+struct ast_xml_doc *ast_xml_read_memory(char *buffer, size_t size)
+{
+ xmlDoc *doc;
+
+ if (!buffer) {
+ return NULL;
+ }
+
+ if (!(doc = xmlParseMemory(buffer, (int) size))) {
+ /* process xinclude elements. */
+ if (xmlXIncludeProcess(doc) < 0) {
+ xmlFreeDoc(doc);
+ return NULL;
+ }
+ }
+
+ return (struct ast_xml_doc *) doc;
+}
+
void ast_xml_close(struct ast_xml_doc *doc)
{
if (!doc) {
@@ -164,6 +184,16 @@ struct ast_xml_node *ast_xml_find_element(struct ast_xml_node *root_node, const
return NULL;
}
+struct ast_xml_ns *ast_xml_find_namespace(struct ast_xml_doc *doc, struct ast_xml_node *node, const char *ns_name) {
+ xmlNsPtr ns = xmlSearchNs((xmlDocPtr) doc, (xmlNodePtr) node, (xmlChar *) ns_name);
+ return (struct ast_xml_ns *) ns;
+}
+
+const char *ast_xml_get_ns_href(struct ast_xml_ns *ns)
+{
+ return (const char *) ((xmlNsPtr) ns)->href;
+}
+
const char *ast_xml_get_text(struct ast_xml_node *node)
{
if (!node) {