diff --git a/CHANGES b/CHANGES
index 2ea3e5ff79b893d217cb3f45216d6a2e450986d7..218da9cfa8df8e4cef0d36db90c629d403b701b4 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 8a58932a833bf2742b87baeaa25fd9ddc297cdc9..b1de21d5f155652a2a7ef87dc0acb5b8b66070df 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 356106e4ac260088eb9ffd07e98a3b54a6b00a22..d9ae8a6f56deaf713e4c0acc33d39ca8192ef811 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 5e522e797121492746bf4392faf74af7b096195c..b8052b0fd5299db89c320dc05c4c49c9c4a0ec9c 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 bd6cb1889bf45c6cb0efaf08773adf7e4cd1c458..91773b05c226e01d3e010cd7cd97a1568eeccaee 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,3195 +1631,3779 @@ const struct ast_channel_tech sip_tech = {
  */
 struct ast_channel_tech sip_tech_info;
 
-/*! \brief Working TLS connection configuration */
-static struct ast_tls_config sip_tls_cfg;
-
-/*! \brief Default TLS connection configuration */
-static struct ast_tls_config default_tls_cfg;
+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);
 
-/*! \brief The TCP server definition */
-static struct ast_tcptls_session_args sip_tcp_desc = {
-	.accept_fd = -1,
-	.master = AST_PTHREADT_NULL,
-	.tls_cfg = NULL,
-	.poll_timeout = -1,
-	.name = "SIP TCP server",
-	.accept_fn = ast_tcptls_server_root,
-	.worker_fn = sip_tcp_worker_fn,
+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,
 };
 
-/*! \brief The TCP/TLS server definition */
-static struct ast_tcptls_session_args sip_tls_desc = {
-	.accept_fd = -1,
-	.master = AST_PTHREADT_NULL,
-	.tls_cfg = &sip_tls_cfg,
-	.poll_timeout = -1,
-	.name = "SIP TLS server",
-	.accept_fn = ast_tcptls_server_root,
-	.worker_fn = sip_tcp_worker_fn,
-};
+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;
 
-/*! \brief Append to SIP dialog history
-	\return Always returns 0 */
-#define append_history(p, event, fmt , args... )	append_history_full(p, "%-15s " fmt, event, ## args)
+	return !strcmp(agent_pvt->notify_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
+}
 
-struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
+static struct ast_cc_agent *find_sip_cc_agent_by_notify_uri(const char * const uri)
 {
-	if (p)
-#ifdef REF_DEBUG
-		__ao2_ref_debug(p, 1, tag, file, line, func);
-#else
-		ao2_ref(p, 1);
-#endif
-	else
-		ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
-	return p;
+	struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_notify_uri_helper, (char *)uri, "SIP");
+	return agent;
 }
 
-struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
+static int find_by_subscribe_uri_helper(void *obj, void *arg, int flags)
 {
-	if (p)
-#ifdef REF_DEBUG
-		__ao2_ref_debug(p, -1, tag, file, line, func);
-#else
-		ao2_ref(p, -1);
-#endif
-	return NULL;
+	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;
 }
 
-/*! \brief map from an integer value to a string.
- * If no match is found, return errorstring
- */
-static const char *map_x_s(const struct _map_x_s *table, int x, const char *errorstring)
+static struct ast_cc_agent *find_sip_cc_agent_by_subscribe_uri(const char * const uri)
 {
-	const struct _map_x_s *cur;
-
-	for (cur = table; cur->s; cur++)
-		if (cur->x == x)
-			return cur->s;
-	return errorstring;
+	struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_subscribe_uri_helper, (char *)uri, "SIP");
+	return agent;
 }
 
-/*! \brief map from a string to an integer value, case insensitive.
- * If no match is found, return errorvalue.
- */
-static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
+static int find_by_callid_helper(void *obj, void *arg, int flags)
 {
-	const struct _map_x_s *cur;
+	struct ast_cc_agent *agent = obj;
+	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
+	struct sip_pvt *call_pvt = arg;
 
-	for (cur = table; cur->s; cur++)
-		if (!strcasecmp(cur->s, s))
-			return cur->x;
-	return errorvalue;
+	return !strcmp(agent_pvt->original_callid, call_pvt->callid) ? CMP_MATCH | CMP_STOP : 0;
 }
 
-static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
+static struct ast_cc_agent *find_sip_cc_agent_by_original_callid(struct sip_pvt *pvt)
 {
-	enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
-	int i;
+	struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_callid_helper, pvt, "SIP");
+	return agent;
+}
 
-	for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
-		if (!strcasecmp(text, sip_reason_table[i].text)) {
-			ast = sip_reason_table[i].code;
-			break;
-		}
+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;
 	}
 
-	return ast;
+	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 const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code)
+static int sip_offer_timer_expire(const void *data)
 {
-	if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
-		return sip_reason_table[code].text;
-	}
+	struct ast_cc_agent *agent = (struct ast_cc_agent *) data;
+	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
 
-	return "unknown";
+	agent_pvt->offer_timer_id = -1;
+
+	return ast_cc_failed(agent->core_id, "SIP agent %s's offer timer expired", agent->device_name);
 }
 
-/*!
- * \brief generic function for determining if a correct transport is being
- * used to contact a peer
- *
- * this is done as a macro so that the "tmpl" var can be passed either a
- * sip_request or a sip_peer
- */
-#define check_request_transport(peer, tmpl) ({ \
-	int ret = 0; \
-	if (peer->socket.type == tmpl->socket.type) \
-		; \
-	else if (!(peer->transports & tmpl->socket.type)) {\
-		ast_log(LOG_ERROR, \
-			"'%s' is not a valid transport for '%s'. we only use '%s'! ending call.\n", \
-			get_transport(tmpl->socket.type), peer->name, get_transport_list(peer->transports) \
-			); \
-		ret = 1; \
-	} else if (peer->socket.type & SIP_TRANSPORT_TLS) { \
-		ast_log(LOG_WARNING, \
-			"peer '%s' HAS NOT USED (OR SWITCHED TO) TLS in favor of '%s' (but this was allowed in sip.conf)!\n", \
-			peer->name, get_transport(tmpl->socket.type) \
-		); \
-	} else { \
-		ast_debug(1, \
-			"peer '%s' has contacted us over %s even though we prefer %s.\n", \
-			peer->name, get_transport(tmpl->socket.type), get_transport(peer->socket.type) \
-		); \
-	}\
-	(ret); \
-})
+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;
 
-/*! \brief
- * duplicate a list of channel variables, \return the copy.
- */
-static struct ast_variable *copy_vars(struct ast_variable *src)
+	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 ast_variable *res = NULL, *tmp, *v = NULL;
+	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
 
-	for (v = src ; v ; v = v->next) {
-		if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
-			tmp->next = res;
-			res = tmp;
-		}
-	}
-	return res;
+	AST_SCHED_DEL(sched, agent_pvt->offer_timer_id);
+	return 0;
 }
 
-static void tcptls_packet_destructor(void *obj)
+static void sip_cc_agent_ack(struct ast_cc_agent *agent)
 {
-	struct tcptls_packet *packet = obj;
+	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
 
-	ast_free(packet->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 void sip_tcptls_client_args_destructor(void *obj)
+static int sip_cc_agent_status_request(struct ast_cc_agent *agent)
 {
-	struct ast_tcptls_session_args *args = obj;
-	if (args->tls_cfg) {
-		ast_free(args->tls_cfg->certfile);
-		ast_free(args->tls_cfg->pvtfile);
-		ast_free(args->tls_cfg->cipher);
-		ast_free(args->tls_cfg->cafile);
-		ast_free(args->tls_cfg->capath);
-	}
-	ast_free(args->tls_cfg);
-	ast_free((char *) args->name);
+	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 void sip_threadinfo_destructor(void *obj)
+static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent)
 {
-	struct sip_threadinfo *th = obj;
-	struct tcptls_packet *packet;
-	if (th->alert_pipe[1] > -1) {
-		close(th->alert_pipe[0]);
+	/* 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);
 	}
-	if (th->alert_pipe[1] > -1) {
-		close(th->alert_pipe[1]);
+	/* 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;
 	}
-	th->alert_pipe[0] = th->alert_pipe[1] = -1;
 
-	while ((packet = AST_LIST_REMOVE_HEAD(&th->packet_q, entry))) {
-		ao2_t_ref(packet, -1, "thread destruction, removing packet from frame queue");
+	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);
+}
 
-	if (th->tcptls_session) {
-		ao2_t_ref(th->tcptls_session, -1, "remove tcptls_session for sip_threadinfo object");
+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);
 }
 
-/*! \brief creates a sip_threadinfo object and links it into the threadt table. */
-static struct sip_threadinfo *sip_threadinfo_create(struct ast_tcptls_session_instance *tcptls_session, int transport)
+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_threadinfo *th;
+	struct sip_monitor_instance *monitor_instance = ao2_alloc(sizeof(*monitor_instance), sip_monitor_instance_destructor);
 
-	if (!tcptls_session || !(th = ao2_alloc(sizeof(*th), sip_threadinfo_destructor))) {
+	if (!monitor_instance) {
 		return NULL;
 	}
 
-	th->alert_pipe[0] = th->alert_pipe[1] = -1;
-
-	if (pipe(th->alert_pipe) == -1) {
-		ao2_t_ref(th, -1, "Failed to open alert pipe on sip_threadinfo");
-		ast_log(LOG_ERROR, "Could not create sip alert pipe in tcptls thread, error %s\n", strerror(errno));
+	if (ast_string_field_init(monitor_instance, 256)) {
+		ao2_ref(monitor_instance, -1);
 		return NULL;
 	}
-	ao2_t_ref(tcptls_session, +1, "tcptls_session ref for sip_threadinfo object");
-	th->tcptls_session = tcptls_session;
-	th->type = transport ? transport : (tcptls_session->ssl ? SIP_TRANSPORT_TLS: SIP_TRANSPORT_TCP);
-	ao2_t_link(threadt, th, "Adding new tcptls helper thread");
-	ao2_t_ref(th, -1, "Decrementing threadinfo ref from alloc, only table ref remains");
-	return th;
+
+	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;
 }
 
-/*! \brief used to indicate to a tcptls thread that data is ready to be written */
-static int sip_tcptls_write(struct ast_tcptls_session_instance *tcptls_session, const void *buf, size_t len)
+static int find_sip_monitor_instance_by_subscription_pvt(void *obj, void *arg, int flags)
 {
-	int res = len;
-	struct sip_threadinfo *th = NULL;
-	struct tcptls_packet *packet = NULL;
-	struct sip_threadinfo tmp = {
-		.tcptls_session = tcptls_session,
-	};
-	enum sip_tcptls_alert alert = TCPTLS_ALERT_DATA;
+	struct sip_monitor_instance *monitor_instance = obj;
+	return monitor_instance->subscription_pvt == arg ? CMP_MATCH | CMP_STOP : 0;
+}
 
-	if (!tcptls_session) {
-		return XMIT_ERROR;
-	}
+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;
+}
 
-	ast_mutex_lock(&tcptls_session->lock);
+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);
 
-	if ((tcptls_session->fd == -1) ||
-		!(th = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread")) ||
-		!(packet = ao2_alloc(sizeof(*packet), tcptls_packet_destructor)) ||
-		!(packet->data = ast_str_create(len))) {
-		goto tcptls_write_setup_error;
-	}
+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,
+};
 
-	/* goto tcptls_write_error should _NOT_ be used beyond this point */
-	ast_str_set(&packet->data, 0, "%s", (char *) buf);
-	packet->len = len;
+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;
 
-	/* alert tcptls thread handler that there is a packet to be sent.
-	 * must lock the thread info object to guarantee control of the
-	 * packet queue */
-	ao2_lock(th);
-	if (write(th->alert_pipe[1], &alert, sizeof(alert)) == -1) {
-		ast_log(LOG_ERROR, "write() to alert pipe failed: %s\n", strerror(errno));
-		ao2_t_ref(packet, -1, "could not write to alert pipe, remove packet");
-		packet = NULL;
-		res = XMIT_ERROR;
-	} else { /* it is safe to queue the frame after issuing the alert when we hold the threadinfo lock */
-		AST_LIST_INSERT_TAIL(&th->packet_q, packet, entry);
+	if (!monitor_instance) {
+		return -1;
 	}
-	ao2_unlock(th);
-
-	ast_mutex_unlock(&tcptls_session->lock);
-	ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo object after finding it");
-	return res;
 
-tcptls_write_setup_error:
-	if (th) {
-		ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo obj, could not create packet");
-	}
-	if (packet) {
-		ao2_t_ref(packet, -1, "could not allocate packet's data");
+	if (!(monitor_instance->subscription_pvt = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL))) {
+		return -1;
 	}
-	ast_mutex_unlock(&tcptls_session->lock);
 
-	return XMIT_ERROR;
+	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;
 }
 
-/*! \brief SIP TCP connection handler */
-static void *sip_tcp_worker_fn(void *data)
+static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity)
 {
-	struct ast_tcptls_session_instance *tcptls_session = data;
+	struct ast_str *body = ast_str_alloca(size);
+	char tuple_id[32];
 
-	return _sip_tcp_helper_thread(NULL, tcptls_session);
+	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;
 }
 
-/*! \brief SIP TCP thread management function
-	This function reads from the socket, parses the packet into a request
-*/
-static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session)
+static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor)
 {
-	int res, cl;
-	struct sip_request req = { 0, } , reqcpy = { 0, };
-	struct sip_threadinfo *me = NULL;
-	char buf[1024] = "";
-	struct pollfd fds[2] = { { 0 }, { 0 }, };
-	struct ast_tcptls_session_args *ca = NULL;
+	struct sip_monitor_instance *monitor_instance = monitor->private_data;
+	enum sip_publish_type publish_type;
+	struct cc_epa_entry *cc_entry;
 
-	/* If this is a server session, then the connection has already been setup,
-	 * simply create the threadinfo object so we can access this thread for writing.
-	 * 
-	 * if this is a client connection more work must be done.
-	 * 1. We own the parent session args for a client connection.  This pointer needs
-	 *    to be held on to so we can decrement it's ref count on thread destruction.
-	 * 2. The threadinfo object was created before this thread was launched, however
-	 *    it must be found within the threadt table.
-	 * 3. Last, the tcptls_session must be started.
-	 */
-	if (!tcptls_session->client) {
-		if (!(me = sip_threadinfo_create(tcptls_session, tcptls_session->ssl ? SIP_TRANSPORT_TLS : SIP_TRANSPORT_TCP))) {
-			goto cleanup;
+	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;
 		}
-		ao2_t_ref(me, +1, "Adding threadinfo ref for tcp_helper_thread");
+		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 {
-		struct sip_threadinfo tmp = {
-			.tcptls_session = tcptls_session,
-		};
+		publish_type = SIP_PUBLISH_MODIFY;
+		cc_entry = monitor_instance->suspension_entry->instance_data;
+	}
 
-		if ((!(ca = tcptls_session->parent)) ||
-			(!(me = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread"))) ||
-			(!(tcptls_session = ast_tcptls_client_start(tcptls_session)))) {
-			goto cleanup;
-		}
+	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);
+}
 
-	me->threadid = pthread_self();
-	ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
+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;
+}
 
-	/* set up pollfd to watch for reads on both the socket and the alert_pipe */
-	fds[0].fd = tcptls_session->fd;
-	fds[1].fd = me->alert_pipe[0];
-	fds[0].events = fds[1].events = POLLIN | POLLPRI;
+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 (!(req.data = ast_str_create(SIP_MIN_PACKET)))
-		goto cleanup;
-	if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET)))
-		goto cleanup;
+	if (!monitor_instance) {
+		return -1;
+	}
 
-	for (;;) {
-		struct ast_str *str_save;
+	ast_assert(monitor_instance->suspension_entry != NULL);
 
-		res = ast_poll(fds, 2, -1); /* polls for both socket and alert_pipe */
-		if (res < 0) {
-			ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
-			goto cleanup;
-		}
+	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);
+}
 
-		/* handle the socket event, check for both reads from the socket fd,
-		 * and writes from alert_pipe fd */
-		if (fds[0].revents) { /* there is data on the socket to be read */
+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;
+}
 
-			fds[0].revents = 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);
+}
 
-			/* clear request structure */
-			str_save = req.data;
-			memset(&req, 0, sizeof(req));
-			req.data = str_save;
-			ast_str_reset(req.data);
+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;
 
-			str_save = reqcpy.data;
-			memset(&reqcpy, 0, sizeof(reqcpy));
-			reqcpy.data = str_save;
-			ast_str_reset(reqcpy.data);
+	if (ast_strlen_zero(call_info)) {
+		/* No Call-Info present. Definitely no CC offer */
+		return -1;
+	}
 
-			memset(buf, 0, sizeof(buf));
+	uri = strsep(&call_info, ";");
 
-			if (tcptls_session->ssl) {
-				set_socket_transport(&req.socket, SIP_TRANSPORT_TLS);
-				req.socket.port = htons(ourport_tls);
-			} else {
-				set_socket_transport(&req.socket, SIP_TRANSPORT_TCP);
-				req.socket.port = htons(ourport_tcp);
-			}
-			req.socket.fd = tcptls_session->fd;
+	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;
+	}
 
-			/* Read in headers one line at a time */
-			while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
-				ast_mutex_lock(&tcptls_session->lock);
-				if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
-					ast_mutex_unlock(&tcptls_session->lock);
-					goto cleanup;
-				}
-				ast_mutex_unlock(&tcptls_session->lock);
-				if (me->stop)
-					 goto cleanup;
-				ast_str_append(&req.data, 0, "%s", buf);
-				req.len = req.data->used;
-			}
-			copy_request(&reqcpy, &req);
-			parse_request(&reqcpy);
-			/* In order to know how much to read, we need the content-length header */
-			if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
-				while (cl > 0) {
-					size_t bytes_read;
-					ast_mutex_lock(&tcptls_session->lock);
-					if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, cl), tcptls_session->f))) {
-						ast_mutex_unlock(&tcptls_session->lock);
-						goto cleanup;
-					}
-					buf[bytes_read] = '\0';
-					ast_mutex_unlock(&tcptls_session->lock);
-					if (me->stop)
-						goto cleanup;
-					cl -= strlen(buf);
-					ast_str_append(&req.data, 0, "%s", buf);
-					req.len = req.data->used;
-				}
-			}
-			/*! \todo XXX If there's no Content-Length or if the content-length and what
-					we receive is not the same - we should generate an error */
-
-			req.socket.tcptls_session = tcptls_session;
-			handle_request_do(&req, &tcptls_session->remote_address);
+	/* 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 (fds[1].revents) { /* alert_pipe indicates there is data in the send queue to be sent */
-			enum sip_tcptls_alert alert;
-			struct tcptls_packet *packet;
-
-			fds[1].revents = 0;
-
-			if (read(me->alert_pipe[0], &alert, sizeof(alert)) == -1) {
-				ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
-				continue;
-			}
+	if ((*service = service_string_to_service_type(service_str)) == AST_CC_NONE) {
+		/* Invalid service offered */
+		return -1;
+	}
 
-			switch (alert) {
-			case TCPTLS_ALERT_STOP:
-				goto cleanup;
-			case TCPTLS_ALERT_DATA:
-				ao2_lock(me);
-				if (!(packet = AST_LIST_REMOVE_HEAD(&me->packet_q, entry))) {
-					ast_log(LOG_WARNING, "TCPTLS thread alert_pipe indicated packet should be sent, but frame_q is empty");
-				} else if (ast_tcptls_server_write(tcptls_session, ast_str_buffer(packet->data), packet->len) == -1) {
-					ast_log(LOG_WARNING, "Failure to write to tcp/tls socket\n");
-				}
+	ast_copy_string(subscribe_uri, get_in_brackets(uri), size);
 
-				if (packet) {
-					ao2_t_ref(packet, -1, "tcptls packet sent, this is no longer needed");
-				}
-				ao2_unlock(me);
-				break;
-			default:
-				ast_log(LOG_ERROR, "Unknown tcptls thread alert '%d'\n", alert);
-			}
-		}
-	}
+	return 0;
+}
 
-	ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
+/*
+ * \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];
 
-cleanup:
-	if (me) {
-		ao2_t_unlink(threadt, me, "Removing tcptls helper thread, thread is closing");
-		ao2_t_ref(me, -1, "Removing tcp_helper_threads threadinfo ref");
-	}
-	if (reqcpy.data) {
-		ast_free(reqcpy.data);
+	if (monitor_policy == AST_CC_MONITOR_NEVER) {
+		/* Don't bother, just return */
+		return;
 	}
 
-	if (req.data) {
-		ast_free(req.data);
-		req.data = NULL;
+	if ((core_id = ast_cc_get_current_core_id(pvt->owner)) == -1) {
+		/* For some reason, CC is invalid, so don't try it! */
+		return;
 	}
 
-	/* if client, we own the parent session arguments and must decrement ref */
-	if (ca) {
-		ao2_t_ref(ca, -1, "closing tcptls thread, getting rid of client tcptls_session arguments");
-	}
+	ast_channel_get_device_name(pvt->owner, interface_name, sizeof(interface_name));
 
-	if (tcptls_session) {
-		ast_mutex_lock(&tcptls_session->lock);
-		if (tcptls_session->f) {
-			fclose(tcptls_session->f);
-			tcptls_session->f = NULL;
+	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;
 		}
-		if (tcptls_session->fd != -1) {
-			close(tcptls_session->fd);
-			tcptls_session->fd = -1;
+		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;
 		}
-		tcptls_session->parent = NULL;
-		ast_mutex_unlock(&tcptls_session->lock);
+		/* 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;
+	}
 
-		ao2_ref(tcptls_session, -1);
-		tcptls_session = NULL;
+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);
 	}
-	return NULL;
 }
 
+/*! \brief Working TLS connection configuration */
+static struct ast_tls_config sip_tls_cfg;
 
-/*!
- * helper functions to unreference various types of objects.
- * By handling them this way, we don't have to declare the
- * destructor on each call, which removes the chance of errors.
- */
-static void *unref_peer(struct sip_peer *peer, char *tag)
+/*! \brief Default TLS connection configuration */
+static struct ast_tls_config default_tls_cfg;
+
+/*! \brief The TCP server definition */
+static struct ast_tcptls_session_args sip_tcp_desc = {
+	.accept_fd = -1,
+	.master = AST_PTHREADT_NULL,
+	.tls_cfg = NULL,
+	.poll_timeout = -1,
+	.name = "SIP TCP server",
+	.accept_fn = ast_tcptls_server_root,
+	.worker_fn = sip_tcp_worker_fn,
+};
+
+/*! \brief The TCP/TLS server definition */
+static struct ast_tcptls_session_args sip_tls_desc = {
+	.accept_fd = -1,
+	.master = AST_PTHREADT_NULL,
+	.tls_cfg = &sip_tls_cfg,
+	.poll_timeout = -1,
+	.name = "SIP TLS server",
+	.accept_fn = ast_tcptls_server_root,
+	.worker_fn = sip_tcp_worker_fn,
+};
+
+/*! \brief Append to SIP dialog history
+	\return Always returns 0 */
+#define append_history(p, event, fmt , args... )	append_history_full(p, "%-15s " fmt, event, ## args)
+
+struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
 {
-	ao2_t_ref(peer, -1, tag);
-	return NULL;
+	if (p)
+#ifdef REF_DEBUG
+		__ao2_ref_debug(p, 1, tag, file, line, func);
+#else
+		ao2_ref(p, 1);
+#endif
+	else
+		ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
+	return p;
 }
 
-static struct sip_peer *ref_peer(struct sip_peer *peer, char *tag)
+struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
 {
-	ao2_t_ref(peer, 1, tag);
-	return peer;
+	if (p)
+#ifdef REF_DEBUG
+		__ao2_ref_debug(p, -1, tag, file, line, func);
+#else
+		ao2_ref(p, -1);
+#endif
+	return NULL;
 }
 
-/*! \brief maintain proper refcounts for a sip_pvt's outboundproxy
- *
- * This function sets pvt's outboundproxy pointer to the one referenced
- * by the proxy parameter. Because proxy may be a refcounted object, and
- * because pvt's old outboundproxy may also be a refcounted object, we need
- * to maintain the proper refcounts.
- *
- * \param pvt The sip_pvt for which we wish to set the outboundproxy
- * \param proxy The sip_proxy which we will point pvt towards.
- * \return Returns void
+/*! \brief map from an integer value to a string.
+ * If no match is found, return errorstring
  */
-static void ref_proxy(struct sip_pvt *pvt, struct sip_proxy *proxy)
+static const char *map_x_s(const struct _map_x_s *table, int x, const char *errorstring)
 {
-	struct sip_proxy *old_obproxy = pvt->outboundproxy;
-	/* The sip_cfg.outboundproxy is statically allocated, and so
-	 * we don't ever need to adjust refcounts for it
-	 */
-	if (proxy && proxy != &sip_cfg.outboundproxy) {
-		ao2_ref(proxy, +1);
-	}
-	pvt->outboundproxy = proxy;
-	if (old_obproxy && old_obproxy != &sip_cfg.outboundproxy) {
-		ao2_ref(old_obproxy, -1);
-	}
+	const struct _map_x_s *cur;
+
+	for (cur = table; cur->s; cur++)
+		if (cur->x == x)
+			return cur->s;
+	return errorstring;
 }
 
-/*!
- * \brief Unlink a dialog from the dialogs container, as well as any other places
- * that it may be currently stored.
- *
- * \note A reference to the dialog must be held before calling this function, and this
- * function does not release that reference.
+/*! \brief map from a string to an integer value, case insensitive.
+ * If no match is found, return errorvalue.
  */
-void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist)
+static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
 {
-	struct sip_pkt *cp;
-
-	dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
+	const struct _map_x_s *cur;
 
-	ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
+	for (cur = table; cur->s; cur++)
+		if (!strcasecmp(cur->s, s))
+			return cur->x;
+	return errorvalue;
+}
 
-	/* Unlink us from the owner (channel) if we have one */
-	if (dialog->owner) {
-		if (lockowner)
-			ast_channel_lock(dialog->owner);
-		ast_debug(1, "Detaching from channel %s\n", dialog->owner->name);
-		dialog->owner->tech_pvt = dialog_unref(dialog->owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
-		if (lockowner)
-			ast_channel_unlock(dialog->owner);
-	}
-	if (dialog->registry) {
-		if (dialog->registry->call == dialog)
-			dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
-		dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
-	}
-	if (dialog->stateid > -1) {
-		ast_extension_state_del(dialog->stateid, NULL);
-		dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
-		dialog->stateid = -1; /* shouldn't we 'zero' this out? */
-	}
-	/* Remove link from peer to subscription of MWI */
-	if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog)
-		dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
-	if (dialog->relatedpeer && dialog->relatedpeer->call == dialog)
-		dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
+static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
+{
+	enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
+	int i;
 
-	/* remove all current packets in this dialog */
-	while((cp = dialog->packets)) {
-		dialog->packets = dialog->packets->next;
-		AST_SCHED_DEL(sched, cp->retransid);
-		dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
-		if (cp->data) {
-			ast_free(cp->data);
+	for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
+		if (!strcasecmp(text, sip_reason_table[i].text)) {
+			ast = sip_reason_table[i].code;
+			break;
 		}
-		ast_free(cp);
 	}
 
-	AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
+	return ast;
+}
 
-	AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
-	
-	if (dialog->autokillid > -1)
-		AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
-
-	if (dialog->request_queue_sched_id > -1) {
-		AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr"));
-	}
-
-	AST_SCHED_DEL_UNREF(sched, dialog->provisional_keepalive_sched_id, dialog_unref(dialog, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
-
-	if (dialog->t38id > -1) {
-		AST_SCHED_DEL_UNREF(sched, dialog->t38id, dialog_unref(dialog, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
+static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code)
+{
+	if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
+		return sip_reason_table[code].text;
 	}
 
-	dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
-	return NULL;
+	return "unknown";
 }
 
-void *registry_unref(struct sip_registry *reg, char *tag)
-{
-	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount - 1);
-	ASTOBJ_UNREF(reg, sip_registry_destroy);
-	return NULL;
-}
+/*!
+ * \brief generic function for determining if a correct transport is being
+ * used to contact a peer
+ *
+ * this is done as a macro so that the "tmpl" var can be passed either a
+ * sip_request or a sip_peer
+ */
+#define check_request_transport(peer, tmpl) ({ \
+	int ret = 0; \
+	if (peer->socket.type == tmpl->socket.type) \
+		; \
+	else if (!(peer->transports & tmpl->socket.type)) {\
+		ast_log(LOG_ERROR, \
+			"'%s' is not a valid transport for '%s'. we only use '%s'! ending call.\n", \
+			get_transport(tmpl->socket.type), peer->name, get_transport_list(peer->transports) \
+			); \
+		ret = 1; \
+	} else if (peer->socket.type & SIP_TRANSPORT_TLS) { \
+		ast_log(LOG_WARNING, \
+			"peer '%s' HAS NOT USED (OR SWITCHED TO) TLS in favor of '%s' (but this was allowed in sip.conf)!\n", \
+			peer->name, get_transport(tmpl->socket.type) \
+		); \
+	} else { \
+		ast_debug(1, \
+			"peer '%s' has contacted us over %s even though we prefer %s.\n", \
+			peer->name, get_transport(tmpl->socket.type), get_transport(peer->socket.type) \
+		); \
+	}\
+	(ret); \
+})
 
-/*! \brief Add object reference to SIP registry */
-static struct sip_registry *registry_addref(struct sip_registry *reg, char *tag)
+/*! \brief
+ * duplicate a list of channel variables, \return the copy.
+ */
+static struct ast_variable *copy_vars(struct ast_variable *src)
 {
-	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount + 1);
-	return ASTOBJ_REF(reg);	/* Add pointer to registry in packet */
-}
-
-/*! \brief Interface structure with callbacks used to connect to UDPTL module*/
-static struct ast_udptl_protocol sip_udptl = {
-	type: "SIP",
-	get_udptl_info: sip_get_udptl_peer,
-	set_udptl_peer: sip_set_udptl_peer,
-};
-
-static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
-	__attribute__((format(printf, 2, 3)));
-
+	struct ast_variable *res = NULL, *tmp, *v = NULL;
 
-/*! \brief Convert transfer status to string */
-static const char *referstatus2str(enum referstatus rstatus)
-{
-	return map_x_s(referstatusstrings, rstatus, "");
+	for (v = src ; v ; v = v->next) {
+		if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
+			tmp->next = res;
+			res = tmp;
+		}
+	}
+	return res;
 }
 
-static inline void pvt_set_needdestroy(struct sip_pvt *pvt, const char *reason)
+static void tcptls_packet_destructor(void *obj)
 {
-	append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason);
-	pvt->needdestroy = 1;
-}
+	struct tcptls_packet *packet = obj;
 
