diff -urN certified-asterisk-1.8.15-cert5-orig/channels/chan_sip.c certified-asterisk-1.8.15-cert5/channels/chan_sip.c --- certified-asterisk-1.8.15-cert5-orig/channels/chan_sip.c 2014-03-10 14:12:16.000000000 -0400 +++ certified-asterisk-1.8.15-cert5/channels/chan_sip.c 2014-07-27 19:47:05.000000000 -0400 @@ -266,6 +266,7 @@ #include "asterisk/aoc.h" #include "asterisk/custom_control_frame.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" @@ -628,6 +629,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 */ }; @@ -1384,6 +1386,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(char *context, char *exten, struct state_notify_data *data, struct sip_pvt *p); @@ -1437,6 +1440,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); @@ -1525,6 +1530,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); @@ -1576,6 +1583,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, int debug, uint32_t seqno, struct ast_sockaddr *addr, int *nounlock); @@ -3017,6 +3025,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"); } @@ -4721,6 +4733,11 @@ dialog_unlink_all(peer->mwipvt); 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); @@ -5473,6 +5490,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; } @@ -5856,6 +5877,9 @@ /* Remove link from peer to subscription of MWI */ if (p->relatedpeer && p->relatedpeer->mwipvt) p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt"); + /* 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"); @@ -13282,6 +13306,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))) { + 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))) { + 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"}, @@ -18096,6 +18262,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 = unref_peer(peer, "sip_show_peer: unref_peer: done with peer ptr"); } else if (peer && type == 1) { /* manager listing */ @@ -18870,8 +19038,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(mailbox_str->str, "") : "", cur->expiry @@ -19597,6 +19765,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 = 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 = 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 = 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 = 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) { @@ -20020,6 +20295,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'; } @@ -20029,10 +20308,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 = 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); + } + + 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 */ @@ -22736,6 +23054,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_copy_string(chan->context, context, sizeof(chan->context)); + ast_copy_string(chan->exten, extension, sizeof(chan->exten)); + chan->priority = 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) { @@ -25270,13 +25615,13 @@ /*! \brief Handle incoming SUBSCRIBE request */ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e) { - int gotdest = 0; int res = 0; struct sip_peer *authpeer = NULL; const char *eventheader = get_header(req, "Event"); /* Get Event package name */ int resubscribe = (p->subscribed != NONE) && !req->ignore; char *event_end; ptrdiff_t event_len = 0; + int senddonotdisturb = 0, sendcallforward = 0; if (p->initreq.headers) { /* We already have a dialog */ @@ -25375,39 +25720,38 @@ return 0; } - if (strncmp(eventheader, "message-summary", MAX(event_len, 15)) && strncmp(eventheader, "call-completion", MAX(event_len, 15))) { - /* Get destination right away */ - gotdest = get_destination(p, NULL, NULL); - } - /* Get full contact header - this needs to be used as a request URI in NOTIFY's */ parse_ok_contact(p, req); - build_contact(p); - if (gotdest != SIP_GET_DEST_EXTEN_FOUND) { - if (gotdest == SIP_GET_DEST_INVALID_URI) { - transmit_response(p, "416 Unsupported URI scheme", req); - } else { - transmit_response(p, "404 Not Found", req); - } - pvt_set_needdestroy(p, "subscription target not found"); - if (authpeer) { - unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)"); - } - return 0; - } /* Initialize tag for new subscriptions */ if (ast_strlen_zero(p->tag)) make_our_tag(p); if (!strncmp(eventheader, "presence", MAX(event_len, 8)) || !strncmp(eventheader, "dialog", MAX(event_len, 6))) { /* Presence, RFC 3842 */ + int gotdest; unsigned int pidf_xml; const char *accept; int start = 0; enum subscriptiontype subscribed = NONE; const char *unknown_acceptheader = NULL; + /* Get destination right away */ + gotdest = get_destination(p, NULL, NULL); + + if (gotdest != SIP_GET_DEST_EXTEN_FOUND) { + if (gotdest == SIP_GET_DEST_INVALID_URI) { + transmit_response(p, "416 Unsupported URI scheme", req); + } else { + transmit_response(p, "404 Not Found", req); + } + pvt_set_needdestroy(p, "subscription target not found"); + if (authpeer) { + unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)"); + } + return 0; + } + /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ accept = __get_header(req, "Accept", &start); while ((subscribed == NONE) && !ast_strlen_zero(accept)) { @@ -25479,6 +25823,33 @@ } else { p->subscribed = subscribed; } + } else if (!strncmp(eventheader, "as-feature-event", MAX(event_len, 16) )) { + 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) { + 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) { + unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr"); + } + p->relatedpeer = ref_peer(authpeer, "setting dialog's relatedpeer pointer"); + } + /* Do not release authpeer here */ } else if (!strncmp(eventheader, "message-summary", MAX(event_len, 15))) { int start = 0; int found_supported = 0; @@ -25593,6 +25964,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 { @@ -25616,6 +25990,28 @@ ao2_lock(p); 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(get_header(req, "Content-Length"))) { + struct sip_peer *peer = p->relatedpeer; + ref_peer(peer, "ensure a peer ref is held during feature events sending"); + sip_send_donotdisturb(peer); + sip_send_callforward(peer); + unref_peer(peer, "release a peer ref now that feature events is sent"); + } + if (p->relatedpeer && senddonotdisturb) { + struct sip_peer *peer = p->relatedpeer; + ref_peer(peer, "ensure a peer ref is held during feature events sending"); + sip_send_donotdisturb(peer); + unref_peer(peer, "release a peer ref now that feature events is sent"); + } + if (p->relatedpeer && sendcallforward) { + struct sip_peer *peer = p->relatedpeer; + ref_peer(peer, "ensure a peer ref is held during feature events sending"); + sip_send_callforward(peer); + 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; @@ -25657,6 +26053,171 @@ 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 + int content_length; + const char *content_length_str = get_header(req, "Content-Length"); + const char *content_type = get_header(req, "Content-Type"); + char featureevent_body[SIPBUFSIZE]; + struct ast_xml_doc *featureevent_doc = NULL; + struct ast_xml_node *root_node; + + if (!atoi(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 (ast_strlen_zero(content_length_str)) { + ast_log(LOG_WARNING, "No content length. Can't determine bounds of feature events document\n"); + return FALSE; + } + + if (sscanf(content_length_str, "%30d", &content_length) != 1) { + ast_log(LOG_WARNING, "Invalid content length provided\n"); + return FALSE; + } + + if (content_length > sizeof(featureevent_body)) { + ast_log(LOG_WARNING, "Content length of feature events document truncated to %d bytes\n", (int) sizeof(featureevent_body)); + content_length = sizeof(featureevent_body); + } + + get_pidf_body(req, featureevent_body, content_length); + + 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) { @@ -28591,6 +29152,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. */ @@ -30760,6 +31332,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"), @@ -31045,6 +31619,8 @@ MEMBER(sip_peer, inRinging, 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-1.8.15-cert5-orig/channels/sip/include/sip.h certified-asterisk-1.8.15-cert5/channels/sip/include/sip.h --- certified-asterisk-1.8.15-cert5-orig/channels/sip/include/sip.h 2013-03-27 11:19:48.000000000 -0400 +++ certified-asterisk-1.8.15-cert5/channels/sip/include/sip.h 2014-07-27 19:41:15.000000000 -0400 @@ -427,6 +427,7 @@ DIALOG_INFO_XML, CPIM_PIDF_XML, PIDF_XML, + FEATURE_EVENTS, MWI_NOTIFICATION, CALL_COMPLETION, }; @@ -994,6 +995,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 */ @@ -1023,6 +1025,7 @@ * for incoming calls */ unsigned short req_secure_signaling:1;/*!< Whether we are required to have secure signaling or not */ + 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 */ @@ -1218,6 +1221,7 @@ AST_STRING_FIELD(mwi_from); /*!< Name to place in From header for outgoing NOTIFY requests */ AST_STRING_FIELD(engine); /*!< RTP Engine to use */ AST_STRING_FIELD(unsolicited_mailbox); /*!< Mailbox to store received unsolicited MWI NOTIFY messages information in */ + 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. @@ -1240,6 +1244,7 @@ int inUse; /*!< Number of calls in use */ int inRinging; /*!< 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 */ @@ -1277,6 +1282,7 @@ struct ast_ha *directmediaha; /*!< 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) */