diff -urN asterisk-certified-13.8-cert3-orig/channels/chan_sip.c asterisk-certified-13.8-cert3/channels/chan_sip.c
--- asterisk-certified-13.8-cert3-orig/channels/chan_sip.c	2016-09-08 12:34:59.000000000 -0400
+++ asterisk-certified-13.8-cert3/channels/chan_sip.c	2016-10-26 15:08:03.000000000 -0400
@@ -264,6 +264,7 @@
 #include "asterisk/data.h"
 #include "asterisk/aoc.h"
 #include "asterisk/message.h"
+#include "asterisk/dial.h"
 #include "sip/include/sip.h"
 #include "sip/include/globals.h"
 #include "sip/include/config_parser.h"
@@ -669,6 +670,7 @@
 	{ CPIM_PIDF_XML,   "presence", "application/cpim-pidf+xml",   "cpim-pidf+xml" },  /* RFC 3863 */
 	{ PIDF_XML,        "presence", "application/pidf+xml",        "pidf+xml" },       /* RFC 3863 */
 	{ XPIDF_XML,       "presence", "application/xpidf+xml",       "xpidf+xml" },       /* Pre-RFC 3863 with MS additions */
+	{ FEATURE_EVENTS,   "as-feature-event", "application/x-as-feature-event+xml", "as-feature-event" }, /* EMCA-323 application server feature events */
 	{ MWI_NOTIFICATION,	"message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */
 };
 
@@ -1264,6 +1266,7 @@
 static void sip_refer_destroy(struct sip_pvt *p);
 static int sip_notify_alloc(struct sip_pvt *p);
 static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
+static int do_feature_trigger(const char *context, const char *extension, ast_string_field name);
 static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer);
 static void check_for_nat(const struct ast_sockaddr *them, struct sip_pvt *p);
 
@@ -1321,6 +1324,8 @@
 static char *sip_do_debug_peer(int fd, const char *arg);
 static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_cli_donotdisturb(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_cli_callforward(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static int sip_dtmfmode(struct ast_channel *chan, const char *data);
 static int sip_addheader(struct ast_channel *chan, const char *data);
@@ -1406,6 +1411,8 @@
 static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, int full, int timeout);
 static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
 static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
+static int sip_send_donotdisturb(struct sip_peer *peer);
+static int sip_send_callforward(struct sip_peer *peer);
 static int get_domain(const char *str, char *domain, int len);
 static void get_realm(struct sip_pvt *p, const struct sip_request *req);
 static char *get_content(struct sip_request *req);
@@ -1461,6 +1468,7 @@
 static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
 static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
 static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
+static int handle_subscribe_featureevent(struct sip_peer *peer, struct sip_request *req, uint32_t seqno, int *senddonotdisturb, int *sendcallforward);
 static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
 static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
 static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
@@ -3338,6 +3346,10 @@
 	if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
 		dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
 	}
+	/* Remove link from peer to subscription for Feature Events */
+	if (dialog->relatedpeer && dialog->relatedpeer->fepvt == dialog) {
+		dialog->relatedpeer->fepvt = dialog_unref(dialog->relatedpeer->fepvt, "delete ->relatedpeer->fepvt");
+	}
 	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");
 	}
@@ -5245,6 +5257,11 @@
 		peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
 	}
 
+	if (peer->fepvt) {	/* We have an active subscription, delete it */
+		dialog_unlink_all(peer->fepvt);
+		peer->mwipvt = dialog_unref(peer->fepvt, "unreffing peer->fepvt");
+	}
+	
 	if (peer->chanvars) {
 		ast_variables_destroy(peer->chanvars);
 		peer->chanvars = NULL;
@@ -6207,6 +6224,10 @@
 		dialog->fromdomainport = peer->fromdomainport;
 	}
 	dialog->callingpres = peer->callingpres;
+	dialog->donotdisturb = peer->donotdisturb;
+	if (!ast_strlen_zero(peer->callforward)) {
+		ast_string_field_set(dialog, callforward, peer->callforward);
+	}
 
 	return 0;
 }
@@ -6599,6 +6620,9 @@
 	/* Remove link from peer to subscription of MWI */
 	if (p->relatedpeer && p->relatedpeer->mwipvt == p)
 		p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+	/* Remove link from peer to subscription for Feature Events */
