[Asterisk-code-review] res pjsip notify.c: enable in-dialog NOTIFY (asterisk[15])

Nathan Bruning asteriskteam at digium.com
Thu Feb 22 13:23:46 CST 2018


Nathan Bruning has uploaded this change for review. ( https://gerrit.asterisk.org/8374


Change subject: res_pjsip_notify.c: enable in-dialog NOTIFY
......................................................................

res_pjsip_notify.c: enable in-dialog NOTIFY

This patch adds support to send in-dialog SIP NOTIFY commands on
chan_pjsip channels, similar to the functionality recently added
for chan_sip (ASTERISK-27461).

This extends res_pjsip_notify to allow for in-dialog messages.
A minor addition to res_pjsip is needed in order to fetch the active
SIP dialog.

ASTERISK-27697

Change-Id: If7f3151a6d633e414d5dc319d5efc1443c43dd29
---
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip_notify.c
3 files changed, 204 insertions(+), 7 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/74/8374/1

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

-- 
To view, visit https://gerrit.asterisk.org/8374
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 15
Gerrit-MessageType: newchange
Gerrit-Change-Id: If7f3151a6d633e414d5dc319d5efc1443c43dd29
Gerrit-Change-Number: 8374
Gerrit-PatchSet: 1
Gerrit-Owner: Nathan Bruning <nathan at iperity.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20180222/1e7cd842/attachment-0001.html>


More information about the asterisk-code-review mailing list