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