+	if (p->relatedpeer && p->relatedpeer->fepvt)
+		p->relatedpeer->fepvt = dialog_unref(p->relatedpeer->fepvt, "delete ->relatedpeer->fepvt");
 	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");
 
@@ -15552,6 +15576,146 @@
 	}
 }
 
+/*! \brief Notify peer that the do not disturb status has changed */
+static int sip_send_donotdisturb(struct sip_peer *peer)
+{
+	struct sip_request req;
+	struct sip_pvt *p;
+	struct ast_str *str = ast_str_alloca(4000);
+		
+	if (ast_sockaddr_isnull(&peer->addr) && ast_sockaddr_isnull(&peer->defaddr)) {
+		return 0;
+	}
+
+	if (peer->fepvt) {
+		p = peer->fepvt;
+	} else {
+		/* Build temporary dialog for this message */
+		if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL, NULL))) {
+			return -1;
+		}
+
+		/* If we don't set the socket type to 0, then create_addr_from_peer will fail immediately if the peer
+		 * uses any transport other than UDP. We set the type to 0 here and then let create_addr_from_peer copy
+		 * the peer's socket information to the sip_pvt we just allocated
+		 */
+		set_socket_transport(&p->socket, 0);
+		if (create_addr_from_peer(p, peer)) {
+			/* Maybe they're not registered, etc. */
+			dialog_unlink_all(p);
+			dialog_unref(p, "unref dialog p just created via sip_alloc");
+			return -1;
+		}
+		/* Recalculate our side, and recalculate Call ID */
+		ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
+		build_via(p);
+
+		/* Change the dialog callid. */
+		change_callid_pvt(p, NULL);
+
+		/* Destroy this session after 32 secs */
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	}
+
+	ast_set_flag(&p->flags[0], SIP_OUTGOING);
+	initreqprep(&req, p, SIP_NOTIFY, NULL);
+
+	add_header(&req, "Event", "as-feature-event");
+	if (p->expiry) {
+		add_header(&req, "Subscription-State", "active");
+	} else {
+		add_header(&req, "Subscription-State", "terminated;reason=timeout");
+	}
+	add_header(&req, "Content-Type", "application/x-as-feature-event+xml");
+
+	ast_str_append(&str, 0, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
+	ast_str_append(&str, 0, "<DoNotDisturbEvent xmlns=\"http://www.ecma-international.org/standards/ecma-323/csta/ed3\">\n");
+	ast_str_append(&str, 0, "<device>%s</device>\n", peer->name);
+	ast_str_append(&str, 0, "<doNotDisturbOn>%s</doNotDisturbOn>\n", peer->donotdisturb ? "true" : "false");
+	ast_str_append(&str, 0, "</DoNotDisturbEvent>\n");
+	
+	add_content(&req, ast_str_buffer(str));
+
+	if (!p->initreq.headers) {
+		initialize_initreq(p, &req);
+	}
+
+	send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+
+	return 0;
+}
+
+/*! \brief Notify peer that the call forwarding extension has changed */
+static int sip_send_callforward(struct sip_peer *peer)
+{
+	struct sip_request req;
+	struct sip_pvt *p;
+	struct ast_str *str = ast_str_alloca(4000);
+		
+	if (ast_sockaddr_isnull(&peer->addr) && ast_sockaddr_isnull(&peer->defaddr)) {
+		return 0;
+	}
+
+	if (peer->fepvt) {
+		p = peer->fepvt;
+	} else {
+		/* Build temporary dialog for this message */
+		if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL, NULL))) {
+			return -1;
+		}
+
+		/* If we don't set the socket type to 0, then create_addr_from_peer will fail immediately if the peer
+		 * uses any transport other than UDP. We set the type to 0 here and then let create_addr_from_peer copy
+		 * the peer's socket information to the sip_pvt we just allocated
+		 */
+		set_socket_transport(&p->socket, 0);
+		if (create_addr_from_peer(p, peer)) {
+			/* Maybe they're not registered, etc. */
+			dialog_unlink_all(p);
+			dialog_unref(p, "unref dialog p just created via sip_alloc");
+			return -1;
+		}
+		/* Recalculate our side, and recalculate Call ID */
+		ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
+		build_via(p);
+
+		/* Change the dialog callid. */
+		change_callid_pvt(p, NULL);
+
+		/* Destroy this session after 32 secs */
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	}
+
+	ast_set_flag(&p->flags[0], SIP_OUTGOING);
+	initreqprep(&req, p, SIP_NOTIFY, NULL);
+
+	add_header(&req, "Event", "as-feature-event");
+	if (p->expiry) {
+		add_header(&req, "Subscription-State", "active");
+	} else {
+		add_header(&req, "Subscription-State", "terminated;reason=timeout");
+	}
+	add_header(&req, "Content-Type", "application/x-as-feature-event+xml");
+
+	ast_str_append(&str, 0, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
+	ast_str_append(&str, 0, "<ForwardingEvent xmlns=\"http://www.ecma-international.org/standards/ecma-323/csta/ed3\">\n");
+	ast_str_append(&str, 0, "<device>%s</device>\n", peer->name);
+	ast_str_append(&str, 0, "<forwardingType>forwardImmediate</forwardingType>\n");
+	ast_str_append(&str, 0, "<forwardStatus>%s</forwardStatus>\n", !ast_strlen_zero(peer->callforward) ? "true" : "false");
+	ast_str_append(&str, 0, "<forwardTo>%s</forwardTo>\n", peer->callforward);
+	ast_str_append(&str, 0, "</ForwardingEvent>\n");
+	
+	add_content(&req, ast_str_buffer(str));
+
+	if (!p->initreq.headers) {
+		initialize_initreq(p, &req);
+	}
+
+	send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+	
+	return 0;
+}
+
 static const struct _map_x_s regstatestrings[] = {
 	{ REG_STATE_FAILED,     "Failed" },
 	{ REG_STATE_UNREGISTERED, "Unregistered"},
@@ -20927,6 +21091,8 @@
 		ast_cli(fd, "  Parkinglot   : %s\n", peer->parkinglot);
 		ast_cli(fd, "  Use Reason   : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON)));
 		ast_cli(fd, "  Encryption   : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP)));
+		ast_cli(fd, "  DND          : %s\n", AST_CLI_YESNO(peer->donotdisturb));
+		ast_cli(fd, "  CallFwd Ext. : %s\n", peer->callforward);
 		ast_cli(fd, "\n");
 		peer = sip_unref_peer(peer, "sip_show_peer: sip_unref_peer: done with peer ptr");
 	} else  if (peer && type == 1) { /* manager listing */
@@ -21741,8 +21907,8 @@
 				S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
 			   	cur->callid,
 				/* the 'complete' exten/context is hidden in the refer_to field for subscriptions */
-				cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri,
-				cur->subscribed == MWI_NOTIFICATION ? "<none>" : ast_extension_state2str(cur->laststate),
+				cur->subscribed == MWI_NOTIFICATION || cur->subscribed == FEATURE_EVENTS ? "--" : cur->subscribeuri,
+				cur->subscribed == MWI_NOTIFICATION || cur->subscribed == FEATURE_EVENTS ? "<none>" : ast_extension_state2str(cur->laststate),
 				subscription_type2str(cur->subscribed),
 				cur->subscribed == MWI_NOTIFICATION ? S_OR(ast_str_buffer(mailbox_str), "<none>") : "<none>",
 				cur->expiry
@@ -22517,6 +22683,113 @@
 	return CLI_SUCCESS;
 }
 
+/*! \brief Enable/Disable DoNotDisturb on a peer */
+static char *sip_cli_donotdisturb(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sip_peer *peer;
+	int donotdisturb;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sip donotdisturb {on|off}";
+		e->usage =
+			"Usage: sip donotdisturb {on|off} <peer>\n"
+			"       Enables/Disables do not disturb on a SIP peer\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 3) {
+			return complete_sip_peer(a->word, a->n, 0);
+		}
+		return NULL;
+	}
+
+	if (a->argc < 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (!strcasecmp(a->argv[2], "on")) {
+		donotdisturb = 1;
+	} else if (!strcasecmp(a->argv[2], "off")) {
+		donotdisturb = 0;
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (!(peer = sip_find_peer(a->argv[3], NULL, TRUE, FINDPEERS, FALSE, 0))) {
+		ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
+	} else {
+		ast_cli(a->fd, "Do Not Disturb on '%s' %s\n", peer->name, donotdisturb ? "enabled" : "disabled");
+		peer->donotdisturb = donotdisturb;
+		if (!peer->is_realtime) {
+			ast_db_put("SIP/DoNotDisturb", peer->name, donotdisturb ? "yes" : "no");
+		}
+		sip_send_donotdisturb(peer);
+		do_feature_trigger("sip-feature-events-dnd", donotdisturb ? "on" : "off", peer->name);
+		peer = sip_unref_peer(peer, "unref after sip_find_peer");
+	}
+
+	return CLI_SUCCESS;
+}
+
+/*! \brief Sets/Removes the call fowarding extension for a peer */
+static char *sip_cli_callforward(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sip_peer *peer;
+	const char *callforward;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sip callforward {on|off}";
+		e->usage =
+			"Usage: sip callforward {on <peer> <extension>|off <peer>}\n"
+			"       Sets/Removes the call forwarding extension for a SIP peer\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 3) {
+			return complete_sip_peer(a->word, a->n, 0);
+		}
+		return NULL;
+	}
+
+	if (a->argc < 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (!strcasecmp(a->argv[2], "on")) {
+		if (a->argc < 5) {
+			return CLI_SHOWUSAGE;
+		}
+		callforward = a->argv[4];
+	} else if (!strcasecmp(a->argv[2], "off")) {
+		callforward = "";
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (!(peer = sip_find_peer(a->argv[3], NULL, TRUE, FINDPEERS, FALSE, 0))) {
+		ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
+	} else {
+		if (ast_strlen_zero(callforward)) {
+			ast_cli(a->fd, "Call forwarding on '%s' removed\n", peer->name);
+		} else {
+			ast_cli(a->fd, "Call forwarding on '%s' set to %s\n", peer->name, callforward);
+		}
+		ast_string_field_set(peer, callforward, callforward);
+		if (!peer->is_realtime) {
+			if (ast_strlen_zero(peer->callforward)) {
+				ast_db_del("SIP/CallForward", peer->name);
+			} else {
+				ast_db_put("SIP/CallForward", peer->name, callforward);
+			}
+		}
+		sip_send_callforward(peer);
+		do_feature_trigger("sip-feature-events-cf", ast_strlen_zero(callforward) ? "off" : callforward, peer->name);
+		peer = sip_unref_peer(peer, "unref after sip_find_peer");
+	}
+
+ 	return CLI_SUCCESS;
+ }
+
 /*! \brief Enable/Disable SIP History logging (CLI) */
 static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -22957,6 +23230,10 @@
 		} else {
 			buf[0] = '\0';
 		}
+	} else if (!strncasecmp(colname, "donotdisturb", 12)) {
+		ast_copy_string(colname, peer->donotdisturb ? "yes" : "no", len);
+	} else if (!strncasecmp(colname, "callforward", 11)) {
+		ast_copy_string(buf, peer->callforward, len);
 	} else {
 		buf[0] = '\0';
 	}