-/*! \brief Initialize the initital request packet in the pvt structure.
- 	This packet is used for creating replies and future requests in
-	a dialog */
-static void initialize_initreq(struct sip_pvt *p, struct sip_request *req)
-{
-	if (p->initreq.headers)
-		ast_debug(1, "Initializing already initialized SIP dialog %s (presumably reinvite)\n", p->callid);
-	else
-		ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
-	/* Use this as the basis */
-	copy_request(&p->initreq, req);
-	parse_request(&p->initreq);
-	if (req->debug)
-		ast_verbose("Initreq: %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+	ast_free(packet->data);
 }
 
-/*! \brief Encapsulate setting of SIP_ALREADYGONE to be able to trace it with debugging */
-static void sip_alreadygone(struct sip_pvt *dialog)
+static void sip_tcptls_client_args_destructor(void *obj)
 {
-	ast_debug(3, "Setting SIP_ALREADYGONE on dialog %s\n", dialog->callid);
-	dialog->alreadygone = 1;
+	struct ast_tcptls_session_args *args = obj;
+	if (args->tls_cfg) {
+		ast_free(args->tls_cfg->certfile);
+		ast_free(args->tls_cfg->pvtfile);
+		ast_free(args->tls_cfg->cipher);
+		ast_free(args->tls_cfg->cafile);
+		ast_free(args->tls_cfg->capath);
+	}
+	ast_free(args->tls_cfg);
+	ast_free((char *) args->name);
 }
 
-/*! Resolve DNS srv name or host name in a sip_proxy structure */
-static int proxy_update(struct sip_proxy *proxy)
+static void sip_threadinfo_destructor(void *obj)
 {
-	/* if it's actually an IP address and not a name,
-           there's no need for a managed lookup */
-	if (!inet_aton(proxy->name, &proxy->ip.sin_addr)) {
-		/* Ok, not an IP address, then let's check if it's a domain or host */
-		/* XXX Todo - if we have proxy port, don't do SRV */
-		if (ast_get_ip_or_srv(&proxy->ip, proxy->name, sip_cfg.srvlookup ? "_sip._udp" : NULL) < 0) {
-			ast_log(LOG_WARNING, "Unable to locate host '%s'\n", proxy->name);
-			return FALSE;
-		}
+	struct sip_threadinfo *th = obj;
+	struct tcptls_packet *packet;
+	if (th->alert_pipe[1] > -1) {
+		close(th->alert_pipe[0]);
 	}
-	proxy->last_dnsupdate = time(NULL);
-	return TRUE;
-}
+	if (th->alert_pipe[1] > -1) {
+		close(th->alert_pipe[1]);
+	}
+	th->alert_pipe[0] = th->alert_pipe[1] = -1;
 
-/*! \brief converts ascii port to int representation. If no
- *  pt buffer is provided or the pt has errors when being converted
- *  to an int value, the port provided as the standard is used.
- */
-unsigned int port_str2int(const char *pt, unsigned int standard)
-{
-	int port = standard;
-	if (ast_strlen_zero(pt) || (sscanf(pt, "%30d", &port) != 1) || (port < 1) || (port > 65535)) {
-		port = standard;
+	while ((packet = AST_LIST_REMOVE_HEAD(&th->packet_q, entry))) {
+		ao2_t_ref(packet, -1, "thread destruction, removing packet from frame queue");
 	}
 
-	return port;
+	if (th->tcptls_session) {
+		ao2_t_ref(th->tcptls_session, -1, "remove tcptls_session for sip_threadinfo object");
+	}
 }
 
-/*! \brief Allocate and initialize sip proxy */
-static struct sip_proxy *proxy_allocate(char *name, char *port, int force)
+/*! \brief creates a sip_threadinfo object and links it into the threadt table. */
+static struct sip_threadinfo *sip_threadinfo_create(struct ast_tcptls_session_instance *tcptls_session, int transport)
 {
-	struct sip_proxy *proxy;
+	struct sip_threadinfo *th;
 
-	if (ast_strlen_zero(name)) {
+	if (!tcptls_session || !(th = ao2_alloc(sizeof(*th), sip_threadinfo_destructor))) {
 		return NULL;
 	}
 
-	proxy = ao2_alloc(sizeof(*proxy), NULL);
-	if (!proxy)
-		return NULL;
-	proxy->force = force;
-	ast_copy_string(proxy->name, name, sizeof(proxy->name));
-	proxy->ip.sin_port = htons(port_str2int(port, STANDARD_SIP_PORT));
-	proxy_update(proxy);
-	return proxy;
-}
+	th->alert_pipe[0] = th->alert_pipe[1] = -1;
 
-/*! \brief Get default outbound proxy or global proxy */
-static struct sip_proxy *obproxy_get(struct sip_pvt *dialog, struct sip_peer *peer)
-{
-	if (peer && peer->outboundproxy) {
-		if (sipdebug)
-			ast_debug(1, "OBPROXY: Applying peer OBproxy to this call\n");
-		append_history(dialog, "OBproxy", "Using peer obproxy %s", peer->outboundproxy->name);
-		return peer->outboundproxy;
-	}
-	if (sip_cfg.outboundproxy.name[0]) {
-		if (sipdebug)
-			ast_debug(1, "OBPROXY: Applying global OBproxy to this call\n");
-		append_history(dialog, "OBproxy", "Using global obproxy %s", sip_cfg.outboundproxy.name);
-		return &sip_cfg.outboundproxy;
+	if (pipe(th->alert_pipe) == -1) {
+		ao2_t_ref(th, -1, "Failed to open alert pipe on sip_threadinfo");
+		ast_log(LOG_ERROR, "Could not create sip alert pipe in tcptls thread, error %s\n", strerror(errno));
+		return NULL;
 	}
-	if (sipdebug)
-		ast_debug(1, "OBPROXY: Not applying OBproxy to this call\n");
-	return NULL;
+	ao2_t_ref(tcptls_session, +1, "tcptls_session ref for sip_threadinfo object");
+	th->tcptls_session = tcptls_session;
+	th->type = transport ? transport : (tcptls_session->ssl ? SIP_TRANSPORT_TLS: SIP_TRANSPORT_TCP);
+	ao2_t_link(threadt, th, "Adding new tcptls helper thread");
+	ao2_t_ref(th, -1, "Decrementing threadinfo ref from alloc, only table ref remains");
+	return th;
 }
 
-/*! \brief returns true if 'name' (with optional trailing whitespace)
- * matches the sip method 'id'.
- * Strictly speaking, SIP methods are case SENSITIVE, but we do
- * a case-insensitive comparison to be more tolerant.
- * following Jon Postel's rule: Be gentle in what you accept, strict with what you send
- */
-static int method_match(enum sipmethod id, const char *name)
+/*! \brief used to indicate to a tcptls thread that data is ready to be written */
+static int sip_tcptls_write(struct ast_tcptls_session_instance *tcptls_session, const void *buf, size_t len)
 {
-	int len = strlen(sip_methods[id].text);
-	int l_name = name ? strlen(name) : 0;
-	/* true if the string is long enough, and ends with whitespace, and matches */
-	return (l_name >= len && name[len] < 33 &&
-		!strncasecmp(sip_methods[id].text, name, len));
-}
+	int res = len;
+	struct sip_threadinfo *th = NULL;
+	struct tcptls_packet *packet = NULL;
+	struct sip_threadinfo tmp = {
+		.tcptls_session = tcptls_session,
+	};
+	enum sip_tcptls_alert alert = TCPTLS_ALERT_DATA;
 
-/*! \brief  find_sip_method: Find SIP method from header */
-static int find_sip_method(const char *msg)
-{
-	int i, res = 0;
-	
-	if (ast_strlen_zero(msg))
-		return 0;
-	for (i = 1; i < ARRAY_LEN(sip_methods) && !res; i++) {
-		if (method_match(i, msg))
-			res = sip_methods[i].id;
+	if (!tcptls_session) {
+		return XMIT_ERROR;
 	}
-	return res;
-}
-
-/*! \brief Parse supported header in incoming packet */
-static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported)
-{
-	char *next, *sep;
-	char *temp;
-	unsigned int profile = 0;
-	int i, found;
 
-	if (ast_strlen_zero(supported) )
-		return 0;
-	temp = ast_strdupa(supported);
+	ast_mutex_lock(&tcptls_session->lock);
 
-	if (sipdebug)
-		ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", supported);
-
-	for (next = temp; next; next = sep) {
-		found = FALSE;
-		if ( (sep = strchr(next, ',')) != NULL)
-			*sep++ = '\0';
-		next = ast_skip_blanks(next);
-		if (sipdebug)
-			ast_debug(3, "Found SIP option: -%s-\n", next);
-		for (i = 0; i < ARRAY_LEN(sip_options); i++) {
-			if (!strcasecmp(next, sip_options[i].text)) {
-				profile |= sip_options[i].id;
-				found = TRUE;
-				if (sipdebug)
-					ast_debug(3, "Matched SIP option: %s\n", next);
-				break;
-			}
-		}
+	if ((tcptls_session->fd == -1) ||
+		!(th = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread")) ||
+		!(packet = ao2_alloc(sizeof(*packet), tcptls_packet_destructor)) ||
+		!(packet->data = ast_str_create(len))) {
+		goto tcptls_write_setup_error;
+	}
 
-		/* This function is used to parse both Suported: and Require: headers.
-		Let the caller of this function know that an unknown option tag was
-		encountered, so that if the UAC requires it then the request can be
-		rejected with a 420 response. */
-		if (!found)
-			profile |= SIP_OPT_UNKNOWN;
+	/* goto tcptls_write_error should _NOT_ be used beyond this point */
+	ast_str_set(&packet->data, 0, "%s", (char *) buf);
+	packet->len = len;
 
-		if (!found && sipdebug) {
-			if (!strncasecmp(next, "x-", 2))
-				ast_debug(3, "Found private SIP option, not supported: %s\n", next);
-			else
-				ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
-		}
+	/* alert tcptls thread handler that there is a packet to be sent.
+	 * must lock the thread info object to guarantee control of the
+	 * packet queue */
+	ao2_lock(th);
+	if (write(th->alert_pipe[1], &alert, sizeof(alert)) == -1) {
+		ast_log(LOG_ERROR, "write() to alert pipe failed: %s\n", strerror(errno));
+		ao2_t_ref(packet, -1, "could not write to alert pipe, remove packet");
+		packet = NULL;
+		res = XMIT_ERROR;
+	} else { /* it is safe to queue the frame after issuing the alert when we hold the threadinfo lock */
+		AST_LIST_INSERT_TAIL(&th->packet_q, packet, entry);
 	}
+	ao2_unlock(th);
 
-	if (pvt)
-		pvt->sipoptions = profile;
-	return profile;
-}
+	ast_mutex_unlock(&tcptls_session->lock);
+	ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo object after finding it");
+	return res;
 
-/*! \brief See if we pass debug IP filter */
-static inline int sip_debug_test_addr(const struct sockaddr_in *addr)
-{
-	if (!sipdebug)
-		return 0;
-	if (debugaddr.sin_addr.s_addr) {
-		if (((ntohs(debugaddr.sin_port) != 0)
-			&& (debugaddr.sin_port != addr->sin_port))
-			|| (debugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
-			return 0;
+tcptls_write_setup_error:
+	if (th) {
+		ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo obj, could not create packet");
 	}
-	return 1;
-}
-
-/*! \brief The real destination address for a write */
-static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
-{
-	if (p->outboundproxy)
-		return &p->outboundproxy->ip;
+	if (packet) {
+		ao2_t_ref(packet, -1, "could not allocate packet's data");
+	}
+	ast_mutex_unlock(&tcptls_session->lock);
 
-	return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT) ? &p->recv : &p->sa;
+	return XMIT_ERROR;
 }
 
-/*! \brief Display SIP nat mode */
-static const char *sip_nat_mode(const struct sip_pvt *p)
+/*! \brief SIP TCP connection handler */
+static void *sip_tcp_worker_fn(void *data)
 {
-	return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) ? "NAT" : "no NAT";
-}
+	struct ast_tcptls_session_instance *tcptls_session = data;
 
-/*! \brief Test PVT for debugging output */
-static inline int sip_debug_test_pvt(struct sip_pvt *p)
-{
-	if (!sipdebug)
-		return 0;
-	return sip_debug_test_addr(sip_real_dst(p));
+	return _sip_tcp_helper_thread(NULL, tcptls_session);
 }
 
-/*! \brief Return int representing a bit field of transport types found in const char *transport */
-static int get_transport_str2enum(const char *transport)
+/*! \brief SIP TCP thread management function
+	This function reads from the socket, parses the packet into a request
+*/
+static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session)
 {
-	int res = 0;
+	int res, cl;
+	struct sip_request req = { 0, } , reqcpy = { 0, };
+	struct sip_threadinfo *me = NULL;
+	char buf[1024] = "";
+	struct pollfd fds[2] = { { 0 }, { 0 }, };
+	struct ast_tcptls_session_args *ca = NULL;
 
-	if (ast_strlen_zero(transport)) {
-		return res;
-	}
+	/* If this is a server session, then the connection has already been setup,
+	 * simply create the threadinfo object so we can access this thread for writing.
+	 * 
+	 * if this is a client connection more work must be done.
+	 * 1. We own the parent session args for a client connection.  This pointer needs
+	 *    to be held on to so we can decrement it's ref count on thread destruction.
+	 * 2. The threadinfo object was created before this thread was launched, however
+	 *    it must be found within the threadt table.
+	 * 3. Last, the tcptls_session must be started.
+	 */
+	if (!tcptls_session->client) {
+		if (!(me = sip_threadinfo_create(tcptls_session, tcptls_session->ssl ? SIP_TRANSPORT_TLS : SIP_TRANSPORT_TCP))) {
+			goto cleanup;
+		}
+		ao2_t_ref(me, +1, "Adding threadinfo ref for tcp_helper_thread");
+	} else {
+		struct sip_threadinfo tmp = {
+			.tcptls_session = tcptls_session,
+		};
 
-	if (!strcasecmp(transport, "udp")) {
-		res |= SIP_TRANSPORT_UDP;
-	}
-	if (!strcasecmp(transport, "tcp")) {
-		res |= SIP_TRANSPORT_TCP;
-	}
-	if (!strcasecmp(transport, "tls")) {
-		res |= SIP_TRANSPORT_TLS;
+		if ((!(ca = tcptls_session->parent)) ||
+			(!(me = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread"))) ||
+			(!(tcptls_session = ast_tcptls_client_start(tcptls_session)))) {
+			goto cleanup;
+		}
 	}
 
-	return res;
-}
+	me->threadid = pthread_self();
+	ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
 
-/*! \brief Return configuration of transports for a device */
-static inline const char *get_transport_list(unsigned int transports) {
-	switch (transports) {
-		case SIP_TRANSPORT_UDP:
-			return "UDP";
-		case SIP_TRANSPORT_TCP:
-			return "TCP";
-		case SIP_TRANSPORT_TLS:
-			return "TLS";
-		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TCP:
-			return "TCP,UDP";
-		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TLS:
-			return "TLS,UDP";
-		case SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS:
-			return "TLS,TCP";
-		default:
-			return transports ?
-				"TLS,TCP,UDP" : "UNKNOWN";	
-	}
-}
+	/* set up pollfd to watch for reads on both the socket and the alert_pipe */
+	fds[0].fd = tcptls_session->fd;
+	fds[1].fd = me->alert_pipe[0];
+	fds[0].events = fds[1].events = POLLIN | POLLPRI;
 
-/*! \brief Return transport as string */
-static inline const char *get_transport(enum sip_transport t)
-{
-	switch (t) {
-	case SIP_TRANSPORT_UDP:
-		return "UDP";
-	case SIP_TRANSPORT_TCP:
-		return "TCP";
-	case SIP_TRANSPORT_TLS:
-		return "TLS";
-	}
+	if (!(req.data = ast_str_create(SIP_MIN_PACKET)))
+		goto cleanup;
+	if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET)))
+		goto cleanup;
 
-	return "UNKNOWN";
-}
+	for (;;) {
+		struct ast_str *str_save;
 
-/*! \brief Return transport of dialog.
-	\note this is based on a false assumption. We don't always use the
-	outbound proxy for all requests in a dialog. It depends on the
-	"force" parameter. The FIRST request is always sent to the ob proxy.
-	\todo Fix this function to work correctly
-*/
-static inline const char *get_transport_pvt(struct sip_pvt *p)
-{
-	if (p->outboundproxy && p->outboundproxy->transport) {
-		set_socket_transport(&p->socket, p->outboundproxy->transport);
-	}
+		res = ast_poll(fds, 2, -1); /* polls for both socket and alert_pipe */
+		if (res < 0) {
+			ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
+			goto cleanup;
+		}
 
-	return get_transport(p->socket.type);
-}
+		/* handle the socket event, check for both reads from the socket fd,
+		 * and writes from alert_pipe fd */
+		if (fds[0].revents) { /* there is data on the socket to be read */
 
-/*! \brief Transmit SIP message
-	Sends a SIP request or response on a given socket (in the pvt)
-	Called by retrans_pkt, send_request, send_response and
-	__sip_reliable_xmit
-	\return length of transmitted message, XMIT_ERROR on known network failures -1 on other failures.
-*/
-static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len)
-{
-	int res = 0;
-	const struct sockaddr_in *dst = sip_real_dst(p);
+			fds[0].revents = 0;
 
-	ast_debug(2, "Trying to put '%.11s' onto %s socket destined for %s:%d\n", data->str, get_transport_pvt(p), ast_inet_ntoa(dst->sin_addr), htons(dst->sin_port));
+			/* clear request structure */
+			str_save = req.data;
+			memset(&req, 0, sizeof(req));
+			req.data = str_save;
+			ast_str_reset(req.data);
 
-	if (sip_prepare_socket(p) < 0)
-		return XMIT_ERROR;
+			str_save = reqcpy.data;
+			memset(&reqcpy, 0, sizeof(reqcpy));
+			reqcpy.data = str_save;
+			ast_str_reset(reqcpy.data);
 
-	if (p->socket.type == SIP_TRANSPORT_UDP) {
-		res = sendto(p->socket.fd, data->str, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in));
-	} else if (p->socket.tcptls_session) {
-		res = sip_tcptls_write(p->socket.tcptls_session, data->str, len);
-	} else {
-		ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
-		return XMIT_ERROR;
-	}
+			memset(buf, 0, sizeof(buf));
 
-	if (res == -1) {
-		switch (errno) {
-		case EBADF: 		/* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
-		case EHOSTUNREACH: 	/* Host can't be reached */
-		case ENETDOWN: 		/* Interface down */
-		case ENETUNREACH:	/* Network failure */
-		case ECONNREFUSED:      /* ICMP port unreachable */
-			res = XMIT_ERROR;	/* Don't bother with trying to transmit again */
-		}
-	}
-	if (res != len)
-		ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno));
+			if (tcptls_session->ssl) {
+				set_socket_transport(&req.socket, SIP_TRANSPORT_TLS);
+				req.socket.port = htons(ourport_tls);
+			} else {
+				set_socket_transport(&req.socket, SIP_TRANSPORT_TCP);
+				req.socket.port = htons(ourport_tcp);
+			}
+			req.socket.fd = tcptls_session->fd;
 
-	return res;
-}
+			/* Read in headers one line at a time */
+			while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
+				ast_mutex_lock(&tcptls_session->lock);
+				if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
+					ast_mutex_unlock(&tcptls_session->lock);
+					goto cleanup;
+				}
+				ast_mutex_unlock(&tcptls_session->lock);
+				if (me->stop)
+					 goto cleanup;
+				ast_str_append(&req.data, 0, "%s", buf);
+				req.len = req.data->used;
+			}
+			copy_request(&reqcpy, &req);
+			parse_request(&reqcpy);
+			/* In order to know how much to read, we need the content-length header */
+			if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
+				while (cl > 0) {
+					size_t bytes_read;
+					ast_mutex_lock(&tcptls_session->lock);
+					if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, cl), tcptls_session->f))) {
+						ast_mutex_unlock(&tcptls_session->lock);
+						goto cleanup;
+					}
+					buf[bytes_read] = '\0';
+					ast_mutex_unlock(&tcptls_session->lock);
+					if (me->stop)
+						goto cleanup;
+					cl -= strlen(buf);
+					ast_str_append(&req.data, 0, "%s", buf);
+					req.len = req.data->used;
+				}
+			}
+			/*! \todo XXX If there's no Content-Length or if the content-length and what
+					we receive is not the same - we should generate an error */
 
-/*! \brief Build a Via header for a request */
-static void build_via(struct sip_pvt *p)
-{
-	/* Work around buggy UNIDEN UIP200 firmware */
-	const char *rport = (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT)) ? ";rport" : "";
+			req.socket.tcptls_session = tcptls_session;
+			handle_request_do(&req, &tcptls_session->remote_address);
+		}
 
-	/* z9hG4bK is a magic cookie.  See RFC 3261 section 8.1.1.7 */
-	snprintf(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x%s",
-		 get_transport_pvt(p),
-		 ast_inet_ntoa(p->ourip.sin_addr),
-		 ntohs(p->ourip.sin_port), (int) p->branch, rport);
-}
+		if (fds[1].revents) { /* alert_pipe indicates there is data in the send queue to be sent */
+			enum sip_tcptls_alert alert;
+			struct tcptls_packet *packet;
 
-/*! \brief NAT fix - decide which IP address to use for Asterisk server?
- *
- * Using the localaddr structure built up with localnet statements in sip.conf
- * apply it to their address to see if we need to substitute our
- * externip or can get away with our internal bindaddr
- * 'us' is always overwritten.
- */
-static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us, struct sip_pvt *p)
-{
-	struct sockaddr_in theirs;
-	/* Set want_remap to non-zero if we want to remap 'us' to an externally
-	 * reachable IP address and port. This is done if:
-	 * 1. we have a localaddr list (containing 'internal' addresses marked
-	 *    as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
-	 *    and AST_SENSE_ALLOW on 'external' ones);
-	 * 2. either stunaddr or externip is set, so we know what to use as the
-	 *    externally visible address;
-	 * 3. the remote address, 'them', is external;
-	 * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
-	 *    when passed to ast_apply_ha() so it does need to be remapped.
-	 *    This fourth condition is checked later.
-	 */
-	int want_remap;
+			fds[1].revents = 0;
 
-	*us = internip;		/* starting guess for the internal address */
-	/* now ask the system what would it use to talk to 'them' */
-	ast_ouraddrfor(them, &us->sin_addr);
-	theirs.sin_addr = *them;
+			if (read(me->alert_pipe[0], &alert, sizeof(alert)) == -1) {
+				ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
+				continue;
+			}
 
-	want_remap = localaddr &&
-		(externip.sin_addr.s_addr || stunaddr.sin_addr.s_addr) &&
-		ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
+			switch (alert) {
+			case TCPTLS_ALERT_STOP:
+				goto cleanup;
+			case TCPTLS_ALERT_DATA:
+				ao2_lock(me);
+				if (!(packet = AST_LIST_REMOVE_HEAD(&me->packet_q, entry))) {
+					ast_log(LOG_WARNING, "TCPTLS thread alert_pipe indicated packet should be sent, but frame_q is empty");
+				} else if (ast_tcptls_server_write(tcptls_session, ast_str_buffer(packet->data), packet->len) == -1) {
+					ast_log(LOG_WARNING, "Failure to write to tcp/tls socket\n");
+				}
 
-	if (want_remap &&
-	    (!sip_cfg.matchexterniplocally || !ast_apply_ha(localaddr, us)) ) {
-		/* if we used externhost or stun, see if it is time to refresh the info */
-		if (externexpire && time(NULL) >= externexpire) {
-			if (stunaddr.sin_addr.s_addr) {
-				ast_stun_request(sipsock, &stunaddr, NULL, &externip);
-			} else {
-				if (ast_parse_arg(externhost, PARSE_INADDR, &externip))
-					ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
-			}
-			externexpire = time(NULL) + externrefresh;
-		}
-		if (externip.sin_addr.s_addr) {
-			*us = externip;
-			switch (p->socket.type) {
-			case SIP_TRANSPORT_TCP:
-				us->sin_port = htons(externtcpport);
-				break;
-			case SIP_TRANSPORT_TLS:
-				us->sin_port = htons(externtlsport);
+				if (packet) {
+					ao2_t_ref(packet, -1, "tcptls packet sent, this is no longer needed");
+				}
+				ao2_unlock(me);
 				break;
-			case SIP_TRANSPORT_UDP:
-				break; /* fall through */
 			default:
-				us->sin_port = htons(STANDARD_SIP_PORT); /* we should never get here */
-			}
-		}
-		else
-			ast_log(LOG_WARNING, "stun failed\n");
-		ast_debug(1, "Target address %s is not local, substituting externip\n",
-			ast_inet_ntoa(*(struct in_addr *)&them->s_addr));
-	} else if (p) {
-		/* no remapping, but we bind to a specific address, so use it. */
-		switch (p->socket.type) {
-		case SIP_TRANSPORT_TCP:
-			if (sip_tcp_desc.local_address.sin_addr.s_addr) {
-				*us = sip_tcp_desc.local_address;
-			} else {
-				us->sin_port = sip_tcp_desc.local_address.sin_port;
-			}
-			break;
-		case SIP_TRANSPORT_TLS:
-			if (sip_tls_desc.local_address.sin_addr.s_addr) {
-				*us = sip_tls_desc.local_address;
-			} else {
-				us->sin_port = sip_tls_desc.local_address.sin_port;
-			}
-				break;
-		case SIP_TRANSPORT_UDP:
-			/* fall through on purpose */
-		default:
-			if (bindaddr.sin_addr.s_addr) {
-				*us = bindaddr;
+				ast_log(LOG_ERROR, "Unknown tcptls thread alert '%d'\n", alert);
 			}
 		}
-	} else if (bindaddr.sin_addr.s_addr) {
-		*us = bindaddr;
 	}
-	ast_debug(3, "Setting SIP_TRANSPORT_%s with address %s:%d\n", get_transport(p->socket.type), ast_inet_ntoa(us->sin_addr), ntohs(us->sin_port));
-}
 
-/*! \brief Append to SIP dialog history with arg list  */
-static __attribute__((format(printf, 2, 0))) void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
-{
-	char buf[80], *c = buf; /* max history length */
-	struct sip_history *hist;
-	int l;
+	ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
 
-	vsnprintf(buf, sizeof(buf), fmt, ap);
-	strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
-	l = strlen(buf) + 1;
-	if (!(hist = ast_calloc(1, sizeof(*hist) + l)))
-		return;
-	if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
-		ast_free(hist);
-		return;
+cleanup:
+	if (me) {
+		ao2_t_unlink(threadt, me, "Removing tcptls helper thread, thread is closing");
+		ao2_t_ref(me, -1, "Removing tcp_helper_threads threadinfo ref");
 	}
-	memcpy(hist->event, buf, l);
-	if (p->history_entries == MAX_HISTORY_ENTRIES) {
-		struct sip_history *oldest;
-		oldest = AST_LIST_REMOVE_HEAD(p->history, list);
-		p->history_entries--;
-		ast_free(oldest);
+	if (reqcpy.data) {
+		ast_free(reqcpy.data);
 	}
-	AST_LIST_INSERT_TAIL(p->history, hist, list);
-	p->history_entries++;
-}
-
-/*! \brief Append to SIP dialog history with arg list  */
-static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
-{
-	va_list ap;
 
-	if (!p)
-		return;
+	if (req.data) {
+		ast_free(req.data);
+		req.data = NULL;
+	}
 
-	if (!p->do_history && !recordhistory && !dumphistory)
-		return;
+	/* if client, we own the parent session arguments and must decrement ref */
+	if (ca) {
+		ao2_t_ref(ca, -1, "closing tcptls thread, getting rid of client tcptls_session arguments");
+	}
 
-	va_start(ap, fmt);
-	append_history_va(p, fmt, ap);
-	va_end(ap);
+	if (tcptls_session) {
+		ast_mutex_lock(&tcptls_session->lock);
+		if (tcptls_session->f) {
+			fclose(tcptls_session->f);
+			tcptls_session->f = NULL;
+		}
+		if (tcptls_session->fd != -1) {
+			close(tcptls_session->fd);
+			tcptls_session->fd = -1;
+		}
+		tcptls_session->parent = NULL;
+		ast_mutex_unlock(&tcptls_session->lock);
 
-	return;
+		ao2_ref(tcptls_session, -1);
+		tcptls_session = NULL;
+	}
+	return NULL;
 }
 
-/*! \brief Retransmit SIP message if no answer (Called from scheduler) */
-static int retrans_pkt(const void *data)
+
+/*!
+ * helper functions to unreference various types of objects.
+ * By handling them this way, we don't have to declare the
+ * destructor on each call, which removes the chance of errors.
+ */
+static void *unref_peer(struct sip_peer *peer, char *tag)
 {
-	struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
-	int reschedule = DEFAULT_RETRANS;
-	int xmitres = 0;
-	
-	/* Lock channel PVT */
-	sip_pvt_lock(pkt->owner);
+	ao2_t_ref(peer, -1, tag);
+	return NULL;
+}
 
-	if (pkt->retrans < MAX_RETRANS) {
-		pkt->retrans++;
- 		if (!pkt->timer_t1) {	/* Re-schedule using timer_a and timer_t1 */
-			if (sipdebug)
- 				ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method);
-		} else {
- 			int siptimer_a;
+static struct sip_peer *ref_peer(struct sip_peer *peer, char *tag)
+{
+	ao2_t_ref(peer, 1, tag);
+	return peer;
+}
 
- 			if (sipdebug)
- 				ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method);
- 			if (!pkt->timer_a)
- 				pkt->timer_a = 2 ;
- 			else
- 				pkt->timer_a = 2 * pkt->timer_a;
+/*! \brief maintain proper refcounts for a sip_pvt's outboundproxy
+ *
+ * This function sets pvt's outboundproxy pointer to the one referenced
+ * by the proxy parameter. Because proxy may be a refcounted object, and
+ * because pvt's old outboundproxy may also be a refcounted object, we need
+ * to maintain the proper refcounts.
+ *
+ * \param pvt The sip_pvt for which we wish to set the outboundproxy
+ * \param proxy The sip_proxy which we will point pvt towards.
+ * \return Returns void
+ */
+static void ref_proxy(struct sip_pvt *pvt, struct sip_proxy *proxy)
+{
+	struct sip_proxy *old_obproxy = pvt->outboundproxy;
+	/* The sip_cfg.outboundproxy is statically allocated, and so
+	 * we don't ever need to adjust refcounts for it
+	 */
+	if (proxy && proxy != &sip_cfg.outboundproxy) {
+		ao2_ref(proxy, +1);
+	}
+	pvt->outboundproxy = proxy;
+	if (old_obproxy && old_obproxy != &sip_cfg.outboundproxy) {
+		ao2_ref(old_obproxy, -1);
+	}
+}
 
- 			/* For non-invites, a maximum of 4 secs */
- 			siptimer_a = pkt->timer_t1 * pkt->timer_a;	/* Double each time */
- 			if (pkt->method != SIP_INVITE && siptimer_a > 4000)
- 				siptimer_a = 4000;
- 		
- 			/* Reschedule re-transmit */
-			reschedule = siptimer_a;
- 			ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid);
- 		}
+/*!
+ * \brief Unlink a dialog from the dialogs container, as well as any other places
+ * that it may be currently stored.
+ *
+ * \note A reference to the dialog must be held before calling this function, and this
+ * function does not release that reference.
+ */
+void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist)
+{
+	struct sip_pkt *cp;
 
-		if (sip_debug_test_pvt(pkt->owner)) {
-			const struct sockaddr_in *dst = sip_real_dst(pkt->owner);
-			ast_verbose("Retransmitting #%d (%s) to %s:%d:\n%s\n---\n",
-				pkt->retrans, sip_nat_mode(pkt->owner),
-				ast_inet_ntoa(dst->sin_addr),
-				ntohs(dst->sin_port), pkt->data->str);
-		}
+	dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
 
-		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str);
-		xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
-		sip_pvt_unlock(pkt->owner);
-		if (xmitres == XMIT_ERROR)
-			ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid);
-		else
-			return  reschedule;
-	}
-	/* Too many retries */
-	if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) {
-		if (pkt->is_fatal || sipdebug)	/* Tell us if it's critical or if we're debugging */
-			ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s) -- See doc/sip-retransmit.txt.\n",
-				pkt->owner->callid, pkt->seqno,
-				pkt->is_fatal ? "Critical" : "Non-critical", pkt->is_resp ? "Response" : "Request");
-	} else if (pkt->method == SIP_OPTIONS && sipdebug) {
-			ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s)  -- See doc/sip-retransmit.txt.\n", pkt->owner->callid);
+	ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
 
+	/* Unlink us from the owner (channel) if we have one */
+	if (dialog->owner) {
+		if (lockowner)
+			ast_channel_lock(dialog->owner);
+		ast_debug(1, "Detaching from channel %s\n", dialog->owner->name);
+		dialog->owner->tech_pvt = dialog_unref(dialog->owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
+		if (lockowner)
+			ast_channel_unlock(dialog->owner);
 	}
-	if (xmitres == XMIT_ERROR) {
-		ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid);
-		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
-	} else
-		append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
- 		
-	pkt->retransid = -1;
+	if (dialog->registry) {
+		if (dialog->registry->call == dialog)
+			dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
+		dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
+	}
+	if (dialog->stateid > -1) {
+		ast_extension_state_del(dialog->stateid, NULL);
+		dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
+		dialog->stateid = -1; /* shouldn't we 'zero' this out? */
+	}
+	/* Remove link from peer to subscription of MWI */
+	if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog)
+		dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+	if (dialog->relatedpeer && dialog->relatedpeer->call == dialog)
+		dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
 
-	if (pkt->is_fatal) {
-		while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
-			sip_pvt_unlock(pkt->owner);	/* SIP_PVT, not channel */
-			usleep(1);
-			sip_pvt_lock(pkt->owner);
+	/* remove all current packets in this dialog */
+	while((cp = dialog->packets)) {
+		dialog->packets = dialog->packets->next;
+		AST_SCHED_DEL(sched, cp->retransid);
+		dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
+		if (cp->data) {
+			ast_free(cp->data);
 		}
+		ast_free(cp);
+	}
 
-		if (pkt->owner->owner && !pkt->owner->owner->hangupcause)
-			pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE;
-		
-		if (pkt->owner->owner) {
-			sip_alreadygone(pkt->owner);
-			ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see doc/sip-retransmit.txt).\n", pkt->owner->callid);
-			ast_queue_hangup_with_cause(pkt->owner->owner, AST_CAUSE_PROTOCOL_ERROR);
-			ast_channel_unlock(pkt->owner->owner);
-		} else {
-			/* If no channel owner, destroy now */
+	AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
 
-			/* Let the peerpoke system expire packets when the timer expires for poke_noanswer */
-			if (pkt->method != SIP_OPTIONS && pkt->method != SIP_REGISTER) {
-				pvt_set_needdestroy(pkt->owner, "no response to critical packet");
-				sip_alreadygone(pkt->owner);
-				append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately");
-			}
-		}
-	}
+	AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
+	
+	if (dialog->autokillid > -1)
+		AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
 
-	if (pkt->method == SIP_BYE) {
-		/* We're not getting answers on SIP BYE's.  Tear down the call anyway. */
-		if (pkt->owner->owner)
-			ast_channel_unlock(pkt->owner->owner);
-		append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway.");
-		pvt_set_needdestroy(pkt->owner, "no response to BYE");
+	if (dialog->request_queue_sched_id > -1) {
+		AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr"));
 	}
 
-	/* Remove the packet */
-	for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
-		if (cur == pkt) {
-			UNLINK(cur, pkt->owner->packets, prev);
-			sip_pvt_unlock(pkt->owner);
-			if (pkt->owner)
-				pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
-			if (pkt->data)
-				ast_free(pkt->data);
-			pkt->data = NULL;
-			ast_free(pkt);
-			return 0;
-		}
+	AST_SCHED_DEL_UNREF(sched, dialog->provisional_keepalive_sched_id, dialog_unref(dialog, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
+
+	if (dialog->t38id > -1) {
+		AST_SCHED_DEL_UNREF(sched, dialog->t38id, dialog_unref(dialog, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
 	}
-	/* error case */
-	ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
-	sip_pvt_unlock(pkt->owner);
-	return 0;
+
+	dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
+	return NULL;
 }
 
-/*! \brief Transmit packet with retransmits
-	\return 0 on success, -1 on failure to allocate packet
-*/
-static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod)
+void *registry_unref(struct sip_registry *reg, char *tag)
 {
-	struct sip_pkt *pkt = NULL;
-	int siptimer_a = DEFAULT_RETRANS;
-	int xmitres = 0;
-	int respid;
+	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount - 1);
+	ASTOBJ_UNREF(reg, sip_registry_destroy);
+	return NULL;
+}
 
-	if (sipmethod == SIP_INVITE) {
-		/* Note this is a pending invite */
-		p->pendinginvite = seqno;
-	}
+/*! \brief Add object reference to SIP registry */
+static struct sip_registry *registry_addref(struct sip_registry *reg, char *tag)
+{
+	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount + 1);
+	return ASTOBJ_REF(reg);	/* Add pointer to registry in packet */
+}
 
-	/* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
-	/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
-	/*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
-	if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
-		xmitres = __sip_xmit(p, data, len);	/* Send packet */
-		if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
-			append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
-			return AST_FAILURE;
-		} else {
-			return AST_SUCCESS;
-		}
-	}
+/*! \brief Interface structure with callbacks used to connect to UDPTL module*/
+static struct ast_udptl_protocol sip_udptl = {
+	type: "SIP",
+	get_udptl_info: sip_get_udptl_peer,
+	set_udptl_peer: sip_set_udptl_peer,
+};
 
-	if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
-		return AST_FAILURE;
-	/* copy data, add a terminator and save length */
-	if (!(pkt->data = ast_str_create(len))) {
-		ast_free(pkt);
-		return AST_FAILURE;
-	}
-	ast_str_set(&pkt->data, 0, "%s%s", data->str, "\0");
-	pkt->packetlen = len;
-	/* copy other parameters from the caller */
-	pkt->method = sipmethod;
-	pkt->seqno = seqno;
-	pkt->is_resp = resp;
-	pkt->is_fatal = fatal;
-	pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
-	pkt->next = p->packets;
-	p->packets = pkt;	/* Add it to the queue */
-	if (resp) {
-		/* Parse out the response code */
-		if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %30u", &respid) == 1) {
-			pkt->response_code = respid;
-		}
-	}
-	pkt->timer_t1 = p->timer_t1;	/* Set SIP timer T1 */
-	pkt->retransid = -1;
-	if (pkt->timer_t1)
-		siptimer_a = pkt->timer_t1 * 2;
+static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
+	__attribute__((format(printf, 2, 3)));
 
-	/* Schedule retransmission */
-	AST_SCHED_REPLACE_VARIABLE(pkt->retransid, sched, siptimer_a, retrans_pkt, pkt, 1);
-	if (sipdebug)
-		ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id  #%d\n", pkt->retransid);
 
-	xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);	/* Send packet */
+/*! \brief Convert transfer status to string */
+static const char *referstatus2str(enum referstatus rstatus)
+{
+	return map_x_s(referstatusstrings, rstatus, "");
+}
 
-	if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
-		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
-		ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
-		AST_SCHED_DEL(sched, pkt->retransid);
-		p->packets = pkt->next;
-		pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
-		ast_free(pkt->data);
-		ast_free(pkt);
-		return AST_FAILURE;
-	} else {
-		return AST_SUCCESS;
-	}
+static inline void pvt_set_needdestroy(struct sip_pvt *pvt, const char *reason)
+{
+	append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason);
+	pvt->needdestroy = 1;
 }
 
