diff -urN certified-asterisk-11.2-cert2-orig/channels/chan_sip.c certified-asterisk-11.2-cert2/channels/chan_sip.c
--- certified-asterisk-11.2-cert2-orig/channels/chan_sip.c 2014-07-16 21:26:56.409670146 -0400
+++ certified-asterisk-11.2-cert2/channels/chan_sip.c 2014-07-18 22:42:33.477543421 -0400
@@ -267,6 +267,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"
@@ -663,6 +664,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 */
};
@@ -1445,6 +1447,7 @@
static void ast_quiet_chan(struct ast_channel *chan);
static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
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);
/*--- Device monitoring and Device/extension state/event handling */
static int extensionstate_update(const char *context, const char *exten, struct state_notify_data *data, struct sip_pvt *p, int force);
@@ -1500,6 +1503,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);
@@ -1586,6 +1591,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);
@@ -1640,6 +1647,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, struct ast_sockaddr *addr, uint32_t seqno, int *nounlock);
@@ -3418,6 +3426,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");
}
@@ -5202,6 +5214,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;
@@ -6100,6 +6117,10 @@
dialog->chanvars = copy_vars(peer->chanvars);
if (peer->fromdomainport)
dialog->fromdomainport = peer->fromdomainport;
+ dialog->donotdisturb = peer->donotdisturb;
+ if (!ast_strlen_zero(peer->callforward)) {
+ ast_string_field_set(dialog, callforward, peer->callforward);
+ }
return 0;
}
@@ -6498,6 +6519,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");
@@ -14900,6 +14924,148 @@
}
}
+/*! \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_contact(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_contact(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"},
@@ -19873,6 +20039,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 */
@@ -20685,8 +20853,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
@@ -21460,6 +21628,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)
{
@@ -21895,6 +22170,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';
}
@@ -21904,10 +22183,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 ${SIPCHANINFO()} Dialplan function - reads sip channel data */
@@ -24743,6 +25061,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, 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 */
static int sip_t38_abort(const void *data)
{
@@ -27394,6 +27739,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 */
@@ -27591,6 +27937,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;
@@ -27694,6 +28067,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 {
@@ -27717,6 +28093,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;
@@ -27794,6 +28192,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)
{
@@ -30944,6 +31493,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. */
@@ -33254,6 +33814,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"),
@@ -34015,6 +34577,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 certified-asterisk-11.2-cert2-orig/channels/sip/include/sip.h certified-asterisk-11.2-cert2/channels/sip/include/sip.h
--- certified-asterisk-11.2-cert2-orig/channels/sip/include/sip.h 2013-03-27 11:24:42.000000000 -0400
+++ certified-asterisk-11.2-cert2/channels/sip/include/sip.h 2014-07-17 21:26:48.869027162 -0400
@@ -457,6 +457,7 @@
DIALOG_INFO_XML,
CPIM_PIDF_XML,
PIDF_XML,
+ FEATURE_EVENTS,
MWI_NOTIFICATION,
CALL_COMPLETION,
};
@@ -1061,6 +1062,7 @@
AST_STRING_FIELD(last_presence_subtype); /*!< The last presence subtype sent for a subscription. */
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(callforward); /*!< Call Forward target */
);
char via[128]; /*!< Via: header */
int maxforwards; /*!< SIP Loop prevention */
@@ -1093,6 +1095,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 */
@@ -1299,6 +1302,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 sip_transport default_outbound_transport; /*!< Peer Registration may change the default outbound transport.
@@ -1321,6 +1325,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 */
int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */
int busy_level; /*!< Level of active channels where we signal busy */
@@ -1362,6 +1367,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) */