<p>Nathan Bruning has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/8374">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_notify.c: enable in-dialog NOTIFY<br><br>This patch adds support to send in-dialog SIP NOTIFY commands on<br>chan_pjsip channels, similar to the functionality recently added<br>for chan_sip (ASTERISK-27461).<br><br>This extends res_pjsip_notify to allow for in-dialog messages.<br>A minor addition to res_pjsip is needed in order to fetch the active<br>SIP dialog.<br><br>ASTERISK-27697<br><br>Change-Id: If7f3151a6d633e414d5dc319d5efc1443c43dd29<br>---<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip_notify.c<br>3 files changed, 204 insertions(+), 7 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/74/8374/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h<br>index 2a7eb0a..a9b765c 100644<br>--- a/include/asterisk/res_pjsip.h<br>+++ b/include/asterisk/res_pjsip.h<br>@@ -3101,4 +3101,14 @@<br> */<br> void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element);<br> <br>+/*!<br>+ * \brief Get the dialog associated with a channel, if one exists. The channel must be locked.<br>+ * \since 15.3<br>+ *<br>+ * \param channel The channel of which to request the dialog<br>+ *<br>+ * \return the pjsip_dialog, or NULL<br>+ */<br>+struct pjsip_dialog* ast_sip_get_dialog_for_channel(struct ast_channel* ch);<br>+<br> #endif /* _RES_PJSIP_H */<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index 8849b0b..b112342 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -19,6 +19,7 @@<br> #include "asterisk.h"<br> <br> #include <pjsip.h><br>+#include <pjsip_ua.h><br> /* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */<br> #include <pjsip_simple.h><br> #include <pjsip/sip_transaction.h><br>@@ -44,6 +45,7 @@<br> #include "asterisk/test.h"<br> #include "asterisk/res_pjsip_presence_xml.h"<br> #include "asterisk/res_pjproject.h"<br>+#include "asterisk/res_pjsip_session.h"<br> <br> /*** MODULEINFO<br> <depend>pjproject</depend><br>@@ -4741,6 +4743,25 @@<br> return ast_threadpool_queue_size(sip_threadpool);<br> }<br> <br>+struct pjsip_dialog* ast_sip_get_dialog_for_channel(struct ast_channel* ch) {<br>+<br>+ if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {<br>+ ast_log(LOG_WARNING, "Cannot get dialog for a non-PJSIP channel\n");<br>+ return NULL;<br>+ }<br>+<br>+ struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ch);<br>+ struct ast_sip_session *session = channel->session;<br>+<br>+ if (!session || !session->inv_session || session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {<br>+ ast_log(LOG_WARNING, "Cannot get dialog; no session or disconnected\n");<br>+ return NULL;<br>+ }<br>+<br>+ return session->inv_session->dlg;<br>+}<br>+<br>+<br> #ifdef TEST_FRAMEWORK<br> AST_TEST_DEFINE(xml_sanitization_end_null)<br> {<br>diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c<br>index 59b7c6e..d46dc1b 100644<br>--- a/res/res_pjsip_notify.c<br>+++ b/res/res_pjsip_notify.c<br>@@ -33,11 +33,13 @@<br> #include "asterisk/pbx.h"<br> #include "asterisk/res_pjsip.h"<br> #include "asterisk/sorcery.h"<br>+#include "asterisk/channel.h"<br>+#include "asterisk/res_pjsip.h"<br> <br> /*** DOCUMENTATION<br> <manager name="PJSIPNotify" language="en_US"><br> <synopsis><br>- Send a NOTIFY to either an endpoint or an arbitrary URI.<br>+ Send a NOTIFY to either an endpoint, an arbitrary URI or inside a SIP dialog.<br> </synopsis><br> <syntax><br> <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /><br>@@ -47,6 +49,9 @@<br> <parameter name="URI" required="false"><br> <para>Abritrary URI to which to send the NOTIFY.</para><br> </parameter><br>+ <parameter name="channel" required="false"><br>+ <para>Channel name to send the NOTIFY. Must be a PJSIP channel.</para><br>+ </parameter><br> <parameter name="Variable" required="true"><br> <para>Appends variables as headers/content to the NOTIFY. If the variable is<br> named <literal>Content</literal>, then the value will compose the body<br>@@ -55,14 +60,14 @@<br> </parameter><br> </syntax><br> <description><br>- <para>Sends a NOTIFY to an endpoint or an arbitrary URI.</para><br>+ <para>Sends a NOTIFY to an endpoint, an arbitrary URI or inside a SIP dialog.</para><br> <para>All parameters for this event must be specified in the body of this<br> request via multiple <literal>Variable: name=value</literal> sequences.</para><br>- <note><para>One (and only one) of <literal>Endpoint</literal> or<br>- <literal>URI</literal> must be specified. If <literal>URI</literal> is used,<br>- the default outbound endpoint will be used to send the message. If the default<br>- outbound endpoint isn't configured, this command can not send to an arbitrary<br>- URI.</para></note><br>+ <note><para>One (and only one) of <literal>Endpoint</literal>,<br>+ <literal>URI</literal> or <literal>Channel</literal> must be specified.<br>+ If <literal>URI</literal> is used, the default outbound endpoint will be used<br>+ to send the message. If the default outbound endpoint isn't configured, this command<br>+ can not send to an arbitrary URI.</para></note><br> </description><br> </manager><br> <configInfo name="res_pjsip_notify" language="en_US"><br>@@ -289,6 +294,16 @@<br> void (*build_notify)(pjsip_tx_data *, void *);<br> };<br> <br>+/*!<br>+ * \internal<br>+ * \brief Structure to hold task data for notifications (channel variant)<br>+ */<br>+struct notify_channel_data {<br>+ struct ast_channel* channel;<br>+ void *info;<br>+ void (*build_notify)(pjsip_tx_data *, void *);<br>+};<br>+<br> static void notify_cli_uri_data_destroy(void *obj)<br> {<br> struct notify_uri_data *data = obj;<br>@@ -381,6 +396,19 @@<br> ast_variables_destroy(info);<br> }<br> <br>+/*!<br>+ * \internal<br>+ * \brief Destroy the notify AMI channel data releasing any resources.<br>+ */<br>+static void notify_ami_channel_data_destroy(void *obj)<br>+{<br>+ struct notify_channel_data *data = obj;<br>+ struct ast_variable *info = data->info;<br>+<br>+ ast_variables_destroy(info);<br>+ ast_channel_unref(data->channel);<br>+}<br>+<br> static void build_ami_notify(pjsip_tx_data *tdata, void *info);<br> <br> /*!<br>@@ -424,6 +452,26 @@<br> return NULL;<br> }<br> <br>+ data->info = info;<br>+ data->build_notify = build_ami_notify;<br>+<br>+ return data;<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Construct a notify channel data object for AMI.<br>+ */<br>+static struct notify_channel_data* notify_ami_channel_data_create(<br>+ struct ast_channel* channel, void *info)<br>+{<br>+ struct notify_channel_data *data = ao2_alloc(sizeof(*data),<br>+ notify_ami_channel_data_destroy);<br>+ if (!data) {<br>+ return NULL;<br>+ }<br>+<br>+ data->channel = channel;<br> data->info = info;<br> data->build_notify = build_ami_notify;<br> <br>@@ -672,9 +720,53 @@<br> return 0;<br> }<br> <br>+/*!<br>+ * \internal<br>+ * \brief Send a notify request to a channel.<br>+ */<br>+static int notify_channel(void *obj)<br>+{<br>+ RAII_VAR(struct notify_channel_data *, data, obj, ao2_cleanup);<br>+ pjsip_tx_data *tdata;<br>+ struct pjsip_dialog * dlg;<br>+<br>+ ast_channel_lock(data->channel);<br>+ ast_log(LOG_DEBUG, "Sending notity on channel %s\n", ast_channel_name(data->channel));<br>+ dlg = ast_sip_get_dialog_for_channel(data->channel);<br>+<br>+ // keep the channel locked so the dialog can't go away<br>+ ast_log(LOG_DEBUG, "Channel has active dialog: %p\n", dlg);<br>+<br>+ if (!dlg) {<br>+ ast_channel_unlock(data->channel);<br>+ ast_log(LOG_WARNING, "SIP NOTIFY - No active dialog for channel\n");<br>+ return -1;<br>+ }<br>+<br>+ if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {<br>+ ast_channel_unlock(data->channel);<br>+ ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request to channel");<br>+ return -1;<br>+ }<br>+<br>+ ast_sip_add_header(tdata, "Subscription-State", "terminated");<br>+ data->build_notify(tdata, data->info);<br>+<br>+ if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) {<br>+ ast_channel_unlock(data->channel);<br>+ ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request to channel\n");<br>+ return -1;<br>+ }<br>+<br>+ ast_channel_unlock(data->channel);<br>+<br>+ return 0;<br>+}<br>+<br> enum notify_result {<br> SUCCESS,<br> INVALID_ENDPOINT,<br>+ INVALID_CHANNEL,<br> ALLOC_ERROR,<br> TASK_PUSH_ERROR<br> };<br>@@ -684,6 +776,9 @@<br> <br> typedef struct notify_uri_data *(*task_uri_data_create)(<br> const char *uri, void *info);<br>+<br>+typedef struct notify_channel_data *(*task_channel_data_create)(<br>+ struct ast_channel* channel, void *info);<br> /*!<br> * \internal<br> * \brief Send a NOTIFY request to the endpoint within a threaded task.<br>@@ -725,6 +820,34 @@<br> }<br> <br> if (ast_sip_push_task(NULL, notify_uri, data)) {<br>+ ao2_cleanup(data);<br>+ return TASK_PUSH_ERROR;<br>+ }<br>+<br>+ return SUCCESS;<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Send a NOTIFY request in a channel within an threaded task.<br>+ */<br>+static enum notify_result push_notify_channel(const char *channel_name, void *info,<br>+ task_channel_data_create data_create)<br>+{<br>+ struct notify_channel_data *data;<br>+<br>+ // note: this increases the refcount of the channel<br>+ struct ast_channel* ch = ast_channel_get_by_name(channel_name);<br>+ if (!ch) {<br>+ ast_log(LOG_WARNING, "No channel found with name %s", channel_name);<br>+ return INVALID_CHANNEL;<br>+ }<br>+<br>+ if (!(data = data_create(ch, info))) {<br>+ return ALLOC_ERROR;<br>+ }<br>+<br>+ if (ast_sip_push_task(NULL, notify_channel, data)) {<br> ao2_cleanup(data);<br> return TASK_PUSH_ERROR;<br> }<br>@@ -915,6 +1038,10 @@<br> }<br> <br> switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {<br>+ case INVALID_CHANNEL:<br>+ /* Shouldn't be possible. */<br>+ ast_assert(0);<br>+ break;<br> case INVALID_ENDPOINT:<br> ast_variables_destroy(vars);<br> astman_send_error_va(s, m, "Unable to retrieve endpoint %s",<br>@@ -944,6 +1071,10 @@<br> struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);<br> <br> switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {<br>+ case INVALID_CHANNEL:<br>+ /* Shouldn't be possible. */<br>+ ast_assert(0);<br>+ break;<br> case INVALID_ENDPOINT:<br> /* Shouldn't be possible. */<br> ast_assert(0);<br>@@ -964,12 +1095,45 @@<br> <br> /*!<br> * \internal<br>+ * \brief Completes SIPNotify AMI command in channel mode.<br>+ */<br>+static void manager_notify_channel(struct mansession *s,<br>+ const struct message *m, const char *channel)<br>+{<br>+ struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);<br>+<br>+ switch (push_notify_channel(channel, vars, notify_ami_channel_data_create)) {<br>+ case INVALID_CHANNEL:<br>+ ast_variables_destroy(vars);<br>+ astman_send_error(s, m, "Channel not found");<br>+ break;<br>+ case INVALID_ENDPOINT:<br>+ /* Shouldn't be possible. */<br>+ ast_assert(0);<br>+ break;<br>+ case ALLOC_ERROR:<br>+ ast_variables_destroy(vars);<br>+ astman_send_error(s, m, "Unable to allocate NOTIFY task data");<br>+ break;<br>+ case TASK_PUSH_ERROR:<br>+ /* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */<br>+ astman_send_error(s, m, "Unable to push Notify task");<br>+ break;<br>+ case SUCCESS:<br>+ astman_send_ack(s, m, "NOTIFY sent");<br>+ break;<br>+ }<br>+}<br>+<br>+/*!<br>+ * \internal<br> * \brief AMI entry point to send a SIP notify to an endpoint.<br> */<br> static int manager_notify(struct mansession *s, const struct message *m)<br> {<br> const char *endpoint_name = astman_get_header(m, "Endpoint");<br> const char *uri = astman_get_header(m, "URI");<br>+ const char *channel = astman_get_header(m, "Channel");<br> <br> if (!ast_strlen_zero(endpoint_name) && !ast_strlen_zero(uri)) {<br> astman_send_error(s, m, "PJSIPNotify action can not handle a request specifying "<br>@@ -978,6 +1142,8 @@<br> manager_notify_endpoint(s, m, endpoint_name);<br> } else if (!ast_strlen_zero(uri)) {<br> manager_notify_uri(s, m, uri);<br>+ } else if (!ast_strlen_zero(channel)) {<br>+ manager_notify_channel(s, m, channel);<br> } else {<br> astman_send_error(s, m, "PJSIPNotify requires either an endpoint name or a SIP URI.");<br> }<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/8374">change 8374</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/8374"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 15 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: If7f3151a6d633e414d5dc319d5efc1443c43dd29 </div>
<div style="display:none"> Gerrit-Change-Number: 8374 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Nathan Bruning <nathan@iperity.com> </div>