@@ -22966,10 +23243,49 @@
 	return 0;
 }
 
+static int function_sippeer_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+	struct sip_peer *peer;
+	char *colname;
+
+	if ((colname = strchr(data, ','))) {
+		*colname++ = '\0';
+	} else {
+		colname = "";
+	}
+
+	if (!(peer = sip_find_peer(data, NULL, TRUE, FINDPEERS, FALSE, 0))) {
+		return -1;
+	}
+
+	if (!strncasecmp(colname, "donotdisturb", 12)) {
+		peer->donotdisturb = ast_true(value);
+		if (!peer->is_realtime) {
+			ast_db_put("SIP/DoNotDisturb", peer->name, peer->donotdisturb ? "yes" : "no");
+		}
+		sip_send_donotdisturb(peer);
+	} else if (!strncasecmp(colname, "callforward", 11)) {
+		ast_string_field_set(peer, callforward, value);
+		if (!peer->is_realtime) {
+			if (ast_strlen_zero(peer->callforward)) {
+				ast_db_del("SIP/CallForward", peer->name);
+			} else {
+				ast_db_put("SIP/CallForward", peer->name, peer->callforward);
+			}
+		}
+		sip_send_callforward(peer);
+	}
+
+	sip_unref_peer(peer, "sip_unref_peer from function_sippeer_write, just before return");
+ 
+ 	return 0;
+ }
+
 /*! \brief Structure to declare a dialplan function: SIPPEER */
 static struct ast_custom_function sippeer_function = {
 	.name = "SIPPEER",
 	.read = function_sippeer,
+	.write = function_sippeer_write
 };
 
 /*! \brief update redirecting information for a channel based on headers
@@ -25510,6 +25826,33 @@
 	return 0;
 }
 
+static int do_feature_trigger(const char *context, const char *extension, ast_string_field name)
+{	
+	struct ast_channel *chan;
+	
+	chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", NULL, NULL, 0, "SIPFeatureTrigger");
+	
+	if (!chan) {
+		ast_log(LOG_ERROR, "Unable to allocate channel for feature trigger\n");
+		return -1;
+	}
+	
+	ast_set_callerid(chan, name, name, name);
+	ast_channel_context_set(chan, context);
+	ast_channel_exten_set(chan, extension);
+	ast_channel_priority_set(chan, 1);
+	
+	if (ast_pbx_run(chan)) {
+		ast_log(LOG_ERROR, "Unable to run PBX on feature trigger\n");
+		ast_hangup(chan);
+		chan = NULL;
+		return -1;
+	}
+	
+	chan = NULL;
+	return 0;
+}
+
 /*!
  * \brief Called to deny a T38 reinvite if the core does not respond to our request
  *
@@ -28020,6 +28363,7 @@
 	char *event = ast_strdupa(sip_get_header(req, "Event")); /* Get Event package name */
 	int resubscribe = (p->subscribed != NONE) && !req->ignore;
 	char *options;