-/*! \brief Kill a SIP dialog (called only by the scheduler)
- * The scheduler has a reference to this dialog when p->autokillid != -1,
- * and we are called using that reference. So if the event is not
- * rescheduled, we need to call dialog_unref().
- */
-static int __sip_autodestruct(const void *data)
+/*! \brief Initialize the initital request packet in the pvt structure.
+ 	This packet is used for creating replies and future requests in
+	a dialog */
+static void initialize_initreq(struct sip_pvt *p, struct sip_request *req)
 {
-	struct sip_pvt *p = (struct sip_pvt *)data;
+	if (p->initreq.headers)
+		ast_debug(1, "Initializing already initialized SIP dialog %s (presumably reinvite)\n", p->callid);
+	else
+		ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+	/* Use this as the basis */
+	copy_request(&p->initreq, req);
+	parse_request(&p->initreq);
+	if (req->debug)
+		ast_verbose("Initreq: %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+}
 
-	/* If this is a subscription, tell the phone that we got a timeout */
-	if (p->subscribed) {
-		transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE);	/* Send last notification */
-		p->subscribed = NONE;
-		append_history(p, "Subscribestatus", "timeout");
-		ast_debug(3, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>");
-		return 10000;	/* Reschedule this destruction so that we know that it's gone */
-	}
+/*! \brief Encapsulate setting of SIP_ALREADYGONE to be able to trace it with debugging */
+static void sip_alreadygone(struct sip_pvt *dialog)
+{
+	ast_debug(3, "Setting SIP_ALREADYGONE on dialog %s\n", dialog->callid);
+	dialog->alreadygone = 1;
+}
 
-	/* If there are packets still waiting for delivery, delay the destruction */
-	if (p->packets) {
-		if (!p->needdestroy) {
-			char method_str[31];
-			ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
-			append_history(p, "ReliableXmit", "timeout");
-			if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
-				if (method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
-					pvt_set_needdestroy(p, "autodestruct");
-				}
-			}
-			return 10000;
-		} else {
-			/* They've had their chance to respond. Time to bail */
-			__sip_pretend_ack(p);
+/*! Resolve DNS srv name or host name in a sip_proxy structure */
+static int proxy_update(struct sip_proxy *proxy)
+{
+	/* if it's actually an IP address and not a name,
+           there's no need for a managed lookup */
+	if (!inet_aton(proxy->name, &proxy->ip.sin_addr)) {
+		/* Ok, not an IP address, then let's check if it's a domain or host */
+		/* XXX Todo - if we have proxy port, don't do SRV */
+		if (ast_get_ip_or_srv(&proxy->ip, proxy->name, sip_cfg.srvlookup ? "_sip._udp" : NULL) < 0) {
+			ast_log(LOG_WARNING, "Unable to locate host '%s'\n", proxy->name);
+			return FALSE;
 		}
 	}
+	proxy->last_dnsupdate = time(NULL);
+	return TRUE;
+}
 
-	if (p->subscribed == MWI_NOTIFICATION) {
-		if (p->relatedpeer) {
-			p->relatedpeer = unref_peer(p->relatedpeer, "__sip_autodestruct: unref peer p->relatedpeer");	/* Remove link to peer. If it's realtime, make sure it's gone from memory) */
-		}
+/*! \brief converts ascii port to int representation. If no
+ *  pt buffer is provided or the pt has errors when being converted
+ *  to an int value, the port provided as the standard is used.
+ */
+unsigned int port_str2int(const char *pt, unsigned int standard)
+{
+	int port = standard;
+	if (ast_strlen_zero(pt) || (sscanf(pt, "%30d", &port) != 1) || (port < 1) || (port > 65535)) {
+		port = standard;
 	}
 
-	/* Reset schedule ID */
-	p->autokillid = -1;
-
-	if (p->owner) {
-		ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
-		ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
-	} else if (p->refer && !p->alreadygone) {
-		ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
-		transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
-		append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
-		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-	} else {
-		append_history(p, "AutoDestroy", "%s", p->callid);
-		ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
-		dialog_unlink_all(p, TRUE, TRUE); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
-		/* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
-		/* sip_destroy(p); */		/* Go ahead and destroy dialog. All attempts to recover is done */
-		/* sip_destroy also absorbs the reference */
-	}
-	dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
-	return 0;
+	return port;
 }
 
-/*! \brief Schedule destruction of SIP dialog */
-void sip_scheddestroy(struct sip_pvt *p, int ms)
+/*! \brief Allocate and initialize sip proxy */
+static struct sip_proxy *proxy_allocate(char *name, char *port, int force)
 {
-	if (ms < 0) {
-		if (p->timer_t1 == 0) {
-			p->timer_t1 = global_t1;	/* Set timer T1 if not set (RFC 3261) */
-		}
-		if (p->timer_b == 0) {
-			p->timer_b = global_timer_b;  /* Set timer B if not set (RFC 3261) */
-		}
-		ms = p->timer_t1 * 64;
+	struct sip_proxy *proxy;
+
+	if (ast_strlen_zero(name)) {
+		return NULL;
 	}
-	if (sip_debug_test_pvt(p))
-		ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
-	if (sip_cancel_destroy(p))
-		ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
 
-	if (p->do_history)
-		append_history(p, "SchedDestroy", "%d ms", ms);
-	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
+	proxy = ao2_alloc(sizeof(*proxy), NULL);
+	if (!proxy)
+		return NULL;
+	proxy->force = force;
+	ast_copy_string(proxy->name, name, sizeof(proxy->name));
+	proxy->ip.sin_port = htons(port_str2int(port, STANDARD_SIP_PORT));
+	proxy_update(proxy);
+	return proxy;
+}
 
-	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0)
-		stop_session_timer(p);
+/*! \brief Get default outbound proxy or global proxy */
+static struct sip_proxy *obproxy_get(struct sip_pvt *dialog, struct sip_peer *peer)
+{
+	if (peer && peer->outboundproxy) {
+		if (sipdebug)
+			ast_debug(1, "OBPROXY: Applying peer OBproxy to this call\n");
+		append_history(dialog, "OBproxy", "Using peer obproxy %s", peer->outboundproxy->name);
+		return peer->outboundproxy;
+	}
+	if (sip_cfg.outboundproxy.name[0]) {
+		if (sipdebug)
+			ast_debug(1, "OBPROXY: Applying global OBproxy to this call\n");
+		append_history(dialog, "OBproxy", "Using global obproxy %s", sip_cfg.outboundproxy.name);
+		return &sip_cfg.outboundproxy;
+	}
+	if (sipdebug)
+		ast_debug(1, "OBPROXY: Not applying OBproxy to this call\n");
+	return NULL;
 }
 
-/*! \brief Cancel destruction of SIP dialog.
- * Be careful as this also absorbs the reference - if you call it
- * from within the scheduler, this might be the last reference.
+/*! \brief returns true if 'name' (with optional trailing whitespace)
+ * matches the sip method 'id'.
+ * Strictly speaking, SIP methods are case SENSITIVE, but we do
+ * a case-insensitive comparison to be more tolerant.
+ * following Jon Postel's rule: Be gentle in what you accept, strict with what you send
  */
-int sip_cancel_destroy(struct sip_pvt *p)
+static int method_match(enum sipmethod id, const char *name)
 {
-	int res = 0;
-	if (p->autokillid > -1) {
-		int res3;
+	int len = strlen(sip_methods[id].text);
+	int l_name = name ? strlen(name) : 0;
+	/* true if the string is long enough, and ends with whitespace, and matches */
+	return (l_name >= len && name[len] < 33 &&
+		!strncasecmp(sip_methods[id].text, name, len));
+}
 
-		if (!(res3 = ast_sched_del(sched, p->autokillid))) {
-			append_history(p, "CancelDestroy", "");
-			p->autokillid = -1;
-			dialog_unref(p, "dialog unrefd because autokillid is de-sched'd");
-		}
+/*! \brief  find_sip_method: Find SIP method from header */
+static int find_sip_method(const char *msg)
+{
+	int i, res = 0;
+	
+	if (ast_strlen_zero(msg))
+		return 0;
+	for (i = 1; i < ARRAY_LEN(sip_methods) && !res; i++) {
+		if (method_match(i, msg))
+			res = sip_methods[i].id;
 	}
 	return res;
 }
 
-/*! \brief Acknowledges receipt of a packet and stops retransmission
- * called with p locked*/
-int __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
+/*! \brief Parse supported header in incoming packet */
+static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported)
 {
-	struct sip_pkt *cur, *prev = NULL;
-	const char *msg = "Not Found";	/* used only for debugging */
-	int res = FALSE;
+	char *next, *sep;
+	char *temp;
+	unsigned int profile = 0;
+	int i, found;
 
-	/* If we have an outbound proxy for this dialog, then delete it now since
-	  the rest of the requests in this dialog needs to follow the routing.
-	  If obforcing is set, we will keep the outbound proxy during the whole
-	  dialog, regardless of what the SIP rfc says
-	*/
-	if (p->outboundproxy && !p->outboundproxy->force){
-		ref_proxy(p, NULL);
-	}
+	if (ast_strlen_zero(supported) )
+		return 0;
+	temp = ast_strdupa(supported);
 
-	for (cur = p->packets; cur; prev = cur, cur = cur->next) {
-		if (cur->seqno != seqno || cur->is_resp != resp)
-			continue;
-		if (cur->is_resp || cur->method == sipmethod) {
-			res = TRUE;
-			msg = "Found";
-			if (!resp && (seqno == p->pendinginvite)) {
-				ast_debug(1, "Acked pending invite %d\n", p->pendinginvite);
-				p->pendinginvite = 0;
-			}
-			if (cur->retransid > -1) {
-				if (sipdebug)
-					ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
-			}
-			/* This odd section is designed to thwart a
-			 * race condition in the packet scheduler. There are
-			 * two conditions under which deleting the packet from the
-			 * scheduler can fail.
-			 *
-			 * 1. The packet has been removed from the scheduler because retransmission
-			 * is being attempted. The problem is that if the packet is currently attempting
-			 * retransmission and we are at this point in the code, then that MUST mean
-			 * that retrans_pkt is waiting on p's lock. Therefore we will relinquish the
-			 * lock temporarily to allow retransmission.
-			 *
-			 * 2. The packet has reached its maximum number of retransmissions and has
-			 * been permanently removed from the packet scheduler. If this is the case, then
-			 * the packet's retransid will be set to -1. The atomicity of the setting and checking
-			 * of the retransid to -1 is ensured since in both cases p's lock is held.
-			 */
-			while (cur->retransid > -1 && ast_sched_del(sched, cur->retransid)) {
-				sip_pvt_unlock(p);
-				usleep(1);
-				sip_pvt_lock(p);
+	if (sipdebug)
+		ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", supported);
+
+	for (next = temp; next; next = sep) {
+		found = FALSE;
+		if ( (sep = strchr(next, ',')) != NULL)
+			*sep++ = '\0';
+		next = ast_skip_blanks(next);
+		if (sipdebug)
+			ast_debug(3, "Found SIP option: -%s-\n", next);
+		for (i = 0; i < ARRAY_LEN(sip_options); i++) {
+			if (!strcasecmp(next, sip_options[i].text)) {
+				profile |= sip_options[i].id;
+				found = TRUE;
+				if (sipdebug)
+					ast_debug(3, "Matched SIP option: %s\n", next);
+				break;
 			}
-			UNLINK(cur, p->packets, prev);
-			dialog_unref(cur->owner, "unref pkt cur->owner dialog from sip ack before freeing pkt");
-			if (cur->data)
-				ast_free(cur->data);
-			ast_free(cur);
-			break;
 		}
-	}
-	ast_debug(1, "Stopping retransmission on '%s' of %s %d: Match %s\n",
-		p->callid, resp ? "Response" : "Request", seqno, msg);
-	return res;
-}
 
-/*! \brief Pretend to ack all packets
- * called with p locked */
-void __sip_pretend_ack(struct sip_pvt *p)
-{
-	struct sip_pkt *cur = NULL;
+		/* This function is used to parse both Suported: and Require: headers.
+		Let the caller of this function know that an unknown option tag was
+		encountered, so that if the UAC requires it then the request can be
+		rejected with a 420 response. */
+		if (!found)
+			profile |= SIP_OPT_UNKNOWN;
 
-	while (p->packets) {
-		int method;
-		if (cur == p->packets) {
-			ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text);
-			return;
+		if (!found && sipdebug) {
+			if (!strncasecmp(next, "x-", 2))
+				ast_debug(3, "Found private SIP option, not supported: %s\n", next);
+			else
+				ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
 		}
-		cur = p->packets;
-		method = (cur->method) ? cur->method : find_sip_method(cur->data->str);
-		__sip_ack(p, cur->seqno, cur->is_resp, method);
 	}
+
+	if (pvt)
+		pvt->sipoptions = profile;
+	return profile;
 }
 
-/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
-int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
+/*! \brief See if we pass debug IP filter */
+static inline int sip_debug_test_addr(const struct sockaddr_in *addr)
 {
-	struct sip_pkt *cur;
-	int res = FALSE;
-
-	for (cur = p->packets; cur; cur = cur->next) {
-		if (cur->seqno == seqno && cur->is_resp == resp &&
-			(cur->is_resp || method_match(sipmethod, cur->data->str))) {
-			/* this is our baby */
-			if (cur->retransid > -1) {
-				if (sipdebug)
-					ast_debug(4, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
-			}
-			AST_SCHED_DEL(sched, cur->retransid);
-			res = TRUE;
-			break;
-		}
+	if (!sipdebug)
+		return 0;
+	if (debugaddr.sin_addr.s_addr) {
+		if (((ntohs(debugaddr.sin_port) != 0)
+			&& (debugaddr.sin_port != addr->sin_port))
+			|| (debugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+			return 0;
 	}
-	ast_debug(1, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res == -1 ? "Not Found" : "Found");
-	return res;
+	return 1;
 }
 
+/*! \brief The real destination address for a write */
+static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
+{
+	if (p->outboundproxy)
+		return &p->outboundproxy->ip;
+
+	return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT) ? &p->recv : &p->sa;
+}
 
-/*! \brief Copy SIP request, parse it */
-static void parse_copy(struct sip_request *dst, const struct sip_request *src)
+/*! \brief Display SIP nat mode */
+static const char *sip_nat_mode(const struct sip_pvt *p)
 {
-	copy_request(dst, src);
-	parse_request(dst);
+	return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) ? "NAT" : "no NAT";
 }
 
-/*! \brief add a blank line if no body */
-static void add_blank(struct sip_request *req)
+/*! \brief Test PVT for debugging output */
+static inline int sip_debug_test_pvt(struct sip_pvt *p)
 {
-	if (!req->lines) {
-		/* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
-		ast_str_append(&req->data, 0, "\r\n");
-		req->len = ast_str_strlen(req->data);
-	}
+	if (!sipdebug)
+		return 0;
+	return sip_debug_test_addr(sip_real_dst(p));
 }
 
-static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
+/*! \brief Return int representing a bit field of transport types found in const char *transport */
+static int get_transport_str2enum(const char *transport)
 {
-	const char *msg = NULL;
+	int res = 0;
 
-	if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) {
-		msg = "183 Session Progress";
+	if (ast_strlen_zero(transport)) {
+		return res;
 	}
 
-	if (pvt->invitestate < INV_COMPLETED) {
-		if (with_sdp) {
-			transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE, FALSE, FALSE);
-		} else {
-			transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq);
-		}
-		return PROVIS_KEEPALIVE_TIMEOUT;
+	if (!strcasecmp(transport, "udp")) {
+		res |= SIP_TRANSPORT_UDP;
+	}
+	if (!strcasecmp(transport, "tcp")) {
+		res |= SIP_TRANSPORT_TCP;
+	}
+	if (!strcasecmp(transport, "tls")) {
+		res |= SIP_TRANSPORT_TLS;
 	}
 
-	return 0;
+	return res;
 }
 
-static int send_provisional_keepalive(const void *data) {
-	struct sip_pvt *pvt = (struct sip_pvt *) data;
-
-	return send_provisional_keepalive_full(pvt, 0);
+/*! \brief Return configuration of transports for a device */
+static inline const char *get_transport_list(unsigned int transports) {
+	switch (transports) {
+		case SIP_TRANSPORT_UDP:
+			return "UDP";
+		case SIP_TRANSPORT_TCP:
+			return "TCP";
+		case SIP_TRANSPORT_TLS:
+			return "TLS";
+		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TCP:
+			return "TCP,UDP";
+		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TLS:
+			return "TLS,UDP";
+		case SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS:
+			return "TLS,TCP";
+		default:
+			return transports ?
+				"TLS,TCP,UDP" : "UNKNOWN";	
+	}
 }
 
-static int send_provisional_keepalive_with_sdp(const void *data) {
-	struct sip_pvt *pvt = (void *)data;
+/*! \brief Return transport as string */
+static inline const char *get_transport(enum sip_transport t)
+{
+	switch (t) {
+	case SIP_TRANSPORT_UDP:
+		return "UDP";
+	case SIP_TRANSPORT_TCP:
+		return "TCP";
+	case SIP_TRANSPORT_TLS:
+		return "TLS";
+	}
 
-	return send_provisional_keepalive_full(pvt, 1);
+	return "UNKNOWN";
 }
 
-static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp)
+/*! \brief Return transport of dialog.
+	\note this is based on a false assumption. We don't always use the
+	outbound proxy for all requests in a dialog. It depends on the
+	"force" parameter. The FIRST request is always sent to the ob proxy.
+	\todo Fix this function to work correctly
+*/
+static inline const char *get_transport_pvt(struct sip_pvt *p)
 {
-	AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id, dialog_unref(pvt, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
+	if (p->outboundproxy && p->outboundproxy->transport) {
+		set_socket_transport(&p->socket, p->outboundproxy->transport);
+	}
 
-	pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
-		with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, dialog_ref(pvt, "Increment refcount to pass dialog pointer to sched callback"));
+	return get_transport(p->socket.type);
 }
 
-/*! \brief Transmit response on SIP request*/
-static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
+/*! \brief Transmit SIP message
+	Sends a SIP request or response on a given socket (in the pvt)
+	Called by retrans_pkt, send_request, send_response and
+	__sip_reliable_xmit
+	\return length of transmitted message, XMIT_ERROR on known network failures -1 on other failures.
+*/
+static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len)
 {
-	int res;
+	int res = 0;
+	const struct sockaddr_in *dst = sip_real_dst(p);
 
-	add_blank(req);
-	if (sip_debug_test_pvt(p)) {
-		const struct sockaddr_in *dst = sip_real_dst(p);
+	ast_debug(2, "Trying to put '%.11s' onto %s socket destined for %s:%d\n", data->str, get_transport_pvt(p), ast_inet_ntoa(dst->sin_addr), htons(dst->sin_port));
 
-		ast_verbose("\n<--- %sTransmitting (%s) to %s:%d --->\n%s\n<------------>\n",
-			reliable ? "Reliably " : "", sip_nat_mode(p),
-			ast_inet_ntoa(dst->sin_addr),
-			ntohs(dst->sin_port), req->data->str);
-	}
-	if (p->do_history) {
-		struct sip_request tmp = { .rlPart1 = 0, };
-		parse_copy(&tmp, req);
-		append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"),
-			(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlPart2) : sip_methods[tmp.method].text);
-		ast_free(tmp.data);
+	if (sip_prepare_socket(p) < 0)
+		return XMIT_ERROR;
+
+	if (p->socket.type == SIP_TRANSPORT_UDP) {
+		res = sendto(p->socket.fd, data->str, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in));
+	} else if (p->socket.tcptls_session) {
+		res = sip_tcptls_write(p->socket.tcptls_session, data->str, len);
+	} else {
+		ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
+		return XMIT_ERROR;
 	}
 
-	/* If we are sending a final response to an INVITE, stop retransmitting provisional responses */
-	if (p->initreq.method == SIP_INVITE && reliable == XMIT_CRITICAL) {
-		AST_SCHED_DEL_UNREF(sched, p->provisional_keepalive_sched_id, dialog_unref(p, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
+	if (res == -1) {
+		switch (errno) {
+		case EBADF: 		/* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
+		case EHOSTUNREACH: 	/* Host can't be reached */
+		case ENETDOWN: 		/* Interface down */
+		case ENETUNREACH:	/* Network failure */
+		case ECONNREFUSED:      /* ICMP port unreachable */
+			res = XMIT_ERROR;	/* Don't bother with trying to transmit again */
+		}
 	}
+	if (res != len)
+		ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno));
 
-	res = (reliable) ?
-		 __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
-		__sip_xmit(p, req->data, req->len);
-	ast_free(req->data);
-	req->data = NULL;
-	if (res > 0)
-		return 0;
 	return res;
 }
 
-/*! \brief Send SIP Request to the other part of the dialogue 
-	\return see \ref __sip_xmit
-*/
-static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
+/*! \brief Build a Via header for a request */
+static void build_via(struct sip_pvt *p)
 {
-	int res;
-
-	/* If we have an outbound proxy, reset peer address
-		Only do this once.
-	*/
-	if (p->outboundproxy) {
-		p->sa = p->outboundproxy->ip;
-	}
+	/* Work around buggy UNIDEN UIP200 firmware */
+	const char *rport = (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT)) ? ";rport" : "";
 
-	add_blank(req);
-	if (sip_debug_test_pvt(p)) {
-		if (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT))
-			ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), req->data->str);
-		else
-			ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data->str);
-	}
-	if (p->do_history) {
-		struct sip_request tmp = { .rlPart1 = 0, };
-		parse_copy(&tmp, req);
-		append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
-		ast_free(tmp.data);
-	}
-	res = (reliable) ?
-		__sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
-		__sip_xmit(p, req->data, req->len);
-	if (req->data) {
-		ast_free(req->data);
-		req->data = NULL;
-	}
-	return res;
+	/* z9hG4bK is a magic cookie.  See RFC 3261 section 8.1.1.7 */
+	snprintf(p->via, sizeof(p->via), "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x%s",
+		 get_transport_pvt(p),
+		 ast_inet_ntoa(p->ourip.sin_addr),
+		 ntohs(p->ourip.sin_port), (int) p->branch, rport);
 }
 
-static void enable_dsp_detect(struct sip_pvt *p)
+/*! \brief NAT fix - decide which IP address to use for Asterisk server?
+ *
+ * Using the localaddr structure built up with localnet statements in sip.conf
+ * apply it to their address to see if we need to substitute our
+ * externip or can get away with our internal bindaddr
+ * 'us' is always overwritten.
+ */
+static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us, struct sip_pvt *p)
 {
-	int features = 0;
+	struct sockaddr_in theirs;
+	/* Set want_remap to non-zero if we want to remap 'us' to an externally
+	 * reachable IP address and port. This is done if:
+	 * 1. we have a localaddr list (containing 'internal' addresses marked
+	 *    as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
+	 *    and AST_SENSE_ALLOW on 'external' ones);
+	 * 2. either stunaddr or externip is set, so we know what to use as the
+	 *    externally visible address;
+	 * 3. the remote address, 'them', is external;
+	 * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
+	 *    when passed to ast_apply_ha() so it does need to be remapped.
+	 *    This fourth condition is checked later.
+	 */
+	int want_remap;
 
-	if (p->dsp) {
-		return;
-	}
+	*us = internip;		/* starting guess for the internal address */
+	/* now ask the system what would it use to talk to 'them' */
+	ast_ouraddrfor(them, &us->sin_addr);
+	theirs.sin_addr = *them;
 
-	if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
-            (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
-		if (!p->rtp || ast_rtp_instance_dtmf_mode_set(p->rtp, AST_RTP_DTMF_MODE_INBAND)) {
-			features |= DSP_FEATURE_DIGIT_DETECT;
-                }
-	}
+	want_remap = localaddr &&
+		(externip.sin_addr.s_addr || stunaddr.sin_addr.s_addr) &&
+		ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
 
-	if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
-		features |= DSP_FEATURE_FAX_DETECT;
+	if (want_remap &&
+	    (!sip_cfg.matchexterniplocally || !ast_apply_ha(localaddr, us)) ) {
+		/* if we used externhost or stun, see if it is time to refresh the info */
+		if (externexpire && time(NULL) >= externexpire) {
+			if (stunaddr.sin_addr.s_addr) {
+				ast_stun_request(sipsock, &stunaddr, NULL, &externip);
+			} else {
+				if (ast_parse_arg(externhost, PARSE_INADDR, &externip))
+					ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
+			}
+			externexpire = time(NULL) + externrefresh;
+		}
+		if (externip.sin_addr.s_addr) {
+			*us = externip;
+			switch (p->socket.type) {
+			case SIP_TRANSPORT_TCP:
+				us->sin_port = htons(externtcpport);
+				break;
+			case SIP_TRANSPORT_TLS:
+				us->sin_port = htons(externtlsport);
+				break;
+			case SIP_TRANSPORT_UDP:
+				break; /* fall through */
+			default:
+				us->sin_port = htons(STANDARD_SIP_PORT); /* we should never get here */
+			}
+		}
+		else
+			ast_log(LOG_WARNING, "stun failed\n");
+		ast_debug(1, "Target address %s is not local, substituting externip\n",
+			ast_inet_ntoa(*(struct in_addr *)&them->s_addr));
+	} else if (p) {
+		/* no remapping, but we bind to a specific address, so use it. */
+		switch (p->socket.type) {
+		case SIP_TRANSPORT_TCP:
+			if (sip_tcp_desc.local_address.sin_addr.s_addr) {
+				*us = sip_tcp_desc.local_address;
+			} else {
+				us->sin_port = sip_tcp_desc.local_address.sin_port;
+			}
+			break;
+		case SIP_TRANSPORT_TLS:
+			if (sip_tls_desc.local_address.sin_addr.s_addr) {
+				*us = sip_tls_desc.local_address;
+			} else {
+				us->sin_port = sip_tls_desc.local_address.sin_port;
+			}
+				break;
+		case SIP_TRANSPORT_UDP:
+			/* fall through on purpose */
+		default:
+			if (bindaddr.sin_addr.s_addr) {
+				*us = bindaddr;
+			}
+		}
+	} else if (bindaddr.sin_addr.s_addr) {
+		*us = bindaddr;
 	}
+	ast_debug(3, "Setting SIP_TRANSPORT_%s with address %s:%d\n", get_transport(p->socket.type), ast_inet_ntoa(us->sin_addr), ntohs(us->sin_port));
+}
 
-	if (!features) {
-		return;
-	}
+/*! \brief Append to SIP dialog history with arg list  */
+static __attribute__((format(printf, 2, 0))) void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
+{
+	char buf[80], *c = buf; /* max history length */
+	struct sip_history *hist;
+	int l;
 
-	if (!(p->dsp = ast_dsp_new())) {
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
+	l = strlen(buf) + 1;
+	if (!(hist = ast_calloc(1, sizeof(*hist) + l)))
+		return;
+	if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
+		ast_free(hist);
 		return;
 	}
-
-	ast_dsp_set_features(p->dsp, features);
-	if (global_relaxdtmf) {
-		ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+	memcpy(hist->event, buf, l);
+	if (p->history_entries == MAX_HISTORY_ENTRIES) {
+		struct sip_history *oldest;
+		oldest = AST_LIST_REMOVE_HEAD(p->history, list);
+		p->history_entries--;
+		ast_free(oldest);
 	}
+	AST_LIST_INSERT_TAIL(p->history, hist, list);
+	p->history_entries++;
 }
 
-static void disable_dsp_detect(struct sip_pvt *p)
+/*! \brief Append to SIP dialog history with arg list  */
+static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
 {
-	if (p->dsp) {
-		ast_dsp_free(p->dsp);
-		p->dsp = NULL;
-	}
-}
+	va_list ap;
 
-/*! \brief Set an option on a SIP dialog */
-static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen)
-{
-	int res = -1;
-	struct sip_pvt *p = chan->tech_pvt;
+	if (!p)
+		return;
 
-	switch (option) {
-	case AST_OPTION_FORMAT_READ:
-		res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data);
-		break;
-	case AST_OPTION_FORMAT_WRITE:
-		res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data);
-		break;
-	case AST_OPTION_MAKE_COMPATIBLE:
-		res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
-		break;
-	case AST_OPTION_DIGIT_DETECT:
-		if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
-		    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
-			char *cp = (char *) data;
+	if (!p->do_history && !recordhistory && !dumphistory)
+		return;
 
-			ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name);
-			if (*cp) {
-				enable_dsp_detect(p);
-			} else {
-				disable_dsp_detect(p);
-			}
-			res = 0;
-		}
-		break;
-	default:
-		break;
-	}
+	va_start(ap, fmt);
+	append_history_va(p, fmt, ap);
+	va_end(ap);
 
-	return res;
+	return;
 }
 
-/*! \brief Query an option on a SIP dialog */
-static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen)
+/*! \brief Retransmit SIP message if no answer (Called from scheduler) */
+static int retrans_pkt(const void *data)
 {
-	int res = -1;
-	enum ast_t38_state state = T38_STATE_UNAVAILABLE;
-	struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt;
-	char *cp;
-
-	switch (option) {
-	case AST_OPTION_T38_STATE:
-		/* Make sure we got an ast_t38_state enum passed in */
-		if (*datalen != sizeof(enum ast_t38_state)) {
-			ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
-			return -1;
-		}
+	struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
+	int reschedule = DEFAULT_RETRANS;
+	int xmitres = 0;
+	
+	/* Lock channel PVT */
+	sip_pvt_lock(pkt->owner);
 
-		sip_pvt_lock(p);
+	if (pkt->retrans < MAX_RETRANS) {
+		pkt->retrans++;
+ 		if (!pkt->timer_t1) {	/* Re-schedule using timer_a and timer_t1 */
+			if (sipdebug)
+ 				ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method);
+		} else {
+ 			int siptimer_a;
 
-		/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
-		if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
-			switch (p->t38.state) {
-			case T38_LOCAL_REINVITE:
-			case T38_PEER_REINVITE:
-				state = T38_STATE_NEGOTIATING;
-				break;
-			case T38_ENABLED:
-				state = T38_STATE_NEGOTIATED;
-				break;
-			default:
-				state = T38_STATE_UNKNOWN;
-			}
-		}
+ 			if (sipdebug)
+ 				ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method);
+ 			if (!pkt->timer_a)
+ 				pkt->timer_a = 2 ;
+ 			else
+ 				pkt->timer_a = 2 * pkt->timer_a;
 
-		sip_pvt_unlock(p);
+ 			/* For non-invites, a maximum of 4 secs */
+ 			siptimer_a = pkt->timer_t1 * pkt->timer_a;	/* Double each time */
+ 			if (pkt->method != SIP_INVITE && siptimer_a > 4000)
+ 				siptimer_a = 4000;
+ 		
+ 			/* Reschedule re-transmit */
+			reschedule = siptimer_a;
+ 			ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid);
+ 		}
 
-		*((enum ast_t38_state *) data) = state;
-		res = 0;
+		if (sip_debug_test_pvt(pkt->owner)) {
+			const struct sockaddr_in *dst = sip_real_dst(pkt->owner);
+			ast_verbose("Retransmitting #%d (%s) to %s:%d:\n%s\n---\n",
+				pkt->retrans, sip_nat_mode(pkt->owner),
+				ast_inet_ntoa(dst->sin_addr),
+				ntohs(dst->sin_port), pkt->data->str);
+		}
 
-		break;
-	case AST_OPTION_DIGIT_DETECT:
-		cp = (char *) data;
-		*cp = p->dsp ? 1 : 0;
-		ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
-		break;
-	default:
-		break;
+		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str);
+		xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
+		sip_pvt_unlock(pkt->owner);
+		if (xmitres == XMIT_ERROR)
+			ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid);
+		else
+			return  reschedule;
 	}
+	/* Too many retries */
+	if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) {
+		if (pkt->is_fatal || sipdebug)	/* Tell us if it's critical or if we're debugging */
+			ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s) -- See doc/sip-retransmit.txt.\n",
+				pkt->owner->callid, pkt->seqno,
+				pkt->is_fatal ? "Critical" : "Non-critical", pkt->is_resp ? "Response" : "Request");
+	} else if (pkt->method == SIP_OPTIONS && sipdebug) {
+			ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s)  -- See doc/sip-retransmit.txt.\n", pkt->owner->callid);
 
-	return res;
-}
-
-/*! \brief Locate closing quote in a string, skipping escaped quotes.
- * optionally with a limit on the search.
- * start must be past the first quote.
- */
-const char *find_closing_quote(const char *start, const char *lim)
-{
-	char last_char = '\0';
-	const char *s;
-	for (s = start; *s && s != lim; last_char = *s++) {
-		if (*s == '"' && last_char != '\\')
-			break;
 	}
-	return s;
-}
-
-/*! \brief Send message with Access-URL header, if this is an HTML URL only! */
-static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
-{
-	struct sip_pvt *p = chan->tech_pvt;
+	if (xmitres == XMIT_ERROR) {
+		ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid);
+		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+	} else
+		append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+ 		
+	pkt->retransid = -1;
 
-	if (subclass != AST_HTML_URL)
-		return -1;
+	if (pkt->is_fatal) {
+		while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
+			sip_pvt_unlock(pkt->owner);	/* SIP_PVT, not channel */
+			usleep(1);
+			sip_pvt_lock(pkt->owner);
+		}
 
-	ast_string_field_build(p, url, "<%s>;mode=active", data);
+		if (pkt->owner->owner && !pkt->owner->owner->hangupcause)
+			pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE;
+		
+		if (pkt->owner->owner) {
+			sip_alreadygone(pkt->owner);
+			ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see doc/sip-retransmit.txt).\n", pkt->owner->callid);
+			ast_queue_hangup_with_cause(pkt->owner->owner, AST_CAUSE_PROTOCOL_ERROR);
+			ast_channel_unlock(pkt->owner->owner);
+		} else {
+			/* If no channel owner, destroy now */
 
-	if (sip_debug_test_pvt(p))
-		ast_debug(1, "Send URL %s, state = %d!\n", data, chan->_state);
+			/* Let the peerpoke system expire packets when the timer expires for poke_noanswer */
+			if (pkt->method != SIP_OPTIONS && pkt->method != SIP_REGISTER) {
+				pvt_set_needdestroy(pkt->owner, "no response to critical packet");
+				sip_alreadygone(pkt->owner);
+				append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately");
+			}
+		}
+	}
 
-	switch (chan->_state) {
-	case AST_STATE_RING:
-		transmit_response(p, "100 Trying", &p->initreq);
-		break;
-	case AST_STATE_RINGING:
-		transmit_response(p, "180 Ringing", &p->initreq);
-		break;
-	case AST_STATE_UP:
-		if (!p->pendinginvite) {		/* We are up, and have no outstanding invite */
-			transmit_reinvite_with_sdp(p, FALSE, FALSE);
-		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
-			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);	
-		}	
-		break;
-	default:
-		ast_log(LOG_WARNING, "Don't know how to send URI when state is %d!\n", chan->_state);
+	if (pkt->method == SIP_BYE) {
+		/* We're not getting answers on SIP BYE's.  Tear down the call anyway. */
+		if (pkt->owner->owner)
+			ast_channel_unlock(pkt->owner->owner);
+		append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway.");
+		pvt_set_needdestroy(pkt->owner, "no response to BYE");
 	}
 
+	/* Remove the packet */
+	for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
+		if (cur == pkt) {
+			UNLINK(cur, pkt->owner->packets, prev);
+			sip_pvt_unlock(pkt->owner);
+			if (pkt->owner)
+				pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
+			if (pkt->data)
+				ast_free(pkt->data);
+			pkt->data = NULL;
+			ast_free(pkt);
+			return 0;
+		}
+	}
+	/* error case */
+	ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
+	sip_pvt_unlock(pkt->owner);
 	return 0;
 }
 
-/*! \brief Deliver SIP call ID for the call */
-static const char *sip_get_callid(struct ast_channel *chan)
-{
-	return chan->tech_pvt ? ((struct sip_pvt *) chan->tech_pvt)->callid : "";
-}
-
-/*! \brief Send SIP MESSAGE text within a call
-	Called from PBX core sendtext() application */
-static int sip_sendtext(struct ast_channel *ast, const char *text)
+/*! \brief Transmit packet with retransmits
+	\return 0 on success, -1 on failure to allocate packet
+*/
+static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod)
 {
-	struct sip_pvt *dialog = ast->tech_pvt;
-	int debug = sip_debug_test_pvt(dialog);
+	struct sip_pkt *pkt = NULL;
+	int siptimer_a = DEFAULT_RETRANS;
+	int xmitres = 0;
+	int respid;
 
-	if (!dialog)
-		return -1;
-	/* NOT ast_strlen_zero, because a zero-length message is specifically
-	 * allowed by RFC 3428 (See section 10, Examples) */
-	if (!text)
-		return 0;
-	if(!is_method_allowed(&dialog->allowed_methods, SIP_MESSAGE)) {
-		ast_debug(2, "Trying to send MESSAGE to device that does not support it.\n");
-		return(0);
+	if (sipmethod == SIP_INVITE) {
+		/* Note this is a pending invite */
+		p->pendinginvite = seqno;
 	}
-	if (debug)
-		ast_verbose("Sending text %s on %s\n", text, ast->name);
-	transmit_message_with_text(dialog, text);
-	return 0;	
-}
 
-/*! \brief Update peer object in realtime storage
-	If the Asterisk system name is set in asterisk.conf, we will use
-	that name and store that in the "regserver" field in the sippeers
-	table to facilitate multi-server setups.
-*/
-static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *defaultuser, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms)
-{
-	char port[10];
-	char ipaddr[INET_ADDRSTRLEN];
-	char regseconds[20];
-	char *tablename = NULL;
-	char str_lastms[20];
-
-	const char *sysname = ast_config_AST_SYSTEM_NAME;
-	char *syslabel = NULL;
+	/* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
+	/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
+	/*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
+	if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
+		xmitres = __sip_xmit(p, data, len);	/* Send packet */
+		if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
+			append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
+			return AST_FAILURE;
+		} else {
+			return AST_SUCCESS;
+		}
+	}
 
-	time_t nowtime = time(NULL) + expirey;
-	const char *fc = fullcontact ? "fullcontact" : NULL;
+	if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
+		return AST_FAILURE;
+	/* copy data, add a terminator and save length */
+	if (!(pkt->data = ast_str_create(len))) {
+		ast_free(pkt);
+		return AST_FAILURE;
+	}
+	ast_str_set(&pkt->data, 0, "%s%s", data->str, "\0");
+	pkt->packetlen = len;
+	/* copy other parameters from the caller */
+	pkt->method = sipmethod;
+	pkt->seqno = seqno;
+	pkt->is_resp = resp;
+	pkt->is_fatal = fatal;
+	pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
+	pkt->next = p->packets;
+	p->packets = pkt;	/* Add it to the queue */
+	if (resp) {
+		/* Parse out the response code */
+		if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %30u", &respid) == 1) {
+			pkt->response_code = respid;
+		}
+	}
+	pkt->timer_t1 = p->timer_t1;	/* Set SIP timer T1 */
+	pkt->retransid = -1;
+	if (pkt->timer_t1)
+		siptimer_a = pkt->timer_t1 * 2;
 
-	int realtimeregs = ast_check_realtime("sipregs");
+	/* Schedule retransmission */
+	AST_SCHED_REPLACE_VARIABLE(pkt->retransid, sched, siptimer_a, retrans_pkt, pkt, 1);
+	if (sipdebug)
+		ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id  #%d\n", pkt->retransid);
 
-	tablename = realtimeregs ? "sipregs" : "sippeers";
-	
+	xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);	/* Send packet */
 
-	snprintf(str_lastms, sizeof(str_lastms), "%d", lastms);
-	snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);	/* Expiration time */
-	ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
-	snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
-	
-	if (ast_strlen_zero(sysname))	/* No system name, disable this */
-		sysname = NULL;
-	else if (sip_cfg.rtsave_sysname)
-		syslabel = "regserver";
-
-	if (fc) {
-		ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
-			"port", port, "regseconds", regseconds,
-			deprecated_username ? "username" : "defaultuser", defaultuser,
-			"useragent", useragent, "lastms", str_lastms,
-			fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
+	if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
+		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+		ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
+		AST_SCHED_DEL(sched, pkt->retransid);
+		p->packets = pkt->next;
+		pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
+		ast_free(pkt->data);
+		ast_free(pkt);
+		return AST_FAILURE;
 	} else {
-		ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
-			"port", port, "regseconds", regseconds,
-			"useragent", useragent, "lastms", str_lastms,
-			deprecated_username ? "username" : "defaultuser", defaultuser,
-			syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */
+		return AST_SUCCESS;
 	}
 }
 
-/*! \brief Automatically add peer extension to dial plan */
-static void register_peer_exten(struct sip_peer *peer, int onoff)
+/*! \brief Kill a SIP dialog (called only by the scheduler)
+ * The scheduler has a reference to this dialog when p->autokillid != -1,
+ * and we are called using that reference. So if the event is not
+ * rescheduled, we need to call dialog_unref().
+ */
+static int __sip_autodestruct(const void *data)
 {
-	char multi[256];
-	char *stringp, *ext, *context;
-	struct pbx_find_info q = { .stacklen = 0 };
+	struct sip_pvt *p = (struct sip_pvt *)data;
 
-	/* XXX note that sip_cfg.regcontext is both a global 'enable' flag and
-	 * the name of the global regexten context, if not specified
-	 * individually.
-	 */
-	if (ast_strlen_zero(sip_cfg.regcontext))
-		return;
+	/* If this is a subscription, tell the phone that we got a timeout */
+	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");
+		ast_debug(3, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>");
+		return 10000;	/* Reschedule this destruction so that we know that it's gone */
+	}
 
-	ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
-	stringp = multi;
-	while ((ext = strsep(&stringp, "&"))) {
-		if ((context = strchr(ext, '@'))) {
-			*context++ = '\0';	/* split ext@context */
-			if (!ast_context_find(context)) {
-				ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
-				continue;
+	/* If there are packets still waiting for delivery, delay the destruction */
+	if (p->packets) {
+		if (!p->needdestroy) {
+			char method_str[31];
+			ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
+			append_history(p, "ReliableXmit", "timeout");
+			if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
+				if (method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
+					pvt_set_needdestroy(p, "autodestruct");
+				}
 			}
+			return 10000;
 		} else {
-			context = sip_cfg.regcontext;
-		}
-		if (onoff) {
-			if (!ast_exists_extension(NULL, context, ext, 1, NULL)) {
-				ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
-					 ast_strdup(peer->name), ast_free_ptr, "SIP");
-			}
-		} else if (pbx_find_extension(NULL, NULL, &q, context, ext, 1, NULL, "", E_MATCH)) {
-			ast_context_remove_extension(context, ext, 1, NULL);
+			/* They've had their chance to respond. Time to bail */
+			__sip_pretend_ack(p);
 		}
 	}
-}
-
-/*! Destroy mailbox subscriptions */
-static void destroy_mailbox(struct sip_mailbox *mailbox)
-{
-	if (mailbox->mailbox)
-		ast_free(mailbox->mailbox);
-	if (mailbox->context)
-		ast_free(mailbox->context);
-	if (mailbox->event_sub)
-		ast_event_unsubscribe(mailbox->event_sub);
-	ast_free(mailbox);
-}
 
-/*! Destroy all peer-related mailbox subscriptions */
-static void clear_peer_mailboxes(struct sip_peer *peer)
-{
-	struct sip_mailbox *mailbox;
+	if (p->subscribed == MWI_NOTIFICATION) {
+		if (p->relatedpeer) {
+			p->relatedpeer = unref_peer(p->relatedpeer, "__sip_autodestruct: unref peer p->relatedpeer");	/* Remove link to peer. If it's realtime, make sure it's gone from memory) */
+		}
+	}
 
-	while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry)))
-		destroy_mailbox(mailbox);
-}
+	/* Reset schedule ID */
+	p->autokillid = -1;
 
-static void sip_destroy_peer_fn(void *peer)
-{
-	sip_destroy_peer(peer);
+	if (p->owner) {
+		ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
+		ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
+	} else if (p->refer && !p->alreadygone) {
+		ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
+		transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
+		append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	} else {
+		append_history(p, "AutoDestroy", "%s", p->callid);
+		ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
+		dialog_unlink_all(p, TRUE, TRUE); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
+		/* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
+		/* sip_destroy(p); */		/* Go ahead and destroy dialog. All attempts to recover is done */
+		/* sip_destroy also absorbs the reference */
+	}
+	dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
+	return 0;
 }
 
