mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 12:36:58 +00:00
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. ASTERISK-27697 Change-Id: If7f3151a6d633e414d5dc319d5efc1443c43dd29
This commit is contained in:
committed by
Richard Mudgett
parent
fabfe701bb
commit
1cd704de36
5
CHANGES
5
CHANGES
@@ -122,6 +122,11 @@ res_pjproject
|
|||||||
MALLOC_DEBUG. The cache gets in the way of determining if the pool contents
|
MALLOC_DEBUG. The cache gets in the way of determining if the pool contents
|
||||||
are used after free and who freed it.
|
are used after free and who freed it.
|
||||||
|
|
||||||
|
res_pjsip_notify
|
||||||
|
------------------
|
||||||
|
* Extend the PJSIPNotify AMI command to send an in-dialog notify on a
|
||||||
|
channel.
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
--- Functionality changes from Asterisk 15.2.0 to Asterisk 15.3.0 ------------
|
--- Functionality changes from Asterisk 15.2.0 to Asterisk 15.3.0 ------------
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
@@ -3801,8 +3801,6 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
|
|||||||
{
|
{
|
||||||
const pjsip_method *pmethod = get_pjsip_method(method);
|
const pjsip_method *pmethod = get_pjsip_method(method);
|
||||||
|
|
||||||
ast_assert(endpoint != NULL);
|
|
||||||
|
|
||||||
if (!pmethod) {
|
if (!pmethod) {
|
||||||
ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method);
|
ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -3811,6 +3809,7 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
|
|||||||
if (dlg) {
|
if (dlg) {
|
||||||
return create_in_dialog_request(pmethod, dlg, tdata);
|
return create_in_dialog_request(pmethod, dlg, tdata);
|
||||||
} else {
|
} else {
|
||||||
|
ast_assert(endpoint != NULL);
|
||||||
return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);
|
return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
#include "asterisk.h"
|
#include "asterisk.h"
|
||||||
|
|
||||||
#include <pjsip.h>
|
#include <pjsip.h>
|
||||||
|
#include <pjsip_ua.h>
|
||||||
|
|
||||||
#include "asterisk/cli.h"
|
#include "asterisk/cli.h"
|
||||||
#include "asterisk/config.h"
|
#include "asterisk/config.h"
|
||||||
@@ -32,12 +33,13 @@
|
|||||||
#include "asterisk/module.h"
|
#include "asterisk/module.h"
|
||||||
#include "asterisk/pbx.h"
|
#include "asterisk/pbx.h"
|
||||||
#include "asterisk/res_pjsip.h"
|
#include "asterisk/res_pjsip.h"
|
||||||
|
#include "asterisk/res_pjsip_session.h"
|
||||||
#include "asterisk/sorcery.h"
|
#include "asterisk/sorcery.h"
|
||||||
|
|
||||||
/*** DOCUMENTATION
|
/*** DOCUMENTATION
|
||||||
<manager name="PJSIPNotify" language="en_US">
|
<manager name="PJSIPNotify" language="en_US">
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
<syntax>
|
<syntax>
|
||||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||||
@@ -47,6 +49,9 @@
|
|||||||
<parameter name="URI" required="false">
|
<parameter name="URI" required="false">
|
||||||
<para>Abritrary URI to which to send the NOTIFY.</para>
|
<para>Abritrary URI to which to send the NOTIFY.</para>
|
||||||
</parameter>
|
</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">
|
<parameter name="Variable" required="true">
|
||||||
<para>Appends variables as headers/content to the NOTIFY. If the variable is
|
<para>Appends variables as headers/content to the NOTIFY. If the variable is
|
||||||
named <literal>Content</literal>, then the value will compose the body
|
named <literal>Content</literal>, then the value will compose the body
|
||||||
@@ -55,14 +60,14 @@
|
|||||||
</parameter>
|
</parameter>
|
||||||
</syntax>
|
</syntax>
|
||||||
<description>
|
<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
|
<para>All parameters for this event must be specified in the body of this
|
||||||
request via multiple <literal>Variable: name=value</literal> sequences.</para>
|
request via multiple <literal>Variable: name=value</literal> sequences.</para>
|
||||||
<note><para>One (and only one) of <literal>Endpoint</literal> or
|
<note><para>One (and only one) of <literal>Endpoint</literal>,
|
||||||
<literal>URI</literal> must be specified. If <literal>URI</literal> is used,
|
<literal>URI</literal>, or <literal>Channel</literal> must be specified.
|
||||||
the default outbound endpoint will be used to send the message. If the default
|
If <literal>URI</literal> is used, the default outbound endpoint will be used
|
||||||
outbound endpoint isn't configured, this command can not send to an arbitrary
|
to send the message. If the default outbound endpoint isn't configured, this command
|
||||||
URI.</para></note>
|
can not send to an arbitrary URI.</para></note>
|
||||||
</description>
|
</description>
|
||||||
</manager>
|
</manager>
|
||||||
<configInfo name="res_pjsip_notify" language="en_US">
|
<configInfo name="res_pjsip_notify" language="en_US">
|
||||||
@@ -289,6 +294,16 @@ struct notify_uri_data {
|
|||||||
void (*build_notify)(pjsip_tx_data *, void *);
|
void (*build_notify)(pjsip_tx_data *, void *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Structure to hold task data for notifications (channel variant)
|
||||||
|
*/
|
||||||
|
struct notify_channel_data {
|
||||||
|
struct ast_sip_session *session;
|
||||||
|
void *info;
|
||||||
|
void (*build_notify)(pjsip_tx_data *, void *);
|
||||||
|
};
|
||||||
|
|
||||||
static void notify_cli_uri_data_destroy(void *obj)
|
static void notify_cli_uri_data_destroy(void *obj)
|
||||||
{
|
{
|
||||||
struct notify_uri_data *data = obj;
|
struct notify_uri_data *data = obj;
|
||||||
@@ -381,6 +396,19 @@ static void notify_ami_uri_data_destroy(void *obj)
|
|||||||
ast_variables_destroy(info);
|
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;
|
||||||
|
|
||||||
|
ao2_cleanup(data->session);
|
||||||
|
ast_variables_destroy(info);
|
||||||
|
}
|
||||||
|
|
||||||
static void build_ami_notify(pjsip_tx_data *tdata, void *info);
|
static void build_ami_notify(pjsip_tx_data *tdata, void *info);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -430,6 +458,28 @@ static struct notify_uri_data* notify_ami_uri_data_create(
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Construct a notify channel data object for AMI.
|
||||||
|
*/
|
||||||
|
static struct notify_channel_data *notify_ami_channel_data_create(
|
||||||
|
struct ast_sip_session *session, void *info)
|
||||||
|
{
|
||||||
|
struct notify_channel_data *data;
|
||||||
|
|
||||||
|
data = ao2_alloc_options(sizeof(*data), notify_ami_channel_data_destroy,
|
||||||
|
AO2_ALLOC_OPT_LOCK_NOLOCK);
|
||||||
|
if (!data) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->session = session;
|
||||||
|
data->info = info;
|
||||||
|
data->build_notify = build_ami_notify;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Checks if the given header name is not allowed.
|
* \brief Checks if the given header name is not allowed.
|
||||||
@@ -672,9 +722,45 @@ static int notify_uri(void *obj)
|
|||||||
return 0;
|
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;
|
||||||
|
|
||||||
|
if (!data->session->channel
|
||||||
|
|| !data->session->inv_session
|
||||||
|
|| data->session->inv_session->state < PJSIP_INV_STATE_EARLY
|
||||||
|
|| data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel));
|
||||||
|
|
||||||
|
dlg = data->session->inv_session->dlg;
|
||||||
|
|
||||||
|
if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {
|
||||||
|
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)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
enum notify_result {
|
enum notify_result {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
INVALID_ENDPOINT,
|
INVALID_ENDPOINT,
|
||||||
|
INVALID_CHANNEL,
|
||||||
ALLOC_ERROR,
|
ALLOC_ERROR,
|
||||||
TASK_PUSH_ERROR
|
TASK_PUSH_ERROR
|
||||||
};
|
};
|
||||||
@@ -684,6 +770,10 @@ typedef struct notify_data *(*task_data_create)(
|
|||||||
|
|
||||||
typedef struct notify_uri_data *(*task_uri_data_create)(
|
typedef struct notify_uri_data *(*task_uri_data_create)(
|
||||||
const char *uri, void *info);
|
const char *uri, void *info);
|
||||||
|
|
||||||
|
typedef struct notify_channel_data *(*task_channel_data_create)(
|
||||||
|
struct ast_sip_session *session, void *info);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Send a NOTIFY request to the endpoint within a threaded task.
|
* \brief Send a NOTIFY request to the endpoint within a threaded task.
|
||||||
@@ -732,6 +822,68 @@ static enum notify_result push_notify_uri(const char *uri, void *info,
|
|||||||
return SUCCESS;
|
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;
|
||||||
|
struct ast_channel *ch;
|
||||||
|
struct ast_sip_session *session;
|
||||||
|
struct ast_sip_channel_pvt *ch_pvt;
|
||||||
|
|
||||||
|
/* note: this increases the refcount of the channel */
|
||||||
|
ch = ast_channel_get_by_name(channel_name);
|
||||||
|
if (!ch) {
|
||||||
|
ast_debug(1, "No channel found with name %s", channel_name);
|
||||||
|
return INVALID_CHANNEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {
|
||||||
|
ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name);
|
||||||
|
ast_channel_unref(ch);
|
||||||
|
return INVALID_CHANNEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_channel_lock(ch);
|
||||||
|
ch_pvt = ast_channel_tech_pvt(ch);
|
||||||
|
session = ch_pvt->session;
|
||||||
|
|
||||||
|
if (!session || !session->inv_session
|
||||||
|
|| session->inv_session->state < PJSIP_INV_STATE_EARLY
|
||||||
|
|| session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
|
||||||
|
ast_debug(1, "No active session for channel %s\n", channel_name);
|
||||||
|
ast_channel_unlock(ch);
|
||||||
|
ast_channel_unref(ch);
|
||||||
|
return INVALID_CHANNEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ao2_ref(session, +1);
|
||||||
|
ast_channel_unlock(ch);
|
||||||
|
|
||||||
|
/* don't keep a reference to the channel, we've got a reference to the session */
|
||||||
|
ast_channel_unref(ch);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* data_create will take ownership of the session,
|
||||||
|
* and take care of releasing the ref.
|
||||||
|
*/
|
||||||
|
data = data_create(session, info);
|
||||||
|
if (!data) {
|
||||||
|
ao2_ref(session, -1);
|
||||||
|
return ALLOC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sip_push_task(session->serializer, notify_channel, data)) {
|
||||||
|
ao2_ref(data, -1);
|
||||||
|
return TASK_PUSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Do completion on the endpoint.
|
* \brief Do completion on the endpoint.
|
||||||
@@ -915,6 +1067,10 @@ static void manager_notify_endpoint(struct mansession *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
|
switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
|
||||||
|
case INVALID_CHANNEL:
|
||||||
|
/* Shouldn't be possible. */
|
||||||
|
ast_assert(0);
|
||||||
|
break;
|
||||||
case INVALID_ENDPOINT:
|
case INVALID_ENDPOINT:
|
||||||
ast_variables_destroy(vars);
|
ast_variables_destroy(vars);
|
||||||
astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
|
astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
|
||||||
@@ -944,6 +1100,10 @@ static void manager_notify_uri(struct mansession *s,
|
|||||||
struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
|
struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
|
||||||
|
|
||||||
switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {
|
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:
|
case INVALID_ENDPOINT:
|
||||||
/* Shouldn't be possible. */
|
/* Shouldn't be possible. */
|
||||||
ast_assert(0);
|
ast_assert(0);
|
||||||
@@ -962,6 +1122,38 @@ static void manager_notify_uri(struct mansession *s,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \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
|
* \internal
|
||||||
* \brief AMI entry point to send a SIP notify to an endpoint.
|
* \brief AMI entry point to send a SIP notify to an endpoint.
|
||||||
@@ -970,16 +1162,32 @@ static int manager_notify(struct mansession *s, const struct message *m)
|
|||||||
{
|
{
|
||||||
const char *endpoint_name = astman_get_header(m, "Endpoint");
|
const char *endpoint_name = astman_get_header(m, "Endpoint");
|
||||||
const char *uri = astman_get_header(m, "URI");
|
const char *uri = astman_get_header(m, "URI");
|
||||||
|
const char *channel = astman_get_header(m, "Channel");
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
if (!ast_strlen_zero(endpoint_name) && !ast_strlen_zero(uri)) {
|
if (!ast_strlen_zero(endpoint_name)) {
|
||||||
astman_send_error(s, m, "PJSIPNotify action can not handle a request specifying "
|
++count;
|
||||||
"both 'URI' and 'Endpoint'. You must use only one of the two.\n");
|
}
|
||||||
|
if (!ast_strlen_zero(uri)) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
if (!ast_strlen_zero(channel)) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 < count) {
|
||||||
|
astman_send_error(s, m,
|
||||||
|
"PJSIPNotify requires either an endpoint name, a SIP URI, or a channel. "
|
||||||
|
"You must use only one of them.");
|
||||||
} else if (!ast_strlen_zero(endpoint_name)) {
|
} else if (!ast_strlen_zero(endpoint_name)) {
|
||||||
manager_notify_endpoint(s, m, endpoint_name);
|
manager_notify_endpoint(s, m, endpoint_name);
|
||||||
} else if (!ast_strlen_zero(uri)) {
|
} else if (!ast_strlen_zero(uri)) {
|
||||||
manager_notify_uri(s, m, uri);
|
manager_notify_uri(s, m, uri);
|
||||||
|
} else if (!ast_strlen_zero(channel)) {
|
||||||
|
manager_notify_channel(s, m, channel);
|
||||||
} else {
|
} else {
|
||||||
astman_send_error(s, m, "PJSIPNotify requires either an endpoint name or a SIP URI.");
|
astman_send_error(s, m,
|
||||||
|
"PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user