<p>Jenkins2 <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/8374">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Richard Mudgett: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Jenkins2: Approved for Submit

</div><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><br>ASTERISK-27697<br><br>Change-Id: If7f3151a6d633e414d5dc319d5efc1443c43dd29<br>---<br>M CHANGES<br>M res/res_pjsip.c<br>M res/res_pjsip_notify.c<br>3 files changed, 225 insertions(+), 13 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/CHANGES b/CHANGES<br>index 84c302a..0b50561 100644<br>--- a/CHANGES<br>+++ b/CHANGES<br>@@ -19,6 +19,11 @@<br>    MALLOC_DEBUG.  The cache gets in the way of determining if the pool contents<br>    are used after free and who freed it.<br> <br>+res_pjsip_notify<br>+------------------<br>+ * Extend the PJSIPNotify AMI command to send an in-dialog notify on a<br>+   channel.<br>+<br> ------------------------------------------------------------------------------<br> --- Functionality changes from Asterisk 15.2.0 to Asterisk 15.3.0 ------------<br> ------------------------------------------------------------------------------<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index 277606b..acb305a 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -3616,8 +3616,6 @@<br> {<br>   const pjsip_method *pmethod = get_pjsip_method(method);<br> <br>-   ast_assert(endpoint != NULL);<br>-<br>      if (!pmethod) {<br>               ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method);<br>                 return -1;<br>@@ -3626,6 +3624,7 @@<br>     if (dlg) {<br>            return create_in_dialog_request(pmethod, dlg, tdata);<br>         } else {<br>+             ast_assert(endpoint != NULL);<br>                 return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);<br>  }<br> }<br>diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c<br>index 59b7c6e..26c670f 100644<br>--- a/res/res_pjsip_notify.c<br>+++ b/res/res_pjsip_notify.c<br>@@ -25,6 +25,7 @@<br> #include "asterisk.h"<br> <br> #include <pjsip.h><br>+#include <pjsip_ua.h><br> <br> #include "asterisk/cli.h"<br> #include "asterisk/config.h"<br>@@ -32,12 +33,13 @@<br> #include "asterisk/module.h"<br> #include "asterisk/pbx.h"<br> #include "asterisk/res_pjsip.h"<br>+#include "asterisk/res_pjsip_session.h"<br> #include "asterisk/sorcery.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_sip_session *session;<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>+        ao2_cleanup(data->session);<br>+       ast_variables_destroy(info);<br>+}<br>+<br> static void build_ami_notify(pjsip_tx_data *tdata, void *info);<br> <br> /*!<br>@@ -424,6 +452,28 @@<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_sip_session *session, void *info)<br>+{<br>+     struct notify_channel_data *data;<br>+<br>+ data = ao2_alloc_options(sizeof(*data), notify_ami_channel_data_destroy,<br>+             AO2_ALLOC_OPT_LOCK_NOLOCK);<br>+  if (!data) {<br>+         return NULL;<br>+ }<br>+<br>+ data->session = session;<br>   data->info = info;<br>         data->build_notify = build_ami_notify;<br> <br>@@ -672,9 +722,45 @@<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>+ if (!data->session->channel<br>+            || !data->session->inv_session<br>+         || data->session->inv_session->state < PJSIP_INV_STATE_EARLY<br>+             || data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {<br>+             return -1;<br>+   }<br>+<br>+ ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel));<br>+<br>+ dlg = data->session->inv_session->dlg;<br>+<br>+   if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {<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>+            return -1;<br>+   }<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 +770,10 @@<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_sip_session *session, void *info);<br>+<br> /*!<br>  * \internal<br>  * \brief Send a NOTIFY request to the endpoint within a threaded task.<br>@@ -726,6 +816,68 @@<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>+    struct ast_channel *ch;<br>+      struct ast_sip_session *session;<br>+     struct ast_sip_channel_pvt *ch_pvt;<br>+<br>+       /* note: this increases the refcount of the channel */<br>+       ch = ast_channel_get_by_name(channel_name);<br>+  if (!ch) {<br>+           ast_debug(1, "No channel found with name %s", channel_name);<br>+               return INVALID_CHANNEL;<br>+      }<br>+<br>+ if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {<br>+              ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name);<br>+               ast_channel_unref(ch);<br>+               return INVALID_CHANNEL;<br>+      }<br>+<br>+ ast_channel_lock(ch);<br>+        ch_pvt = ast_channel_tech_pvt(ch);<br>+   session = ch_pvt->session;<br>+<br>+     if (!session || !session->inv_session<br>+                     || session->inv_session->state < PJSIP_INV_STATE_EARLY<br>+                      || session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {<br>+              ast_debug(1, "No active session for channel %s\n", channel_name);<br>+          ast_channel_unlock(ch);<br>+              ast_channel_unref(ch);<br>+               return INVALID_CHANNEL;<br>+      }<br>+<br>+ ao2_ref(session, +1);<br>+        ast_channel_unlock(ch);<br>+<br>+   /* don't keep a reference to the channel, we've got a reference to the session */<br>+    ast_channel_unref(ch);<br>+<br>+    /*<br>+    * data_create will take ownership of the session,<br>+    * and take care of releasing the ref.<br>+        */<br>+  data = data_create(session, info);<br>+   if (!data) {<br>+         ao2_ref(session, -1);<br>+                return ALLOC_ERROR;<br>+  }<br>+<br>+ if (ast_sip_push_task(session->serializer, notify_channel, data)) {<br>+               ao2_ref(data, -1);<br>            return TASK_PUSH_ERROR;<br>       }<br> <br>@@ -915,6 +1067,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 +1100,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,22 +1124,70 @@<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>+     int count = 0;<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>-                  "both 'URI' and 'Endpoint'. You must use only one of the two.\n");<br>+ if (!ast_strlen_zero(endpoint_name)) {<br>+               ++count;<br>+     }<br>+    if (!ast_strlen_zero(uri)) {<br>+         ++count;<br>+     }<br>+    if (!ast_strlen_zero(channel)) {<br>+             ++count;<br>+     }<br>+<br>+ if (1 < count) {<br>+          astman_send_error(s, m,<br>+                      "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.  "<br>+                 "You must use only one of them.");<br>  } else if (!ast_strlen_zero(endpoint_name)) {<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>+             astman_send_error(s, m,<br>+                      "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");<br>  }<br> <br>  return 0;<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: merged </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: 10 </div>
<div style="display:none"> Gerrit-Owner: Nathan Bruning <nathan@iperity.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Nathan Bruning <nathan@iperity.com> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Sean Bright <sean.bright@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: lvl <digium@lvlconsultancy.nl> </div>