-/*! \brief Destroy peer object from memory */
-static void sip_destroy_peer(struct sip_peer *peer)
+/*! \brief Schedule destruction of SIP dialog */
+void sip_scheddestroy(struct sip_pvt *p, int ms)
 {
-	ast_debug(3, "Destroying SIP peer %s\n", peer->name);
-	if (peer->outboundproxy)
-		ao2_ref(peer->outboundproxy, -1);
-	peer->outboundproxy = NULL;
-
-	/* Delete it, it needs to disappear */
-	if (peer->call) {
-		dialog_unlink_all(peer->call, TRUE, TRUE);
-		peer->call = dialog_unref(peer->call, "peer->call is being unset");
-	}
-	
-
-	if (peer->mwipvt) {	/* We have an active subscription, delete it */
-		dialog_unlink_all(peer->mwipvt, TRUE, TRUE);
-		peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
-	}
-	
-	if (peer->chanvars) {
-		ast_variables_destroy(peer->chanvars);
-		peer->chanvars = NULL;
+	if (ms < 0) {
+		if (p->timer_t1 == 0) {
+			p->timer_t1 = global_t1;	/* Set timer T1 if not set (RFC 3261) */
+		}
+		if (p->timer_b == 0) {
+			p->timer_b = global_timer_b;  /* Set timer B if not set (RFC 3261) */
+		}
+		ms = p->timer_t1 * 64;
 	}
-	
-	register_peer_exten(peer, FALSE);
-	ast_free_ha(peer->ha);
-	if (peer->selfdestruct)
-		ast_atomic_fetchadd_int(&apeerobjs, -1);
-	else if (peer->is_realtime) {
-		ast_atomic_fetchadd_int(&rpeerobjs, -1);
-		ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
-	} else
-		ast_atomic_fetchadd_int(&speerobjs, -1);
-	clear_realm_authentication(peer->auth);
-	peer->auth = NULL;
-	if (peer->dnsmgr)
-		ast_dnsmgr_release(peer->dnsmgr);
-	clear_peer_mailboxes(peer);
+	if (sip_debug_test_pvt(p))
+		ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
+	if (sip_cancel_destroy(p))
+		ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
 
-	if (peer->socket.tcptls_session) {
-		ao2_ref(peer->socket.tcptls_session, -1);
-		peer->socket.tcptls_session = NULL;
-	}
+	if (p->do_history)
+		append_history(p, "SchedDestroy", "%d ms", ms);
+	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
 
-	ast_string_field_free_memory(peer);
+	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0)
+		stop_session_timer(p);
 }
 
-/*! \brief Update peer data in database (if used) */
-static void update_peer(struct sip_peer *p, int expire)
+/*! \brief Cancel destruction of SIP dialog.
+ * Be careful as this also absorbs the reference - if you call it
+ * from within the scheduler, this might be the last reference.
+ */
+int sip_cancel_destroy(struct sip_pvt *p)
 {
-	int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
-	if (sip_cfg.peer_rtupdate &&
-	    (p->is_realtime || rtcachefriends)) {
-		realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, p->useragent, expire, p->deprecated_username, p->lastms);
-	}
-}
+	int res = 0;
+	if (p->autokillid > -1) {
+		int res3;
 
-static struct ast_variable *get_insecure_variable_from_config(struct ast_config *cfg)
-{
-	struct ast_variable *var = NULL;
-	struct ast_flags flags = {0};
-	char *cat = NULL;
-	const char *insecure;
-	while ((cat = ast_category_browse(cfg, cat))) {
-		insecure = ast_variable_retrieve(cfg, cat, "insecure");
-		set_insecure_flags(&flags, insecure, -1);
-		if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
-			var = ast_category_root(cfg, cat);
-			break;
+		if (!(res3 = ast_sched_del(sched, p->autokillid))) {
+			append_history(p, "CancelDestroy", "");
+			p->autokillid = -1;
+			dialog_unref(p, "dialog unrefd because autokillid is de-sched'd");
 		}
 	}
-	return var;
+	return res;
 }
 
-static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
+/*! \brief Acknowledges receipt of a packet and stops retransmission
+ * called with p locked*/
+int __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
 {
-	struct ast_variable *tmp;
-	for (tmp = var; tmp; tmp = tmp->next) {
-		if (!newpeername && !strcasecmp(tmp->name, "name"))
-			newpeername = tmp->value;
+	struct sip_pkt *cur, *prev = NULL;
+	const char *msg = "Not Found";	/* used only for debugging */
+	int res = FALSE;
+
+	/* If we have an outbound proxy for this dialog, then delete it now since
+	  the rest of the requests in this dialog needs to follow the routing.
+	  If obforcing is set, we will keep the outbound proxy during the whole
+	  dialog, regardless of what the SIP rfc says
+	*/
+	if (p->outboundproxy && !p->outboundproxy->force){
+		ref_proxy(p, NULL);
 	}
-	return newpeername;
-}
 
-/*! \brief  realtime_peer: Get peer from realtime storage
- * Checks the "sippeers" realtime family from extconfig.conf
- * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
- * This returns a pointer to a peer and because we use build_peer, we can rest
- * assured that the refcount is bumped.
-*/
-static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin, int devstate_only)
-{
-	struct sip_peer *peer;
-	struct ast_variable *var = NULL;
-	struct ast_variable *varregs = NULL;
-	struct ast_variable *tmp;
-	struct ast_config *peerlist = NULL;
-	char ipaddr[INET_ADDRSTRLEN];
-	char portstring[6]; /*up to 5 digits plus null terminator*/
-	char *cat = NULL;
-	unsigned short portnum;
-	int realtimeregs = ast_check_realtime("sipregs");
-
-	/* First check on peer name */
-	if (newpeername) {
-		if (realtimeregs)
-			varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-
-		var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", SENTINEL);
-		if (!var && sin)
-			var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), SENTINEL);
-		if (!var) {
-			var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
-			/*!\note
-			 * If this one loaded something, then we need to ensure that the host
-			 * field matched.  The only reason why we can't have this as a criteria
-			 * is because we only have the IP address and the host field might be
-			 * set as a name (and the reverse PTR might not match).
+	for (cur = p->packets; cur; prev = cur, cur = cur->next) {
+		if (cur->seqno != seqno || cur->is_resp != resp)
+			continue;
+		if (cur->is_resp || cur->method == sipmethod) {
+			res = TRUE;
+			msg = "Found";
+			if (!resp && (seqno == p->pendinginvite)) {
+				ast_debug(1, "Acked pending invite %d\n", p->pendinginvite);
+				p->pendinginvite = 0;
+			}
+			if (cur->retransid > -1) {
+				if (sipdebug)
+					ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
+			}
+			/* This odd section is designed to thwart a
+			 * race condition in the packet scheduler. There are
+			 * two conditions under which deleting the packet from the
+			 * scheduler can fail.
+			 *
+			 * 1. The packet has been removed from the scheduler because retransmission
+			 * is being attempted. The problem is that if the packet is currently attempting
+			 * retransmission and we are at this point in the code, then that MUST mean
+			 * that retrans_pkt is waiting on p's lock. Therefore we will relinquish the
+			 * lock temporarily to allow retransmission.
+			 *
+			 * 2. The packet has reached its maximum number of retransmissions and has
+			 * been permanently removed from the packet scheduler. If this is the case, then
+			 * the packet's retransid will be set to -1. The atomicity of the setting and checking
+			 * of the retransid to -1 is ensured since in both cases p's lock is held.
 			 */
-			if (var && sin) {
-				for (tmp = var; tmp; tmp = tmp->next) {
-					if (!strcasecmp(tmp->name, "host")) {
-						struct hostent *hp;
-						struct ast_hostent ahp;
-						if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
-							/* No match */
-							ast_variables_destroy(var);
-							var = NULL;
-						}
-						break;
-					}
-				}
+			while (cur->retransid > -1 && ast_sched_del(sched, cur->retransid)) {
+				sip_pvt_unlock(p);
+				usleep(1);
+				sip_pvt_lock(p);
 			}
+			UNLINK(cur, p->packets, prev);
+			dialog_unref(cur->owner, "unref pkt cur->owner dialog from sip ack before freeing pkt");
+			if (cur->data)
+				ast_free(cur->data);
+			ast_free(cur);
+			break;
 		}
 	}
+	ast_debug(1, "Stopping retransmission on '%s' of %s %d: Match %s\n",
+		p->callid, resp ? "Response" : "Request", seqno, msg);
+	return res;
+}
 
-	if (!var && sin) {	/* Then check on IP address for dynamic peers */
-		ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
-		portnum = ntohs(sin->sin_port);
-		sprintf(portstring, "%u", portnum);
-		var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL);	/* First check for fixed IP hosts */
-		if (var) {
-			if (realtimeregs) {
-				newpeername = get_name_from_variable(var, newpeername);
-				varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-			}
-		} else {
-			if (realtimeregs)
-				varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
-			else
-				var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
-			if (varregs) {
-				newpeername = get_name_from_variable(varregs, newpeername);
-				var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
-			}
-		}
-		if (!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
-			peerlist = ast_load_realtime_multientry("sippeers", "host", ipaddr, SENTINEL);
-			if (peerlist) {
-				var = get_insecure_variable_from_config(peerlist);
-				if(var) {
-					if (realtimeregs) {
-						newpeername = get_name_from_variable(var, newpeername);
-						varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-					}
-				} else { /*var wasn't found in the list of "hosts", so try "ipaddr"*/
-					peerlist = NULL;
-					cat = NULL;
-					peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
-					if(peerlist) {
-						var = get_insecure_variable_from_config(peerlist);
-						if(var) {
-							if (realtimeregs) {
-								newpeername = get_name_from_variable(var, newpeername);
-								varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-							}
-						}
-					}
-				}
-			} else {
-				if (realtimeregs) {
-					peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, SENTINEL);
-					if (peerlist) {
-						varregs = get_insecure_variable_from_config(peerlist);
-						if (varregs) {
-							newpeername = get_name_from_variable(varregs, newpeername);
-							var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
-						}
-					}
-				} else {
-					peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
-					if (peerlist) {
-						var = get_insecure_variable_from_config(peerlist);
-						if (var) {
-							newpeername = get_name_from_variable(var, newpeername);
-							varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-						}
-					}
-				}
-			}
+/*! \brief Pretend to ack all packets
+ * called with p locked */
+void __sip_pretend_ack(struct sip_pvt *p)
+{
+	struct sip_pkt *cur = NULL;
+
+	while (p->packets) {
+		int method;
+		if (cur == p->packets) {
+			ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text);
+			return;
 		}
+		cur = p->packets;
+		method = (cur->method) ? cur->method : find_sip_method(cur->data->str);
+		__sip_ack(p, cur->seqno, cur->is_resp, method);
 	}
+}
 
-	if (!var) {
-		if (peerlist)
-			ast_config_destroy(peerlist);
-		return NULL;
-	}
+/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
+int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
+{
+	struct sip_pkt *cur;
+	int res = FALSE;
 
-	for (tmp = var; tmp; tmp = tmp->next) {
-		/* If this is type=user, then skip this object. */
-		if (!strcasecmp(tmp->name, "type") &&
-		    !strcasecmp(tmp->value, "user")) {
-			if(peerlist)
-				ast_config_destroy(peerlist);
-			else {
-				ast_variables_destroy(var);
-				ast_variables_destroy(varregs);
+	for (cur = p->packets; cur; cur = cur->next) {
+		if (cur->seqno == seqno && cur->is_resp == resp &&
+			(cur->is_resp || method_match(sipmethod, cur->data->str))) {
+			/* this is our baby */
+			if (cur->retransid > -1) {
+				if (sipdebug)
+					ast_debug(4, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
 			}
-			return NULL;
-		} else if (!newpeername && !strcasecmp(tmp->name, "name")) {
-			newpeername = tmp->value;
+			AST_SCHED_DEL(sched, cur->retransid);
+			res = TRUE;
+			break;
 		}
 	}
-	
-	if (!newpeername) {	/* Did not find peer in realtime */
-		ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", ipaddr);
-		if(peerlist)
-			ast_config_destroy(peerlist);
-		else
-			ast_variables_destroy(var);
-		return NULL;
+	ast_debug(1, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res == -1 ? "Not Found" : "Found");
+	return res;
+}
+
+
+/*! \brief Copy SIP request, parse it */
+static void parse_copy(struct sip_request *dst, const struct sip_request *src)
+{
+	copy_request(dst, src);
+	parse_request(dst);
+}
+
+/*! \brief add a blank line if no body */
+static void add_blank(struct sip_request *req)
+{
+	if (!req->lines) {
+		/* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
+		ast_str_append(&req->data, 0, "\r\n");
+		req->len = ast_str_strlen(req->data);
 	}
+}
+
+static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
+{
+	const char *msg = NULL;
 
+	if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) {
+		msg = "183 Session Progress";
+	}
 
-	/* Peer found in realtime, now build it in memory */
-	peer = build_peer(newpeername, var, varregs, TRUE, devstate_only);
-	if (!peer) {
-		if(peerlist)
-			ast_config_destroy(peerlist);
-		else {
-			ast_variables_destroy(var);
-			ast_variables_destroy(varregs);
+	if (pvt->invitestate < INV_COMPLETED) {
+		if (with_sdp) {
+			transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE, FALSE, FALSE);
+		} else {
+			transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq);
 		}
-		return NULL;
+		return PROVIS_KEEPALIVE_TIMEOUT;
 	}
 
-	ast_debug(3, "-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
+	return 0;
+}
 
-	if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && !devstate_only) {
-		/* Cache peer */
-		ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
-		if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
-			AST_SCHED_REPLACE_UNREF(peer->expire, sched, sip_cfg.rtautoclear * 1000, expire_register, peer,
-					unref_peer(_data, "remove registration ref"),
-					unref_peer(peer, "remove registration ref"),
-					ref_peer(peer, "add registration ref"));
-		}
-		ao2_t_link(peers, peer, "link peer into peers table");
-		if (peer->addr.sin_addr.s_addr) {
-			ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
-		}
-	}
-	peer->is_realtime = 1;
-	if (peerlist)
-		ast_config_destroy(peerlist);
-	else {
-		ast_variables_destroy(var);
-		ast_variables_destroy(varregs);
-	}
+static int send_provisional_keepalive(const void *data) {
+	struct sip_pvt *pvt = (struct sip_pvt *) data;
 
-	return peer;
+	return send_provisional_keepalive_full(pvt, 0);
 }
 
-/* Function to assist finding peers by name only */
-static int find_by_name(void *obj, void *arg, void *data, int flags)
-{
-	struct sip_peer *search = obj, *match = arg;
-	int *which_objects = data;
+static int send_provisional_keepalive_with_sdp(const void *data) {
+	struct sip_pvt *pvt = (void *)data;
 
-	/* Usernames in SIP uri's are case sensitive. Domains are not */
-	if (strcmp(search->name, match->name)) {
-		return 0;
-	}
+	return send_provisional_keepalive_full(pvt, 1);
+}
 
-	switch (*which_objects) {
-	case FINDUSERS:
-		if (!(search->type & SIP_TYPE_USER)) {
-			return 0;
-		}
-		break;
-	case FINDPEERS:
-		if (!(search->type & SIP_TYPE_PEER)) {
-			return 0;
-		}
-		break;
-	case FINDALLDEVICES:
-		break;
-	}
+static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp)
+{
+	AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id, dialog_unref(pvt, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
 
-	return CMP_MATCH | CMP_STOP;
+	pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
+		with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, dialog_ref(pvt, "Increment refcount to pass dialog pointer to sched callback"));
 }
 
-/*!
- * \brief Locate device by name or ip address
- *
- * \param which_objects Define which objects should be matched when doing a lookup
- *        by name.  Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
- *        Note that this option is not used at all when doing a lookup by IP.
- *
- *	This is used on find matching device on name or ip/port.
- * If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
- *
- * \note Avoid using this function in new functions if there is a way to avoid it,
- * since it might cause a database lookup.
- */
-static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int which_objects, int devstate_only, int transport)
+/*! \brief Transmit response on SIP request*/
+static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
 {
-	struct sip_peer *p = NULL;
-	struct sip_peer tmp_peer;
+	int res;
 
-	if (peer) {
-		ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
-		p = ao2_t_callback_data(peers, OBJ_POINTER, find_by_name, &tmp_peer, &which_objects, "ao2_find in peers table");
-	} else if (sin) { /* search by addr? */
-		tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr;
-		tmp_peer.addr.sin_port = sin->sin_port;
-		tmp_peer.flags[0].flags = 0;
-		tmp_peer.transports = transport;
-		p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
-		if (!p) {
-			ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
-			p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
-			if (p) {
-				return p;
-			}
-		}
+	add_blank(req);
+	if (sip_debug_test_pvt(p)) {
+		const struct sockaddr_in *dst = sip_real_dst(p);
+
+		ast_verbose("\n<--- %sTransmitting (%s) to %s:%d --->\n%s\n<------------>\n",
+			reliable ? "Reliably " : "", sip_nat_mode(p),
+			ast_inet_ntoa(dst->sin_addr),
+			ntohs(dst->sin_port), req->data->str);
+	}
+	if (p->do_history) {
+		struct sip_request tmp = { .rlPart1 = 0, };
+		parse_copy(&tmp, req);
+		append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"),
+			(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlPart2) : sip_methods[tmp.method].text);
+		ast_free(tmp.data);
 	}
 
-	if (!p && (realtime || devstate_only)) {
-		p = realtime_peer(peer, sin, devstate_only);
+	/* If we are sending a final response to an INVITE, stop retransmitting provisional responses */
+	if (p->initreq.method == SIP_INVITE && reliable == XMIT_CRITICAL) {
+		AST_SCHED_DEL_UNREF(sched, p->provisional_keepalive_sched_id, dialog_unref(p, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
 	}
 
-	return p;
+	res = (reliable) ?
+		 __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
+		__sip_xmit(p, req->data, req->len);
+	ast_free(req->data);
+	req->data = NULL;
+	if (res > 0)
+		return 0;
+	return res;
 }
 
-/*! \brief Set nat mode on the various data sockets */
-static void do_setnat(struct sip_pvt *p)
+/*! \brief Send SIP Request to the other part of the dialogue 
+	\return see \ref __sip_xmit
+*/
+static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
 {
-	const char *mode;
-	int natflags;
-
-	natflags = ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
-	mode = natflags ? "On" : "Off";
+	int res;
 
-	if (p->rtp) {
-		ast_debug(1, "Setting NAT on RTP to %s\n", mode);
-		ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags);
+	/* If we have an outbound proxy, reset peer address
+		Only do this once.
+	*/
+	if (p->outboundproxy) {
+		p->sa = p->outboundproxy->ip;
 	}
-	if (p->vrtp) {
-		ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
-		ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags);
+
+	add_blank(req);
+	if (sip_debug_test_pvt(p)) {
+		if (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT))
+			ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), req->data->str);
+		else
+			ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data->str);
 	}
-	if (p->udptl) {
-		ast_debug(1, "Setting NAT on UDPTL to %s\n", mode);
-		ast_udptl_setnat(p->udptl, natflags);
+	if (p->do_history) {
+		struct sip_request tmp = { .rlPart1 = 0, };
+		parse_copy(&tmp, req);
+		append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
+		ast_free(tmp.data);
 	}
-	if (p->trtp) {
-		ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
-		ast_rtp_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags);
+	res = (reliable) ?
+		__sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
+		__sip_xmit(p, req->data, req->len);
+	if (req->data) {
+		ast_free(req->data);
+		req->data = NULL;
 	}
+	return res;
 }
 
-/*! \brief Change the T38 state on a SIP dialog */
-static void change_t38_state(struct sip_pvt *p, int state)
+static void enable_dsp_detect(struct sip_pvt *p)
 {
-	int old = p->t38.state;
-	struct ast_channel *chan = p->owner;
-	struct ast_control_t38_parameters parameters = { .request_response = 0 };
-
-	/* Don't bother changing if we are already in the state wanted */
-	if (old == state)
-		return;
-
-	p->t38.state = state;
-	ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
+	int features = 0;
 
-	/* If no channel was provided we can't send off a control frame */
-	if (!chan)
+	if (p->dsp) {
 		return;
-
-	/* Given the state requested and old state determine what control frame we want to queue up */
-	switch (state) {
-	case T38_PEER_REINVITE:
-		parameters = p->t38.their_parms;
-		parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
-		parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
-		ast_udptl_set_tag(p->udptl, "SIP/%s", p->username);
-		break;
-	case T38_ENABLED:
-		parameters = p->t38.their_parms;
-		parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
-		parameters.request_response = AST_T38_NEGOTIATED;
-		ast_udptl_set_tag(p->udptl, "SIP/%s", p->username);
-		break;
-	case T38_DISABLED:
-		if (old == T38_ENABLED) {
-			parameters.request_response = AST_T38_TERMINATED;
-		} else if (old == T38_LOCAL_REINVITE) {
-			parameters.request_response = AST_T38_REFUSED;
-		}
-		break;
-	case T38_LOCAL_REINVITE:
-		/* wait until we get a peer response before responding to local reinvite */
-		break;
 	}
 
-	/* Woot we got a message, create a control frame and send it on! */
-	if (parameters.request_response)
-		ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
-}
+	if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
+            (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
+		if (!p->rtp || ast_rtp_instance_dtmf_mode_set(p->rtp, AST_RTP_DTMF_MODE_INBAND)) {
+			features |= DSP_FEATURE_DIGIT_DETECT;
+                }
+	}
 
-/*! \brief Set the global T38 capabilities on a SIP dialog structure */
-static void set_t38_capabilities(struct sip_pvt *p)
-{
-	if (p->udptl) {
-		if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
-                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
-		} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
-			ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
-		} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
-			ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
-		}
+	if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
+		features |= DSP_FEATURE_FAX_DETECT;
 	}
-}
 
-static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *from_sock)
-{
-	if (to_sock->tcptls_session) {
-		ao2_ref(to_sock->tcptls_session, -1);
-		to_sock->tcptls_session = NULL;
+	if (!features) {
+		return;
 	}
 
-	if (from_sock->tcptls_session) {
-		ao2_ref(from_sock->tcptls_session, +1);
+	if (!(p->dsp = ast_dsp_new())) {
+		return;
 	}
 
-	*to_sock = *from_sock;
+	ast_dsp_set_features(p->dsp, features);
+	if (global_relaxdtmf) {
+		ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+	}
 }
 
-/*! \brief Initialize RTP portion of a dialog
- * \return -1 on failure, 0 on success
- */
-static int dialog_initialize_rtp(struct sip_pvt *dialog)
+static void disable_dsp_detect(struct sip_pvt *p)
 {
-	if (!sip_methods[dialog->method].need_rtp) {
-		return 0;
+	if (p->dsp) {
+		ast_dsp_free(p->dsp);
+		p->dsp = NULL;
 	}
+}
 
-	if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
-		return -1;
-	}
+/*! \brief Set an option on a SIP dialog */
+static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen)
+{
+	int res = -1;
+	struct sip_pvt *p = chan->tech_pvt;
 
-	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (dialog->capability & AST_FORMAT_VIDEO_MASK)) {
-		if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
-			return -1;
-		}
-		ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout);
-		ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout);
+	switch (option) {
+	case AST_OPTION_FORMAT_READ:
+		res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data);
+		break;
+	case AST_OPTION_FORMAT_WRITE:
+		res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data);
+		break;
+	case AST_OPTION_MAKE_COMPATIBLE:
+		res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
+		break;
+	case AST_OPTION_DIGIT_DETECT:
+		if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
+		    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
+			char *cp = (char *) data;
 
-		ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1);
+			ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name);
+			if (*cp) {
+				enable_dsp_detect(p);
+			} else {
+				disable_dsp_detect(p);
+			}
+			res = 0;
+		}
+		break;
+	default:
+		break;
 	}
 
-	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT)) {
-		if (!(dialog->trtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
+	return res;
+}
+
+/*! \brief Query an option on a SIP dialog */
+static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen)
+{
+	int res = -1;
+	enum ast_t38_state state = T38_STATE_UNAVAILABLE;
+	struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt;
+	char *cp;
+
+	switch (option) {
+	case AST_OPTION_T38_STATE:
+		/* Make sure we got an ast_t38_state enum passed in */
+		if (*datalen != sizeof(enum ast_t38_state)) {
+			ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
 			return -1;
 		}
-		ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout);
-		ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout);
 
-		ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1);
-	}
+		sip_pvt_lock(p);
 
-	ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout);
-	ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout);
+		/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
+		if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
+			switch (p->t38.state) {
+			case T38_LOCAL_REINVITE:
+			case T38_PEER_REINVITE:
+				state = T38_STATE_NEGOTIATING;
+				break;
+			case T38_ENABLED:
+				state = T38_STATE_NEGOTIATED;
+				break;
+			default:
+				state = T38_STATE_UNKNOWN;
+			}
+		}
 
-	ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1);
-	ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
-	ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+		sip_pvt_unlock(p);
 
-	ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, 0, "SIP RTP");
+		*((enum ast_t38_state *) data) = state;
+		res = 0;
 
-	do_setnat(dialog);
+		break;
+	case AST_OPTION_DIGIT_DETECT:
+		cp = (char *) data;
+		*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;
+	}
 
-	return 0;
+	return res;
 }
 
-/*! \brief Create address structure from peer reference.
- *	This function copies data from peer to the dialog, so we don't have to look up the peer
- *	again from memory or database during the life time of the dialog.
- *
- * \return -1 on error, 0 on success.
- *
+/*! \brief Locate closing quote in a string, skipping escaped quotes.
+ * optionally with a limit on the search.
+ * start must be past the first quote.
  */
-static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
+const char *find_closing_quote(const char *start, const char *lim)
 {
+	char last_char = '\0';
+	const char *s;
+	for (s = start; *s && s != lim; last_char = *s++) {
+		if (*s == '"' && last_char != '\\')
+			break;
+	}
+	return s;
+}
 
-	/* this checks that the dialog is contacting the peer on a valid
-	 * transport type based on the peers transport configuration,
-	 * otherwise, this function bails out */
-	if (dialog->socket.type && check_request_transport(peer, dialog))
-		return -1;
-	copy_socket_data(&dialog->socket, &peer->socket);
+/*! \brief Send message with Access-URL header, if this is an HTML URL only! */
+static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
+{
+	struct sip_pvt *p = chan->tech_pvt;
 
-	if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) &&
-	    (!peer->maxms || ((peer->lastms >= 0)  && (peer->lastms <= peer->maxms)))) {
-		dialog->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
-		dialog->recv = dialog->sa;
-	} else
+	if (subclass != AST_HTML_URL)
 		return -1;
 
-	ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
-	ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
-	dialog->capability = peer->capability;
-	dialog->prefs = peer->prefs;
-	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) {
-		if (!dialog->udptl) {
-			/* t38pt_udptl was enabled in the peer and not in [general] */
-			dialog->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
-		}
-		dialog->t38_maxdatagram = peer->t38_maxdatagram;
-		set_t38_capabilities(dialog);
-	} else if (dialog->udptl) {
-		ast_udptl_destroy(dialog->udptl);
-		dialog->udptl = NULL;
+	ast_string_field_build(p, url, "<%s>;mode=active", data);
+
+	if (sip_debug_test_pvt(p))
+		ast_debug(1, "Send URL %s, state = %d!\n", data, chan->_state);
+
+	switch (chan->_state) {
+	case AST_STATE_RING:
+		transmit_response(p, "100 Trying", &p->initreq);
+		break;
+	case AST_STATE_RINGING:
+		transmit_response(p, "180 Ringing", &p->initreq);
+		break;
+	case AST_STATE_UP:
+		if (!p->pendinginvite) {		/* We are up, and have no outstanding invite */
+			transmit_reinvite_with_sdp(p, FALSE, FALSE);
+		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);	
+		}	
+		break;
+	default:
+		ast_log(LOG_WARNING, "Don't know how to send URI when state is %d!\n", chan->_state);
 	}
 
-	ast_string_field_set(dialog, engine, peer->engine);
+	return 0;
+}
 
-	if (dialog_initialize_rtp(dialog)) {
+/*! \brief Deliver SIP call ID for the call */
+static const char *sip_get_callid(struct ast_channel *chan)
+{
+	return chan->tech_pvt ? ((struct sip_pvt *) chan->tech_pvt)->callid : "";
+}
+
+/*! \brief Send SIP MESSAGE text within a call
+	Called from PBX core sendtext() application */
+static int sip_sendtext(struct ast_channel *ast, const char *text)
+{
+	struct sip_pvt *dialog = ast->tech_pvt;
+	int debug = sip_debug_test_pvt(dialog);
+
+	if (!dialog)
 		return -1;
+	/* NOT ast_strlen_zero, because a zero-length message is specifically
+	 * allowed by RFC 3428 (See section 10, Examples) */
+	if (!text)
+		return 0;
+	if(!is_method_allowed(&dialog->allowed_methods, SIP_MESSAGE)) {
+		ast_debug(2, "Trying to send MESSAGE to device that does not support it.\n");
+		return(0);
 	}
+	if (debug)
+		ast_verbose("Sending text %s on %s\n", text, ast->name);
+	transmit_message_with_text(dialog, text);
+	return 0;	
+}
 
-	if (dialog->rtp) { /* Audio */
-		ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
-		ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
-		ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout);
-		ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout);
-		/* Set Frame packetization */
-		ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs);
-		dialog->autoframing = peer->autoframing;
-	}
-	if (dialog->vrtp) { /* Video */
-		ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout);
-		ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout);
-	}
-	if (dialog->trtp) { /* Realtime text */
-		ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout);
-		ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout);
-	}
+/*! \brief Update peer object in realtime storage
+	If the Asterisk system name is set in asterisk.conf, we will use
+	that name and store that in the "regserver" field in the sippeers
+	table to facilitate multi-server setups.
+*/
+static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *defaultuser, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms)
+{
+	char port[10];
+	char ipaddr[INET_ADDRSTRLEN];
+	char regseconds[20];
+	char *tablename = NULL;
+	char str_lastms[20];
 
-	ast_string_field_set(dialog, peername, peer->name);
-	ast_string_field_set(dialog, authname, peer->username);
-	ast_string_field_set(dialog, username, peer->username);
-	ast_string_field_set(dialog, peersecret, peer->secret);
-	ast_string_field_set(dialog, peermd5secret, peer->md5secret);
-	ast_string_field_set(dialog, mohsuggest, peer->mohsuggest);
-	ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
-	ast_string_field_set(dialog, tohost, peer->tohost);
-	ast_string_field_set(dialog, fullcontact, peer->fullcontact);
-	ast_string_field_set(dialog, accountcode, peer->accountcode);
-	ast_string_field_set(dialog, context, peer->context);
-	ast_string_field_set(dialog, cid_num, peer->cid_num);
-	ast_string_field_set(dialog, cid_name, peer->cid_name);
-	ast_string_field_set(dialog, mwi_from, peer->mwi_from);
-	ast_string_field_set(dialog, parkinglot, peer->parkinglot);
-	ast_string_field_set(dialog, engine, peer->engine);
-	ref_proxy(dialog, obproxy_get(dialog, peer));
-	dialog->callgroup = peer->callgroup;
-	dialog->pickupgroup = peer->pickupgroup;
-	dialog->allowtransfer = peer->allowtransfer;
-	dialog->jointnoncodeccapability = dialog->noncodeccapability;
-	dialog->rtptimeout = peer->rtptimeout;
-	dialog->peerauth = peer->auth;
-	dialog->maxcallbitrate = peer->maxcallbitrate;
-	dialog->disallowed_methods = peer->disallowed_methods;
-	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)) {
-		ast_string_field_set(dialog, fromdomain, peer->fromdomain);
-		if (!dialog->initreq.headers) {
-			char *c;
-			char *tmpcall = ast_strdupa(dialog->callid);
-			/* this sure looks to me like we are going to change the callid on this dialog!! */
-			c = strchr(tmpcall, '@');
-			if (c) {
-				*c = '\0';
-				ao2_t_unlink(dialogs, dialog, "About to change the callid -- remove the old name");
-				ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
-				ao2_t_link(dialogs, dialog, "New dialog callid -- inserted back into table");
-			}
-		}
-	}
-	if (!ast_strlen_zero(peer->fromuser))
-		ast_string_field_set(dialog, fromuser, peer->fromuser);
-	if (!ast_strlen_zero(peer->language))
-		ast_string_field_set(dialog, language, peer->language);
-	/* Set timer T1 to RTT for this peer (if known by qualify=) */
-	/* Minimum is settable or default to 100 ms */
-	/* If there is a maxms and lastms from a qualify use that over a manual T1
-	   value. Otherwise, use the peer's T1 value. */
-	if (peer->maxms && peer->lastms)
-		dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
-	else
-		dialog->timer_t1 = peer->timer_t1;
+	const char *sysname = ast_config_AST_SYSTEM_NAME;
+	char *syslabel = NULL;
 
-	/* Set timer B to control transaction timeouts, the peer setting is the default and overrides
-	   the known timer */
-	if (peer->timer_b)
-		dialog->timer_b = peer->timer_b;
-	else
-		dialog->timer_b = 64 * dialog->timer_t1;
+	time_t nowtime = time(NULL) + expirey;
+	const char *fc = fullcontact ? "fullcontact" : NULL;
 
-	if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
-	    (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
-		dialog->noncodeccapability |= AST_RTP_DTMF;
-	else
-		dialog->noncodeccapability &= ~AST_RTP_DTMF;
-	if (peer->call_limit)
-		ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
-	if (!dialog->portinuri)
-		dialog->portinuri = peer->portinuri;
+	int realtimeregs = ast_check_realtime("sipregs");
+
+	tablename = realtimeregs ? "sipregs" : "sippeers";
 	
-	dialog->chanvars = copy_vars(peer->chanvars);
 
-	return 0;
+	snprintf(str_lastms, sizeof(str_lastms), "%d", lastms);
+	snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);	/* Expiration time */
+	ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
+	snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
+	
+	if (ast_strlen_zero(sysname))	/* No system name, disable this */
+		sysname = NULL;
+	else if (sip_cfg.rtsave_sysname)
+		syslabel = "regserver";
+
+	if (fc) {
+		ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
+			"port", port, "regseconds", regseconds,
+			deprecated_username ? "username" : "defaultuser", defaultuser,
+			"useragent", useragent, "lastms", str_lastms,
+			fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
+	} else {
+		ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
+			"port", port, "regseconds", regseconds,
+			"useragent", useragent, "lastms", str_lastms,
+			deprecated_username ? "username" : "defaultuser", defaultuser,
+			syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */
+	}
 }
 
-/*! \brief create address structure from device name
- *      Or, if peer not found, find it in the global DNS
- *      returns TRUE (-1) on failure, FALSE on success */
-static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin, int newdialog, struct sockaddr_in *remote_address)
+/*! \brief Automatically add peer extension to dial plan */
+static void register_peer_exten(struct sip_peer *peer, int onoff)
 {
-	struct hostent *hp;
-	struct ast_hostent ahp;
-	struct sip_peer *peer;
-	char *port;
-	int portno = 0;
-	char host[MAXHOSTNAMELEN], *hostn;
-	char peername[256];
-	int srv_ret = 0;
+	char multi[256];
+	char *stringp, *ext, *context;
+	struct pbx_find_info q = { .stacklen = 0 };
 
-	ast_copy_string(peername, opeer, sizeof(peername));
-	port = strchr(peername, ':');
-	if (port) {
-		*port++ = '\0';
-		dialog->portinuri = 1;
-	}
-	dialog->sa.sin_family = AF_INET;
-	dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
-	dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
-	peer = find_peer(peername, NULL, TRUE, FINDPEERS, FALSE, 0);
+	/* XXX note that sip_cfg.regcontext is both a global 'enable' flag and
+	 * the name of the global regexten context, if not specified
+	 * individually.
+	 */
+	if (ast_strlen_zero(sip_cfg.regcontext))
+		return;
 
-	if (peer) {
-		int res;
-		if (newdialog) {
-			set_socket_transport(&dialog->socket, 0);
-		}
-		res = create_addr_from_peer(dialog, peer);
-		if (remote_address && remote_address->sin_addr.s_addr) {
-			dialog->sa = dialog->recv = *remote_address;
-		} else if (!ast_strlen_zero(port)) {
-			if ((portno = atoi(port))) {
-				dialog->sa.sin_port = dialog->recv.sin_port = htons(portno);
+	ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
+	stringp = multi;
+	while ((ext = strsep(&stringp, "&"))) {
+		if ((context = strchr(ext, '@'))) {
+			*context++ = '\0';	/* split ext@context */
+			if (!ast_context_find(context)) {
+				ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
+				continue;
 			}
-		}
-		unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup");
-		return res;
-	}
-
-	if (dialog_initialize_rtp(dialog)) {
-		return -1;
-	}
-
-	ast_string_field_set(dialog, tohost, peername);
-	dialog->allowed_methods &= ~sip_cfg.disallowed_methods;
-
-	/* Get the outbound proxy information */
-	ref_proxy(dialog, obproxy_get(dialog, NULL));
-
-	if (sin) {
-		/* This address should be updated using dnsmgr */
-		memcpy(&dialog->sa.sin_addr, &sin->sin_addr, sizeof(dialog->sa.sin_addr));
-		if (!sin->sin_port) {
-			portno = port_str2int(port, (dialog->socket.type == SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT);
 		} else {
-			portno = ntohs(sin->sin_port);
+			context = sip_cfg.regcontext;
 		}
-	} else {
-
-		/* Let's see if we can find the host in DNS. First try DNS SRV records,
-		   then hostname lookup */
-		/*! \todo Fix this function. When we ask for SRV, we should check all transports
-			  In the future, we should first check NAPTR to find out transport preference
-		 */
-		hostn = peername;
- 		/* Section 4.2 of RFC 3263 specifies that if a port number is specified, then
-		 * an A record lookup should be used instead of SRV.
-		 */
-		if (!port && sip_cfg.srvlookup) {
-			char service[MAXHOSTNAMELEN];
-			int tportno;
-	
-			snprintf(service, sizeof(service), "_sip._%s.%s", get_transport(dialog->socket.type), peername);
-			srv_ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service);
-			if (srv_ret > 0) {
-				hostn = host;
-				portno = tportno;
+		if (onoff) {
+			if (!ast_exists_extension(NULL, context, ext, 1, NULL)) {
+				ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
+					 ast_strdup(peer->name), ast_free_ptr, "SIP");
 			}
+		} else if (pbx_find_extension(NULL, NULL, &q, context, ext, 1, NULL, "", E_MATCH)) {
+			ast_context_remove_extension(context, ext, 1, NULL);
 		}
-	 	if (!portno)
-			portno = port_str2int(port, (dialog->socket.type == SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT);
-		hp = ast_gethostbyname(hostn, &ahp);
-		if (!hp) {
-			ast_log(LOG_WARNING, "No such host: %s\n", peername);
-			return -1;
-		}
-		memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr));
 	}
-
-	if (!dialog->socket.type)
-		set_socket_transport(&dialog->socket, SIP_TRANSPORT_UDP);
-	if (!dialog->socket.port)
-		dialog->socket.port = bindaddr.sin_port;
-	dialog->sa.sin_port = htons(portno);
-	dialog->recv = dialog->sa;
-	return 0;
 }
 
-/*! \brief Scheduled congestion on a call.
- * Only called by the scheduler, must return the reference when done.
- */
-static int auto_congest(const void *arg)
+/*! Destroy mailbox subscriptions */
+static void destroy_mailbox(struct sip_mailbox *mailbox)
 {
-	struct sip_pvt *p = (struct sip_pvt *)arg;
+	if (mailbox->mailbox)
+		ast_free(mailbox->mailbox);
+	if (mailbox->context)
+		ast_free(mailbox->context);
+	if (mailbox->event_sub)
+		ast_event_unsubscribe(mailbox->event_sub);
+	ast_free(mailbox);
+}
 
-	sip_pvt_lock(p);
-	p->initid = -1;	/* event gone, will not be rescheduled */
-	if (p->owner) {
-		/* XXX fails on possible deadlock */
-		if (!ast_channel_trylock(p->owner)) {
-			append_history(p, "Cong", "Auto-congesting (timer)");
-			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
-			ast_channel_unlock(p->owner);
-		}
+/*! Destroy all peer-related mailbox subscriptions */
+static void clear_peer_mailboxes(struct sip_peer *peer)
+{
+	struct sip_mailbox *mailbox;
 
-		/* Give the channel a chance to act before we proceed with destruction */
-		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-	}
-	sip_pvt_unlock(p);
-	dialog_unref(p, "unreffing arg passed into auto_congest callback (p->initid)");
-	return 0;
+	while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry)))
+		destroy_mailbox(mailbox);
 }
 