+	int senddonotdisturb = 0, sendcallforward = 0;
 
 	if (p->initreq.headers) {
 		/* We already have a dialog */
@@ -28217,6 +28561,33 @@
 		} else {
 			p->subscribed = subscribed;
 		}
+	} else if (!strcmp(event, "as-feature-event")) {	
+		if (!authpeer || handle_subscribe_featureevent(authpeer, req, seqno, &senddonotdisturb, &sendcallforward) == -1) {
+			transmit_response(p, "489 Bad Event", req);
+			pvt_set_needdestroy(p, "unknown format");
+			if (authpeer) {
+				sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 4)");
+			}
+			return 0;
+		}
+		
+		p->subscribed = FEATURE_EVENTS;
+		if (authpeer->fepvt != p) {	/* Destroy old PVT if this is a new one */
+			if (authpeer->fepvt) {
+				/* We only allow one subscription per peer */
+				dialog_unlink_all(authpeer->fepvt);
+				authpeer->fepvt = dialog_unref(authpeer->fepvt, "unref dialog authpeer->fepvt");
+			}
+			authpeer->fepvt = dialog_ref(p, "setting peers' fepvt to p");
+		}
+
+		if (p->relatedpeer != authpeer) {
+			if (p->relatedpeer) {
+				sip_unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
+			}
+			p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");
+		}
+		/* Do not release authpeer here */
 	} else if (!strcmp(event, "message-summary")) {
 		int start = 0;
 		int found_supported = 0;
@@ -28321,6 +28692,9 @@
 			if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) {
 				ast_debug(2, "%s subscription for mailbox notification - peer %s\n",
 						action, p->relatedpeer->name);
+			} else if (p->subscribed == FEATURE_EVENTS) {
+				ast_debug(2, "%s feature event subscription for peer %s\n",
+						action, p->username);						
 			} else if (p->subscribed == CALL_COMPLETION) {
 				ast_debug(2, "%s CC subscription for peer %s\n", action, p->username);
 			} else {
@@ -28347,6 +28721,28 @@
 				ao2_lock(p);
 				sip_unref_peer(peer, "release a peer ref now that MWI is sent");
 			}
