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, "\n");
+ ast_str_append(&str, 0, "\n");
+ ast_str_append(&str, 0, "%s\n", peer->name);
+ ast_str_append(&str, 0, "%s\n", peer->donotdisturb ? "true" : "false");
+ ast_str_append(&str, 0, "\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, "\n");
+ ast_str_append(&str, 0, "\n");
+ ast_str_append(&str, 0, "%s\n", peer->name);
+ ast_str_append(&str, 0, "forwardImmediate\n");
+ ast_str_append(&str, 0, "%s\n", !ast_strlen_zero(peer->callforward) ? "true" : "false");
+ ast_str_append(&str, 0, "%s\n", peer->callforward);
+ ast_str_append(&str, 0, "\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 ? "" : ast_extension_state2str(cur->laststate),
+ cur->subscribed == MWI_NOTIFICATION || cur->subscribed == FEATURE_EVENTS ? "--" : cur->subscribeuri,
+ cur->subscribed == MWI_NOTIFICATION || cur->subscribed == FEATURE_EVENTS ? "" : ast_extension_state2str(cur->laststate),
subscription_type2str(cur->subscribed),
cur->subscribed == MWI_NOTIFICATION ? S_OR(ast_str_buffer(mailbox_str), "") : "",
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} \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 |off }\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) */