+static void sip_destroy_peer_fn(void *peer)
+{
+	sip_destroy_peer(peer);
+}
 
-/*! \brief Initiate SIP call from PBX
- *      used from the dial() application      */
-static int sip_call(struct ast_channel *ast, char *dest, int timeout)
+/*! \brief Destroy peer object from memory */
+static void sip_destroy_peer(struct sip_peer *peer)
 {
-	int res;
-	struct sip_pvt *p = ast->tech_pvt;	/* chan is locked, so the reference cannot go away */
-	struct varshead *headp;
-	struct ast_var_t *current;
-	const char *referer = NULL;   /* SIP referrer */
+	ast_debug(3, "Destroying SIP peer %s\n", peer->name);
+	if (peer->outboundproxy)
+		ao2_ref(peer->outboundproxy, -1);
+	peer->outboundproxy = NULL;
 
-	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;
+	/* Delete it, it needs to disappear */
+	if (peer->call) {
+		dialog_unlink_all(peer->call, TRUE, TRUE);
+		peer->call = dialog_unref(peer->call, "peer->call is being unset");
 	}
+	
 
-	/* Check whether there is vxml_url, distinctive ring variables */
-	headp=&ast->varshead;
-	AST_LIST_TRAVERSE(headp, current, entries) {
-		/* Check whether there is a VXML_URL variable */
-		if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) {
-			p->options->vxml_url = ast_var_value(current);
-		} else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
-			p->options->uri_options = ast_var_value(current);
-		} else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
-			/* Check whether there is a variable with a name starting with SIPADDHEADER */
-			p->options->addsipheaders = 1;
-		} else if (!strcasecmp(ast_var_name(current), "SIPFROMDOMAIN")) {
-			ast_string_field_set(p, fromdomain, ast_var_value(current));
-		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) {
-			/* This is a transfered call */
-			p->options->transfer = 1;
-		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) {
-			/* This is the referrer */
-			referer = ast_var_value(current);
-		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
-			/* We're replacing a call. */
-			p->options->replaces = ast_var_value(current);
-		}
+	if (peer->mwipvt) {	/* We have an active subscription, delete it */
+		dialog_unlink_all(peer->mwipvt, TRUE, TRUE);
+		peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
 	}
-
-	res = 0;
-	ast_set_flag(&p->flags[0], SIP_OUTGOING);
-
-	/* T.38 re-INVITE FAX detection should never be done for outgoing calls,
-	 * so ensure it is disabled.
-	 */
-	ast_clear_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38);
-
-	if (p->options->transfer) {
-		char buf[SIPBUFSIZE/2];
-
-		if (referer) {
-			if (sipdebug)
-				ast_debug(3, "Call for %s transfered by %s\n", p->username, referer);
-			snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
-		} else
-			snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
-		ast_string_field_set(p, cid_name, buf);
+	
+	if (peer->chanvars) {
+		ast_variables_destroy(peer->chanvars);
+		peer->chanvars = NULL;
 	}
-	ast_debug(1, "Outgoing Call for %s\n", p->username);
-
-	res = update_call_counter(p, INC_CALL_RINGING);
+	
+	register_peer_exten(peer, FALSE);
+	ast_free_ha(peer->ha);
+	if (peer->selfdestruct)
+		ast_atomic_fetchadd_int(&apeerobjs, -1);
+	else if (peer->is_realtime) {
+		ast_atomic_fetchadd_int(&rpeerobjs, -1);
+		ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
+	} else
+		ast_atomic_fetchadd_int(&speerobjs, -1);
+	clear_realm_authentication(peer->auth);
+	peer->auth = NULL;
+	if (peer->dnsmgr)
+		ast_dnsmgr_release(peer->dnsmgr);
+	clear_peer_mailboxes(peer);
 
-	if (res == -1) {
-		ast->hangupcause = AST_CAUSE_USER_BUSY;
-		return res;
+	if (peer->socket.tcptls_session) {
+		ao2_ref(peer->socket.tcptls_session, -1);
+		peer->socket.tcptls_session = NULL;
 	}
-	p->callingpres = ast->cid.cid_pres;
-	p->jointcapability = ast_rtp_instance_available_formats(p->rtp, p->capability, p->prefcodec);
-	p->jointnoncodeccapability = p->noncodeccapability;
-
-	/* If there are no audio formats left to offer, punt */
-	if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
-		ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
-		res = -1;
-	} else {
-		int xmitres;
 
-		sip_pvt_lock(p);
-		xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
-		sip_pvt_unlock(p);
-		if (xmitres == XMIT_ERROR)
-			return -1;
-		p->invitestate = INV_CALLING;
+	ast_cc_config_params_destroy(peer->cc_params);
 
-		/* Initialize auto-congest time */
-		AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p,
-								dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"),
-								dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"),
-								dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") );
-	}
-	return res;
+	ast_string_field_free_memory(peer);
 }
 
-/*! \brief Destroy registry object
-	Objects created with the register= statement in static configuration */
-static void sip_registry_destroy(struct sip_registry *reg)
+/*! \brief Update peer data in database (if used) */
+static void update_peer(struct sip_peer *p, int expire)
 {
-	/* Really delete */
-	ast_debug(3, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
-
-	if (reg->call) {
-		/* Clear registry before destroying to ensure
-		   we don't get reentered trying to grab the registry lock */
-		reg->call->registry = registry_unref(reg->call->registry, "destroy reg->call->registry");
-		ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
-		dialog_unlink_all(reg->call, TRUE, TRUE);
-		reg->call = dialog_unref(reg->call, "unref reg->call");
-		/* reg->call = sip_destroy(reg->call); */
+	int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
+	if (sip_cfg.peer_rtupdate &&
+	    (p->is_realtime || rtcachefriends)) {
+		realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, p->useragent, expire, p->deprecated_username, p->lastms);
 	}
-	AST_SCHED_DEL(sched, reg->expire);	
-	AST_SCHED_DEL(sched, reg->timeout);
-	
-	ast_string_field_free_memory(reg);
-	ast_atomic_fetchadd_int(&regobjs, -1);
-	ast_dnsmgr_release(reg->dnsmgr);
-	ast_free(reg);
 }
 
-/*! \brief Destroy MWI subscription object */
-static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi)
+static struct ast_variable *get_insecure_variable_from_config(struct ast_config *cfg)
 {
-	if (mwi->call) {
-		mwi->call->mwi = NULL;
-		sip_destroy(mwi->call);
+	struct ast_variable *var = NULL;
+	struct ast_flags flags = {0};
+	char *cat = NULL;
+	const char *insecure;
+	while ((cat = ast_category_browse(cfg, cat))) {
+		insecure = ast_variable_retrieve(cfg, cat, "insecure");
+		set_insecure_flags(&flags, insecure, -1);
+		if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
+			var = ast_category_root(cfg, cat);
+			break;
+		}
 	}
-	
-	AST_SCHED_DEL(sched, mwi->resub);
-	ast_string_field_free_memory(mwi);
-	ast_dnsmgr_release(mwi->dnsmgr);
-	ast_free(mwi);
+	return var;
 }
 
-/*! \brief Execute destruction of SIP dialog structure, release memory */
-void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
+static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
 {
-	struct sip_request *req;
-
-	if (p->stimer) {
-		ast_free(p->stimer);
-		p->stimer = NULL;
+	struct ast_variable *tmp;
+	for (tmp = var; tmp; tmp = tmp->next) {
+		if (!newpeername && !strcasecmp(tmp->name, "name"))
+			newpeername = tmp->value;
 	}
+	return newpeername;
+}
 
-	if (sip_debug_test_pvt(p))
-		ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
-
-	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
-		update_call_counter(p, DEC_CALL_LIMIT);
-		ast_debug(2, "This call did not properly clean up call limits. Call ID %s\n", p->callid);
-	}
+/*! \brief  realtime_peer: Get peer from realtime storage
+ * Checks the "sippeers" realtime family from extconfig.conf
+ * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
+ * This returns a pointer to a peer and because we use build_peer, we can rest
+ * assured that the refcount is bumped.
+*/
+static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin, int devstate_only)
+{
+	struct sip_peer *peer;
+	struct ast_variable *var = NULL;
+	struct ast_variable *varregs = NULL;
+	struct ast_variable *tmp;
+	struct ast_config *peerlist = NULL;
+	char ipaddr[INET_ADDRSTRLEN];
+	char portstring[6]; /*up to 5 digits plus null terminator*/
+	char *cat = NULL;
+	unsigned short portnum;
+	int realtimeregs = ast_check_realtime("sipregs");
 
-	/* Unlink us from the owner if we have one */
-	if (p->owner) {
-		if (lockowner)
-			ast_channel_lock(p->owner);
-		if (option_debug)
-			ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
-		p->owner->tech_pvt = NULL;
-		/* Make sure that the channel knows its backend is going away */
-		p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
-		if (lockowner)
-			ast_channel_unlock(p->owner);
-		/* Give the channel a chance to react before deallocation */
-		usleep(1);
-	}
+	/* First check on peer name */
+	if (newpeername) {
+		if (realtimeregs)
+			varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
 
-	/* Remove link from peer to subscription of MWI */
-	if (p->relatedpeer && p->relatedpeer->mwipvt)
-		p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
-	if (p->relatedpeer && p->relatedpeer->call == p)
-		p->relatedpeer->call = dialog_unref(p->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
-	
-	if (p->relatedpeer)
-		p->relatedpeer = unref_peer(p->relatedpeer,"unsetting a dialog relatedpeer field in sip_destroy");
-	
-	if (p->registry) {
-		if (p->registry->call == p)
-			p->registry->call = dialog_unref(p->registry->call, "nulling out the registry's call dialog field in unlink_all");
-		p->registry = registry_unref(p->registry, "delete p->registry");
-	}
-	
-	if (p->mwi) {
-		p->mwi->call = NULL;
+		var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", SENTINEL);
+		if (!var && sin)
+			var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), SENTINEL);
+		if (!var) {
+			var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
+			/*!\note
+			 * If this one loaded something, then we need to ensure that the host
+			 * field matched.  The only reason why we can't have this as a criteria
+			 * is because we only have the IP address and the host field might be
+			 * set as a name (and the reverse PTR might not match).
+			 */
+			if (var && sin) {
+				for (tmp = var; tmp; tmp = tmp->next) {
+					if (!strcasecmp(tmp->name, "host")) {
+						struct hostent *hp;
+						struct ast_hostent ahp;
+						if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
+							/* No match */
+							ast_variables_destroy(var);
+							var = NULL;
+						}
+						break;
+					}
+				}
+			}
+		}
 	}
 
-	if (dumphistory)
-		sip_dump_history(p);
-
-	if (p->options)
-		ast_free(p->options);
-
-	if (p->notify) {
-		ast_variables_destroy(p->notify->headers);
-		ast_free(p->notify->content);
-		ast_free(p->notify);
-	}
-	if (p->rtp) {
-		ast_rtp_instance_destroy(p->rtp);
+	if (!var && sin) {	/* Then check on IP address for dynamic peers */
+		ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
+		portnum = ntohs(sin->sin_port);
+		sprintf(portstring, "%u", portnum);
+		var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL);	/* First check for fixed IP hosts */
+		if (var) {
+			if (realtimeregs) {
+				newpeername = get_name_from_variable(var, newpeername);
+				varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
+			}
+		} else {
+			if (realtimeregs)
+				varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
+			else
+				var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
+			if (varregs) {
+				newpeername = get_name_from_variable(varregs, newpeername);
+				var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
+			}
+		}
+		if (!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
+			peerlist = ast_load_realtime_multientry("sippeers", "host", ipaddr, SENTINEL);
+			if (peerlist) {
+				var = get_insecure_variable_from_config(peerlist);
+				if(var) {
+					if (realtimeregs) {
+						newpeername = get_name_from_variable(var, newpeername);
+						varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
+					}
+				} else { /*var wasn't found in the list of "hosts", so try "ipaddr"*/
+					peerlist = NULL;
+					cat = NULL;
+					peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
+					if(peerlist) {
+						var = get_insecure_variable_from_config(peerlist);
+						if(var) {
+							if (realtimeregs) {
+								newpeername = get_name_from_variable(var, newpeername);
+								varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
+							}
+						}
+					}
+				}
+			} else {
+				if (realtimeregs) {
+					peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, SENTINEL);
+					if (peerlist) {
+						varregs = get_insecure_variable_from_config(peerlist);
+						if (varregs) {
+							newpeername = get_name_from_variable(varregs, newpeername);
+							var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
+						}
+					}
+				} else {
+					peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
+					if (peerlist) {
+						var = get_insecure_variable_from_config(peerlist);
+						if (var) {
+							newpeername = get_name_from_variable(var, newpeername);
+							varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
+						}
+					}
+				}
+			}
+		}
 	}
-	if (p->vrtp) {
-		ast_rtp_instance_destroy(p->vrtp);
+
+	if (!var) {
+		if (peerlist)
+			ast_config_destroy(peerlist);
+		return NULL;
 	}
-	if (p->trtp) {
-		ast_rtp_instance_destroy(p->trtp);
+
+	for (tmp = var; tmp; tmp = tmp->next) {
+		/* If this is type=user, then skip this object. */
+		if (!strcasecmp(tmp->name, "type") &&
+		    !strcasecmp(tmp->value, "user")) {
+			if(peerlist)
+				ast_config_destroy(peerlist);
+			else {
+				ast_variables_destroy(var);
+				ast_variables_destroy(varregs);
+			}
+			return NULL;
+		} else if (!newpeername && !strcasecmp(tmp->name, "name")) {
+			newpeername = tmp->value;
+		}
 	}
-	if (p->udptl)
-		ast_udptl_destroy(p->udptl);
-	if (p->refer)
-		ast_free(p->refer);
-	if (p->route) {
-		free_old_route(p->route);
-		p->route = NULL;
+	
+	if (!newpeername) {	/* Did not find peer in realtime */
+		ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", ipaddr);
+		if(peerlist)
+			ast_config_destroy(peerlist);
+		else
+			ast_variables_destroy(var);
+		return NULL;
 	}
-	if (p->initreq.data)
-		ast_free(p->initreq.data);
 
-	/* Destroy Session-Timers if allocated */
-	if (p->stimer) {
-		p->stimer->quit_flag = 1;
-		if (p->stimer->st_active == TRUE && p->stimer->st_schedid > -1) {
-			AST_SCHED_DEL_UNREF(sched, p->stimer->st_schedid,
-					dialog_unref(p, "removing session timer ref"));
+
+	/* Peer found in realtime, now build it in memory */
+	peer = build_peer(newpeername, var, varregs, TRUE, devstate_only);
+	if (!peer) {
+		if(peerlist)
+			ast_config_destroy(peerlist);
+		else {
+			ast_variables_destroy(var);
+			ast_variables_destroy(varregs);
 		}
-		ast_free(p->stimer);
-		p->stimer = NULL;
+		return NULL;
 	}
 
-	/* Clear history */
-	if (p->history) {
-		struct sip_history *hist;
-		while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) {
-			ast_free(hist);
-			p->history_entries--;
+	ast_debug(3, "-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
+
+	if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && !devstate_only) {
+		/* Cache peer */
+		ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
+		if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
+			AST_SCHED_REPLACE_UNREF(peer->expire, sched, sip_cfg.rtautoclear * 1000, expire_register, peer,
+					unref_peer(_data, "remove registration ref"),
+					unref_peer(peer, "remove registration ref"),
+					ref_peer(peer, "add registration ref"));
+		}
+		ao2_t_link(peers, peer, "link peer into peers table");
+		if (peer->addr.sin_addr.s_addr) {
+			ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
 		}
-		ast_free(p->history);
-		p->history = NULL;
 	}
-
-	while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) {
-		ast_free(req);
+	peer->is_realtime = 1;
+	if (peerlist)
+		ast_config_destroy(peerlist);
+	else {
+		ast_variables_destroy(var);
+		ast_variables_destroy(varregs);
 	}
 
-	if (p->chanvars) {
-		ast_variables_destroy(p->chanvars);
-		p->chanvars = NULL;
-	}
+	return peer;
+}
 
-	ast_string_field_free_memory(p);
+/* Function to assist finding peers by name only */
+static int find_by_name(void *obj, void *arg, void *data, int flags)
+{
+	struct sip_peer *search = obj, *match = arg;
+	int *which_objects = data;
 
-	if (p->socket.tcptls_session) {
-		ao2_ref(p->socket.tcptls_session, -1);
-		p->socket.tcptls_session = NULL;
+	/* Usernames in SIP uri's are case sensitive. Domains are not */
+	if (strcmp(search->name, match->name)) {
+		return 0;
+	}
+
+	switch (*which_objects) {
+	case FINDUSERS:
+		if (!(search->type & SIP_TYPE_USER)) {
+			return 0;
+		}
+		break;
+	case FINDPEERS:
+		if (!(search->type & SIP_TYPE_PEER)) {
+			return 0;
+		}
+		break;
+	case FINDALLDEVICES:
+		break;
 	}
+
+	return CMP_MATCH | CMP_STOP;
 }
 
-/*! \brief  update_call_counter: Handle call_limit for SIP devices
- * Setting a call-limit will cause calls above the limit not to be accepted.
- *
- * Remember that for a type=friend, there's one limit for the user and
- * another for the peer, not a combined call limit.
- * This will cause unexpected behaviour in subscriptions, since a "friend"
- * is *two* devices in Asterisk, not one.
+/*!
+ * \brief Locate device by name or ip address
  *
- * Thought: For realtime, we should probably update storage with inuse counter...
+ * \param which_objects Define which objects should be matched when doing a lookup
+ *        by name.  Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
+ *        Note that this option is not used at all when doing a lookup by IP.
  *
- * \return 0 if call is ok (no call limit, below threshold)
- *	-1 on rejection of call
+ *	This is used on find matching device on name or ip/port.
+ * If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
  *
+ * \note Avoid using this function in new functions if there is a way to avoid it,
+ * since it might cause a database lookup.
  */
-static int update_call_counter(struct sip_pvt *fup, int event)
+static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int which_objects, int devstate_only, int transport)
 {
-	char name[256];
-	int *inuse = NULL, *call_limit = NULL, *inringing = NULL;
-	int outgoing = fup->outgoing_call;
 	struct sip_peer *p = NULL;
+	struct sip_peer tmp_peer;
 
-	ast_debug(3, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
+	if (peer) {
+		ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
+		p = ao2_t_callback_data(peers, OBJ_POINTER, find_by_name, &tmp_peer, &which_objects, "ao2_find in peers table");
+	} else if (sin) { /* search by addr? */
+		tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr;
+		tmp_peer.addr.sin_port = sin->sin_port;
+		tmp_peer.flags[0].flags = 0;
+		tmp_peer.transports = transport;
+		p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+		if (!p) {
+			ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
+			p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+			if (p) {
+				return p;
+			}
+		}
+	}
 
+	if (!p && (realtime || devstate_only)) {
+		p = realtime_peer(peer, sin, devstate_only);
+	}
 
-	/* Test if we need to check call limits, in order to avoid
-	   realtime lookups if we do not need it */
-	if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT) && !ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD))
-		return 0;
+	return p;
+}
 
-	ast_copy_string(name, fup->username, sizeof(name));
+/*! \brief Set nat mode on the various data sockets */
+static void do_setnat(struct sip_pvt *p)
+{
+	const char *mode;
+	int natflags;
 
-	/* Check the list of devices */
-	if ((p = find_peer(ast_strlen_zero(fup->peername) ? name : fup->peername, NULL, TRUE, FINDALLDEVICES, FALSE, 0))) {
-		inuse = &p->inUse;
-		call_limit = &p->call_limit;
-		inringing = &p->inRinging;
-		ast_copy_string(name, fup->peername, sizeof(name));
-	}
-	if (!p) {
-		ast_debug(2, "%s is not a local device, no call limit\n", name);
-		return 0;
+	natflags = ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
+	mode = natflags ? "On" : "Off";
+
+	if (p->rtp) {
+		ast_debug(1, "Setting NAT on RTP to %s\n", mode);
+		ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags);
+	}
+	if (p->vrtp) {
+		ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
+		ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags);
+	}
+	if (p->udptl) {
+		ast_debug(1, "Setting NAT on UDPTL to %s\n", mode);
+		ast_udptl_setnat(p->udptl, natflags);
 	}
+	if (p->trtp) {
+		ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
+		ast_rtp_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags);
+	}
+}
 
-	switch(event) {
-	/* incoming and outgoing affects the inUse counter */
-	case DEC_CALL_LIMIT:
-		/* Decrement inuse count if applicable */
-		if (inuse) {
-			sip_pvt_lock(fup);
-			ao2_lock(p);
-			if (*inuse > 0) {
-				if (ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
-					(*inuse)--;
-					ast_clear_flag(&fup->flags[0], SIP_INC_COUNT);
-				}
-			} else {
-				*inuse = 0;
-			}
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
-		}
+/*! \brief Change the T38 state on a SIP dialog */
+static void change_t38_state(struct sip_pvt *p, int state)
+{
+	int old = p->t38.state;
+	struct ast_channel *chan = p->owner;
+	struct ast_control_t38_parameters parameters = { .request_response = 0 };
 
-		/* Decrement ringing count if applicable */
-		if (inringing) {
-			sip_pvt_lock(fup);
-			ao2_lock(p);
-			if (*inringing > 0) {
-				if (ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
-					(*inringing)--;
-					ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
-				}
-			} else {
-			   *inringing = 0;
-			}
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
-		}
+	/* Don't bother changing if we are already in the state wanted */
+	if (old == state)
+		return;
 
-		/* Decrement onhold count if applicable */
-		sip_pvt_lock(fup);
-		ao2_lock(p);
-		if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && sip_cfg.notifyhold) {
-			ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD);
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
-			sip_peer_hold(fup, FALSE);
-		} else {
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
-		}
-		if (sipdebug)
-			ast_debug(2, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", "peer", name, *call_limit);
-		break;
+	p->t38.state = state;
+	ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
 
-	case INC_CALL_RINGING:
-	case INC_CALL_LIMIT:
-		/* If call limit is active and we have reached the limit, reject the call */
-		if (*call_limit > 0 ) {
-			if (*inuse >= *call_limit) {
-				ast_log(LOG_NOTICE, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", "peer", name, *call_limit);
-				unref_peer(p, "update_call_counter: unref peer p, call limit exceeded");
-				return -1;
-			}
-		}
-		if (inringing && (event == INC_CALL_RINGING)) {
-			sip_pvt_lock(fup);
-			ao2_lock(p);
-			if (!ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
-				(*inringing)++;
-				ast_set_flag(&fup->flags[0], SIP_INC_RINGING);
-			}
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
-		}
-		if (inuse) {
-			sip_pvt_lock(fup);
-			ao2_lock(p);
-			if (!ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
-				(*inuse)++;
-				ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
-			}
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
-		}
-		if (sipdebug) {
-			ast_debug(2, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", "peer", name, *inuse, *call_limit);
-		}
-		break;
+	/* If no channel was provided we can't send off a control frame */
+	if (!chan)
+		return;
 
-	case DEC_CALL_RINGING:
-		if (inringing) {
-			sip_pvt_lock(fup);
-			ao2_lock(p);
-			if (ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
-				if (*inringing > 0) {
-					(*inringing)--;
-				}
-				ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
-			}
-			ao2_unlock(p);
-			sip_pvt_unlock(fup);
+	/* Given the state requested and old state determine what control frame we want to queue up */
+	switch (state) {
+	case T38_PEER_REINVITE:
+		parameters = p->t38.their_parms;
+		parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
+		parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
+		ast_udptl_set_tag(p->udptl, "SIP/%s", p->username);
+		break;
+	case T38_ENABLED:
+		parameters = p->t38.their_parms;
+		parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
+		parameters.request_response = AST_T38_NEGOTIATED;
+		ast_udptl_set_tag(p->udptl, "SIP/%s", p->username);
+		break;
+	case T38_DISABLED:
+		if (old == T38_ENABLED) {
+			parameters.request_response = AST_T38_TERMINATED;
+		} else if (old == T38_LOCAL_REINVITE) {
+			parameters.request_response = AST_T38_REFUSED;
 		}
 		break;
-
-	default:
-		ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
+	case T38_LOCAL_REINVITE:
+		/* wait until we get a peer response before responding to local reinvite */
+		break;
 	}
 
-	if (p) {
-		ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->name);
-		unref_peer(p, "update_call_counter: unref_peer from call counter");
-	}
-	return 0;
+	/* Woot we got a message, create a control frame and send it on! */
+	if (parameters.request_response)
+		ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
 }
 
+/*! \brief Set the global T38 capabilities on a SIP dialog structure */
+static void set_t38_capabilities(struct sip_pvt *p)
+{
+	if (p->udptl) {
+		if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
+                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
+		} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
+			ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
+		} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
+			ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
+		}
+	}
+}
 
-static void sip_destroy_fn(void *p)
+static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *from_sock)
 {
-	sip_destroy(p);
+	if (to_sock->tcptls_session) {
+		ao2_ref(to_sock->tcptls_session, -1);
+		to_sock->tcptls_session = NULL;
+	}
+
+	if (from_sock->tcptls_session) {
+		ao2_ref(from_sock->tcptls_session, +1);
+	}
+
+	*to_sock = *from_sock;
 }
 
-/*! \brief Destroy SIP call structure.
- * Make it return NULL so the caller can do things like
- *	foo = sip_destroy(foo);
- * and reduce the chance of bugs due to dangling pointers.
+/*! \brief Initialize RTP portion of a dialog
+ * \return -1 on failure, 0 on success
  */
-struct sip_pvt *sip_destroy(struct sip_pvt *p)
+static int dialog_initialize_rtp(struct sip_pvt *dialog)
 {
-	ast_debug(3, "Destroying SIP dialog %s\n", p->callid);
-	__sip_destroy(p, TRUE, TRUE);
-	return NULL;
+	if (!sip_methods[dialog->method].need_rtp) {
+		return 0;
+	}
+
+	if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
+		return -1;
+	}
+
+	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (dialog->capability & AST_FORMAT_VIDEO_MASK)) {
+		if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
+			return -1;
+		}
+		ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout);
+		ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout);
+
+		ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1);
+	}
+
+	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT)) {
+		if (!(dialog->trtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
+			return -1;
+		}
+		ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout);
+		ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout);
+
+		ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1);
+	}
+
+	ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout);
+	ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout);
+
+	ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1);
+	ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
+	ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+
+	ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, 0, "SIP RTP");
+
+	do_setnat(dialog);
+
+	return 0;
 }
 
-/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
-int hangup_sip2cause(int cause)
+/*! \brief Create address structure from peer reference.
+ *	This function copies data from peer to the dialog, so we don't have to look up the peer
+ *	again from memory or database during the life time of the dialog.
+ *
+ * \return -1 on error, 0 on success.
+ *
+ */
+static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
 {
-	/* Possible values taken from causes.h */
 
-	switch(cause) {
-		case 401:	/* Unauthorized */
-			return AST_CAUSE_CALL_REJECTED;
-		case 403:	/* Not found */
-			return AST_CAUSE_CALL_REJECTED;
-		case 404:	/* Not found */
+	/* this checks that the dialog is contacting the peer on a valid
+	 * transport type based on the peers transport configuration,
+	 * otherwise, this function bails out */
+	if (dialog->socket.type && check_request_transport(peer, dialog))
+		return -1;
+	copy_socket_data(&dialog->socket, &peer->socket);
+
+	if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) &&
+	    (!peer->maxms || ((peer->lastms >= 0)  && (peer->lastms <= peer->maxms)))) {
+		dialog->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
+		dialog->recv = dialog->sa;
+	} else
+		return -1;
+
+	ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
+	ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+	dialog->capability = peer->capability;
+	dialog->prefs = peer->prefs;
+	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) {
+		if (!dialog->udptl) {
+			/* t38pt_udptl was enabled in the peer and not in [general] */
+			dialog->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
+		}
+		dialog->t38_maxdatagram = peer->t38_maxdatagram;
+		set_t38_capabilities(dialog);
+	} else if (dialog->udptl) {
+		ast_udptl_destroy(dialog->udptl);
+		dialog->udptl = NULL;
+	}
+
+	ast_string_field_set(dialog, engine, peer->engine);
+
+	if (dialog_initialize_rtp(dialog)) {
+		return -1;
+	}
+
+	if (dialog->rtp) { /* Audio */
+		ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
+		ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+		ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout);
+		ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout);
+		/* Set Frame packetization */
+		ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs);
+		dialog->autoframing = peer->autoframing;
+	}
+	if (dialog->vrtp) { /* Video */
+		ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout);
+		ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout);
+	}
+	if (dialog->trtp) { /* Realtime text */
+		ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout);
+		ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout);
+	}
+
+	ast_string_field_set(dialog, peername, peer->name);
+	ast_string_field_set(dialog, authname, peer->username);
+	ast_string_field_set(dialog, username, peer->username);
+	ast_string_field_set(dialog, peersecret, peer->secret);
+	ast_string_field_set(dialog, peermd5secret, peer->md5secret);
+	ast_string_field_set(dialog, mohsuggest, peer->mohsuggest);
+	ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
+	ast_string_field_set(dialog, tohost, peer->tohost);
+	ast_string_field_set(dialog, fullcontact, peer->fullcontact);
+	ast_string_field_set(dialog, accountcode, peer->accountcode);
+	ast_string_field_set(dialog, context, peer->context);
+	ast_string_field_set(dialog, cid_num, peer->cid_num);
+	ast_string_field_set(dialog, cid_name, peer->cid_name);
+	ast_string_field_set(dialog, mwi_from, peer->mwi_from);
+	ast_string_field_set(dialog, parkinglot, peer->parkinglot);
+	ast_string_field_set(dialog, engine, peer->engine);
+	ref_proxy(dialog, obproxy_get(dialog, peer));
+	dialog->callgroup = peer->callgroup;
+	dialog->pickupgroup = peer->pickupgroup;
+	dialog->allowtransfer = peer->allowtransfer;
+	dialog->jointnoncodeccapability = dialog->noncodeccapability;
+	dialog->rtptimeout = peer->rtptimeout;
+	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)) {
+		ast_string_field_set(dialog, fromdomain, peer->fromdomain);
+		if (!dialog->initreq.headers) {
+			char *c;
+			char *tmpcall = ast_strdupa(dialog->callid);
+			/* this sure looks to me like we are going to change the callid on this dialog!! */
+			c = strchr(tmpcall, '@');
+			if (c) {
+				*c = '\0';
+				ao2_t_unlink(dialogs, dialog, "About to change the callid -- remove the old name");
+				ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
+				ao2_t_link(dialogs, dialog, "New dialog callid -- inserted back into table");
+			}
+		}
+	}
+	if (!ast_strlen_zero(peer->fromuser))
+		ast_string_field_set(dialog, fromuser, peer->fromuser);
+	if (!ast_strlen_zero(peer->language))
+		ast_string_field_set(dialog, language, peer->language);
+	/* Set timer T1 to RTT for this peer (if known by qualify=) */
+	/* Minimum is settable or default to 100 ms */
+	/* If there is a maxms and lastms from a qualify use that over a manual T1
+	   value. Otherwise, use the peer's T1 value. */
+	if (peer->maxms && peer->lastms)
+		dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
+	else
+		dialog->timer_t1 = peer->timer_t1;
+
+	/* Set timer B to control transaction timeouts, the peer setting is the default and overrides
+	   the known timer */
+	if (peer->timer_b)
+		dialog->timer_b = peer->timer_b;
+	else
+		dialog->timer_b = 64 * dialog->timer_t1;
+
+	if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
+	    (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
+		dialog->noncodeccapability |= AST_RTP_DTMF;
+	else
+		dialog->noncodeccapability &= ~AST_RTP_DTMF;
+	if (peer->call_limit)
+		ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
+	if (!dialog->portinuri)
+		dialog->portinuri = peer->portinuri;
+	
+	dialog->chanvars = copy_vars(peer->chanvars);
+
+	return 0;
+}
+
+/*! \brief create address structure from device name
+ *      Or, if peer not found, find it in the global DNS
+ *      returns TRUE (-1) on failure, FALSE on success */
+static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin, int newdialog, struct sockaddr_in *remote_address)
+{
+	struct hostent *hp;
+	struct ast_hostent ahp;
+	struct sip_peer *peer;
+	char *port;
+	int portno = 0;
+	char host[MAXHOSTNAMELEN], *hostn;
+	char peername[256];
+	int srv_ret = 0;
+
+	ast_copy_string(peername, opeer, sizeof(peername));
+	port = strchr(peername, ':');
+	if (port) {
+		*port++ = '\0';
+		dialog->portinuri = 1;
+	}
+	dialog->sa.sin_family = AF_INET;
+	dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
+	dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
+	peer = find_peer(peername, NULL, TRUE, FINDPEERS, FALSE, 0);
+
+	if (peer) {
+		int res;
+		if (newdialog) {
+			set_socket_transport(&dialog->socket, 0);
+		}
+		res = create_addr_from_peer(dialog, peer);
+		if (remote_address && remote_address->sin_addr.s_addr) {
+			dialog->sa = dialog->recv = *remote_address;
+		} else if (!ast_strlen_zero(port)) {
+			if ((portno = atoi(port))) {
+				dialog->sa.sin_port = dialog->recv.sin_port = htons(portno);
+			}
+		}
+		unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup");
+		return res;
+	}
+
+	if (dialog_initialize_rtp(dialog)) {
+		return -1;
+	}
+
+	ast_string_field_set(dialog, tohost, peername);
+	dialog->allowed_methods &= ~sip_cfg.disallowed_methods;
+
+	/* Get the outbound proxy information */
+	ref_proxy(dialog, obproxy_get(dialog, NULL));
+
+	if (sin) {
+		/* This address should be updated using dnsmgr */
+		memcpy(&dialog->sa.sin_addr, &sin->sin_addr, sizeof(dialog->sa.sin_addr));
+		if (!sin->sin_port) {
+			portno = port_str2int(port, (dialog->socket.type == SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT);
+		} else {
+			portno = ntohs(sin->sin_port);
+		}
+	} else {
+
+		/* Let's see if we can find the host in DNS. First try DNS SRV records,
+		   then hostname lookup */
+		/*! \todo Fix this function. When we ask for SRV, we should check all transports
+			  In the future, we should first check NAPTR to find out transport preference
+		 */
+		hostn = peername;
+ 		/* Section 4.2 of RFC 3263 specifies that if a port number is specified, then
+		 * an A record lookup should be used instead of SRV.
+		 */
+		if (!port && sip_cfg.srvlookup) {
+			char service[MAXHOSTNAMELEN];
+			int tportno;
+	
+			snprintf(service, sizeof(service), "_sip._%s.%s", get_transport(dialog->socket.type), peername);
+			srv_ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service);
+			if (srv_ret > 0) {
+				hostn = host;
+				portno = tportno;
+			}
+		}
+	 	if (!portno)
+			portno = port_str2int(port, (dialog->socket.type == SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT);
+		hp = ast_gethostbyname(hostn, &ahp);
+		if (!hp) {
+			ast_log(LOG_WARNING, "No such host: %s\n", peername);
+			return -1;
+		}
+		memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr));
+	}
+
+	if (!dialog->socket.type)
+		set_socket_transport(&dialog->socket, SIP_TRANSPORT_UDP);
+	if (!dialog->socket.port)
+		dialog->socket.port = bindaddr.sin_port;
+	dialog->sa.sin_port = htons(portno);
+	dialog->recv = dialog->sa;
+	return 0;
+}
+
+/*! \brief Scheduled congestion on a call.
+ * Only called by the scheduler, must return the reference when done.
+ */
+static int auto_congest(const void *arg)
+{
+	struct sip_pvt *p = (struct sip_pvt *)arg;
+
+	sip_pvt_lock(p);
+	p->initid = -1;	/* event gone, will not be rescheduled */
+	if (p->owner) {
+		/* XXX fails on possible deadlock */
+		if (!ast_channel_trylock(p->owner)) {
+			append_history(p, "Cong", "Auto-congesting (timer)");
+			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+			ast_channel_unlock(p->owner);
+		}
+
+		/* Give the channel a chance to act before we proceed with destruction */
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	}
+	sip_pvt_unlock(p);
+	dialog_unref(p, "unreffing arg passed into auto_congest callback (p->initid)");
+	return 0;
+}
+
+
+/*! \brief Initiate SIP call from PBX
+ *      used from the dial() application      */
+static int sip_call(struct ast_channel *ast, char *dest, int timeout)
+{
+	int res;
+	struct sip_pvt *p = ast->tech_pvt;	/* chan is locked, so the reference cannot go away */
+	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) {
+		/* Check whether there is a VXML_URL variable */
+		if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) {
+			p->options->vxml_url = ast_var_value(current);
+		} else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
+			p->options->uri_options = ast_var_value(current);
+		} else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
+			/* Check whether there is a variable with a name starting with SIPADDHEADER */
+			p->options->addsipheaders = 1;
+		} else if (!strcasecmp(ast_var_name(current), "SIPFROMDOMAIN")) {
+			ast_string_field_set(p, fromdomain, ast_var_value(current));
+		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) {
+			/* This is a transfered call */
+			p->options->transfer = 1;
+		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) {
+			/* This is the referrer */
+			referer = ast_var_value(current);
+		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
+			/* We're replacing a call. */
+			p->options->replaces = ast_var_value(current);
+		}
+	}
+
+	res = 0;
+	ast_set_flag(&p->flags[0], SIP_OUTGOING);
+
+	/* T.38 re-INVITE FAX detection should never be done for outgoing calls,
+	 * so ensure it is disabled.
+	 */
+	ast_clear_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38);
+
+	if (p->options->transfer) {
+		char buf[SIPBUFSIZE/2];
+
+		if (referer) {
+			if (sipdebug)
+				ast_debug(3, "Call for %s transfered by %s\n", p->username, referer);
+			snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
+		} else
+			snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
+		ast_string_field_set(p, cid_name, buf);
+	}
+	ast_debug(1, "Outgoing Call for %s\n", p->username);
+
+	res = update_call_counter(p, INC_CALL_RINGING);
+
+	if (res == -1) {
+		ast->hangupcause = AST_CAUSE_USER_BUSY;
+		return res;
+	}
+	p->callingpres = ast->cid.cid_pres;
+	p->jointcapability = ast_rtp_instance_available_formats(p->rtp, p->capability, p->prefcodec);
+	p->jointnoncodeccapability = p->noncodeccapability;
+
+	/* If there are no audio formats left to offer, punt */
+	if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
+		ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
+		res = -1;
+	} else {
+		int xmitres;
+
+		sip_pvt_lock(p);
+		xmitres = transmit_invite(p, SIP_INVITE, 1, 2, uri);
+		sip_pvt_unlock(p);
+		if (xmitres == XMIT_ERROR)
+			return -1;
+		p->invitestate = INV_CALLING;
+
+		/* Initialize auto-congest time */
+		AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p,
+								dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"),
+								dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"),
+								dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") );
+	}
+	return res;
+}
+
+/*! \brief Destroy registry object
+	Objects created with the register= statement in static configuration */
+static void sip_registry_destroy(struct sip_registry *reg)
+{
+	/* Really delete */
+	ast_debug(3, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
+
+	if (reg->call) {
+		/* Clear registry before destroying to ensure
+		   we don't get reentered trying to grab the registry lock */
+		reg->call->registry = registry_unref(reg->call->registry, "destroy reg->call->registry");
+		ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
+		dialog_unlink_all(reg->call, TRUE, TRUE);
+		reg->call = dialog_unref(reg->call, "unref reg->call");
+		/* reg->call = sip_destroy(reg->call); */
+	}
+	AST_SCHED_DEL(sched, reg->expire);	
+	AST_SCHED_DEL(sched, reg->timeout);
+	
+	ast_string_field_free_memory(reg);
+	ast_atomic_fetchadd_int(&regobjs, -1);
+	ast_dnsmgr_release(reg->dnsmgr);
+	ast_free(reg);
+}
+
+/*! \brief Destroy MWI subscription object */
+static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi)
+{
+	if (mwi->call) {
+		mwi->call->mwi = NULL;
+		sip_destroy(mwi->call);
+	}
+	
+	AST_SCHED_DEL(sched, mwi->resub);
+	ast_string_field_free_memory(mwi);
+	ast_dnsmgr_release(mwi->dnsmgr);
+	ast_free(mwi);
+}
+
+/*! \brief Execute destruction of SIP dialog structure, release memory */
+void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
+{
+	struct sip_request *req;
+
+	if (p->stimer) {
+		ast_free(p->stimer);
+		p->stimer = NULL;
+	}
+
+	if (sip_debug_test_pvt(p))
+		ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
+
+	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
+		update_call_counter(p, DEC_CALL_LIMIT);
+		ast_debug(2, "This call did not properly clean up call limits. Call ID %s\n", p->callid);
+	}
+
+	/* Unlink us from the owner if we have one */
+	if (p->owner) {
+		if (lockowner)
+			ast_channel_lock(p->owner);
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
+		p->owner->tech_pvt = NULL;
+		/* Make sure that the channel knows its backend is going away */
+		p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+		if (lockowner)
+			ast_channel_unlock(p->owner);
+		/* Give the channel a chance to react before deallocation */
+		usleep(1);
+	}
+
+	/* Remove link from peer to subscription of MWI */
+	if (p->relatedpeer && p->relatedpeer->mwipvt)
+		p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+	if (p->relatedpeer && p->relatedpeer->call == p)
+		p->relatedpeer->call = dialog_unref(p->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
+	
+	if (p->relatedpeer)
+		p->relatedpeer = unref_peer(p->relatedpeer,"unsetting a dialog relatedpeer field in sip_destroy");
+	
+	if (p->registry) {
+		if (p->registry->call == p)
+			p->registry->call = dialog_unref(p->registry->call, "nulling out the registry's call dialog field in unlink_all");
+		p->registry = registry_unref(p->registry, "delete p->registry");
+	}
+	
+	if (p->mwi) {
+		p->mwi->call = NULL;
+	}
+
+	if (dumphistory)
+		sip_dump_history(p);
+
+	if (p->options)
+		ast_free(p->options);
+
+	if (p->notify) {
+		ast_variables_destroy(p->notify->headers);
+		ast_free(p->notify->content);
+		ast_free(p->notify);
+	}
+	if (p->rtp) {
+		ast_rtp_instance_destroy(p->rtp);
+	}
+	if (p->vrtp) {
+		ast_rtp_instance_destroy(p->vrtp);
+	}
+	if (p->trtp) {
+		ast_rtp_instance_destroy(p->trtp);
+	}
+	if (p->udptl)
+		ast_udptl_destroy(p->udptl);
+	if (p->refer)
+		ast_free(p->refer);
+	if (p->route) {
+		free_old_route(p->route);
+		p->route = NULL;
+	}
+	if (p->initreq.data)
+		ast_free(p->initreq.data);
+
+	/* Destroy Session-Timers if allocated */
+	if (p->stimer) {
+		p->stimer->quit_flag = 1;
+		if (p->stimer->st_active == TRUE && p->stimer->st_schedid > -1) {
+			AST_SCHED_DEL_UNREF(sched, p->stimer->st_schedid,
+					dialog_unref(p, "removing session timer ref"));
+		}
+		ast_free(p->stimer);
+		p->stimer = NULL;
+	}
+
+	/* Clear history */
+	if (p->history) {
+		struct sip_history *hist;
+		while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) {
+			ast_free(hist);
+			p->history_entries--;
+		}
+		ast_free(p->history);
+		p->history = NULL;
+	}
+
+	while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) {
+		ast_free(req);
+	}
+
+	if (p->chanvars) {
+		ast_variables_destroy(p->chanvars);
+		p->chanvars = NULL;
+	}
+
+	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;
+	}
+}
+
+/*! \brief  update_call_counter: Handle call_limit for SIP devices
+ * Setting a call-limit will cause calls above the limit not to be accepted.
+ *
+ * Remember that for a type=friend, there's one limit for the user and
+ * another for the peer, not a combined call limit.
+ * This will cause unexpected behaviour in subscriptions, since a "friend"
+ * is *two* devices in Asterisk, not one.
+ *
+ * Thought: For realtime, we should probably update storage with inuse counter...
+ *
+ * \return 0 if call is ok (no call limit, below threshold)
+ *	-1 on rejection of call
+ *
+ */
+static int update_call_counter(struct sip_pvt *fup, int event)
+{
+	char name[256];
+	int *inuse = NULL, *call_limit = NULL, *inringing = NULL;
+	int outgoing = fup->outgoing_call;
+	struct sip_peer *p = NULL;
+
+	ast_debug(3, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
+
+
+	/* Test if we need to check call limits, in order to avoid
+	   realtime lookups if we do not need it */
+	if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT) && !ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD))
+		return 0;
+
+	ast_copy_string(name, fup->username, sizeof(name));
+
+	/* Check the list of devices */
+	if ((p = find_peer(ast_strlen_zero(fup->peername) ? name : fup->peername, NULL, TRUE, FINDALLDEVICES, FALSE, 0))) {
+		inuse = &p->inUse;
+		call_limit = &p->call_limit;
+		inringing = &p->inRinging;
+		ast_copy_string(name, fup->peername, sizeof(name));
+	}
+	if (!p) {
+		ast_debug(2, "%s is not a local device, no call limit\n", name);
+		return 0;
+	}
+
+	switch(event) {
+	/* incoming and outgoing affects the inUse counter */
+	case DEC_CALL_LIMIT:
+		/* Decrement inuse count if applicable */
+		if (inuse) {
+			sip_pvt_lock(fup);
+			ao2_lock(p);
+			if (*inuse > 0) {
+				if (ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
+					(*inuse)--;
+					ast_clear_flag(&fup->flags[0], SIP_INC_COUNT);
+				}
+			} else {
+				*inuse = 0;
+			}
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+		}
+
+		/* Decrement ringing count if applicable */
+		if (inringing) {
+			sip_pvt_lock(fup);
+			ao2_lock(p);
+			if (*inringing > 0) {
+				if (ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
+					(*inringing)--;
+					ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
+				}
+			} else {
+			   *inringing = 0;
+			}
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+		}
+
+		/* Decrement onhold count if applicable */
+		sip_pvt_lock(fup);
+		ao2_lock(p);
+		if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && sip_cfg.notifyhold) {
+			ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD);
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+			sip_peer_hold(fup, FALSE);
+		} else {
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+		}
+		if (sipdebug)
+			ast_debug(2, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", "peer", name, *call_limit);
+		break;
+
+	case INC_CALL_RINGING:
+	case INC_CALL_LIMIT:
+		/* If call limit is active and we have reached the limit, reject the call */
+		if (*call_limit > 0 ) {
+			if (*inuse >= *call_limit) {
+				ast_log(LOG_NOTICE, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", "peer", name, *call_limit);
+				unref_peer(p, "update_call_counter: unref peer p, call limit exceeded");
+				return -1;
+			}
+		}
+		if (inringing && (event == INC_CALL_RINGING)) {
+			sip_pvt_lock(fup);
+			ao2_lock(p);
+			if (!ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
+				(*inringing)++;
+				ast_set_flag(&fup->flags[0], SIP_INC_RINGING);
+			}
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+		}
+		if (inuse) {
+			sip_pvt_lock(fup);
+			ao2_lock(p);
+			if (!ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
+				(*inuse)++;
+				ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
+			}
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+		}
+		if (sipdebug) {
+			ast_debug(2, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", "peer", name, *inuse, *call_limit);
+		}
+		break;
+
+	case DEC_CALL_RINGING:
+		if (inringing) {
+			sip_pvt_lock(fup);
+			ao2_lock(p);
+			if (ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
+				if (*inringing > 0) {
+					(*inringing)--;
+				}
+				ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
+			}
+			ao2_unlock(p);
+			sip_pvt_unlock(fup);
+		}
+		break;
+
+	default:
+		ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
+	}
+
+	if (p) {
+		ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->name);
+		unref_peer(p, "update_call_counter: unref_peer from call counter");
+	}
+	return 0;
+}
+
+
+static void sip_destroy_fn(void *p)
+{
+	sip_destroy(p);
+}
+
+/*! \brief Destroy SIP call structure.
+ * Make it return NULL so the caller can do things like
+ *	foo = sip_destroy(foo);
+ * and reduce the chance of bugs due to dangling pointers.
+ */
+struct sip_pvt *sip_destroy(struct sip_pvt *p)
+{
+	ast_debug(3, "Destroying SIP dialog %s\n", p->callid);
+	__sip_destroy(p, TRUE, TRUE);
+	return NULL;
+}
+
+/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
+int hangup_sip2cause(int cause)
+{
+	/* Possible values taken from causes.h */
+
+	switch(cause) {
+		case 401:	/* Unauthorized */
+			return AST_CAUSE_CALL_REJECTED;
+		case 403:	/* Not found */
+			return AST_CAUSE_CALL_REJECTED;
+		case 404:	/* Not found */
 			return AST_CAUSE_UNALLOCATED;
 		case 405:	/* Method not allowed */
 			return AST_CAUSE_INTERWORKING;
@@ -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 */
@@ -9664,14 +10588,47 @@ static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt)
 		return;
 	}
 
