res_pjsip_header_funcs: Add new PJSIP_INHERITABLE_HEADER dialplan function

Adds a new PJSIP_INHERITABLE_HEADER dialplan function to add
inheritable headers from the inbound channel to an outbound
bridged channel.  This works similarly to the existing
PJSIP_HEADER function, but will set the header on the bridged
outbound channel's INVITE upon Dial.

Inheritable headers can be updated or removed from the inbound
channel as well as from a pre-dial handler

Resolves: #1670

UserNote: A new PJSIP_HEADER option has been added that allows
inheriting pjsip headers from the inbound to the outbound bridged
channel.
Example- same => n,Set(PJSIP_INHERITABLE_HEADER(add,X-custom-1)=alpha)
will add X-custom-1: alpha to the outbound pjsip channel INVITE
upon Dial.
pull/1790/head
Mike Bradeen 3 months ago
parent eaa6e8ed87
commit 24456a3c49

@ -52,7 +52,7 @@
<enumlist>
<enum name="read"><para>Returns instance <replaceable>number</replaceable>
of header <replaceable>name</replaceable>. A <literal>*</literal>
may be appended to <replaceable>name</replaceable> to iterate over all
may be appended to <replaceable>name</replaceable> to iterate over all
headers <emphasis>beginning with</emphasis> <replaceable>name</replaceable>.
</para></enum>
@ -144,6 +144,94 @@
</example>
</description>
</function>
<function name="PJSIP_INHERITABLE_HEADER" language="en_US">
<since>
<version>20.19.0</version>
<version>22.9.0</version>
<version>23.3.0</version>
</since>
<synopsis>
Adds, updates or removes the specified SIP header from a PJSIP or non-PJSIP channel
to be inherited to an outbound PJSIP channel.
</synopsis>
<syntax>
<parameter name="action" required="true">
<enumlist>
<enum name="add"><para>Adds a new header <replaceable>name</replaceable>
to this channel.</para></enum>
<enum name="update"><para>Updates instance <replaceable>number</replaceable>
of header <replaceable>name</replaceable> to a new value.
The header must already exist.</para></enum>
<enum name="remove"><para>Removes all instances of previously added headers
whose names match <replaceable>name</replaceable>. A <literal>*</literal>
may be appended to <replaceable>name</replaceable> to remove all headers
<emphasis>beginning with</emphasis> <replaceable>name</replaceable>.
<replaceable>name</replaceable> may be set to a single <literal>*</literal>
to clear <emphasis>all</emphasis> previously added headers. In all cases,
the number of headers actually removed is returned.</para></enum>
</enumlist>
</parameter>
<parameter name="name" required="true"><para>The name of the header.</para></parameter>
<parameter name="number" required="false">
<para>If there's more than 1 header with the same name, this specifies which header
to read or update. If not specified, defaults to <literal>1</literal> meaning
the first matching header. Not valid for <literal>add</literal> or
<literal>remove</literal>.</para>
</parameter>
</syntax>
<description>
<para>PJSIP_INHERITABLE_HEADER allows you to write specific SIP headers on a calling
channel to be inherited by an outbound PJSIP channel.</para>
<para>Examples:</para>
<example title="Add an inheritable X-Myheader header with the value of myvalue">
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(add,X-MyHeader)=myvalue)
</example>
<example title="Add an inheritable X-Myheader header with an empty value">
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(add,X-MyHeader)=)
</example>
<example title="Add an inheritable X-MyHeader that will be inherited by child channel">
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(add,X-MyHeader)=myvalue)
</example>
<example title="Update the value of the inheritable header named X-Myheader to newvalue">
; 'X-Myheader' must already exist or the call will fail.
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(update,X-MyHeader)=newvalue)
</example>
<example title="Remove all inheritable headers whose names exactly match X-MyHeader">
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(remove,X-MyHeader)=)
</example>
<example title="Remove all inheritable headers that begin with X-My">
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(remove,X-My*)=)
</example>
<note><para>If you call PJSIP_INHERITABLE_HEADER in a normal dialplan context you'll be
operating on the <emphasis>caller's</emphasis> channel which
will then be inherited to the <emphasis>callee's (outgoing)</emphasis>
channel. Inherited headers can be updated or removed via PJSIP_INHERITABLE_HEADER
in a pre-dial handler. </para>
<para>Headers added via PJSIP_INHERITABLE_HEADER are separate from headers
added via PJSIP_HEADER. A header added via PJSIP_INHERITABLE_HEADER can only
be or removed modified by PJSIP_INHERITABLE_HEADER. A header added via
PJSIP_HEADER can only be modified or removed by PJSIP_HEADER.</para></note>
<example title="Set and modify headers on callee channel">
[handler]
exten => modheader,1,Set(PJSIP_INHERITABLE_HEADER(update,X-MyHeader)=myvalue)
same => n,Set(PJSIP_INHERITABLE_HEADER(update,X-MyHeader2)=myvalueX)
same => n,Set(PJSIP_INHERITABLE_HEADER(remove,X-MyHeader2)=)
[somecontext]
exten => 1,1,Set(PJSIP_INHERITABLE_HEADER(add,X-MyHeader1)=myvalue1)
same => n,Set(PJSIP_INHERITABLE_HEADER(add,X-MyHeader2)=myvalue2)
same => n,Set(PJSIP_INHERITABLE_HEADER(add,X-MyHeader3)=myvalue3)
same => n,Dial(PJSIP/${EXTEN},,b(handler^modheader^1))
</example>
</description>
</function>
<function name="PJSIP_HEADERS" language="en_US">
<since>
<version>16.20.0</version>
@ -323,10 +411,39 @@ struct hdr_list_entry {
};
AST_LIST_HEAD_NOLOCK(hdr_list, hdr_list_entry);
/*! \brief Datastore for saving headers */
static const struct ast_datastore_info header_datastore = {
.type = "header_datastore",
/*!
* \internal
* \brief Duplicate an inheritable headers list for inheritance
*
* Creates copies of the name-value pairs for inheritance to child channels.
*/
static void *inheritable_headers_duplicate(void *data)
{
return ast_variables_dup(data);
}
/*!
* \internal
* \brief Destroy callback for inherited header datastore
*/
static void inheritable_headers_destroy(void *data)
{
ast_variables_destroy(data);
}
/*! \brief Datastore for saving headers in session (contains hdr_list, no destroy needed) */
static const struct ast_datastore_info session_header_datastore = {
.type = "session_header_datastore",
/* No destroy or duplicate callback - data is pool-allocated hdr_list */
};
/*! \brief Datastore for saving inheritable headers (contains hdr_list with ast_malloc'd entries) */
static const struct ast_datastore_info inheritable_header_datastore = {
.type = "inheritable_header_datastore",
.duplicate = inheritable_headers_duplicate,
.destroy = inheritable_headers_destroy,
};
/*! \brief Datastore for saving response headers */
static const struct ast_datastore_info response_header_datastore = {
.type = "response_header_datastore",
@ -377,11 +494,11 @@ static int incoming_request(struct ast_sip_session *session, pjsip_rx_data * rda
{
pj_pool_t *pool = session->inv_session->dlg->pool;
RAII_VAR(struct ast_datastore *, datastore,
ast_sip_session_get_datastore(session, header_datastore.type), ao2_cleanup);
ast_sip_session_get_datastore(session, session_header_datastore.type), ao2_cleanup);
if (!datastore) {
if (!(datastore =
ast_sip_session_alloc_datastore(&header_datastore, header_datastore.type))
ast_sip_session_alloc_datastore(&session_header_datastore, session_header_datastore.type))
||
!(datastore->data = pj_pool_alloc(pool, sizeof(struct hdr_list))) ||
ast_sip_session_add_datastore(session, datastore)) {
@ -580,7 +697,7 @@ static int read_header(void *obj)
list = datastore->data;
AST_LIST_TRAVERSE(list, le, nextptr) {
if (data->header_name[len - 1] == '*') {
if (len >= 1 && data->header_name[len - 1] == '*') {
if (pj_strnicmp2(&le->hdr->name, data->header_name, len - 1) == 0 && i++ == data->header_number) {
hdr = le->hdr;
break;
@ -651,11 +768,11 @@ static int add_header(void *obj)
struct hdr_list *list;
RAII_VAR(struct ast_datastore *, datastore,
ast_sip_session_get_datastore(session, data->header_datastore->type), ao2_cleanup);
ast_sip_session_get_datastore(session, session_header_datastore.type), ao2_cleanup);
if (!datastore) {
if (!(datastore = ast_sip_session_alloc_datastore(data->header_datastore,
data->header_datastore->type))
if (!(datastore = ast_sip_session_alloc_datastore(&session_header_datastore,
session_header_datastore.type))
|| !(datastore->data = pj_pool_alloc(pool, sizeof(struct hdr_list)))
|| ast_sip_session_add_datastore(session, datastore)) {
ast_log(AST_LOG_ERROR, "Unable to create datastore for header functions.\n");
@ -679,6 +796,78 @@ static int add_header(void *obj)
return 0;
}
/*!
* \internal
* \brief Implements PJSIP_INHERITABLE_HEADER 'add' by inserting the specified header into the channel datastore.
*
* Retrieve the inheritable channel header_datastore from the session or create one if it doesn't exist.
* Create and initialize the list if needed.
* Create the pj_strs for name and value.
* Create pjsip_msg and hdr_list_entry.
* Add the entry to the list.
* Locks the channel to protect the channel datastore.
*/
static int add_inheritable_header(struct ast_channel *channel, const char *header_name, const char *header_value)
{
struct ast_variable *headers = NULL;
/* Add to channel datastore to be inherited by bridged channel(s) */
struct ast_datastore *chan_datastore;
struct ast_variable *new_var;
ast_channel_lock(channel);
chan_datastore = ast_channel_datastore_find(channel, &inheritable_header_datastore, NULL);
ast_debug(3, "Adding inheritable header %s with value %s to channel %s\n", header_name,
header_value, ast_channel_name(channel));
if (!chan_datastore) {
/* Create new channel datastore */
chan_datastore = ast_datastore_alloc(&inheritable_header_datastore,
inheritable_header_datastore.type);
if (chan_datastore) {
chan_datastore->data = NULL;
chan_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
ast_channel_datastore_add(channel, chan_datastore);
ast_debug(3, "Created new inheritable SIP header channel datastore for channel %s\n",
ast_channel_name(channel));
} else {
ast_log(LOG_ERROR, "Failed to allocate channel datastore for channel %s to store inheritable SIP headers\n",
ast_channel_name(channel));
ast_channel_unlock(channel);
return -1;
}
} else {
/* Ensure it's marked as inheritable */
if (!chan_datastore->inheritance) {
chan_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
ast_debug(3, "Upgraded SIP header channel datastore to inheritable for channel %s\n",
ast_channel_name(channel));
}
}
/* Add THIS header to the channel datastore */
headers = chan_datastore->data;
new_var = ast_variable_new(header_name, header_value, "");
if (!new_var) {
ast_log(LOG_ERROR, "Failed to allocate variable for channel %s to store inheritable SIP headers\n",
ast_channel_name(channel));
ast_channel_unlock(channel);
return -1;
}
/* Prepend to list */
new_var->next = headers;
chan_datastore->data = new_var;
ast_debug(1, "Inherited Header %s added with value %s for channel %s\n",
header_name, header_value, ast_channel_name(channel));
ast_channel_unlock(channel);
return 0;
}
/*!
* \internal
* \brief Implements PJSIP_HEADER 'update' by finding the specified header and updating it.
@ -695,9 +884,12 @@ static int update_header(void *obj)
pj_pool_t *pool = data->channel->session->inv_session->dlg->pool;
pjsip_hdr *hdr = NULL;
RAII_VAR(struct ast_datastore *, datastore,
ast_sip_session_get_datastore(data->channel->session, data->header_datastore->type),
ast_sip_session_get_datastore(data->channel->session, session_header_datastore.type),
ao2_cleanup);
ast_debug(3, "Attempting to update header %s for channel %p\n", data->header_name,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
if (!datastore || !datastore->data) {
ast_log(AST_LOG_ERROR, "No headers had been previously added to this session.\n");
return -1;
@ -711,11 +903,107 @@ static int update_header(void *obj)
return -1;
}
ast_debug(3, "Found header %s in session datastore, updating value to %s for channel %p\n",
data->header_name, data->header_value,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
pj_strdup2(pool, &((pjsip_generic_string_hdr *) hdr)->hvalue, data->header_value);
return 0;
}
/*!
* \internal
* \brief Implements PJSIP_INHERITABLE_HEADER 'update' by finding the specified header and updating it.
*
* Retrieve the header_datastore from the session or create one if it doesn't exist.
* Create and initialize the list if needed.
* Create the pj_strs for name and value.
* Create pjsip_msg and hdr_list_entry.
* Add the entry to the list.
* Locks the channel to protect the channel datastore.
*/
static int update_inheritable_header(struct ast_channel *channel, const char *header_name, const char *header_value, int header_number)
{
struct ast_datastore *inherited_datastore;
struct ast_variable *headers = NULL;
struct ast_variable *var, *prev = NULL;
int i = 1, num_matching_headers = 0, target_header_index;
ast_channel_lock(channel);
inherited_datastore = ast_channel_datastore_find(channel, &inheritable_header_datastore, NULL);
ast_debug(3, "Attempting to update header %s for channel %s\n", header_name,
ast_channel_name(channel));
if (!inherited_datastore || !inherited_datastore->data) {
ast_log(AST_LOG_ERROR, "No inheritable headers had been previously added to this channel.\n");
ast_channel_unlock(channel);
return -1;
}
headers = inherited_datastore->data;
/* The headers are in reverse order so we need to find out how many there are to know which one to update */
for (var = headers; var; var = var->next) {
if (strcasecmp(var->name, header_name) == 0) {
num_matching_headers++;
}
}
/* As we already counted the matching headers, we can skip walking the list again if the count is 0 or
is less than the requested header number */
if (num_matching_headers == 0) {
ast_log(AST_LOG_ERROR, "There was no header named %s on channel %s.\n", header_name,
ast_channel_name(channel));
ast_channel_unlock(channel);
return -1;
} else if (num_matching_headers < header_number) {
ast_log(AST_LOG_ERROR, "There were only %d headers named %s on channel %s, cannot update header number %d.\n",
num_matching_headers, header_name, ast_channel_name(channel), header_number);
ast_channel_unlock(channel);
return -1;
}
target_header_index = num_matching_headers - header_number + 1;
/* Search for the header in the inherited list */
for (var = headers; var; var = var->next) {
if (strcasecmp(var->name, header_name) == 0 && i++ == target_header_index) {
/* Found the header, create a new one with the new value and free the old */
struct ast_variable *new_var = ast_variable_new(header_name, header_value, "");
if (!new_var) {
ast_log(AST_LOG_ERROR, "Failed to allocate variable for updated header for channel %s\n",
ast_channel_name(channel));
ast_channel_unlock(channel);
return -1;
}
new_var->next = var->next;
if (var == headers) {
/* If we're updating the first header in the list, we need to update the head pointer */
inherited_datastore->data = new_var;
} else if (prev) {
/* Otherwise, we need to link the previous header to the new header */
prev->next = new_var;
}
var->next = NULL;
inheritable_headers_destroy(var);
ast_debug(3, "Updated header %s with new value %s for channel %s\n",
header_name, header_value, ast_channel_name(channel));
ast_channel_unlock(channel);
return 0;
}
prev = var;
}
/* We should never get here*/
ast_log(AST_LOG_ERROR, "Unable to update header %s on channel %s.\n", header_name,
ast_channel_name(channel));
ast_channel_unlock(channel);
return -1;
}
/*!
* \internal
* \brief Implements PJSIP_HEADER 'remove' by finding the specified header and removing it.
@ -732,9 +1020,12 @@ static int remove_header(void *obj)
struct hdr_list_entry *le;
int removed_count = 0;
RAII_VAR(struct ast_datastore *, datastore,
ast_sip_session_get_datastore(data->channel->session, data->header_datastore->type),
ast_sip_session_get_datastore(data->channel->session, session_header_datastore.type),
ao2_cleanup);
ast_debug(3, "Attempting to remove header %s from channel %p\n", data->header_name,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
if (!datastore || !datastore->data) {
ast_log(AST_LOG_ERROR, "No headers had been previously added to this session.\n");
return -1;
@ -742,13 +1033,19 @@ static int remove_header(void *obj)
list = datastore->data;
AST_LIST_TRAVERSE_SAFE_BEGIN(list, le, nextptr) {
if (data->header_name[len - 1] == '*') {
if (len >= 1 && data->header_name[len - 1] == '*') {
if (pj_strnicmp2(&le->hdr->name, data->header_name, len - 1) == 0) {
ast_debug(3, "Found wildcard match, removing header %.*s from channel %p\n",
(int)le->hdr->name.slen, le->hdr->name.ptr,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
AST_LIST_REMOVE_CURRENT(nextptr);
removed_count++;
}
} else {
if (pj_stricmp2(&le->hdr->name, data->header_name) == 0) {
ast_debug(3, "Found exact match, removing header %.*s from channel %p\n",
(int)le->hdr->name.slen, le->hdr->name.ptr,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
AST_LIST_REMOVE_CURRENT(nextptr);
removed_count++;
}
@ -756,10 +1053,118 @@ static int remove_header(void *obj)
}
AST_LIST_TRAVERSE_SAFE_END;
if (removed_count == 0) {
ast_debug(3, "No headers named %s found to remove on channel %s.\n", data->header_name,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
}
if (data->buf && data->len) {
snprintf(data->buf, data->len, "%d", removed_count);
ast_debug(3, "Removed %d headers matching %s from channel %s\n", removed_count, data->header_name,
data->channel->session->channel ? ast_channel_name(data->channel->session->channel) : "(none)");
}
return 0;
}
/*!
* \internal
* \brief Implements PJSIP_INHERITABLE_HEADER 'remove' by finding the specified header and removing it.
*
* Retrieve the header_datastore from the session. Fail if it doesn't exist.
* If the header_name is exactly '*', the entire list is simply destroyed.
* Otherwise search the list for the matching header name which may be a partial name.
* Locks the channel to protect the channel datastore.
*/
static int remove_inheritable_header(struct ast_channel *channel, const char *header_name)
{
size_t len = strlen(header_name);
struct ast_variable *headers = NULL;
struct ast_variable *var, *prev = NULL;
int removed_count = 0;
struct ast_datastore *inherited_datastore;
ast_channel_lock(channel);
inherited_datastore = ast_channel_datastore_find(channel, &inheritable_header_datastore, NULL);
ast_debug(3, "Attempting to remove header %s from channel %s\n",
header_name, ast_channel_name(channel));
/* Remove from inherited datastore */
if (!inherited_datastore || !inherited_datastore->data) {
ast_log(AST_LOG_ERROR, "No inheritable headers had been previously added to channel %s.\n",
ast_channel_name(channel));
ast_channel_unlock(channel);
return -1;
}
headers = inherited_datastore->data;
/* If removing all headers */
if (strcmp(header_name, "*") == 0) {
inheritable_headers_destroy(headers);
inherited_datastore->data = NULL;
ast_debug(3, "Removed all inheritable headers from channel %s\n",
ast_channel_name(channel));
ast_channel_unlock(channel);
return 0;
}
/* Remove specific headers (exact match or wildcard) */
var = headers;
while (var) {
struct ast_variable *next = var->next;
int match = 0;
if (len >= 1 && header_name[len - 1] == '*') {
/* Wildcard match */
match = (strncasecmp(var->name, header_name, len - 1) == 0);
if (match) {
ast_debug(3, "Found wildcard match, removing header %s with value %s from channel %s\n",
var->name, var->value, ast_channel_name(channel));
}
} else {
/* Exact match */
match = (strcasecmp(var->name, header_name) == 0);
if (match) {
ast_debug(3, "Found exact match, removing header %s with value %s from channel %s\n",
var->name, var->value, ast_channel_name(channel));
}
}
if (match) {
/* Remove this variable */
if (var == headers) {
/* If we're removing the first header in the list, we need to update the head pointer */
headers = next;
inherited_datastore->data = headers;
} else if (prev) {
/* Otherwise, we need to link the previous header to the next header */
prev->next = next;
}
/* destroy the variable */
var->next = NULL;
ast_variables_destroy(var);
removed_count++;
var = next;
} else {
prev = var;
var = next;
}
}
/* Update datastore with new list head */
inherited_datastore->data = headers;
if (removed_count == 0) {
ast_debug(3, "No headers matching %s found for channel %s\n",
header_name, ast_channel_name(channel));
}
ast_debug(3, "Removed %d headers matching %s from channel %s\n", removed_count, header_name,
ast_channel_name(channel));
ast_channel_unlock(channel);
return 0;
}
@ -785,7 +1190,7 @@ static int func_read_headers(struct ast_channel *chan, const char *function, cha
header_data.header_value = NULL;
header_data.buf = buf;
header_data.len = len;
header_data.header_datastore = &header_datastore;
header_data.header_datastore = &session_header_datastore;
return ast_sip_push_task_wait_serializer(channel->session->serializer, read_headers, &header_data);
@ -867,7 +1272,7 @@ static int func_read_header(struct ast_channel *chan, const char *function, char
header_data.header_value = NULL;
header_data.buf = buf;
header_data.len = len;
header_data.header_datastore = &header_datastore;
header_data.header_datastore = &session_header_datastore;
if (!strcasecmp(args.action, "read")) {
return ast_sip_push_task_wait_serializer(channel->session->serializer, read_header, &header_data);
@ -950,7 +1355,8 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da
int header_number;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(action);
AST_APP_ARG(header_name); AST_APP_ARG(header_number););
AST_APP_ARG(header_name);
AST_APP_ARG(header_number););
AST_STANDARD_APP_ARGS(args, data);
if (!channel || strncmp(ast_channel_name(chan), "PJSIP/", 6)) {
@ -981,7 +1387,7 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da
header_data.header_value = value;
header_data.buf = NULL;
header_data.len = 0;
header_data.header_datastore = &header_datastore;
header_data.header_datastore = &session_header_datastore;
if (!strcasecmp(args.action, "add")) {
return ast_sip_push_task_wait_serializer(channel->session->serializer,
@ -1000,12 +1406,68 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da
}
}
/*!
* \brief Implements PJSIP_INHERITABLE_HEADER function 'write' callback.
*
* Valid actions are 'add', 'update' and 'remove'.
*/
static int func_write_inheritable_header(struct ast_channel *chan, const char *cmd, char *data,
const char *value)
{
int header_number;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(action);
AST_APP_ARG(header_name);
AST_APP_ARG(header_number););
AST_STANDARD_APP_ARGS(args, data);
if (!chan) {
ast_log(LOG_ERROR, "The PJSIP_INHERITABLE_HEADER function requires a channel.\n");
return -1;
}
if (ast_strlen_zero(args.action)) {
ast_log(AST_LOG_ERROR, "The PJSIP_INHERITABLE_HEADER function requires an action.\n");
return -1;
}
if (ast_strlen_zero(args.header_name)) {
ast_log(AST_LOG_ERROR, "The PJSIP_INHERITABLE_HEADER function requires a header name.\n");
return -1;
}
if (!args.header_number) {
header_number = 1;
} else {
sscanf(args.header_number, "%30d", &header_number);
if (header_number < 1) {
header_number = 1;
}
}
/* TODO: we might want to check channel type and use the serializer if PJSIP */
if (!strcasecmp(args.action, "add")) {
return add_inheritable_header(chan, args.header_name, value);
} else if (!strcasecmp(args.action, "update")) {
return update_inheritable_header(chan, args.header_name, value, header_number);
} else if (!strcasecmp(args.action, "remove")) {
return remove_inheritable_header(chan, args.header_name);
} else {
ast_log(AST_LOG_ERROR,
"Unknown action '%s' is not valid, must be 'add', 'update', or 'remove'.\n",
args.action);
return -1;
}
}
static struct ast_custom_function pjsip_header_function = {
.name = "PJSIP_HEADER",
.read = func_read_header,
.write = func_write_header,
};
static struct ast_custom_function pjsip_header_inherit_function = {
.name = "PJSIP_INHERITABLE_HEADER",
.write = func_write_inheritable_header,
};
static struct ast_custom_function pjsip_headers_function = {
.name = "PJSIP_HEADERS",
.read = func_read_headers
@ -1035,21 +1497,93 @@ static struct ast_custom_function pjsip_response_headers_function = {
*/
static void outgoing_request(struct ast_sip_session *session, pjsip_tx_data * tdata)
{
struct hdr_list *list;
struct hdr_list *hdr_list;
struct hdr_list_entry *le;
RAII_VAR(struct ast_datastore *, datastore,
ast_sip_session_get_datastore(session, header_datastore.type), ao2_cleanup);
int header_count = 0;
struct ast_datastore *inherited_datastore = NULL;
RAII_VAR(struct ast_datastore *, session_datastore,
ast_sip_session_get_datastore(session, session_header_datastore.type), ao2_cleanup);
/* Check if we're past the INVITE stage */
if (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED) {
ast_debug(3, "Already confirmed (state=%d)\n",
session->inv_session->state);
return;
}
if (!datastore || !datastore->data ||
(session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED)) {
/* If we're UAC and have a channel, check the channel datastore for an inheritable header datastore */
if (session->channel && session->inv_session->role == PJSIP_ROLE_UAC) {
struct ast_datastore *chan_datastore = NULL;
ast_channel_lock(session->channel);
chan_datastore = ast_channel_datastore_find(session->channel, &inheritable_header_datastore, NULL);
if (chan_datastore && chan_datastore->inheritance && chan_datastore->data) {
inherited_datastore = ast_sip_session_alloc_datastore(&inheritable_header_datastore,
inheritable_header_datastore.type);
if (inherited_datastore) {
/* The data is in ast_variable format from the duplicate callback. We need to make our own copy to
avoid sharing the pointer. Reverse the order so they appear in the INVITE in the order added. */
inherited_datastore->data = ast_variables_reverse((struct ast_variable *)
inheritable_headers_duplicate((struct ast_variable *) chan_datastore->data));
if (inherited_datastore->data) {
inherited_datastore->inheritance = chan_datastore->inheritance;
ast_sip_session_add_datastore(session, inherited_datastore);
} else {
ast_log(LOG_ERROR, "Failed to duplicate ast_variable list for channel %s\n",
session->channel ? ast_channel_name(session->channel) : "(none)");
ao2_ref(inherited_datastore, -1);
inherited_datastore = NULL;
}
} else {
ast_log(LOG_ERROR, "Failed to allocate inherited session datastore for channel %s\n",
session->channel ? ast_channel_name(session->channel) : "(none)");
}
}
ast_channel_unlock(session->channel);
}
/* If we have no datastores at all, nothing to do */
if (!session_datastore && !inherited_datastore) {
return;
}
list = datastore->data;
AST_LIST_TRAVERSE(list, le, nextptr) {
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) pjsip_hdr_clone(tdata->pool, le->hdr));
/* Add headers from regular session datastore (non-inheritable headers added on this channel) */
if (session_datastore && session_datastore->data) {
hdr_list = session_datastore->data;
AST_LIST_TRAVERSE(hdr_list, le, nextptr) {
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) pjsip_hdr_clone(tdata->pool, le->hdr));
header_count++;
}
ast_debug(3, "Added %d regular header(s) to channel %s\n", header_count,
session->channel ? ast_channel_name(session->channel) : "(none)");
/* Remove datastores after use */
ast_sip_session_remove_datastore(session, session_datastore->uid);
}
ast_sip_session_remove_datastore(session, datastore->uid);
/* Add headers from inherited datastore (inheritable headers from parent channel) */
if (inherited_datastore && inherited_datastore->data) {
int inherited_count = 0;
struct ast_variable *headers = inherited_datastore->data;
struct ast_variable *var;
for (var = headers; var; var = var->next) {
if (!ast_sip_add_header(tdata, var->name, var->value)) {
inherited_count++;
}
}
ast_debug(3, "Added %d inherited header(s) to channel %s\n", inherited_count,
session->channel ? ast_channel_name(session->channel) : "(none)");
header_count += inherited_count;
/* Remove datastores after use */
ast_sip_session_remove_datastore(session, inherited_datastore->uid);
ao2_ref(inherited_datastore, -1);
}
ast_debug(3, "Added total of %d header(s) to channel %s\n", header_count,
session->channel ? ast_channel_name(session->channel) : "(none)");
}
static struct ast_sip_session_supplement header_funcs_supplement = {
@ -1283,6 +1817,7 @@ static int load_module(void)
{
ast_sip_session_register_supplement(&header_funcs_supplement);
ast_custom_function_register(&pjsip_header_function);
ast_custom_function_register(&pjsip_header_inherit_function);
ast_custom_function_register(&pjsip_headers_function);
ast_custom_function_register(&pjsip_response_header_function);
ast_custom_function_register(&pjsip_response_headers_function);
@ -1294,6 +1829,7 @@ static int load_module(void)
static int unload_module(void)
{
ast_custom_function_unregister(&pjsip_header_function);
ast_custom_function_unregister(&pjsip_header_inherit_function);
ast_custom_function_unregister(&pjsip_headers_function);
ast_custom_function_unregister(&pjsip_response_header_function);
ast_custom_function_unregister(&pjsip_response_headers_function);

Loading…
Cancel
Save