<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>