-	/* We at least have a number to place in the Diversion header, which is enough */
-	if (ast_strlen_zero(diverting_name)) {
-		snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s", diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason);
-	} else {
-		snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s", diverting_name, diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason);
+	/* We at least have a number to place in the Diversion header, which is enough */
+	if (ast_strlen_zero(diverting_name)) {
+		snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s", diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason);
+	} else {
+		snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s", diverting_name, diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason);
+	}
+
+	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;
 
-	add_header(req, "Diversion", header_text);
+	/* 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
@@ -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 */
@@ -19444,1014 +20650,1504 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
 			}
 		}
 
-		dlg_min_se = st_get_se(p, FALSE);
-		switch (st_get_mode(p)) {
-		case SESSION_TIMER_MODE_ACCEPT:
-		case SESSION_TIMER_MODE_ORIGINATE:
-			if (uac_max_se > 0 && uac_max_se < dlg_min_se) {
-				transmit_response_with_minse(p, "422 Session Interval Too Small", req, dlg_min_se);
-				p->invitestate = INV_COMPLETED;
-				if (!p->lastinvite) {
-					sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		dlg_min_se = st_get_se(p, FALSE);
+		switch (st_get_mode(p)) {
+		case SESSION_TIMER_MODE_ACCEPT:
+		case SESSION_TIMER_MODE_ORIGINATE:
+			if (uac_max_se > 0 && uac_max_se < dlg_min_se) {
+				transmit_response_with_minse(p, "422 Session Interval Too Small", req, dlg_min_se);
+				p->invitestate = INV_COMPLETED;
+				if (!p->lastinvite) {
+					sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+				}
+				res = -1;
+				goto request_invite_cleanup;
+			}
+
+			p->stimer->st_active_peer_ua = TRUE;
+			st_active = TRUE;
+			if (st_ref == SESSION_TIMER_REFRESHER_AUTO) {
+				st_ref = st_get_refresher(p);
+			}
+
+			if (uac_max_se > 0) {
+				int dlg_max_se = st_get_se(p, TRUE);
+				if (dlg_max_se >= uac_min_se) {
+					st_interval = (uac_max_se < dlg_max_se) ? uac_max_se : dlg_max_se;
+				} else {
+					st_interval = uac_max_se;
+				}
+			} else {
+				/* Set to default max value */
+				st_interval = global_max_se;
+			}
+			break;
+
+		case SESSION_TIMER_MODE_REFUSE:
+			if (p->reqsipoptions & SIP_OPT_TIMER) {
+				transmit_response_with_unsupported(p, "420 Option Disabled", req, required);
+				ast_log(LOG_WARNING, "Received SIP INVITE with supported but disabled option: %s\n", required);
+				p->invitestate = INV_COMPLETED;
+				if (!p->lastinvite) {
+					sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+				}
+				res = -1;
+				goto request_invite_cleanup;
+			}
+			break;
+
+		default:
+			ast_log(LOG_ERROR, "Internal Error %d at %s:%d\n", st_get_mode(p), __FILE__, __LINE__);
+			break;
+		}
+	} else {
+		/* The UAC did not request session-timers.  Asterisk (UAS), will now decide
+		(based on session-timer-mode in sip.conf) whether to run session-timers for
+		this session or not. */
+		switch (st_get_mode(p)) {
+		case SESSION_TIMER_MODE_ORIGINATE:
+			st_active = TRUE;
+			st_interval = st_get_se(p, TRUE);
+			st_ref = SESSION_TIMER_REFRESHER_UAS;
+			p->stimer->st_active_peer_ua = FALSE;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (reinvite == 0) {
+		/* Session-Timers: Start session refresh timer based on negotiation/config */
+		if (st_active == TRUE) {
+			p->stimer->st_active   = TRUE;
+			p->stimer->st_interval = st_interval;
+			p->stimer->st_ref      = st_ref;
+			start_session_timer(p);
+		}
+	} else {
+		if (p->stimer->st_active == TRUE) {
+			/* Session-Timers:  A re-invite request sent within a dialog will serve as
+			a refresh request, no matter whether the re-invite was sent for refreshing
+			the session or modifying it.*/
+			ast_debug (2, "Restarting session-timers on a refresh - %s\n", p->callid);
+
+			/* The UAC may be adjusting the session-timers mid-session */
+			if (st_interval > 0) {
+				p->stimer->st_interval = st_interval;
+				p->stimer->st_ref      = st_ref;
+			}
+
+			restart_session_timer(p);
+			if (p->stimer->st_expirys > 0) {
+				p->stimer->st_expirys--;
+			}
+		}
+	}
+
+	if (!req->ignore && p)
+		p->lastinvite = seqno;
+
+	if (replace_id) {	/* Attended transfer or call pickup - we're the target */
+		if (!ast_strlen_zero(pickup.exten)) {
+			append_history(p, "Xfer", "INVITE/Replace received");
+
+			/* Let the caller know we're giving it a shot */
+			transmit_response(p, "100 Trying", req);
+			ast_setstate(c, AST_STATE_RING);
+
+			/* Do the pickup itself */
+			ast_channel_unlock(c);
+			*nounlock = 1;
+
+			/* since p->owner (c) is unlocked, we need to go ahead and unlock pvt for both
+			 * magic pickup and ast_hangup.  Both of these functions will attempt to lock
+			 * p->owner again, which can cause a deadlock if we already hold a lock on p.
+			 * Locking order is, channel then pvt.  Dead lock avoidance must be used if
+			 * called the other way around. */
+			sip_pvt_unlock(p);
+			do_magic_pickup(c, pickup.exten, pickup.context);
+			/* Now we're either masqueraded or we failed to pickup, in either case we... */
+			ast_hangup(c);
+			sip_pvt_lock(p); /* pvt is expected to remain locked on return, so re-lock it */
+
+			res = 0;
+			goto request_invite_cleanup;
+		} else {
+			/* Go and take over the target call */
+			if (sipdebug)
+				ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
+			res = handle_invite_replaces(p, req, debug, seqno, sin, nounlock);
+			refer_locked = 0;
+			goto request_invite_cleanup;
+		}
+	}
+
+
+	if (c) {	/* We have a call  -either a new call or an old one (RE-INVITE) */
+		enum ast_channel_state c_state = c->_state;
+
+		if (c_state != AST_STATE_UP && reinvite &&
+			(p->invitestate == INV_TERMINATED || p->invitestate == INV_CONFIRMED)) {
+			/* If these conditions are true, and the channel is still in the 'ringing'
+			 * state, then this likely means that we have a situation where the initial
+			 * INVITE transaction has completed *but* the channel's state has not yet been
+			 * changed to UP. The reason this could happen is if the reinvite is received
+			 * on the SIP socket prior to an application calling ast_read on this channel
+			 * to read the answer frame we earlier queued on it. In this case, the reinvite
+			 * is completely legitimate so we need to handle this the same as if the channel
+			 * were already UP. Thus we are purposely falling through to the AST_STATE_UP case.
+			 */
+			c_state = AST_STATE_UP;
+		}
+
+		switch(c_state) {
+		case AST_STATE_DOWN:
+			ast_debug(2, "%s: New call is still down.... Trying... \n", c->name);
+			transmit_provisional_response(p, "100 Trying", req, 0);
+			p->invitestate = INV_PROCEEDING;
+			ast_setstate(c, AST_STATE_RING);
+			if (strcmp(p->exten, ast_pickup_ext())) {	/* Call to extension -start pbx on this call */
+				enum ast_pbx_result result;
+
+				result = ast_pbx_start(c);
+
+				switch(result) {
+				case AST_PBX_FAILED:
+					ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+					p->invitestate = INV_COMPLETED;
+					transmit_response_reliable(p, "503 Unavailable", req);
+					break;
+				case AST_PBX_CALL_LIMIT:
+					ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
+					p->invitestate = INV_COMPLETED;
+					transmit_response_reliable(p, "480 Temporarily Unavailable", req);
+					break;
+				case AST_PBX_SUCCESS:
+					/* nothing to do */
+					break;
 				}
-				res = -1;
-				goto request_invite_cleanup;
-			}
 
-			p->stimer->st_active_peer_ua = TRUE;
-			st_active = TRUE;
-			if (st_ref == SESSION_TIMER_REFRESHER_AUTO) {
-				st_ref = st_get_refresher(p);
-			}
+				if (result) {
 
-			if (uac_max_se > 0) {
-				int dlg_max_se = st_get_se(p, TRUE);
-				if (dlg_max_se >= uac_min_se) {
-					st_interval = (uac_max_se < dlg_max_se) ? uac_max_se : dlg_max_se;
+					/* Unlock locks so ast_hangup can do its magic */
+					ast_channel_unlock(c);
+					sip_pvt_unlock(p);
+					ast_hangup(c);
+					sip_pvt_lock(p);
+					c = NULL;
+				}
+			} else {	/* Pickup call in call group */
+				ast_channel_unlock(c);
+				*nounlock = 1;
+				if (ast_pickup_call(c)) {
+					ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid);
+					transmit_response_reliable(p, "503 Unavailable", req);
+					sip_alreadygone(p);
+					/* Unlock locks so ast_hangup can do its magic */
+					sip_pvt_unlock(p);
+					c->hangupcause = AST_CAUSE_CALL_REJECTED;
 				} else {
-					st_interval = uac_max_se;
+					sip_pvt_unlock(p);
+					c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
 				}
-			} else {
-				/* Set to default max value */
-				st_interval = global_max_se;
+				p->invitestate = INV_COMPLETED;
+				ast_hangup(c);
+				sip_pvt_lock(p);
+				c = NULL;
 			}
 			break;
+		case AST_STATE_RING:
+			transmit_provisional_response(p, "100 Trying", req, 0);
+			p->invitestate = INV_PROCEEDING;
+			break;
+		case AST_STATE_RINGING:
+			transmit_provisional_response(p, "180 Ringing", req, 0);
+			p->invitestate = INV_PROCEEDING;
+			break;
+		case AST_STATE_UP:
+			ast_debug(2, "%s: This call is UP.... \n", c->name);
 
-		case SESSION_TIMER_MODE_REFUSE:
-			if (p->reqsipoptions & SIP_OPT_TIMER) {
-				transmit_response_with_unsupported(p, "420 Option Disabled", req, required);
-				ast_log(LOG_WARNING, "Received SIP INVITE with supported but disabled option: %s\n", required);
-				p->invitestate = INV_COMPLETED;
-				if (!p->lastinvite) {
-					sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-				}
-				res = -1;
-				goto request_invite_cleanup;
+			transmit_response(p, "100 Trying", req);
+
+			if (p->t38.state == T38_PEER_REINVITE) {
+				p->t38id = ast_sched_add(sched, 5000, sip_t38_abort, dialog_ref(p, "passing dialog ptr into sched structure based on t38id for sip_t38_abort."));
+			} else if (p->t38.state == T38_ENABLED) {
+				ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+				transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ?  XMIT_UNRELIABLE : XMIT_CRITICAL)));
+			} else if (p->t38.state == T38_DISABLED) {
+				/* If this is not a re-invite or something to ignore - it's critical */
+				ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+				transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ?  XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE);
 			}
-			break;
 
+			p->invitestate = INV_TERMINATED;
+			break;
 		default:
-			ast_log(LOG_ERROR, "Internal Error %d at %s:%d\n", st_get_mode(p), __FILE__, __LINE__);
+			ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state);
+			transmit_response(p, "100 Trying", req);
 			break;
 		}
 	} else {
-		/* The UAC did not request session-timers.  Asterisk (UAS), will now decide
-		(based on session-timer-mode in sip.conf) whether to run session-timers for
-		this session or not. */
-		switch (st_get_mode(p)) {
-		case SESSION_TIMER_MODE_ORIGINATE:
-			st_active = TRUE;
-			st_interval = st_get_se(p, TRUE);
-			st_ref = SESSION_TIMER_REFRESHER_UAS;
-			p->stimer->st_active_peer_ua = FALSE;
-			break;
+		if (p && (p->autokillid == -1)) {
+			const char *msg;
 
-		default:
-			break;
+			if (!p->jointcapability)
+				msg = "488 Not Acceptable Here (codec error)";
+			else {
+				ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n");
+				msg = "503 Unavailable";
+			}
+			transmit_response_reliable(p, msg, req);
+			p->invitestate = INV_COMPLETED;
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		}
 	}
 
-	if (reinvite == 0) {
-		/* Session-Timers: Start session refresh timer based on negotiation/config */
-		if (st_active == TRUE) {
-			p->stimer->st_active   = TRUE;
-			p->stimer->st_interval = st_interval;
-			p->stimer->st_ref      = st_ref;
-			start_session_timer(p);
+request_invite_cleanup:
+
+	if (refer_locked && p->refer && p->refer->refer_call) {
+		sip_pvt_unlock(p->refer->refer_call);
+		if (p->refer->refer_call->owner) {
+			ast_channel_unlock(p->refer->refer_call->owner);
+		}
+	}
+
+	return res;
+}
+
+/*! \brief  Find all call legs and bridge transferee with target
+ *	called from handle_request_refer
+ *
+ *	\note this function assumes two locks to begin with, sip_pvt transferer and current.chan1 (the pvt's owner)... 
+ *	2 additional locks are held at the beginning of the function, targetcall_pvt, and targetcall_pvt's owner
+ *	channel (which is stored in target.chan1).  These 2 locks _MUST_ be let go by the end of the function.  Do
+ *	not be confused into thinking a pvt's owner is the same thing as the channels locked at the beginning of
+ *	this function, after the masquerade this may not be true.  Be consistent and unlock only the exact same
+ *	pointers that were locked to begin with.
+ *
+ *	If this function is successful, only the transferer pvt lock will remain on return.  Setting nounlock indicates
+ *	to handle_request_do() that the pvt's owner it locked does not require an unlock.
+ */
+static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock)
+{
+	struct sip_dual target;		/* Chan 1: Call from tranferer to Asterisk */
+					/* Chan 2: Call from Asterisk to target */
+	int res = 0;
+	struct sip_pvt *targetcall_pvt;
+	struct ast_party_connected_line connected_to_transferee;
+	struct ast_party_connected_line connected_to_target;
+	char transferer_linkedid[32];
+	struct ast_channel *chans[2];
+
+	/* Check if the call ID of the replaces header does exist locally */
+	if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
+		transferer->refer->replaces_callid_fromtag))) {
+		if (transferer->refer->localtransfer) {
+			/* We did not find the refered call. Sorry, can't accept then */
+			transmit_response(transferer, "202 Accepted", req);
+			/* Let's fake a response from someone else in order
+		   	to follow the standard */
+			transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
+			append_history(transferer, "Xfer", "Refer failed");
+			ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
+			transferer->refer->status = REFER_FAILED;
+			return -1;
 		}
+		/* Fall through for remote transfers that we did not find locally */
+		ast_debug(3, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
+		return 0;
+	}
+
+	/* Ok, we can accept this transfer */
+	transmit_response(transferer, "202 Accepted", req);
+	append_history(transferer, "Xfer", "Refer accepted");
+	if (!targetcall_pvt->owner) {	/* No active channel */
+		ast_debug(4, "SIP attended transfer: Error: No owner of target call\n");
+		/* Cancel transfer */
+		transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
+		append_history(transferer, "Xfer", "Refer failed");
+		ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
+		transferer->refer->status = REFER_FAILED;
+		sip_pvt_unlock(targetcall_pvt);
+		if (targetcall_pvt)
+			ao2_t_ref(targetcall_pvt, -1, "Drop targetcall_pvt pointer");
+		return -1;
+	}
+
+	/* We have a channel, find the bridge */
+	target.chan1 = targetcall_pvt->owner;				/* Transferer to Asterisk */
+	target.chan2 = ast_bridged_channel(targetcall_pvt->owner);	/* Asterisk to target */
+
+	if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
+		/* Wrong state of new channel */
+		if (target.chan2)
+			ast_debug(4, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state));
+		else if (target.chan1->_state != AST_STATE_RING)
+			ast_debug(4, "SIP attended transfer: Error: No target channel\n");
+		else
+			ast_debug(4, "SIP attended transfer: Attempting transfer in ringing state\n");
+	}
+
+	/* Transfer */
+	if (sipdebug) {
+		if (current->chan2)	/* We have two bridges */
+			ast_debug(4, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name);
+		else			/* One bridge, propably transfer of IVR/voicemail etc */
+			ast_debug(4, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name);
+	}
+
+	ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */
+
+	ast_copy_string(transferer_linkedid, transferer->owner->linkedid, sizeof(transferer_linkedid));
+
+	/* Perform the transfer */
+	chans[0] = transferer->owner;
+	chans[1] = target.chan1;
+	ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n",
+		transferer->owner->name,
+		transferer->owner->uniqueid,
+		transferer->callid,
+		target.chan1->name,
+		target.chan1->uniqueid);
+	ast_party_connected_line_init(&connected_to_transferee);
+	ast_party_connected_line_init(&connected_to_target);
+	/* No need to lock current->chan1 here since it was locked in sipsock_read */
+	ast_party_connected_line_copy(&connected_to_transferee, &current->chan1->connected);
+	/* No need to lock target.chan1 here since it was locked in get_sip_pvt_byid_locked */
+	ast_party_connected_line_copy(&connected_to_target, &target.chan1->connected);
+	connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+	res = attempt_transfer(current, &target);
+	if (res) {
+		/* Failed transfer */
+		transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE);
+		append_history(transferer, "Xfer", "Refer failed");
+		ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
+		/* if transfer failed, go ahead and unlock targetcall_pvt and it's owner channel */
+		sip_pvt_unlock(targetcall_pvt);
+		ast_channel_unlock(target.chan1);
 	} else {
-		if (p->stimer->st_active == TRUE) {
-			/* Session-Timers:  A re-invite request sent within a dialog will serve as
-			a refresh request, no matter whether the re-invite was sent for refreshing
-			the session or modifying it.*/
-			ast_debug (2, "Restarting session-timers on a refresh - %s\n", p->callid);
+		/* Transfer succeeded! */
+		const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND");
 
-			/* The UAC may be adjusting the session-timers mid-session */
-			if (st_interval > 0) {
-				p->stimer->st_interval = st_interval;
-				p->stimer->st_ref      = st_ref;
-			}
+		/* target.chan1 was locked in get_sip_pvt_byid_locked, do not unlock target.chan1 before this */
+		ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2);
 
-			restart_session_timer(p);
-			if (p->stimer->st_expirys > 0) {
-				p->stimer->st_expirys--;
-			}
+		/* Tell transferer that we're done. */
+		transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
+		append_history(transferer, "Xfer", "Refer succeeded");
+		transferer->refer->status = REFER_200OK;
+		if (target.chan2 && !ast_strlen_zero(xfersound) && ast_streamfile(target.chan2, xfersound, target.chan2->language) >= 0) {
+			ast_waitstream(target.chan2, "");
 		}
-	}
 
-	if (!req->ignore && p)
-		p->lastinvite = seqno;
+		/* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then
+		 * can queue connected line updates where they need to go.
+		 *
+		 * before a masquerade, all channel and pvt locks must be unlocked.  Any recursive
+		 * channel locks held before this function invalidates channel container locking order.
+		 * Since we are unlocking both the pvt (transferer) and its owner channel (current.chan1)
+		 * it is possible for current.chan1 to be destroyed in the pbx thread.  To prevent this
+		 * we must give c a reference before any unlocking takes place.
+		 */
 
-	if (replace_id) {	/* Attended transfer or call pickup - we're the target */
-		if (!ast_strlen_zero(pickup.exten)) {
-			append_history(p, "Xfer", "INVITE/Replace received");
+		ast_channel_ref(current->chan1);
+		ast_channel_unlock(current->chan1); /* current.chan1 is p->owner before the masq, it was locked by socket_read()*/
+		ast_channel_unlock(target.chan1);
+		*nounlock = 1;  /* we just unlocked the dialog's channel and have no plans of locking it again. */
+		sip_pvt_unlock(targetcall_pvt);
+		sip_pvt_unlock(transferer);
 
-			/* Let the caller know we're giving it a shot */
-			transmit_response(p, "100 Trying", req);
-			ast_setstate(c, AST_STATE_RING);
+		ast_do_masquerade(target.chan1);
 
-			/* Do the pickup itself */
-			ast_channel_unlock(c);
-			*nounlock = 1;
+		ast_channel_lock(transferer); /* the transferer pvt is expected to remain locked on return */
 
-			/* since p->owner (c) is unlocked, we need to go ahead and unlock pvt for both
-			 * magic pickup and ast_hangup.  Both of these functions will attempt to lock
-			 * p->owner again, which can cause a deadlock if we already hold a lock on p.
-			 * Locking order is, channel then pvt.  Dead lock avoidance must be used if
-			 * called the other way around. */
-			sip_pvt_unlock(p);
-			do_magic_pickup(c, pickup.exten, pickup.context);
-			/* Now we're either masqueraded or we failed to pickup, in either case we... */
-			ast_hangup(c);
-			sip_pvt_lock(p); /* pvt is expected to remain locked on return, so re-lock it */
+		ast_indicate(target.chan1, AST_CONTROL_UNHOLD);
 
-			res = 0;
-			goto request_invite_cleanup;
+		if (target.chan2) {
+			ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee);
+			ast_channel_queue_connected_line_update(target.chan2, &connected_to_target);
 		} else {
-			/* Go and take over the target call */
-			if (sipdebug)
-				ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
-			res = handle_invite_replaces(p, req, debug, seqno, sin, nounlock);
-			refer_locked = 0;
-			goto request_invite_cleanup;
+			/* Since target.chan1 isn't actually connected to another channel, there is no way for us
+			 * to queue a frame so that its connected line status will be updated. Instead, we have to
+			 * change it directly. Since we are not the channel thread, we cannot run a connected line
+			 * interception macro on target.chan1
+			 */
+			ast_channel_update_connected_line(target.chan1, &connected_to_target);
 		}
+		ast_channel_unref(current->chan1);
 	}
 
+	/* at this point if the transfer is successful only the transferer pvt should be locked. */
+	ast_party_connected_line_free(&connected_to_target);
+	ast_party_connected_line_free(&connected_to_transferee);
+	if (targetcall_pvt)
+		ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt");
+	return 1;
+}
 
-	if (c) {	/* We have a call  -either a new call or an old one (RE-INVITE) */
-		enum ast_channel_state c_state = c->_state;
 
-		if (c_state != AST_STATE_UP && reinvite &&
-			(p->invitestate == INV_TERMINATED || p->invitestate == INV_CONFIRMED)) {
-			/* If these conditions are true, and the channel is still in the 'ringing'
-			 * state, then this likely means that we have a situation where the initial
-			 * INVITE transaction has completed *but* the channel's state has not yet been
-			 * changed to UP. The reason this could happen is if the reinvite is received
-			 * on the SIP socket prior to an application calling ast_read on this channel
-			 * to read the answer frame we earlier queued on it. In this case, the reinvite
-			 * is completely legitimate so we need to handle this the same as if the channel
-			 * were already UP. Thus we are purposely falling through to the AST_STATE_UP case.
-			 */
-			c_state = AST_STATE_UP;
-		}
+/*! \brief Handle incoming REFER request */
+/*! \page SIP_REFER SIP transfer Support (REFER)
 
-		switch(c_state) {
-		case AST_STATE_DOWN:
-			ast_debug(2, "%s: New call is still down.... Trying... \n", c->name);
-			transmit_provisional_response(p, "100 Trying", req, 0);
-			p->invitestate = INV_PROCEEDING;
-			ast_setstate(c, AST_STATE_RING);
-			if (strcmp(p->exten, ast_pickup_ext())) {	/* Call to extension -start pbx on this call */
-				enum ast_pbx_result result;
+	REFER is used for call transfer in SIP. We get a REFER
+	to place a new call with an INVITE somwhere and then
+	keep the transferor up-to-date of the transfer. If the
+	transfer fails, get back on line with the orginal call.
 
-				result = ast_pbx_start(c);
+	- REFER can be sent outside or inside of a dialog.
+	  Asterisk only accepts REFER inside of a dialog.
 
-				switch(result) {
-				case AST_PBX_FAILED:
-					ast_log(LOG_WARNING, "Failed to start PBX :(\n");
-					p->invitestate = INV_COMPLETED;
-					transmit_response_reliable(p, "503 Unavailable", req);
-					break;
-				case AST_PBX_CALL_LIMIT:
-					ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
-					p->invitestate = INV_COMPLETED;
-					transmit_response_reliable(p, "480 Temporarily Unavailable", req);
-					break;
-				case AST_PBX_SUCCESS:
-					/* nothing to do */
-					break;
-				}
+	- If we get a replaces header, it is an attended transfer
 
-				if (result) {
+	\par Blind transfers
+	The transferor provides the transferee
+	with the transfer targets contact. The signalling between
+	transferer or transferee should not be cancelled, so the
+	call is recoverable if the transfer target can not be reached
+	by the transferee.
 
-					/* Unlock locks so ast_hangup can do its magic */
-					ast_channel_unlock(c);
-					sip_pvt_unlock(p);
-					ast_hangup(c);
-					sip_pvt_lock(p);
-					c = NULL;
-				}
-			} else {	/* Pickup call in call group */
-				ast_channel_unlock(c);
-				*nounlock = 1;
-				if (ast_pickup_call(c)) {
-					ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid);
-					transmit_response_reliable(p, "503 Unavailable", req);
-					sip_alreadygone(p);
-					/* Unlock locks so ast_hangup can do its magic */
-					sip_pvt_unlock(p);
-					c->hangupcause = AST_CAUSE_CALL_REJECTED;
-				} else {
-					sip_pvt_unlock(p);
-					c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
-				}
-				p->invitestate = INV_COMPLETED;
-				ast_hangup(c);
-				sip_pvt_lock(p);
-				c = NULL;
-			}
-			break;
-		case AST_STATE_RING:
-			transmit_provisional_response(p, "100 Trying", req, 0);
-			p->invitestate = INV_PROCEEDING;
-			break;
-		case AST_STATE_RINGING:
-			transmit_provisional_response(p, "180 Ringing", req, 0);
-			p->invitestate = INV_PROCEEDING;
-			break;
-		case AST_STATE_UP:
-			ast_debug(2, "%s: This call is UP.... \n", c->name);
+	In this case, Asterisk receives a TRANSFER from
+	the transferor, thus is the transferee. We should
+	try to set up a call to the contact provided
+	and if that fails, re-connect the current session.
+	If the new call is set up, we issue a hangup.
+	In this scenario, we are following section 5.2
+	in the SIP CC Transfer draft. (Transfer without
+	a GRUU)
+
+	\par Transfer with consultation hold
+	In this case, the transferor
+	talks to the transfer target before the transfer takes place.
+	This is implemented with SIP hold and transfer.
+	Note: The invite From: string could indicate a transfer.
+	(Section 6. Transfer with consultation hold)
+	The transferor places the transferee on hold, starts a call
+	with the transfer target to alert them to the impending
+	transfer, terminates the connection with the target, then
+	proceeds with the transfer (as in Blind transfer above)
+
+	\par Attended transfer
+	The transferor places the transferee
+	on hold, calls the transfer target to alert them,
+	places the target on hold, then proceeds with the transfer
+	using a Replaces header field in the Refer-to header. This
+	will force the transfee to send an Invite to the target,
+	with a replaces header that instructs the target to
+	hangup the call between the transferor and the target.
+	In this case, the Refer/to: uses the AOR address. (The same
+	URI that the transferee used to establish the session with
+	the transfer target (To: ). The Require: replaces header should
+	be in the INVITE to avoid the wrong UA in a forked SIP proxy
+	scenario to answer and have no call to replace with.
+
+	The referred-by header is *NOT* required, but if we get it,
+	can be copied into the INVITE to the transfer target to
+	inform the target about the transferor
+
+	"Any REFER request has to be appropriately authenticated.".
+	
+	We can't destroy dialogs, since we want the call to continue.
+	
+	*/
+static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock)
+{
+	struct sip_dual current;	/* Chan1: Call between asterisk and transferer */
+					/* Chan2: Call between asterisk and transferee */
+
+	int res = 0;
+	struct ast_channel *chans[2];
+	current.req.data = NULL;
+
+	if (req->debug)
+		ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
+
+	if (!p->owner) {
+		/* This is a REFER outside of an existing SIP dialog */
+		/* We can't handle that, so decline it */
+		ast_debug(3, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
+		transmit_response(p, "603 Declined (No dialog)", req);
+		if (!req->ignore) {
+			append_history(p, "Xfer", "Refer failed. Outside of dialog.");
+			sip_alreadygone(p);
+			pvt_set_needdestroy(p, "outside of dialog");
+		}
+		return 0;
+	}
+
+
+	/* Check if transfer is allowed from this device */
+	if (p->allowtransfer == TRANSFER_CLOSED ) {
+		/* Transfer not allowed, decline */
+		transmit_response(p, "603 Declined (policy)", req);
+		append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
+		/* Do not destroy SIP session */
+		return 0;
+	}
 
-			transmit_response(p, "100 Trying", req);
+	if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
+		/* Already have a pending REFER */
+		transmit_response(p, "491 Request pending", req);
+		append_history(p, "Xfer", "Refer failed. Request pending.");
+		return 0;
+	}
 
-			if (p->t38.state == T38_PEER_REINVITE) {
-				p->t38id = ast_sched_add(sched, 5000, sip_t38_abort, dialog_ref(p, "passing dialog ptr into sched structure based on t38id for sip_t38_abort."));
-			} else if (p->t38.state == T38_ENABLED) {
-				ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
-				transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ?  XMIT_UNRELIABLE : XMIT_CRITICAL)));
-			} else if (p->t38.state == T38_DISABLED) {
-				/* If this is not a re-invite or something to ignore - it's critical */
-				ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
-				transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ?  XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE);
-			}
+	/* Allocate memory for call transfer data */
+	if (!p->refer && !sip_refer_allocate(p)) {
+		transmit_response(p, "500 Internal Server Error", req);
+		append_history(p, "Xfer", "Refer failed. Memory allocation error.");
+		return -3;
+	}
 