+		} else if (p->subscribed == FEATURE_EVENTS) {
+			ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+			transmit_response(p, "200 OK", req);
+			if (p->relatedpeer && !atoi(sip_get_header(req, "Content-Length"))) {
+				struct sip_peer *peer = p->relatedpeer;
+				sip_ref_peer(peer, "ensure a peer ref is held during feature events sending");
+				sip_send_donotdisturb(peer);
+				sip_send_callforward(peer);
+				sip_unref_peer(peer, "release a peer ref now that feature events is sent");
+			}
+			if (p->relatedpeer && senddonotdisturb) {
+				struct sip_peer *peer = p->relatedpeer;
+				sip_ref_peer(peer, "ensure a peer ref is held during feature events sending");
+				sip_send_donotdisturb(peer);
+				sip_unref_peer(peer, "release a peer ref now that feature events is sent");
+			}
+			if (p->relatedpeer && sendcallforward) {
+				struct sip_peer *peer = p->relatedpeer;
+				sip_ref_peer(peer, "ensure a peer ref is held during feature events sending");
+				sip_send_callforward(peer);
+				sip_unref_peer(peer, "release a peer ref now that feature events is sent");
+			}
 		} else if (p->subscribed != CALL_COMPLETION) {
 			struct state_notify_data data = { 0, };
 			char *subtype = NULL;
@@ -28424,6 +28820,157 @@
 	return 1;
 }
 
