diff -urN certified-asterisk-11.6-cert4-orig/channels/chan_sip.c certified-asterisk-11.6-cert4/channels/chan_sip.c --- certified-asterisk-11.6-cert4-orig/channels/chan_sip.c 2014-06-13 01:31:53.000000000 -0400 +++ certified-asterisk-11.6-cert4/channels/chan_sip.c 2014-07-27 19:06:11.042031106 -0400 @@ -269,6 +269,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" @@ -665,6 +666,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 */ }; @@ -1449,6 +1451,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); 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); @@ -1507,6 +1510,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); @@ -1593,6 +1598,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); @@ -1647,6 +1654,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); @@ -3433,6 +3441,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"); } @@ -5217,6 +5229,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; @@ -6116,6 +6133,10 @@ if (peer->fromdomainport) 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; } @@ -6514,6 +6535,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"); @@ -14985,6 +15009,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"}, @@ -20064,6 +20230,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 */ @@ -20876,8 +21044,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 @@ -21655,6 +21823,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) { @@ -22090,6 +22365,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'; } @@ -22099,10 +22378,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 */ @@ -24870,6 +25188,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) { @@ -27556,6 +27901,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 */ @@ -27753,6 +28099,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; @@ -27856,6 +28229,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 { @@ -27879,6 +28255,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; @@ -27956,6 +28354,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) { @@ -31151,6 +31700,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. */ @@ -33466,6 +34026,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"), @@ -34280,6 +34842,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.6-cert4-orig/channels/sip/include/sip.h certified-asterisk-11.6-cert4/channels/sip/include/sip.h --- certified-asterisk-11.6-cert4-orig/channels/sip/include/sip.h 2013-09-12 16:19:39.000000000 -0400 +++ certified-asterisk-11.6-cert4/channels/sip/include/sip.h 2014-07-27 19:04:30.941569925 -0400 @@ -457,6 +457,7 @@ DIALOG_INFO_XML, CPIM_PIDF_XML, PIDF_XML, + FEATURE_EVENTS, MWI_NOTIFICATION, CALL_COMPLETION, }; @@ -1060,6 +1061,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 */ @@ -1092,6 +1094,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 */ @@ -1298,6 +1301,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. @@ -1320,6 +1324,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 */ @@ -1361,6 +1366,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) */