-			p->invitestate = INV_TERMINATED;
+	res = get_refer_info(p, req);	/* Extract headers */
+
+	p->refer->status = REFER_SENT;
+
+	if (res != 0) {
+		switch (res) {
+		case -2:	/* Syntax error */
+			transmit_response(p, "400 Bad Request (Refer-to missing)", req);
+			append_history(p, "Xfer", "Refer failed. Refer-to missing.");
+			if (req->debug)
+				ast_debug(1, "SIP transfer to black hole can't be handled (no refer-to: )\n");
+			break;
+		case -3:
+			transmit_response(p, "603 Declined (Non sip: uri)", req);
+			append_history(p, "Xfer", "Refer failed. Non SIP uri");
+			if (req->debug)
+				ast_debug(1, "SIP transfer to non-SIP uri denied\n");
 			break;
 		default:
-			ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state);
-			transmit_response(p, "100 Trying", req);
+			/* Refer-to extension not found, fake a failed transfer */
+			transmit_response(p, "202 Accepted", req);
+			append_history(p, "Xfer", "Refer failed. Bad extension.");
+			transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
+			ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
+			if (req->debug)
+				ast_debug(1, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
 			break;
 		}
-	} else {
-		if (p && (p->autokillid == -1)) {
-			const char *msg;
-
-			if (!p->jointcapability)
-				msg = "488 Not Acceptable Here (codec error)";
-			else {
-				ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n");
-				msg = "503 Unavailable";
-			}
-			transmit_response_reliable(p, msg, req);
-			p->invitestate = INV_COMPLETED;
-			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-		}
+		return 0;
 	}
+	if (ast_strlen_zero(p->context))
+		ast_string_field_set(p, context, sip_cfg.default_context);
 
-request_invite_cleanup:
+	/* If we do not support SIP domains, all transfers are local */
+	if (sip_cfg.allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
+		p->refer->localtransfer = 1;
+		if (sipdebug)
+			ast_debug(3, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
+	} else if (AST_LIST_EMPTY(&domain_list) || check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
+		/* This PBX doesn't bother with SIP domains or domain is local, so this transfer is local */
+		p->refer->localtransfer = 1;
+	} else if (sipdebug)
+			ast_debug(3, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
 
-	if (refer_locked && p->refer && p->refer->refer_call) {
-		sip_pvt_unlock(p->refer->refer_call);
-		if (p->refer->refer_call->owner) {
-			ast_channel_unlock(p->refer->refer_call->owner);
-		}
+	/* Is this a repeat of a current request? Ignore it */
+	/* Don't know what else to do right now. */
+	if (req->ignore)
+		return res;
+
+	/* If this is a blind transfer, we have the following
+	channels to work with:
+	- chan1, chan2: The current call between transferer and transferee (2 channels)
+	- target_channel: A new call from the transferee to the target (1 channel)
+	We need to stay tuned to what happens in order to be able
+	to bring back the call to the transferer */
+
+	/* If this is a attended transfer, we should have all call legs within reach:
+	- chan1, chan2: The call between the transferer and transferee (2 channels)
+	- target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
+	We want to bridge chan2 with targetcall_pvt!
+	
+	The replaces call id in the refer message points
+	to the call leg between Asterisk and the transferer.
+	So we need to connect the target and the transferee channel
+	and hangup the two other channels silently
+	
+	If the target is non-local, the call ID could be on a remote
+	machine and we need to send an INVITE with replaces to the
+	target. We basically handle this as a blind transfer
+	and let the sip_call function catch that we need replaces
+	header in the INVITE.
+	*/
+
+
+	/* Get the transferer's channel */
+	chans[0] = current.chan1 = p->owner;
+
+	/* Find the other part of the bridge (2) - transferee */
+	chans[1] = current.chan2 = ast_bridged_channel(current.chan1);
+
+	if (sipdebug)
+		ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<none>");
+
+	if (!current.chan2 && !p->refer->attendedtransfer) {
+		/* No bridged channel, propably IVR or echo or similar... */
+		/* Guess we should masquerade or something here */
+		/* Until we figure it out, refuse transfer of such calls */
+		if (sipdebug)
+			ast_debug(3, "Refused SIP transfer on non-bridged channel.\n");
+		p->refer->status = REFER_FAILED;
+		append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
+		transmit_response(p, "603 Declined", req);
+		return -1;
 	}
 
-	return res;
-}
+	if (current.chan2) {
+		if (sipdebug)
+			ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
 
-/*! \brief  Find all call legs and bridge transferee with target
- *	called from handle_request_refer
- *
- *	\note this function assumes two locks to begin with, sip_pvt transferer and current.chan1 (the pvt's owner)... 
- *	2 additional locks are held at the beginning of the function, targetcall_pvt, and targetcall_pvt's owner
- *	channel (which is stored in target.chan1).  These 2 locks _MUST_ be let go by the end of the function.  Do
- *	not be confused into thinking a pvt's owner is the same thing as the channels locked at the beginning of
- *	this function, after the masquerade this may not be true.  Be consistent and unlock only the exact same
- *	pointers that were locked to begin with.
- *
- *	If this function is successful, only the transferer pvt lock will remain on return.  Setting nounlock indicates
- *	to handle_request_do() that the pvt's owner it locked does not require an unlock.
- */
-static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock)
-{
-	struct sip_dual target;		/* Chan 1: Call from tranferer to Asterisk */
-					/* Chan 2: Call from Asterisk to target */
-	int res = 0;
-	struct sip_pvt *targetcall_pvt;
-	struct ast_party_connected_line connected_to_transferee;
-	struct ast_party_connected_line connected_to_target;
-	char transferer_linkedid[32];
-	struct ast_channel *chans[2];
+		ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
+	}
 
-	/* Check if the call ID of the replaces header does exist locally */
-	if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
-		transferer->refer->replaces_callid_fromtag))) {
-		if (transferer->refer->localtransfer) {
-			/* We did not find the refered call. Sorry, can't accept then */
-			transmit_response(transferer, "202 Accepted", req);
-			/* Let's fake a response from someone else in order
-		   	to follow the standard */
-			transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
-			append_history(transferer, "Xfer", "Refer failed");
-			ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
-			transferer->refer->status = REFER_FAILED;
-			return -1;
-		}
+	ast_set_flag(&p->flags[0], SIP_GOTREFER);
+
+	/* Attended transfer: Find all call legs and bridge transferee with target*/
+	if (p->refer->attendedtransfer) {
+		if ((res = local_attended_transfer(p, &current, req, seqno, nounlock)))
+			return res;	/* We're done with the transfer */
 		/* Fall through for remote transfers that we did not find locally */
-		ast_debug(3, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
-		return 0;
+		if (sipdebug)
+			ast_debug(4, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
+		/* Fallthrough if we can't find the call leg internally */
 	}
 
-	/* Ok, we can accept this transfer */
-	transmit_response(transferer, "202 Accepted", req);
-	append_history(transferer, "Xfer", "Refer accepted");
-	if (!targetcall_pvt->owner) {	/* No active channel */
-		ast_debug(4, "SIP attended transfer: Error: No owner of target call\n");
-		/* Cancel transfer */
-		transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
-		append_history(transferer, "Xfer", "Refer failed");
-		ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
-		transferer->refer->status = REFER_FAILED;
-		sip_pvt_unlock(targetcall_pvt);
-		if (targetcall_pvt)
-			ao2_t_ref(targetcall_pvt, -1, "Drop targetcall_pvt pointer");
-		return -1;
+
+	/* Parking a call */
+	if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) {
+		/* Must release c's lock now, because it will not longer be accessible after the transfer! */
+		*nounlock = 1;
+		ast_channel_unlock(current.chan1);
+		copy_request(&current.req, req);
+		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+		p->refer->status = REFER_200OK;
+		append_history(p, "Xfer", "REFER to call parking.");
+		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransfer2Parking: Yes\r\n",
+			current.chan1->name,
+			current.chan1->uniqueid,
+			p->callid,
+			current.chan2->name,
+			current.chan2->uniqueid,
+			p->refer->refer_to);
+		if (sipdebug)
+			ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name);
+		sip_park(current.chan2, current.chan1, req, seqno);
+		return res;
+	}
+
+	/* Blind transfers and remote attended xfers */
+	transmit_response(p, "202 Accepted", req);
+
+	if (current.chan1 && current.chan2) {
+		ast_debug(3, "chan1->name: %s\n", current.chan1->name);
+		pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
+	}
+	if (current.chan2) {
+		pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name);
+		pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->refer_to_domain);
+		pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
+		/* One for the new channel */
+		pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
+		/* Attended transfer to remote host, prepare headers for the INVITE */
+		if (p->refer->referred_by)
+			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
+	}
+	/* Generate a Replaces string to be used in the INVITE during attended transfer */
+	if (!ast_strlen_zero(p->refer->replaces_callid)) {
+		char tempheader[SIPBUFSIZE];
+		snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
+				p->refer->replaces_callid_totag ? ";to-tag=" : "",
+				p->refer->replaces_callid_totag,
+				p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
+				p->refer->replaces_callid_fromtag);
+		if (current.chan2)
+			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
 	}
+	/* Must release lock now, because it will not longer
+	   be accessible after the transfer! */
+	*nounlock = 1;
+	/*
+	 * Increase ref count so that we can delay channel destruction until after
+	 * we get a chance to fire off some events.
+	 */
+	ast_channel_ref(current.chan1);
+	ast_channel_unlock(current.chan1);
 
-	/* We have a channel, find the bridge */
-	target.chan1 = targetcall_pvt->owner;				/* Transferer to Asterisk */
-	target.chan2 = ast_bridged_channel(targetcall_pvt->owner);	/* Asterisk to target */
+	/* Connect the call */
 
-	if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
-		/* Wrong state of new channel */
-		if (target.chan2)
-			ast_debug(4, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state));
-		else if (target.chan1->_state != AST_STATE_RING)
-			ast_debug(4, "SIP attended transfer: Error: No target channel\n");
-		else
-			ast_debug(4, "SIP attended transfer: Attempting transfer in ringing state\n");
-	}
+	/* FAKE ringing if not attended transfer */
+	if (!p->refer->attendedtransfer)
+		transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
 
-	/* Transfer */
-	if (sipdebug) {
-		if (current->chan2)	/* We have two bridges */
-			ast_debug(4, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name);
-		else			/* One bridge, propably transfer of IVR/voicemail etc */
-			ast_debug(4, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name);
+	/* For blind transfer, this will lead to a new call */
+	/* For attended transfer to remote host, this will lead to
+	   a new SIP call with a replaces header, if the dial plan allows it
+	*/
+	if (!current.chan2) {
+		/* We have no bridge, so we're talking with Asterisk somehow */
+		/* We need to masquerade this call */
+		/* What to do to fix this situation:
+		   * Set up the new call in a new channel
+		   * Let the new channel masq into this channel
+		   Please add that code here :-)
+		*/
+		p->refer->status = REFER_FAILED;
+		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
+		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
+		append_history(p, "Xfer", "Refer failed (only bridged calls).");
+		ast_channel_unref(current.chan1);
+		return -1;
 	}
+	ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */
 
-	ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */
-
-	ast_copy_string(transferer_linkedid, transferer->owner->linkedid, sizeof(transferer_linkedid));
 
-	/* Perform the transfer */
-	chans[0] = transferer->owner;
-	chans[1] = target.chan1;
-	ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n",
-		transferer->owner->name,
-		transferer->owner->uniqueid,
-		transferer->callid,
-		target.chan1->name,
-		target.chan1->uniqueid);
-	ast_party_connected_line_init(&connected_to_transferee);
-	ast_party_connected_line_init(&connected_to_target);
-	/* No need to lock current->chan1 here since it was locked in sipsock_read */
-	ast_party_connected_line_copy(&connected_to_transferee, &current->chan1->connected);
-	/* No need to lock target.chan1 here since it was locked in get_sip_pvt_byid_locked */
-	ast_party_connected_line_copy(&connected_to_target, &target.chan1->connected);
-	connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-	res = attempt_transfer(current, &target);
-	if (res) {
-		/* Failed transfer */
-		transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE);
-		append_history(transferer, "Xfer", "Refer failed");
-		ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
-		/* if transfer failed, go ahead and unlock targetcall_pvt and it's owner channel */
-		sip_pvt_unlock(targetcall_pvt);
-		ast_channel_unlock(target.chan1);
-	} else {
-		/* Transfer succeeded! */
-		const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND");
+	/* For blind transfers, move the call to the new extensions. For attended transfers on multiple
+	   servers - generate an INVITE with Replaces. Either way, let the dial plan decided  */
+	res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
 
-		/* target.chan1 was locked in get_sip_pvt_byid_locked, do not unlock target.chan1 before this */
-		ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2);
+	if (!res) {
+		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransferContext: %s\r\n",
+			current.chan1->name,
+			current.chan1->uniqueid,
+			p->callid,
+			current.chan2->name,
+			current.chan2->uniqueid,
+			p->refer->refer_to, p->refer->refer_to_context);
+		/* Success  - we have a new channel */
+		ast_debug(3, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
 
-		/* Tell transferer that we're done. */
-		transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
-		append_history(transferer, "Xfer", "Refer succeeded");
-		transferer->refer->status = REFER_200OK;
-		if (target.chan2 && !ast_strlen_zero(xfersound) && ast_streamfile(target.chan2, xfersound, target.chan2->language) >= 0) {
-			ast_waitstream(target.chan2, "");
+		while (ast_channel_trylock(current.chan1)) {
+			sip_pvt_unlock(p);
+			sched_yield();
+			sip_pvt_lock(p);
 		}
 
-		/* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then
-		 * can queue connected line updates where they need to go.
-		 *
-		 * before a masquerade, all channel and pvt locks must be unlocked.  Any recursive
-		 * channel locks held before this function invalidates channel container locking order.
-		 * Since we are unlocking both the pvt (transferer) and its owner channel (current.chan1)
-		 * it is possible for current.chan1 to be destroyed in the pbx thread.  To prevent this
-		 * we must give c a reference before any unlocking takes place.
-		 */
+		/* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */
+		ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2);
+		ast_channel_unlock(current.chan1);
 
-		ast_channel_ref(current->chan1);
-		ast_channel_unlock(current->chan1); /* current.chan1 is p->owner before the masq, it was locked by socket_read()*/
-		ast_channel_unlock(target.chan1);
-		*nounlock = 1;  /* we just unlocked the dialog's channel and have no plans of locking it again. */
-		sip_pvt_unlock(targetcall_pvt);
-		sip_pvt_unlock(transferer);
+		transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
+		if (p->refer->localtransfer)
+			p->refer->status = REFER_200OK;
+		if (p->owner)
+			p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+		append_history(p, "Xfer", "Refer succeeded.");
+		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
+		/* Do not hangup call, the other side do that when we say 200 OK */
+		/* We could possibly implement a timer here, auto congestion */
+		res = 0;
+	} else {
+		ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Don't delay hangup */
+		ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
+		append_history(p, "Xfer", "Refer failed.");
+		/* Failure of some kind */
+		p->refer->status = REFER_FAILED;
+		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
+		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
+		res = -1;
+	}
 
-		ast_do_masquerade(target.chan1);
+	ast_channel_unref(current.chan1);
 
-		ast_channel_lock(transferer); /* the transferer pvt is expected to remain locked on return */
+	return res;
+}
 
-		ast_indicate(target.chan1, AST_CONTROL_UNHOLD);
+/*! \brief Handle incoming CANCEL request */
+static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
+{
+		
+	check_via(p, req);
+	sip_alreadygone(p);
 
-		if (target.chan2) {
-			ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee);
-			ast_channel_queue_connected_line_update(target.chan2, &connected_to_target);
-		} else {
-			/* Since target.chan1 isn't actually connected to another channel, there is no way for us
-			 * to queue a frame so that its connected line status will be updated. Instead, we have to
-			 * change it directly. Since we are not the channel thread, we cannot run a connected line
-			 * interception macro on target.chan1
-			 */
-			ast_channel_update_connected_line(target.chan1, &connected_to_target);
-		}
-		ast_channel_unref(current->chan1);
+	/* At this point, we could have cancelled the invite at the same time
+	   as the other side sends a CANCEL. Our final reply with error code
+	   might not have been received by the other side before the CANCEL
+	   was sent, so let's just give up retransmissions and waiting for
+	   ACK on our error code. The call is hanging up any way. */
+	if (p->invitestate == INV_TERMINATED)
+		__sip_pretend_ack(p);
+	else
+		p->invitestate = INV_CANCELLED;
+	
+	if (p->owner && p->owner->_state == AST_STATE_UP) {
+		/* This call is up, cancel is ignored, we need a bye */
+		transmit_response(p, "200 OK", req);
+		ast_debug(1, "Got CANCEL on an answered call. Ignoring... \n");
+		return 0;
 	}
 
-	/* at this point if the transfer is successful only the transferer pvt should be locked. */
-	ast_party_connected_line_free(&connected_to_target);
-	ast_party_connected_line_free(&connected_to_transferee);
-	if (targetcall_pvt)
-		ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt");
-	return 1;
+	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD))
+		update_call_counter(p, DEC_CALL_LIMIT);
+
+	stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
+	if (p->owner) {
+		ast_set_hangupsource(p->owner, p->owner->name, 0);
+		ast_queue_hangup(p->owner);
+	}
+	else
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	if (p->initreq.len > 0) {
+		struct sip_pkt *pkt, *prev_pkt;
+		/* If the CANCEL we are receiving is a retransmission, and we already have scheduled
+		 * a reliable 487, then we don't want to schedule another one on top of the previous
+		 * one.
+		 *
+		 * As odd as this may sound, we can't rely on the previously-transmitted "reliable"
+		 * response in this situation. What if we've sent all of our reliable responses
+		 * already and now all of a sudden, we get this second CANCEL?
+		 *
+		 * The only way to do this correctly is to cancel our previously-scheduled reliably-
+		 * transmitted response and send a new one in its place.
+		 */
+		for (pkt = p->packets, prev_pkt = NULL; pkt; prev_pkt = pkt, pkt = pkt->next) {
+			if (pkt->seqno == p->lastinvite && pkt->response_code == 487) {
+				AST_SCHED_DEL(sched, pkt->retransid);
+				UNLINK(pkt, p->packets, prev_pkt);
+				ast_free(pkt);
+				break;
+			}
+		}
+		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
+		transmit_response(p, "200 OK", req);
+		return 1;
+	} else {
+		transmit_response(p, "481 Call Leg Does Not Exist", req);
+		return 0;
+	}
 }
 
+/*! \brief Handle incoming BYE request */
+static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
+{
+	struct ast_channel *c=NULL;
+	int res;
+	struct ast_channel *bridged_to;
+	
+	/* If we have an INCOMING invite that we haven't answered, terminate that transaction */
+	if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore) {
+		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
+	}
 
-/*! \brief Handle incoming REFER request */
-/*! \page SIP_REFER SIP transfer Support (REFER)
+	__sip_pretend_ack(p);
 
-	REFER is used for call transfer in SIP. We get a REFER
-	to place a new call with an INVITE somwhere and then
-	keep the transferor up-to-date of the transfer. If the
-	transfer fails, get back on line with the orginal call.
+	p->invitestate = INV_TERMINATED;
 
-	- REFER can be sent outside or inside of a dialog.
-	  Asterisk only accepts REFER inside of a dialog.
+	copy_request(&p->initreq, req);
+	if (sipdebug)
+		ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+	check_via(p, req);
+	sip_alreadygone(p);
 
-	- If we get a replaces header, it is an attended transfer
+	/* Get RTCP quality before end of call */
+	if (p->do_history || p->owner) {
+		char quality_buf[AST_MAX_USER_FIELD], *quality;
+		struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL;
 
-	\par Blind transfers
-	The transferor provides the transferee
-	with the transfer targets contact. The signalling between
-	transferer or transferee should not be cancelled, so the
-	call is recoverable if the transfer target can not be reached
-	by the transferee.
+		/* We need to get the lock on bridge because ast_rtp_instance_set_stats_vars will attempt
+		 * to lock the bridge. This may get hairy...
+		 */
+		while (bridge && ast_channel_trylock(bridge)) {
+			ast_channel_unlock(p->owner);
+			do {
+				/* Can't use DEADLOCK_AVOIDANCE since p is an ao2 object */
+				sip_pvt_unlock(p);
+				usleep(1);
+				sip_pvt_lock(p);
+			} while (p->owner && ast_channel_trylock(p->owner));
+			bridge = p->owner ? ast_bridged_channel(p->owner) : NULL;
+		}
 
-	In this case, Asterisk receives a TRANSFER from
-	the transferor, thus is the transferee. We should
-	try to set up a call to the contact provided
-	and if that fails, re-connect the current session.
-	If the new call is set up, we issue a hangup.
-	In this scenario, we are following section 5.2
-	in the SIP CC Transfer draft. (Transfer without
-	a GRUU)
 
-	\par Transfer with consultation hold
-	In this case, the transferor
-	talks to the transfer target before the transfer takes place.
-	This is implemented with SIP hold and transfer.
-	Note: The invite From: string could indicate a transfer.
-	(Section 6. Transfer with consultation hold)
-	The transferor places the transferee on hold, starts a call
-	with the transfer target to alert them to the impending
-	transfer, terminates the connection with the target, then
-	proceeds with the transfer (as in Blind transfer above)
+		if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
+			if (p->do_history) {
+				append_history(p, "RTCPaudio", "Quality:%s", quality);
 
-	\par Attended transfer
-	The transferor places the transferee
-	on hold, calls the transfer target to alert them,
-	places the target on hold, then proceeds with the transfer
-	using a Replaces header field in the Refer-to header. This
-	will force the transfee to send an Invite to the target,
-	with a replaces header that instructs the target to
-	hangup the call between the transferor and the target.
-	In this case, the Refer/to: uses the AOR address. (The same
-	URI that the transferee used to establish the session with
-	the transfer target (To: ). The Require: replaces header should
-	be in the INVITE to avoid the wrong UA in a forked SIP proxy
-	scenario to answer and have no call to replace with.
+				if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) {
+					append_history(p, "RTCPaudioJitter", "Quality:%s", quality);
+				}
+				if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) {
+					append_history(p, "RTCPaudioLoss", "Quality:%s", quality);
+				}
+				if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) {
+					append_history(p, "RTCPaudioRTT", "Quality:%s", quality);
+				}
+			}
 
-	The referred-by header is *NOT* required, but if we get it,
-	can be copied into the INVITE to the transfer target to
-	inform the target about the transferor
+			if (p->owner) {
+				ast_rtp_instance_set_stats_vars(p->owner, p->rtp);
+			}
 
-	"Any REFER request has to be appropriately authenticated.".
-	
-	We can't destroy dialogs, since we want the call to continue.
-	
-	*/
-static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock)
-{
-	struct sip_dual current;	/* Chan1: Call between asterisk and transferer */
-					/* Chan2: Call between asterisk and transferee */
+		}
 
-	int res = 0;
-	struct ast_channel *chans[2];
-	current.req.data = NULL;
+		if (bridge) {
+			struct sip_pvt *q = bridge->tech_pvt;
 
-	if (req->debug)
-		ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
+			if (IS_SIP_TECH(bridge->tech) && q && q->rtp) {
+				ast_rtp_instance_set_stats_vars(bridge, q->rtp);
+			}
+			ast_channel_unlock(bridge);
+		}
 
-	if (!p->owner) {
-		/* This is a REFER outside of an existing SIP dialog */
-		/* We can't handle that, so decline it */
-		ast_debug(3, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
-		transmit_response(p, "603 Declined (No dialog)", req);
-		if (!req->ignore) {
-			append_history(p, "Xfer", "Refer failed. Outside of dialog.");
-			sip_alreadygone(p);
-			pvt_set_needdestroy(p, "outside of dialog");
+		if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
+			if (p->do_history) {
+				append_history(p, "RTCPvideo", "Quality:%s", quality);
+			}
+			if (p->owner) {
+				pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", quality);
+			}
+		}
+		if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
+			if (p->do_history) {
+				append_history(p, "RTCPtext", "Quality:%s", quality);
+			}
+			if (p->owner) {
+				pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", quality);
+			}
 		}
-		return 0;
 	}
 
+	stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
+	stop_session_timer(p); /* Stop Session-Timer */
 
-	/* Check if transfer is allowed from this device */
-	if (p->allowtransfer == TRANSFER_CLOSED ) {
-		/* Transfer not allowed, decline */
-		transmit_response(p, "603 Declined (policy)", req);
-		append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
-		/* Do not destroy SIP session */
-		return 0;
+	if (!ast_strlen_zero(get_header(req, "Also"))) {
+		ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method.  Ask vendor to support REFER instead\n",
+			ast_inet_ntoa(p->recv.sin_addr));
+		if (ast_strlen_zero(p->context))
+			ast_string_field_set(p, context, sip_cfg.default_context);
+		res = get_also_info(p, req);
+		if (!res) {
+			c = p->owner;
+			if (c) {
+				bridged_to = ast_bridged_channel(c);
+				if (bridged_to) {
+					/* Don't actually hangup here... */
+					ast_queue_control(c, AST_CONTROL_UNHOLD);
+					ast_channel_unlock(c);  /* async_goto can do a masquerade, no locks can be held during a masq */
+					ast_async_goto(bridged_to, p->context, p->refer->refer_to, 1);
+					ast_channel_lock(c);
+				} else
+					ast_queue_hangup(p->owner);
+			}
+		} else {
+			ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
+			if (p->owner)
+				ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
+		}
+	} else if (p->owner) {
+		ast_set_hangupsource(p->owner, p->owner->name, 0);
+		ast_queue_hangup(p->owner);
+		ast_debug(3, "Received bye, issuing owner hangup\n");
+	} else {
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
 	}
+	ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+	transmit_response(p, "200 OK", req);
 
-	if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
-		/* Already have a pending REFER */
-		transmit_response(p, "491 Request pending", req);
-		append_history(p, "Xfer", "Refer failed. Request pending.");
-		return 0;
-	}
+	return 1;
+}
 
-	/* Allocate memory for call transfer data */
-	if (!p->refer && !sip_refer_allocate(p)) {
-		transmit_response(p, "500 Internal Server Error", req);
-		append_history(p, "Xfer", "Refer failed. Memory allocation error.");
-		return -3;
-	}
+/*! \brief Handle incoming MESSAGE request */
+static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
+{
+	if (!req->ignore) {
+		if (req->debug)
+			ast_verbose("Receiving message!\n");
+		receive_message(p, req);
+	} else
+		transmit_response(p, "202 Accepted", req);
+	return 1;
+}
 
-	res = get_refer_info(p, req);	/* Extract headers */
+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;
 
-	p->refer->status = REFER_SENT;
+	ast_assert(expires_int != NULL);
 
-	if (res != 0) {
-		switch (res) {
-		case -2:	/* Syntax error */
-			transmit_response(p, "400 Bad Request (Refer-to missing)", req);
-			append_history(p, "Xfer", "Refer failed. Refer-to missing.");
-			if (req->debug)
-				ast_debug(1, "SIP transfer to black hole can't be handled (no refer-to: )\n");
-			break;
-		case -3:
-			transmit_response(p, "603 Declined (Non sip: uri)", req);
-			append_history(p, "Xfer", "Refer failed. Non SIP uri");
-			if (req->debug)
-				ast_debug(1, "SIP transfer to non-SIP uri denied\n");
-			break;
-		default:
-			/* Refer-to extension not found, fake a failed transfer */
-			transmit_response(p, "202 Accepted", req);
-			append_history(p, "Xfer", "Refer failed. Bad extension.");
-			transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
-			ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
-			if (req->debug)
-				ast_debug(1, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
-			break;
-		}
-		return 0;
+	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 (ast_strlen_zero(p->context))
-		ast_string_field_set(p, context, sip_cfg.default_context);
-
-	/* If we do not support SIP domains, all transfers are local */
-	if (sip_cfg.allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
-		p->refer->localtransfer = 1;
-		if (sipdebug)
-			ast_debug(3, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
-	} else if (AST_LIST_EMPTY(&domain_list) || check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
-		/* This PBX doesn't bother with SIP domains or domain is local, so this transfer is local */
-		p->refer->localtransfer = 1;
-	} else if (sipdebug)
-			ast_debug(3, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
 
-	/* Is this a repeat of a current request? Ignore it */
-	/* Don't know what else to do right now. */
-	if (req->ignore)
-		return res;
+	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;
+	}
 
-	/* If this is a blind transfer, we have the following
-	channels to work with:
-	- chan1, chan2: The current call between transferer and transferee (2 channels)
-	- target_channel: A new call from the transferee to the target (1 channel)
-	We need to stay tuned to what happens in order to be able
-	to bring back the call to the transferer */
+	return SIP_PUBLISH_UNKNOWN;
+}
 
-	/* If this is a attended transfer, we should have all call legs within reach:
-	- chan1, chan2: The call between the transferer and transferee (2 channels)
-	- target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
-	We want to bridge chan2 with targetcall_pvt!
-	
-	The replaces call id in the refer message points
-	to the call leg between Asterisk and the transferer.
-	So we need to connect the target and the transferee channel
-	and hangup the two other channels silently
-	
-	If the target is non-local, the call ID could be on a remote
-	machine and we need to send an INVITE with replaces to the
-	target. We basically handle this as a blind transfer
-	and let the sip_call function catch that we need replaces
-	header in the INVITE.
-	*/
+#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;
+}
 
-	/* Get the transferer's channel */
-	chans[0] = current.chan1 = p->owner;
 
-	/* Find the other part of the bridge (2) - transferee */
-	chans[1] = current.chan2 = ast_bridged_channel(current.chan1);
+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 (sipdebug)
-		ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<none>");
+	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;
+	}
 
-	if (!current.chan2 && !p->refer->attendedtransfer) {
-		/* No bridged channel, propably IVR or echo or similar... */
-		/* Guess we should masquerade or something here */
-		/* Until we figure it out, refuse transfer of such calls */
-		if (sipdebug)
-			ast_debug(3, "Refused SIP transfer on non-bridged channel.\n");
-		p->refer->status = REFER_FAILED;
-		append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
-		transmit_response(p, "603 Declined", req);
-		return -1;
+	/* 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 (current.chan2) {
-		if (sipdebug)
-			ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
+	if (!(ns = ast_xml_find_namespace(doc, presence_node, NULL))) {
+		ast_log(LOG_WARNING, "Couldn't find default namespace...\n");
+		return FALSE;
+	}
 
-		ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
+	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;
 	}
 
-	ast_set_flag(&p->flags[0], SIP_GOTREFER);
+	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;
+	}
 
-	/* Attended transfer: Find all call legs and bridge transferee with target*/
-	if (p->refer->attendedtransfer) {
-		if ((res = local_attended_transfer(p, &current, req, seqno, nounlock)))
-			return res;	/* We're done with the transfer */
-		/* Fall through for remote transfers that we did not find locally */
-		if (sipdebug)
-			ast_debug(4, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
-		/* Fallthrough if we can't find the call leg internally */
+	/* 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;
+}
 
-	/* Parking a call */
-	if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) {
-		/* Must release c's lock now, because it will not longer be accessible after the transfer! */
-		*nounlock = 1;
-		ast_channel_unlock(current.chan1);
-		copy_request(&current.req, req);
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
-		p->refer->status = REFER_200OK;
-		append_history(p, "Xfer", "REFER to call parking.");
-		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransfer2Parking: Yes\r\n",
-			current.chan1->name,
-			current.chan1->uniqueid,
-			p->callid,
-			current.chan2->name,
-			current.chan2->uniqueid,
-			p->refer->refer_to);
-		if (sipdebug)
-			ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name);
-		sip_park(current.chan2, current.chan1, req, seqno);
-		return res;
+/*!
+ * \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;
 	}
 
-	/* Blind transfers and remote attended xfers */
-	transmit_response(p, "202 Accepted", req);
+	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 (current.chan1 && current.chan2) {
-		ast_debug(3, "chan1->name: %s\n", current.chan1->name);
-		pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
+	if (sscanf(content_length_str, "%30d", &content_length) != 1) {
+		ast_log(LOG_WARNING, "Invalid content length provided\n");
+		return FALSE;
 	}
-	if (current.chan2) {
-		pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name);
-		pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->refer_to_domain);
-		pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
-		/* One for the new channel */
-		pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
-		/* Attended transfer to remote host, prepare headers for the INVITE */
-		if (p->refer->referred_by)
-			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
+
+	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);
 	}
-	/* Generate a Replaces string to be used in the INVITE during attended transfer */
-	if (!ast_strlen_zero(p->refer->replaces_callid)) {
-		char tempheader[SIPBUFSIZE];
-		snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
-				p->refer->replaces_callid_totag ? ";to-tag=" : "",
-				p->refer->replaces_callid_totag,
-				p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
-				p->refer->replaces_callid_fromtag);
-		if (current.chan2)
-			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
+
+	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;
 	}
-	/* Must release lock now, because it will not longer
-	   be accessible after the transfer! */
-	*nounlock = 1;
-	/*
-	 * Increase ref count so that we can delay channel destruction until after
-	 * we get a chance to fire off some events.
-	 */
-	ast_channel_ref(current.chan1);
-	ast_channel_unlock(current.chan1);
 
-	/* Connect the call */
+	res = pidf_validate_presence(doc);
+	if (res == TRUE) {
+		*pidf_doc = doc;
+	} else {
+		ast_xml_close(doc);
+	}
+	return res;
+}
 
-	/* FAKE ringing if not attended transfer */
-	if (!p->refer->attendedtransfer)
-		transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
+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;
 
-	/* For blind transfer, this will lead to a new call */
-	/* For attended transfer to remote host, this will lead to
-	   a new SIP call with a replaces header, if the dial plan allows it
-	*/
-	if (!current.chan2) {
-		/* We have no bridge, so we're talking with Asterisk somehow */
-		/* We need to masquerade this call */
-		/* What to do to fix this situation:
-		   * Set up the new call in a new channel
-		   * Let the new channel masq into this channel
-		   Please add that code here :-)
-		*/
-		p->refer->status = REFER_FAILED;
-		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
-		append_history(p, "Xfer", "Refer failed (only bridged calls).");
-		ast_channel_unref(current.chan1);
+	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;
 	}
-	ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */
 
+	agent_pvt = agent->private_data;
 
-	/* For blind transfers, move the call to the new extensions. For attended transfers on multiple
-	   servers - generate an INVITE with Replaces. Either way, let the dial plan decided  */
-	res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
+	if (sip_pidf_validate(req, &pidf_doc) == FALSE) {
+		res = -1;
+		goto cc_publish_cleanup;
+	}
 
-	if (!res) {
-		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransferContext: %s\r\n",
-			current.chan1->name,
-			current.chan1->uniqueid,
-			p->callid,
-			current.chan2->name,
-			current.chan2->uniqueid,
-			p->refer->refer_to, p->refer->refer_to_context);
-		/* Success  - we have a new channel */
-		ast_debug(3, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
+	/* 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;
+	}
 
-		while (ast_channel_trylock(current.chan1)) {
-			sip_pvt_unlock(p);
-			sched_yield();
-			sip_pvt_lock(p);
-		}
+	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;
+	}
 
-		/* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */
-		ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2);
-		ast_channel_unlock(current.chan1);
+	/* 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);
 
-		transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
-		if (p->refer->localtransfer)
-			p->refer->status = REFER_200OK;
-		if (p->owner)
-			p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
-		append_history(p, "Xfer", "Refer succeeded.");
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
-		/* Do not hangup call, the other side do that when we say 200 OK */
-		/* We could possibly implement a timer here, auto congestion */
-		res = 0;
-	} else {
-		ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Don't delay hangup */
-		ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
-		append_history(p, "Xfer", "Refer failed.");
-		/* Failure of some kind */
-		p->refer->status = REFER_FAILED;
-		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
+	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;
 	}
 
-	ast_channel_unref(current.chan1);
+	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;
 }
 