+/*! \brief  Handle incoming feature event SUBSCRIBE body */
+static int handle_subscribe_featureevent(struct sip_peer *peer, struct sip_request *req, uint32_t seqno, int *senddonotdisturb, int *sendcallforward)
+{
+#ifdef HAVE_LIBXML2
+	const char *content_type = sip_get_header(req, "Content-Type");
+	char *featureevent_body;
+	struct ast_xml_doc *featureevent_doc = NULL;
+	struct ast_xml_node *root_node;
+	
+	if (!atoi(sip_get_header(req, "Content-Length"))) {
+		/* Peer is subscribing to the current DoNotDisturb and CallForward state */
+		return 0;
+	}
+	
+	if(seqno == 2) {
+		/* Polycom SoundPoint phones always set do not disturb on boot */
+		return 0;
+	}
+
+	if (strcasecmp(content_type, "application/x-as-feature-event+xml")) {
+		ast_log(LOG_WARNING, "Content type is not x-as-feature-event+xml\n");
+		return -1;
+	}
+
+	if (!(featureevent_body = get_content(req))) {
+		ast_log(LOG_WARNING, "Unable to get feature event body\n");
+		return -1;
+	}
+
+	if (!(featureevent_doc = ast_xml_read_memory(featureevent_body, strlen(featureevent_body)))) {
+		ast_log(LOG_WARNING, "Unable to open XML as-feature-event document. Is it malformed?\n");
+		return -1;
+	}
+
+	if (!(root_node = ast_xml_get_root(featureevent_doc))) {
+		ast_log(LOG_WARNING, "Unable to get root node\n");
+		ast_xml_close(featureevent_doc);
+		return -1;
+	}
+
+	if (!strcmp(ast_xml_node_get_name(root_node), "SetDoNotDisturb")) {
+		int donotdisturb;
+		struct ast_xml_node *set_donotdisturb_node, *set_donotdisturb_children;
+		struct ast_xml_node *donotdisturb_on_node;
+
+		set_donotdisturb_node = root_node;
+
+		if (!(set_donotdisturb_children = ast_xml_node_get_children(set_donotdisturb_node))) {
+			ast_log(LOG_WARNING, "No tuples within SetDoNotDisturb node");
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		if (!(donotdisturb_on_node = ast_xml_find_element(set_donotdisturb_children, "doNotDisturbOn", NULL, NULL))) {
+			ast_log(LOG_WARNING, "Couldn't find doNotDisturbOn node");
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+		
+		if (!strcmp(ast_xml_get_text(donotdisturb_on_node), "true")) {
+			donotdisturb = 1;
+		} else if (!strcmp(ast_xml_get_text(donotdisturb_on_node), "false")) {
+			donotdisturb = 0;
+		} else {
+			ast_log(LOG_WARNING, "Invalid content in doNotDisturbOn node %s\n", ast_xml_get_text(donotdisturb_on_node));
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		*senddonotdisturb = 0;
+		if (peer->donotdisturb != donotdisturb) {
+			peer->donotdisturb = donotdisturb;	
+			if (!peer->is_realtime) {
+				ast_db_put("SIP/DoNotDisturb", peer->name, peer->donotdisturb ? "yes" : "no");
+			}
+			*senddonotdisturb = 1;
+			do_feature_trigger("sip-feature-events-dnd", peer->donotdisturb ? "on" : "off", peer->name);
+		}		
+	} else if (!strcmp(ast_xml_node_get_name(root_node), "SetForwarding")) {
+		char callforward[AST_MAX_EXTENSION];
+		struct ast_xml_node *set_forwarding_node, *set_forwarding_children;
+		struct ast_xml_node *forwarding_type_node, *activate_forward_node, *forward_dn_node;
+
+		set_forwarding_node = root_node;
+
+		if (!(set_forwarding_children = ast_xml_node_get_children(set_forwarding_node))) {
+			ast_log(LOG_WARNING, "No tuples within SetForwarding node");
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		if (!(forwarding_type_node = ast_xml_find_element(set_forwarding_children, "forwardingType", NULL, NULL))) {
+			ast_log(LOG_WARNING, "Couldn't find forwardingType node\n");
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		if (strcmp(ast_xml_get_text(forwarding_type_node), "forwardImmediate")) {
+			ast_log(LOG_WARNING, "forwardingType not supported: %s\n", ast_xml_get_text(forwarding_type_node));
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		if (!(activate_forward_node = ast_xml_find_element(set_forwarding_children, "activateForward", NULL, NULL))) {
+			ast_log(LOG_WARNING, "Couldn't find activateForward node");
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		if (!strcmp(ast_xml_get_text(activate_forward_node), "true")) {
+			if (!(forward_dn_node = ast_xml_find_element(set_forwarding_children, "forwardDN", NULL, NULL))) {
+				ast_log(LOG_WARNING, "Couldn't find forwardDN node\n");
+				ast_xml_close(featureevent_doc);
+				return -1;
+			}
+
+			ast_copy_string(callforward, ast_xml_get_text(forward_dn_node), sizeof(callforward));
+		} else if (!strcmp(ast_xml_get_text(activate_forward_node), "false")) {
+			callforward[0] = '\0';
+		} else {
+			ast_log(LOG_WARNING, "Invalid content in activateForward node: %s\n", ast_xml_get_text(activate_forward_node));
+			ast_xml_close(featureevent_doc);
+			return -1;
+		}
+
+		*sendcallforward = 0;
+		if (strcmp(peer->callforward, callforward)) {
+			ast_string_field_set(peer, callforward, callforward);
+			if (!peer->is_realtime) {
+				if (ast_strlen_zero(peer->callforward)) {
+					ast_db_del("SIP/CallForward", peer->name);
+				} else {
+					ast_db_put("SIP/CallForward", peer->name, peer->callforward);
+				}
+			}
+			*sendcallforward = 1;
+			do_feature_trigger("sip-feature-events-cf", ast_strlen_zero(callforward) ? "off" : callforward, peer->name);
+		}
+	} else {
+		ast_log(LOG_WARNING, "Couldn't find SetDoNotDisturb or SetForwarding node: %s\n", ast_xml_node_get_name(root_node));
+		ast_xml_close(featureevent_doc);
+		return -1;
+	}
+	
+	ast_xml_close(featureevent_doc);
+	return 0;
+#else
+	return -1
+#endif
+}
+
 /*! \brief Handle incoming REGISTER request */
 static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
 {
@@ -31742,6 +32289,17 @@
 	if (peer->host_dynamic && (!peer->is_realtime || !sip_cfg.peer_rtupdate)) {
 		reg_source_db(peer);
 	}
+	
+	if (!peer->is_realtime) {
+		char data[128];
+
+		if (!ast_db_get("SIP/DoNotDisturb", peer->name, data, sizeof(data))) {
+			peer->donotdisturb = ast_true(data);
+		}
+		if (!ast_db_get("SIP/CallForward", peer->name, data, sizeof(data))) {
+			ast_string_field_set(peer, callforward, data);
+		}
+	}
 
 	/* If they didn't request that MWI is sent *only* on subscribe, go ahead and
 	 * subscribe to it now. */
@@ -34044,6 +34602,8 @@
 	AST_CLI_DEFINE(sip_show_settings, "Show SIP global settings"),
 	AST_CLI_DEFINE(sip_show_mwi, "Show MWI subscriptions"),
 	AST_CLI_DEFINE(sip_cli_notify, "Send a notify packet to a SIP peer"),
+	AST_CLI_DEFINE(sip_cli_donotdisturb, "Enables/Disables do not disturb on a SIP peer"),
+	AST_CLI_DEFINE(sip_cli_callforward, "Sets/Removes the call forwarding extension for a SIP peer"),
 	AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
 	AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
 	AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
@@ -34880,6 +35440,8 @@
 	MEMBER(sip_peer, ringing, AST_DATA_INTEGER)		\
 	MEMBER(sip_peer, onhold, AST_DATA_INTEGER)		\
 	MEMBER(sip_peer, call_limit, AST_DATA_INTEGER)		\
+	MEMBER(sip_peer, donotdisturb, AST_DATA_INTEGER)        \
+	MEMBER(sip_peer, callforward, AST_DATA_STRING)          \
 	MEMBER(sip_peer, t38_maxdatagram, AST_DATA_INTEGER)	\
 	MEMBER(sip_peer, maxcallbitrate, AST_DATA_INTEGER)	\
 	MEMBER(sip_peer, rtptimeout, AST_DATA_SECONDS)		\
diff -urN asterisk-certified-13.8-cert3-orig/channels/sip/include/sip.h asterisk-certified-13.8-cert3/channels/sip/include/sip.h
--- asterisk-certified-13.8-cert3-orig/channels/sip/include/sip.h	2016-09-08 12:34:59.000000000 -0400
+++ asterisk-certified-13.8-cert3/channels/sip/include/sip.h	2016-10-26 15:06:41.000000000 -0400
@@ -468,6 +468,7 @@
 	DIALOG_INFO_XML,
 	CPIM_PIDF_XML,
 	PIDF_XML,
+	FEATURE_EVENTS,
 	MWI_NOTIFICATION,
 	CALL_COMPLETION,
 };
@@ -1044,6 +1045,7 @@
 		AST_STRING_FIELD(last_presence_message);   /*!< The last presence message for a subscription */
 		AST_STRING_FIELD(msg_body);     /*!< Text for a MESSAGE body */
 		AST_STRING_FIELD(tel_phone_context);       /*!< The phone-context portion of a TEL URI */
+		AST_STRING_FIELD(callforward);  /*!< Call Forward target */
 	);
 	char via[128];                          /*!< Via: header */
 	int maxforwards;                        /*!< SIP Loop prevention */
@@ -1076,6 +1078,7 @@
 	                                       */
 	unsigned short req_secure_signaling:1;/*!< Whether we are required to have secure signaling or not */
 	unsigned short natdetected:1;         /*!< Whether we detected a NAT when processing the Via */
+	int donotdisturb:1;                   /*!< Peer has set DoNotDisturb */
 	int timer_t1;                     /*!< SIP timer T1, ms rtt */
 	int timer_b;                      /*!< SIP timer B, ms */
 	unsigned int sipoptions;          /*!< Supported SIP options on the other end */
@@ -1280,6 +1283,7 @@
 		AST_STRING_FIELD(record_on_feature); /*!< Feature to use when receiving INFO with record: on during a call */
 		AST_STRING_FIELD(record_off_feature); /*!< Feature to use when receiving INFO with record: off during a call */
 		AST_STRING_FIELD(callback); /*!< Callback extension */
+		AST_STRING_FIELD(callforward);  /*!< Call forwarding extension */
 		);
 	struct sip_socket socket;       /*!< Socket used for this peer */
 	enum ast_transport default_outbound_transport;   /*!< Peer Registration may change the default outbound transport.
@@ -1302,6 +1306,7 @@
 	int inuse;                      /*!< Number of calls in use */
 	int ringing;                    /*!< Number of calls ringing */
 	int onhold;                     /*!< Peer has someone on hold */
+	int donotdisturb:1;             /*!< Peer has set DoNotDisturb */
 	int call_limit;                 /*!< Limit of concurrent calls */
 	unsigned int t38_maxdatagram;            /*!< T.38 FaxMaxDatagram override */
 	int busy_level;                 /*!< Level of active channels where we signal busy */
@@ -1342,6 +1347,7 @@
 	struct ast_acl_list *directmediaacl;   /*!<  Restrict what IPs are allowed to interchange direct media with */
 	struct ast_variable *chanvars;  /*!<  Variables to set for channel created by user */
 	struct sip_pvt *mwipvt;         /*!<  Subscription for MWI */
+	struct sip_pvt *fepvt;          /*!<  Subscription for Feature Events */
 	struct sip_st_cfg stimer;       /*!<  SIP Session-Timers */
 	int timer_t1;                   /*!<  The maximum T1 value for the peer */
 	int timer_b;                    /*!<  The maximum timer B (transaction timeouts) */