-/*! \brief Handle incoming CANCEL request */
-static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
+#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)
 {
-		
-	check_via(p, req);
-	sip_alreadygone(p);
+	struct sip_esc_entry *esc_entry = create_esc_entry(esc, req, expires);
+	int res = 0;
 
-	/* At this point, we could have cancelled the invite at the same time
-	   as the other side sends a CANCEL. Our final reply with error code
-	   might not have been received by the other side before the CANCEL
-	   was sent, so let's just give up retransmissions and waiting for
-	   ACK on our error code. The call is hanging up any way. */
-	if (p->invitestate == INV_TERMINATED)
-		__sip_pretend_ack(p);
-	else
-		p->invitestate = INV_CANCELLED;
-	
-	if (p->owner && p->owner->_state == AST_STATE_UP) {
-		/* This call is up, cancel is ignored, we need a bye */
-		transmit_response(p, "200 OK", req);
-		ast_debug(1, "Got CANCEL on an answered call. Ignoring... \n");
-		return 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;
 	}
 
-	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD))
-		update_call_counter(p, DEC_CALL_LIMIT);
+	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));
 
-	stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
-	if (p->owner) {
-		ast_set_hangupsource(p->owner, p->owner->name, 0);
-		ast_queue_hangup(p->owner);
+	if (esc->callbacks->refresh_handler) {
+		res = esc->callbacks->refresh_handler(p, req, esc, esc_entry);
 	}
-	else
-		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-	if (p->initreq.len > 0) {
-		struct sip_pkt *pkt, *prev_pkt;
-		/* If the CANCEL we are receiving is a retransmission, and we already have scheduled
-		 * a reliable 487, then we don't want to schedule another one on top of the previous
-		 * one.
-		 *
-		 * As odd as this may sound, we can't rely on the previously-transmitted "reliable"
-		 * response in this situation. What if we've sent all of our reliable responses
-		 * already and now all of a sudden, we get this second CANCEL?
-		 *
-		 * The only way to do this correctly is to cancel our previously-scheduled reliably-
-		 * transmitted response and send a new one in its place.
-		 */
-		for (pkt = p->packets, prev_pkt = NULL; pkt; prev_pkt = pkt, pkt = pkt->next) {
-			if (pkt->seqno == p->lastinvite && pkt->response_code == 487) {
-				AST_SCHED_DEL(sched, pkt->retransid);
-				UNLINK(pkt, p->packets, prev_pkt);
-				ast_free(pkt);
-				break;
-			}
-		}
-		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
-		transmit_response(p, "200 OK", req);
-		return 1;
-	} else {
-		transmit_response(p, "481 Call Leg Does Not Exist", req);
-		return 0;
+
+	if (!res) {
+		transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
 	}
+
+	ao2_ref(esc_entry, -1);
+	return res;
 }
 
-/*! \brief Handle incoming BYE request */
-static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
+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 ast_channel *c=NULL;
-	int res;
-	struct ast_channel *bridged_to;
-	
-	/* If we have an INCOMING invite that we haven't answered, terminate that transaction */
-	if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore) {
-		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
-	}
-
-	__sip_pretend_ack(p);
+	struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
+	int expires_ms = expires * 1000;
+	int res = 0;
 
-	p->invitestate = INV_TERMINATED;
+	if (!esc_entry) {
+		transmit_response(p, "412 Conditional Request Failed", req);
+		return -1;
+	}
 
-	copy_request(&p->initreq, req);
-	if (sipdebug)
-		ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
-	check_via(p, req);
-	sip_alreadygone(p);
+	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));
 
-	/* Get RTCP quality before end of call */
-	if (p->do_history || p->owner) {
-		char quality_buf[AST_MAX_USER_FIELD], *quality;
-		struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL;
+	if (esc->callbacks->modify_handler) {
+		res = esc->callbacks->modify_handler(p, req, esc, esc_entry);
+	}
 
-		/* We need to get the lock on bridge because ast_rtp_instance_set_stats_vars will attempt
-		 * to lock the bridge. This may get hairy...
-		 */
-		while (bridge && ast_channel_trylock(bridge)) {
-			ast_channel_unlock(p->owner);
-			do {
-				/* Can't use DEADLOCK_AVOIDANCE since p is an ao2 object */
-				sip_pvt_unlock(p);
-				usleep(1);
-				sip_pvt_lock(p);
-			} while (p->owner && ast_channel_trylock(p->owner));
-			bridge = p->owner ? ast_bridged_channel(p->owner) : NULL;
-		}
+	if (!res) {
+		transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
+	}
 
+	ao2_ref(esc_entry, -1);
+	return res;
+}
 
-		if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
-			if (p->do_history) {
-				append_history(p, "RTCPaudio", "Quality:%s", quality);
+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 ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) {
-					append_history(p, "RTCPaudioJitter", "Quality:%s", quality);
-				}
-				if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) {
-					append_history(p, "RTCPaudioLoss", "Quality:%s", quality);
-				}
-				if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) {
-					append_history(p, "RTCPaudioRTT", "Quality:%s", quality);
-				}
-			}
+	if (!esc_entry) {
+		transmit_response(p, "412 Conditional Request Failed", req);
+		return -1;
+	}
 
-			if (p->owner) {
-				ast_rtp_instance_set_stats_vars(p->owner, p->rtp);
-			}
+	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 (bridge) {
-			struct sip_pvt *q = bridge->tech_pvt;
+	if (!res) {
+		transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
+	}
 
-			if (IS_SIP_TECH(bridge->tech) && q && q->rtp) {
-				ast_rtp_instance_set_stats_vars(bridge, q->rtp);
-			}
-			ast_channel_unlock(bridge);
-		}
+	/* Ref from finding the esc_entry earlier in function */
+	ao2_unlink(esc->compositor, esc_entry);
+	ao2_ref(esc_entry, -1);
+	return res;
+}
 
-		if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
-			if (p->do_history) {
-				append_history(p, "RTCPvideo", "Quality:%s", quality);
-			}
-			if (p->owner) {
-				pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", quality);
-			}
-		}
-		if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
-			if (p->do_history) {
-				append_history(p, "RTCPtext", "Quality:%s", quality);
-			}
-			if (p->owner) {
-				pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", quality);
-			}
-		}
+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;
 	}
 
-	stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
-	stop_session_timer(p); /* Stop Session-Timer */
+	if (!(esc = get_esc(event))) {
+		transmit_response(p, "489 Bad Event", req);
+		return -1;
+	}
 
-	if (!ast_strlen_zero(get_header(req, "Also"))) {
-		ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method.  Ask vendor to support REFER instead\n",
-			ast_inet_ntoa(p->recv.sin_addr));
-		if (ast_strlen_zero(p->context))
-			ast_string_field_set(p, context, sip_cfg.default_context);
-		res = get_also_info(p, req);
-		if (!res) {
-			c = p->owner;
-			if (c) {
-				bridged_to = ast_bridged_channel(c);
-				if (bridged_to) {
-					/* Don't actually hangup here... */
-					ast_queue_control(c, AST_CONTROL_UNHOLD);
-					ast_channel_unlock(c);  /* async_goto can do a masquerade, no locks can be held during a masq */
-					ast_async_goto(bridged_to, p->context, p->refer->refer_to, 1);
-					ast_channel_lock(c);
-				} else
-					ast_queue_hangup(p->owner);
-			}
+	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_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
-			if (p->owner)
-				ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
+			ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+			transmit_response_reliable(p, "403 Forbidden", req);
 		}
-	} else if (p->owner) {
-		ast_set_hangupsource(p->owner, p->owner->name, 0);
-		ast_queue_hangup(p->owner);
-		ast_debug(3, "Received bye, issuing owner hangup\n");
-	} else {
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-		ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
+		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);
 	}
-	ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
-	transmit_response(p, "200 OK", req);
 
-	return 1;
-}
+	publish_type = determine_sip_publish_type(req, event, etag, expires_str, &expires_int);
 
-/*! \brief Handle incoming MESSAGE request */
-static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
-{
-	if (!req->ignore) {
-		if (req->debug)
-			ast_verbose("Receiving message!\n");
-		receive_message(p, req);
-	} else
-		transmit_response(p, "202 Accepted", req);
-	return 1;
+	/* 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)
@@ -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 8e4aa3391d003c8ed94ff553c0425fdb2cfad115..f8b1ede45576867617f61f26dd2567c2421cf1af 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 33b642289d30e133f3f3f21060c17caa3894f4f4..57fc5c1f2bd439246542c7473052b86d8dc19ef5 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 fd3b4111e29c2117068a7181148b831b65edb447..5749e89b30610fdfd67dc35fa3389faba6d64c21 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,210 +1542,876 @@ static void sig_pri_lock_owner(struct sig_pri_pri *pri, int chanpos)
 	}
 }
 
+#if defined(HAVE_PRI_CCSS)
 /*!
  * \internal
- * \brief Handle the call associated PRI subcommand events.
+ * \brief Compare the CC agent private data by libpri cc_id.
  * \since 1.8
  *
- * \param pri sig_pri PRI control structure.
- * \param chanpos Channel position in the span.
- * \param event_id PRI event id
- * \param channel PRI encoded span/channel
- * \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.
- * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ * \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 Nothing
+ * \return values are a combination of enum _cb_results.
  */
-static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int event_id,
-	int channel, const struct pri_subcommands *subcmds, q931_call *call_rsp)
+static int sig_pri_cc_agent_cmp_cc_id(void *obj, void *arg, int flags)
 {
-	int index;
-	struct ast_channel *owner;
-	struct ast_party_redirecting ast_redirecting;
-
-	if (!subcmds) {
-		return;
-	}
-	for (index = 0; index < subcmds->counter_subcmd; ++index) {
-		const struct pri_subcommand *subcmd = &subcmds->subcmd[index];
-
-		switch (subcmd->cmd) {
-		case PRI_SUBCMD_CONNECTED_LINE:
-			sig_pri_lock_owner(pri, chanpos);
-			owner = pri->pvts[chanpos]->owner;
-			if (owner) {
-				struct ast_party_connected_line ast_connected;
-				int caller_id_update;
+	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;
 
-				/* Extract the connected line information */
-				ast_party_connected_line_init(&ast_connected);
-				sig_pri_party_id_convert(&ast_connected.id, &subcmd->u.connected_line.id,
-					pri);
+	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) */
 
-				caller_id_update = 0;
-				if (ast_connected.id.name) {
-					/* Save name for Caller-ID update */
-					ast_copy_string(pri->pvts[chanpos]->cid_name,
-						ast_connected.id.name, sizeof(pri->pvts[chanpos]->cid_name));
-					caller_id_update = 1;
-				}
-				if (ast_connected.id.number) {
-					/* Save number for Caller-ID update */
-					ast_copy_string(pri->pvts[chanpos]->cid_num, ast_connected.id.number,
-						sizeof(pri->pvts[chanpos]->cid_num));
-					pri->pvts[chanpos]->cid_ton = ast_connected.id.number_type;
-					caller_id_update = 1;
-				} else {
-					ast_connected.id.number = ast_strdup("");
-				}
-				ast_connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+#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,
+	};
 
-				pri->pvts[chanpos]->cid_subaddr[0] = '\0';
-#if defined(HAVE_PRI_SUBADDR)
-				if (ast_connected.id.subaddress.valid) {
-					ast_party_subaddress_set(&owner->cid.subaddress,
-						&ast_connected.id.subaddress);
-					if (ast_connected.id.subaddress.str) {
-						ast_copy_string(pri->pvts[chanpos]->cid_subaddr,
-							ast_connected.id.subaddress.str,
-							sizeof(pri->pvts[chanpos]->cid_subaddr));
-					}
-				}
-#endif	/* defined(HAVE_PRI_SUBADDR) */
-				if (caller_id_update) {
-					pri->pvts[chanpos]->callingpres =
-						ast_connected.id.number_presentation;
-					sig_pri_set_caller_id(pri->pvts[chanpos]);
-					ast_set_callerid(owner, S_OR(ast_connected.id.number, NULL),
-						S_OR(ast_connected.id.name, NULL),
-						S_OR(ast_connected.id.number, NULL));
-				}
+	return ast_cc_agent_callback(0, sig_pri_cc_agent_cmp_cc_id, &finder,
+		sig_pri_cc_type_name);
+}
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-				/* Update the connected line information on the other channel */
-				if (event_id != PRI_EVENT_RING) {
-					/* This connected_line update was not from a SETUP message. */
-					ast_channel_queue_connected_line_update(owner, &ast_connected);
-				}
+#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;
 
-				ast_party_connected_line_free(&ast_connected);
-				ast_channel_unlock(owner);
-			}
-			break;
-		case PRI_SUBCMD_REDIRECTING:
-			sig_pri_lock_owner(pri, chanpos);
-			owner = pri->pvts[chanpos]->owner;
-			if (owner) {
-				sig_pri_redirecting_convert(&ast_redirecting, &subcmd->u.redirecting,
-					&owner->redirecting, pri);
+	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) */
 
-/*! \todo XXX Original called data can be put in a channel data store that is inherited. */
+#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,
+	};
 
-				ast_channel_set_redirecting(owner, &ast_redirecting);
-				if (event_id != PRI_EVENT_RING) {
-					/* This redirection was not from a SETUP message. */
-					ast_channel_queue_redirecting_update(owner, &ast_redirecting);
-				}
-				ast_party_redirecting_free(&ast_redirecting);
+	return ao2_callback(sig_pri_cc_monitors, 0, sig_pri_cc_monitor_cmp_cc_id, &finder);
+}
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-				ast_channel_unlock(owner);
-			}
-			break;
-#if defined(HAVE_PRI_CALL_REROUTING)
-		case PRI_SUBCMD_REROUTING:
-			sig_pri_lock_owner(pri, chanpos);
-			owner = pri->pvts[chanpos]->owner;
-			if (owner) {
-				struct pri_party_redirecting pri_deflection;
+#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 (!call_rsp) {
-					ast_channel_unlock(owner);
-					ast_log(LOG_WARNING,
-						"CallRerouting/CallDeflection to '%s' without call!\n",
-						subcmd->u.rerouting.deflection.to.number.str);
-					break;
-				}
+	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) */
 
-				pri_deflection = subcmd->u.rerouting.deflection;
+#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;
 
-				ast_string_field_set(owner, call_forward, pri_deflection.to.number.str);
+	if (!pri->calls->module_ref || !pri->calls->module_unref) {
+		return NULL;
+	}
 
-				/* Adjust the deflecting to number based upon the subscription option. */
-				switch (subcmd->u.rerouting.subscription_option) {
-				case 0:	/* noNotification */
-				case 1:	/* notificationWithoutDivertedToNr */
-					/* Delete the number because the far end is not supposed to see it. */
-					pri_deflection.to.number.presentation =
-						PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED;
-					pri_deflection.to.number.plan =
-						(PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164;
-					pri_deflection.to.number.str[0] = '\0';
-					break;
-				case 2:	/* notificationWithDivertedToNr */
-					break;
-				case 3:	/* notApplicable */
-				default:
-					break;
-				}
-				sig_pri_redirecting_convert(&ast_redirecting, &pri_deflection,
-					&owner->redirecting, pri);
-				ast_channel_set_redirecting(owner, &ast_redirecting);
-				ast_party_redirecting_free(&ast_redirecting);
+	monitor_instance = ao2_alloc(sizeof(*monitor_instance) + strlen(device_name),
+		sig_pri_cc_monitor_instance_destroy);
+	if (!monitor_instance) {
+		return NULL;
+	}
 
-				/*
-				 * Send back positive ACK to CallRerouting/CallDeflection.
-				 *
-				 * Note:  This call will be hungup by the dial application when
-				 * it processes the call_forward string set above.
-				 */
-				pri_rerouting_rsp(pri->pri, call_rsp, subcmd->u.rerouting.invoke_id,
-					PRI_REROUTING_RSP_OK_CLEAR);
+	monitor_instance->cc_id = cc_id;
+	monitor_instance->pri = pri;
+	monitor_instance->core_id = core_id;
+	strcpy(monitor_instance->name, device_name);
 
-				/* This line is BUSY to further attempts by this dialing attempt. */
-				ast_queue_control(owner, AST_CONTROL_BUSY);
+	pri->calls->module_ref();
 
-				ast_channel_unlock(owner);
-			}
-			break;
-#endif	/* defined(HAVE_PRI_CALL_REROUTING) */
-		default:
-			ast_debug(2,
-				"Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n",
-				subcmd->cmd, pri_event2str(event_id), PRI_SPAN(channel),
-				PRI_CHANNEL(channel), pri->span);
-			break;
-		}
-	}
+	ao2_link(sig_pri_cc_monitors, monitor_instance);
+	return monitor_instance;
 }
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-#if defined(HAVE_PRI_CALL_HOLD)
+#if defined(HAVE_PRI_CCSS)
 /*!
  * \internal
- * \brief Attempt to transfer the active call to the held call.
+ * \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 active_call Active call to transfer.
- * \param held_call Held call to transfer.
+ * \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_attempt_transfer(struct sig_pri_pri *pri, q931_call *active_call, q931_call *held_call)
+static int sig_pri_cc_available(struct sig_pri_pri *pri, int chanpos, long cc_id, enum ast_cc_service_type service)
 {
-	int retval;
-	int active_chanpos;
-	int held_chanpos;
-	struct ast_channel *active_ast;
-	struct ast_channel *held_ast;
-	struct ast_channel *bridged;
+	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.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param chanpos Channel position in the span.
+ * \param event_id PRI event id
+ * \param channel PRI encoded span/channel
+ * \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.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int event_id,
+	int channel, const struct pri_subcommands *subcmds, q931_call *call_rsp)
+{
+	int index;
+	struct ast_channel *owner;
+	struct ast_party_redirecting ast_redirecting;
+
+	if (!subcmds) {
+		return;
+	}
+	for (index = 0; index < subcmds->counter_subcmd; ++index) {
+		const struct pri_subcommand *subcmd = &subcmds->subcmd[index];
+
+		switch (subcmd->cmd) {
+		case PRI_SUBCMD_CONNECTED_LINE:
+			sig_pri_lock_owner(pri, chanpos);
+			owner = pri->pvts[chanpos]->owner;
+			if (owner) {
+				struct ast_party_connected_line ast_connected;
+				int caller_id_update;
+
+				/* Extract the connected line information */
+				ast_party_connected_line_init(&ast_connected);
+				sig_pri_party_id_convert(&ast_connected.id, &subcmd->u.connected_line.id,
+					pri);
+
+				caller_id_update = 0;
+				if (ast_connected.id.name) {
+					/* Save name for Caller-ID update */
+					ast_copy_string(pri->pvts[chanpos]->cid_name,
+						ast_connected.id.name, sizeof(pri->pvts[chanpos]->cid_name));
+					caller_id_update = 1;
+				}
+				if (ast_connected.id.number) {
+					/* Save number for Caller-ID update */
+					ast_copy_string(pri->pvts[chanpos]->cid_num, ast_connected.id.number,
+						sizeof(pri->pvts[chanpos]->cid_num));
+					pri->pvts[chanpos]->cid_ton = ast_connected.id.number_type;
+					caller_id_update = 1;
+				} else {
+					ast_connected.id.number = ast_strdup("");
+				}
+				ast_connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+
+				pri->pvts[chanpos]->cid_subaddr[0] = '\0';
+#if defined(HAVE_PRI_SUBADDR)
+				if (ast_connected.id.subaddress.valid) {
+					ast_party_subaddress_set(&owner->cid.subaddress,
+						&ast_connected.id.subaddress);
+					if (ast_connected.id.subaddress.str) {
+						ast_copy_string(pri->pvts[chanpos]->cid_subaddr,
+							ast_connected.id.subaddress.str,
+							sizeof(pri->pvts[chanpos]->cid_subaddr));
+					}
+				}
+#endif	/* defined(HAVE_PRI_SUBADDR) */
+				if (caller_id_update) {
+					pri->pvts[chanpos]->callingpres =
+						ast_connected.id.number_presentation;
+					sig_pri_set_caller_id(pri->pvts[chanpos]);
+					ast_set_callerid(owner, S_OR(ast_connected.id.number, NULL),
+						S_OR(ast_connected.id.name, NULL),
+						S_OR(ast_connected.id.number, NULL));
+				}
+
+				/* Update the connected line information on the other channel */
+				if (event_id != PRI_EVENT_RING) {
+					/* This connected_line update was not from a SETUP message. */
+					ast_channel_queue_connected_line_update(owner, &ast_connected);
+				}
+
+				ast_party_connected_line_free(&ast_connected);
+				ast_channel_unlock(owner);
+			}
+			break;
+		case PRI_SUBCMD_REDIRECTING:
+			sig_pri_lock_owner(pri, chanpos);
+			owner = pri->pvts[chanpos]->owner;
+			if (owner) {
+				sig_pri_redirecting_convert(&ast_redirecting, &subcmd->u.redirecting,
+					&owner->redirecting, pri);
+
+/*! \todo XXX Original called data can be put in a channel data store that is inherited. */
+
+				ast_channel_set_redirecting(owner, &ast_redirecting);
+				if (event_id != PRI_EVENT_RING) {
+					/* This redirection was not from a SETUP message. */
+					ast_channel_queue_redirecting_update(owner, &ast_redirecting);
+				}
+				ast_party_redirecting_free(&ast_redirecting);
+
+				ast_channel_unlock(owner);
+			}
+			break;
+#if defined(HAVE_PRI_CALL_REROUTING)
+		case PRI_SUBCMD_REROUTING:
+			sig_pri_lock_owner(pri, chanpos);
+			owner = pri->pvts[chanpos]->owner;
+			if (owner) {
+				struct pri_party_redirecting pri_deflection;
+
+				if (!call_rsp) {
+					ast_channel_unlock(owner);
+					ast_log(LOG_WARNING,
+						"CallRerouting/CallDeflection to '%s' without call!\n",
+						subcmd->u.rerouting.deflection.to.number.str);
+					break;
+				}
+
+				pri_deflection = subcmd->u.rerouting.deflection;
+
+				ast_string_field_set(owner, call_forward, pri_deflection.to.number.str);
+
+				/* Adjust the deflecting to number based upon the subscription option. */
+				switch (subcmd->u.rerouting.subscription_option) {
+				case 0:	/* noNotification */
+				case 1:	/* notificationWithoutDivertedToNr */
+					/* Delete the number because the far end is not supposed to see it. */
+					pri_deflection.to.number.presentation =
+						PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED;
+					pri_deflection.to.number.plan =
+						(PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164;
+					pri_deflection.to.number.str[0] = '\0';
+					break;
+				case 2:	/* notificationWithDivertedToNr */
+					break;
+				case 3:	/* notApplicable */
+				default:
+					break;
+				}
+				sig_pri_redirecting_convert(&ast_redirecting, &pri_deflection,
+					&owner->redirecting, pri);
+				ast_channel_set_redirecting(owner, &ast_redirecting);
+				ast_party_redirecting_free(&ast_redirecting);
+
+				/*
+				 * Send back positive ACK to CallRerouting/CallDeflection.
+				 *
+				 * Note:  This call will be hungup by the dial application when
+				 * it processes the call_forward string set above.
+				 */
+				pri_rerouting_rsp(pri->pri, call_rsp, subcmd->u.rerouting.invoke_id,
+					PRI_REROUTING_RSP_OK_CLEAR);
+
+				/* This line is BUSY to further attempts by this dialing attempt. */
+				ast_queue_control(owner, AST_CONTROL_BUSY);
+
+				ast_channel_unlock(owner);
+			}
+			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",
+				subcmd->cmd, pri_event2str(event_id), PRI_SPAN(channel),
+				PRI_CHANNEL(channel), pri->span);
+			break;
+		}
+	}
+}
+
+#if defined(HAVE_PRI_CALL_HOLD)
+/*!
+ * \internal
+ * \brief Attempt to transfer the active call to the held call.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param active_call Active call to transfer.
+ * \param held_call Held call to transfer.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *active_call, q931_call *held_call)
+{
+	int retval;
+	int active_chanpos;
+	int held_chanpos;
+	struct ast_channel *active_ast;
+	struct ast_channel *held_ast;
+	struct ast_channel *bridged;
 
 	active_chanpos = pri_find_pri_call(pri, active_call);
 	held_chanpos = pri_find_pri_call(pri, held_call);
@@ -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++) {
@@ -4003,161 +4878,758 @@ void sig_pri_chan_alarm_notify(struct sig_pri_chan *p, int noalarm)
 	}
 }
 
-struct sig_pri_chan *sig_pri_chan_new(void *pvt_data, struct sig_pri_callback *callback, struct sig_pri_pri *pri, int logicalspan, int channo, int trunkgroup)
+struct sig_pri_chan *sig_pri_chan_new(void *pvt_data, struct sig_pri_callback *callback, struct sig_pri_pri *pri, int logicalspan, int channo, int trunkgroup)
+{
+	struct sig_pri_chan *p;
+
+	p = ast_calloc(1, sizeof(*p));
+	if (!p)
+		return p;
+
+	p->logicalspan = logicalspan;
+	p->prioffset = channo;
+	p->mastertrunkgroup = trunkgroup;
+
+	p->calls = callback;
+	p->chan_pvt = pvt_data;
+
+	p->pri = pri;
+
+	return p;
+}
+
+/*!
+ * \brief Delete the sig_pri private channel structure.
+ * \since 1.8
+ *
+ * \param doomed sig_pri private channel structure to delete.
+ *
+ * \return Nothing
+ */
+void sig_pri_chan_delete(struct sig_pri_chan *doomed)
+{
+	ast_free(doomed);
+}
+
+static void build_status(char *s, size_t len, int status, int active)
+{
+	if (!s || len < 1) {
+		return;
+	}
+	s[0] = '\0';
+	if (!(status & DCHAN_NOTINALARM))
+		strncat(s, "In Alarm, ", len - strlen(s) - 1);
+	if (status & DCHAN_UP)
+		strncat(s, "Up", len - strlen(s) - 1);
+	else
+		strncat(s, "Down", len - strlen(s) - 1);
+	if (active)
+		strncat(s, ", Active", len - strlen(s) - 1);
+	else
+		strncat(s, ", Standby", len - strlen(s) - 1);
+	s[len - 1] = '\0';
+}
+
+void sig_pri_cli_show_spans(int fd, int span, struct sig_pri_pri *pri)
+{
+	char status[256];
+	int x;
+	for (x = 0; x < NUM_DCHANS; x++) {
+		if (pri->dchans[x]) {
+			build_status(status, sizeof(status), pri->dchanavail[x], pri->dchans[x] == pri->pri);
+			ast_cli(fd, "PRI span %d/%d: %s\n", span, x, status);
+		}
+	}
+}
+
+void sig_pri_cli_show_span(int fd, int *dchannels, struct sig_pri_pri *pri)
+{
+	int x;
+	char status[256];
+
+	for (x = 0; x < NUM_DCHANS; x++) {
+		if (pri->dchans[x]) {
+#ifdef PRI_DUMP_INFO_STR
+			char *info_str = NULL;
+#endif
+			ast_cli(fd, "%s D-channel: %d\n", pri_order(x), dchannels[x]);
+			build_status(status, sizeof(status), pri->dchanavail[x], pri->dchans[x] == pri->pri);
+			ast_cli(fd, "Status: %s\n", status);
+#ifdef PRI_DUMP_INFO_STR
+			info_str = pri_dump_info_str(pri->pri);
+			if (info_str) {
+				ast_cli(fd, "%s", info_str);
+				free(info_str);
+			}
+#else
+			pri_dump_info(pri->pri);
+#endif
+			ast_cli(fd, "Overlap Recv: %s\n\n", (pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)?"Yes":"No");
+			ast_cli(fd, "\n");
+		}
+	}
+}
+
+int pri_send_keypad_facility_exec(struct sig_pri_chan *p, const char *digits)
+{
+	sig_pri_lock_private(p);
+
+	if (!p->pri || !p->call) {
+		ast_debug(1, "Unable to find pri or call on channel!\n");
+		sig_pri_unlock_private(p);
+		return -1;
+	}
+
+	if (!pri_grab(p, p->pri)) {
+		pri_keypad_facility(p->pri->pri, p->call, digits);
+		pri_rel(p->pri);
+	} else {
+		ast_debug(1, "Unable to grab pri to send keypad facility!\n");
+		sig_pri_unlock_private(p);
+		return -1;
+	}
+
+	sig_pri_unlock_private(p);
+
+	return 0;
+}
+
+int pri_send_callrerouting_facility_exec(struct sig_pri_chan *p, enum ast_channel_state chanstate, const char *destination, const char *original, const char *reason)
+{
+	int res = -1;
+
+	sig_pri_lock_private(p);
+
+	if (!p->pri || !p->call) {
+		ast_log(LOG_DEBUG, "Unable to find pri or call on channel!\n");
+		sig_pri_unlock_private(p);
+		return -1;
+	}
+
+	if (!pri_grab(p, p->pri)) {
+		res = pri_callrerouting_facility(p->pri->pri, p->call, destination, original, reason);
+		pri_rel(p->pri);
+	} else {
+		ast_log(LOG_DEBUG, "Unable to grab pri to send callrerouting facility on span %d!\n", p->pri->span);
+	}
+
+	sig_pri_unlock_private(p);
+
+	return res;
+}
+
+#if defined(HAVE_PRI_SERVICE_MESSAGES)
+int pri_maintenance_bservice(struct pri *pri, struct sig_pri_chan *p, int changestatus)
+{
+	int channel = PVT_TO_CHANNEL(p);
+	int span = PRI_SPAN(channel);
+
+	return pri_maintenance_service(pri, span, channel, changestatus);
+}
+#endif	/* defined(HAVE_PRI_SERVICE_MESSAGES) */
+
+void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_pri_chan *pchan)
+{
+	if (pchan->owner == oldchan) {
+		pchan->owner = newchan;
+	}
+}
+
+#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_chan *p;
+	struct sig_pri_cc_agent_prv *cc_pvt;
 
-	p = ast_calloc(1, sizeof(*p));
-	if (!p)
-		return p;
+	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) */
 
-	p->logicalspan = logicalspan;
-	p->prioffset = channo;
-	p->mastertrunkgroup = trunkgroup;
+#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;
 
-	p->calls = callback;
-	p->chan_pvt = pvt_data;
+	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) */
 
-	p->pri = pri;
+#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;
 
-	return p;
+	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 Delete the sig_pri private channel structure.
+ * \brief Begin monitoring a busy device.
  * \since 1.8
  *
- * \param doomed sig_pri private channel structure to delete.
+ * \param agent CC core agent control.
  *
- * \return Nothing
+ * \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.
  */
-void sig_pri_chan_delete(struct sig_pri_chan *doomed)
+int sig_pri_cc_agent_start_monitoring(struct ast_cc_agent *agent)
 {
-	ast_free(doomed);
+	/* libpri already knows when and how it needs to monitor Party A. */
+	return 0;
 }
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-static void build_status(char *s, size_t len, int status, int active)
+#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)
 {
-	if (!s || len < 1) {
+	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;
 	}
-	s[0] = '\0';
-	if (!(status & DCHAN_NOTINALARM))
-		strncat(s, "In Alarm, ", len - strlen(s) - 1);
-	if (status & DCHAN_UP)
-		strncat(s, "Up", len - strlen(s) - 1);
-	else
-		strncat(s, "Down", len - strlen(s) - 1);
-	if (active)
-		strncat(s, ", Active", len - strlen(s) - 1);
-	else
-		strncat(s, ", Standby", len - strlen(s) - 1);
-	s[len - 1] = '\0';
+	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) */
 
-void sig_pri_cli_show_spans(int fd, int span, struct sig_pri_pri *pri)
+#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)
 {
-	char status[256];
-	int x;
-	for (x = 0; x < NUM_DCHANS; x++) {
-		if (pri->dchans[x]) {
-			build_status(status, sizeof(status), pri->dchanavail[x], pri->dchans[x] == pri->pri);
-			ast_cli(fd, "PRI span %d/%d: %s\n", span, x, status);
-		}
-	}
+	const struct sig_pri_cc_monitor_instance *monitor_instance = obj;
+
+	return monitor_instance->core_id;
 }
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-void sig_pri_cli_show_span(int fd, int *dchannels, struct sig_pri_pri *pri)
+#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)
 {
-	int x;
-	char status[256];
+	struct sig_pri_cc_monitor_instance *monitor_1 = obj;
+	struct sig_pri_cc_monitor_instance *monitor_2 = arg;
 
-	for (x = 0; x < NUM_DCHANS; x++) {
-		if (pri->dchans[x]) {
-#ifdef PRI_DUMP_INFO_STR
-			char *info_str = NULL;
-#endif
-			ast_cli(fd, "%s D-channel: %d\n", pri_order(x), dchannels[x]);
-			build_status(status, sizeof(status), pri->dchanavail[x], pri->dchans[x] == pri->pri);
-			ast_cli(fd, "Status: %s\n", status);
-#ifdef PRI_DUMP_INFO_STR
-			info_str = pri_dump_info_str(pri->pri);
-			if (info_str) {
-				ast_cli(fd, "%s", info_str);
-				free(info_str);
-			}
-#else
-			pri_dump_info(pri->pri);
-#endif
-			ast_cli(fd, "Overlap Recv: %s\n\n", (pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)?"Yes":"No");
-			ast_cli(fd, "\n");
-		}
-	}
+	return monitor_1->core_id == monitor_2->core_id ? CMP_MATCH | CMP_STOP : 0;
 }
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-int pri_send_keypad_facility_exec(struct sig_pri_chan *p, const char *digits)
+#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)
 {
-	sig_pri_lock_private(p);
+	struct sig_pri_cc_monitor_instance *instance;
+	int cc_mode;
+	int res;
 
-	if (!p->pri || !p->call) {
-		ast_debug(1, "Unable to find pri or call on channel!\n");
-		sig_pri_unlock_private(p);
+	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;
 	}
 
-	if (!pri_grab(p, p->pri)) {
-		pri_keypad_facility(p->pri->pri, p->call, digits);
-		pri_rel(p->pri);
-	} else {
-		ast_debug(1, "Unable to grab pri to send keypad facility!\n");
-		sig_pri_unlock_private(p);
-		return -1;
-	}
+	instance = monitor->private_data;
 
-	sig_pri_unlock_private(p);
+	/* 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) */
 
-int pri_send_callrerouting_facility_exec(struct sig_pri_chan *p, enum ast_channel_state chanstate, const char *destination, const char *original, const char *reason)
+#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)
 {
-	int res = -1;
+	struct sig_pri_cc_monitor_instance *instance;
 
-	sig_pri_lock_private(p);
+	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);
 
-	if (!p->pri || !p->call) {
-		ast_log(LOG_DEBUG, "Unable to find pri or call on channel!\n");
-		sig_pri_unlock_private(p);
-		return -1;
-	}
+	return 0;
+}
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-	if (!pri_grab(p, p->pri)) {
-		res = pri_callrerouting_facility(p->pri->pri, p->call, destination, original, reason);
-		pri_rel(p->pri);
-	} else {
-		ast_log(LOG_DEBUG, "Unable to grab pri to send callrerouting facility on span %d!\n", p->pri->span);
+#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);
 
-	sig_pri_unlock_private(p);
+	return 0;
+}
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-	return res;
+#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_SERVICE_MESSAGES)
-int pri_maintenance_bservice(struct pri *pri, struct sig_pri_chan *p, int changestatus)
+#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)
 {
-	int channel = PVT_TO_CHANNEL(p);
-	int span = PRI_SPAN(channel);
+	struct sig_pri_cc_monitor_instance *instance;
 
-	return pri_maintenance_service(pri, span, channel, changestatus);
+	instance = monitor_pvt;
+	if (!instance) {
+		return;
+	}
+	ao2_unlink(sig_pri_cc_monitors, instance);
+	ao2_ref(instance, -1);
 }
-#endif	/* defined(HAVE_PRI_SERVICE_MESSAGES) */
+#endif	/* defined(HAVE_PRI_CCSS) */
 
-void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_pri_chan *pchan)
+/*!
+ * \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 (pchan->owner == oldchan) {
-		pchan->owner = newchan;
+#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 0bccd6ab0b6445bc8a11bf1e2ab4ff60d74ae810..7ea92d7520b89fab98562b13a815c20b37eadcc9 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 ce87f0f23b324b1fa258717a55936d6bcbd9c220..1d900eb58d3b913cad20db5d4bf5748cb9844ee0 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 0000000000000000000000000000000000000000..420e4367b5212a11fb9d972d315115f0171a6d28
--- /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 fb0d06931c1ee68b11e72fed6805217aff258c9c..30229b5fc90ed7ddc4421cbefe3a71779175e340 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 229db2dac621de78bdb5dffc0245e88718667eb1..078d17932fedf58a1ad26e2e42176704440ab000 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 7a4cbb8ebe7990816e14323ec9e6ef95d34c9fb3..9e774683b1ca3170b623193efb6bd1470202610a 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 8097d14edd13decd0d78c8601a3dc2277a0b332e..0427389b15427c7934cce39b5b7ab36f4399e4d8 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 0000000000000000000000000000000000000000..cfe07cbe008ddffd5a88244082b936a838a2cae3
--- /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 0000000000000000000000000000000000000000..191667bb08958106570f4e209152d54ab8c894fe
--- /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 0000000000000000000000000000000000000000..c2d7ec850b7d03b7ae341ca3706704472006236c
--- /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 a32486a3979ab84a12a1f348274ccdf957609d40..119dc42e82c3b12bc851cd56e27c5a03aef93e66 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;
@@ -534,27 +559,6 @@ enum ast_channel_adsicpe {
 	AST_ADSI_OFFHOOKONLY,
 };
 
-/*!
- * \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
  */
@@ -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 0000000000000000000000000000000000000000..f5f7392ddb80ca18856fa9dfd780314ed8854125
--- /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 4c516870e2e99c0daa579353051d8f6de2330b36..2a53ebb46a5385c6e08b1f0849c080753a88be79 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 30119d49bcb159ab23306a6628fcc482261ed0e4..68a0c7eb6d5e20d41e9d4fce8eeef01ca684693a 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 91c43d3fea601e9f9d9e7410f692d0c65b6a944c..dd7c160f42c3866f201e80da053842e446689485 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 770f4d2f5f176007697b24a1079c3224ef5f5a81..e7b809d4ca239896efb8f3875779eaaa9991d098 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 5c78cc9c04fd4408231b99feb98d79a04f733d44..2c30986ccb93612954cd9abaea9b91bf8ed0d596 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 8f557f736d013a48adef4efe0e3b5cae14355fac..57ccc3f631d023901a9ef5e30433e69f3160c4dd 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 0000000000000000000000000000000000000000..4e616011bc343fadc92473032364a5c2f3ea1ba1
--- /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 5be973a49b6b85a98d45e288371cdd06b1f427a8..03c657cff1e895a9d48944a5b610420cb4ddd52d 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 f811987f799c491aa3f67ea7bce7ea58d1ece433..0456990b4dc1775151f3eed95037af4cb5705839 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 36e7dd81298669bdea7a1d2f1dbe29d619ef6805..0f93abc547159a3435b13a9ce8c79e97176e2f4b 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) {