res_pjsip_pubsub: provide a display name for RLS subscriptions

Whereas BLFs allow to show a display name for each RLS entry,
the asterisk provides only the extension now.
This is not end user friendly.

This commit adds a new resource_list option, resource_display_name,
to indicate whether display name of resource or the resource name being
provided for RLS entries.
If this option is enabled, the Display Name will be provided.
This option is disabled by default to remain the previous behavior.
If the 'event' set to 'presence' or 'dialog' the non-empty HINT name
will be set as the Display Name.
The 'message-summary' is not supported yet.

ASTERISK-29891 #close

Change-Id: Ic5306bd5a7c73d03f5477fe235e9b0f41c69c681
pull/24/head
Alexei Gradinari 3 years ago committed by Kevin Harwell
parent b1765c93e4
commit c12cb899de

@ -1502,6 +1502,19 @@
; Time Asterisk should wait, in milliseconds, ; Time Asterisk should wait, in milliseconds,
; before sending notifications. ; before sending notifications.
;resource_display_name=no ; Indicates whether display name of resource
; or the resource name being reported.
; If this option is enabled, the Display Name
; will be reported as resource name.
; If the event set to presence or dialog,
; the HINT name will be set as the Display Name.
; For example:
; exten => 1234,hint,PJSIP/user1234(Alice)
; If enabled the resource name will be 'Alice'.
; If disabled the resource name will be '1234'.
; The message-summary is not supported yet.
;==========================INBOUND_PUBLICATION================================ ;==========================INBOUND_PUBLICATION================================
; See https://wiki.asterisk.org/wiki/display/AST/Exchanging+Device+and+Mailbox+State+Using+PJSIP ; See https://wiki.asterisk.org/wiki/display/AST/Exchanging+Device+and+Mailbox+State+Using+PJSIP
; for more information. ; for more information.

@ -0,0 +1,29 @@
"""res_pjsip_pubsub add resource_list option resource_display_name
Revision ID: 8f72185e437f
Revises: a06d8f8462d9
Create Date: 2022-02-01 10:53:55.875438
"""
# revision identifiers, used by Alembic.
revision = '8f72185e437f'
down_revision = 'a06d8f8462d9'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import ENUM
AST_BOOL_NAME = 'ast_bool_values'
AST_BOOL_VALUES = [ '0', '1',
'off', 'on',
'false', 'true',
'no', 'yes' ]
def upgrade():
ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
op.add_column('ps_resource_list', sa.Column('resource_display_name', ast_bool_values))
def downgrade():
op.drop_column('ps_resource_list', 'resource_display_name')

@ -0,0 +1,10 @@
Subject: res_pjsip_pubsub
A new resource_list option, resource_display_name, indicates
whether display name of resource or the resource name being
provided for RLS entries.
If this option is enabled, the Display Name will be provided.
This option is disabled by default to remain the previous behavior.
If the 'event' set to 'presence' or 'dialog' the non-empty HINT name
will be set as the Display Name.
The 'message-summary' is not supported yet.

@ -292,6 +292,17 @@ struct ast_sip_notifier {
* \return An ao2 object that can be used to create a NOTIFY body. * \return An ao2 object that can be used to create a NOTIFY body.
*/ */
void *(*get_notify_data)(struct ast_sip_subscription *sub); void *(*get_notify_data)(struct ast_sip_subscription *sub);
/*!
* \brief Supply Display Name for resource
*
* \param endpoint The endpoint from which we received the SUBSCRIBE
* \param resource The name of the resource to which the subscription is being made
* \param display_name buffer for Display Name
* \param display_name_size size of display_name buffer
* \retval 0 Success
* \retval -1 Failure
*/
int (*get_resource_display_name)(struct ast_sip_endpoint *endpoint, const char *resource, char *display_name, int display_name_size);
}; };
struct ast_sip_subscriber { struct ast_sip_subscriber {

@ -117,6 +117,7 @@ static void subscription_shutdown(struct ast_sip_subscription *sub);
static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource); static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource);
static int subscription_established(struct ast_sip_subscription *sub); static int subscription_established(struct ast_sip_subscription *sub);
static void *get_notify_data(struct ast_sip_subscription *sub); static void *get_notify_data(struct ast_sip_subscription *sub);
static int get_resource_display_name(struct ast_sip_endpoint *endpoint, const char *resource, char *display_name, int display_name_size);
static void to_ami(struct ast_sip_subscription *sub, static void to_ami(struct ast_sip_subscription *sub,
struct ast_str **buf); struct ast_str **buf);
static int publisher_start(struct ast_sip_outbound_publish *configuration, static int publisher_start(struct ast_sip_outbound_publish *configuration,
@ -128,6 +129,7 @@ struct ast_sip_notifier presence_notifier = {
.new_subscribe = new_subscribe, .new_subscribe = new_subscribe,
.subscription_established = subscription_established, .subscription_established = subscription_established,
.get_notify_data = get_notify_data, .get_notify_data = get_notify_data,
.get_resource_display_name = get_resource_display_name,
}; };
struct ast_sip_notifier dialog_notifier = { struct ast_sip_notifier dialog_notifier = {
@ -135,6 +137,7 @@ struct ast_sip_notifier dialog_notifier = {
.new_subscribe = new_subscribe, .new_subscribe = new_subscribe,
.subscription_established = subscription_established, .subscription_established = subscription_established,
.get_notify_data = get_notify_data, .get_notify_data = get_notify_data,
.get_resource_display_name = get_resource_display_name,
}; };
struct ast_sip_subscription_handler presence_handler = { struct ast_sip_subscription_handler presence_handler = {
@ -424,6 +427,27 @@ static int new_subscribe(struct ast_sip_endpoint *endpoint,
return 200; return 200;
} }
static int get_resource_display_name(struct ast_sip_endpoint *endpoint,
const char *resource, char *display_name, int display_name_size)
{
const char *context;
if (!endpoint || ast_strlen_zero(resource) || !display_name || display_name_size <= 0) {
return -1;
}
context = S_OR(endpoint->subscription.context, endpoint->context);
if (!ast_get_hint(NULL, 0, display_name, display_name_size, NULL, context, resource)) {
ast_log(LOG_NOTICE, "Endpoint '%s': "
"Extension '%s' does not exist in context '%s' or has no associated hint\n",
ast_sorcery_object_get_id(endpoint), resource, context);
return -1;
}
return 0;
}
static int subscription_established(struct ast_sip_subscription *sip_sub) static int subscription_established(struct ast_sip_subscription *sip_sub)
{ {
struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub); struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);

@ -210,6 +210,15 @@
many notifications.</para> many notifications.</para>
</description> </description>
</configOption> </configOption>
<configOption name="resource_display_name" default="no">
<synopsis>Indicates whether display name of resource or the resource name being reported.</synopsis>
<description>
<para>If this option is enabled, the Display Name will be reported as resource name.
If the <replaceable>event</replaceable> set to <literal>presence</literal> or <literal>dialog</literal>,
the non-empty HINT name will be set as the Display Name.
The <literal>message-summary</literal> is not supported yet.</para>
</description>
</configOption>
</configObject> </configObject>
<configObject name="inbound-publication"> <configObject name="inbound-publication">
<synopsis>The configuration for inbound publications</synopsis> <synopsis>The configuration for inbound publications</synopsis>
@ -332,6 +341,8 @@ struct resource_list {
unsigned int full_state; unsigned int full_state;
/*! Time, in milliseconds Asterisk waits before sending a batched notification.*/ /*! Time, in milliseconds Asterisk waits before sending a batched notification.*/
unsigned int notification_batch_interval; unsigned int notification_batch_interval;
/*! Indicates whether display name of resource or the resource name being reported.*/
unsigned int resource_display_name;
}; };
/*! /*!
@ -499,6 +510,8 @@ struct ast_sip_subscription {
pjsip_sip_uri *uri; pjsip_sip_uri *uri;
/*! Data to be persisted with the subscription */ /*! Data to be persisted with the subscription */
struct ast_json *persistence_data; struct ast_json *persistence_data;
/*! Display Name of resource */
char *display_name;
/*! Name of resource being subscribed to */ /*! Name of resource being subscribed to */
char resource[0]; char resource[0];
}; };
@ -886,6 +899,7 @@ struct resource_tree;
struct tree_node { struct tree_node {
AST_VECTOR(, struct tree_node *) children; AST_VECTOR(, struct tree_node *) children;
unsigned int full_state; unsigned int full_state;
char *display_name;
char resource[0]; char resource[0];
}; };
@ -929,7 +943,7 @@ static struct resource_list *retrieve_resource_list(const char *resource, const
* \retval NULL Allocation failure. * \retval NULL Allocation failure.
* \retval non-NULL The newly-allocated tree_node * \retval non-NULL The newly-allocated tree_node
*/ */
static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state) static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state, const char *display_name)
{ {
struct tree_node *node; struct tree_node *node;
@ -944,6 +958,7 @@ static struct tree_node *tree_node_alloc(const char *resource, struct resources
return NULL; return NULL;
} }
node->full_state = full_state; node->full_state = full_state;
node->display_name = ast_strdup(display_name);
if (visited) { if (visited) {
AST_VECTOR_APPEND(visited, resource); AST_VECTOR_APPEND(visited, resource);
@ -971,6 +986,7 @@ static void tree_node_destroy(struct tree_node *node)
tree_node_destroy(AST_VECTOR_GET(&node->children, i)); tree_node_destroy(AST_VECTOR_GET(&node->children, i));
} }
AST_VECTOR_FREE(&node->children); AST_VECTOR_FREE(&node->children);
ast_free(node->display_name);
ast_free(node); ast_free(node);
} }
@ -1035,7 +1051,11 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
if (!child_list) { if (!child_list) {
int resp = handler->notifier->new_subscribe(endpoint, resource); int resp = handler->notifier->new_subscribe(endpoint, resource);
if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) { if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
current = tree_node_alloc(resource, visited, 0); char display_name[AST_MAX_EXTENSION] = "";
if (list->resource_display_name && handler->notifier->get_resource_display_name) {
handler->notifier->get_resource_display_name(endpoint, resource, display_name, sizeof(display_name));
}
current = tree_node_alloc(resource, visited, 0, ast_strlen_zero(display_name) ? NULL : display_name);
if (!current) { if (!current) {
ast_debug(1, ast_debug(1,
"Subscription to leaf resource %s was successful, but encountered allocation error afterwards\n", "Subscription to leaf resource %s was successful, but encountered allocation error afterwards\n",
@ -1053,7 +1073,7 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
} }
} else { } else {
ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource); ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource);
current = tree_node_alloc(resource, visited, child_list->full_state); current = tree_node_alloc(resource, visited, child_list->full_state, NULL);
if (!current) { if (!current) {
ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource); ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
continue; continue;
@ -1139,7 +1159,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) { if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
ast_debug(2, "Subscription '%s->%s' is not to a list\n", ast_debug(2, "Subscription '%s->%s' is not to a list\n",
ast_sorcery_object_get_id(endpoint), resource); ast_sorcery_object_get_id(endpoint), resource);
tree->root = tree_node_alloc(resource, NULL, 0); tree->root = tree_node_alloc(resource, NULL, 0, NULL);
if (!tree->root) { if (!tree->root) {
return 500; return 500;
} }
@ -1152,7 +1172,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
return 500; return 500;
} }
tree->root = tree_node_alloc(resource, &visited, list->full_state); tree->root = tree_node_alloc(resource, &visited, list->full_state, NULL);
if (!tree->root) { if (!tree->root) {
AST_VECTOR_FREE(&visited); AST_VECTOR_FREE(&visited);
return 500; return 500;
@ -1207,6 +1227,7 @@ static void destroy_subscription(struct ast_sip_subscription *sub)
AST_VECTOR_FREE(&sub->children); AST_VECTOR_FREE(&sub->children);
ao2_cleanup(sub->datastores); ao2_cleanup(sub->datastores);
ast_json_unref(sub->persistence_data); ast_json_unref(sub->persistence_data);
ast_free(sub->display_name);
ast_free(sub); ast_free(sub);
} }
@ -1229,7 +1250,7 @@ static void destroy_subscriptions(struct ast_sip_subscription *root)
} }
static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler, static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler,
const char *resource, struct sip_subscription_tree *tree) const char *resource, const char *display_name, struct sip_subscription_tree *tree)
{ {
struct ast_sip_subscription *sub; struct ast_sip_subscription *sub;
pjsip_sip_uri *contact_uri; pjsip_sip_uri *contact_uri;
@ -1240,6 +1261,8 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
} }
strcpy(sub->resource, resource); /* Safe */ strcpy(sub->resource, resource); /* Safe */
sub->display_name = ast_strdup(display_name);
sub->datastores = ast_datastores_alloc(); sub->datastores = ast_datastores_alloc();
if (!sub->datastores) { if (!sub->datastores) {
destroy_subscription(sub); destroy_subscription(sub);
@ -1288,7 +1311,7 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as
int i; int i;
struct ast_sip_subscription *sub; struct ast_sip_subscription *sub;
sub = allocate_subscription(handler, resource, tree); sub = allocate_subscription(handler, resource, current->display_name, tree);
if (!sub) { if (!sub) {
return NULL; return NULL;
} }
@ -1880,7 +1903,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
return NULL; return NULL;
} }
sub = allocate_subscription(handler, resource, sub_tree); sub = allocate_subscription(handler, resource, NULL, sub_tree);
if (!sub) { if (!sub) {
ao2_cleanup(sub_tree); ao2_cleanup(sub_tree);
return NULL; return NULL;
@ -2088,6 +2111,8 @@ struct body_part {
pjsip_evsub_state state; pjsip_evsub_state state;
/*! The actual body part that will be present in the multipart body */ /*! The actual body part that will be present in the multipart body */
pjsip_multipart_part *part; pjsip_multipart_part *part;
/*! Display name for the resource */
const char *display_name;
}; };
/*! /*!
@ -2186,7 +2211,7 @@ static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool, struct ast_sip_sub
for (i = 0; i < AST_VECTOR_SIZE(body_parts); ++i) { for (i = 0; i < AST_VECTOR_SIZE(body_parts); ++i) {
const struct body_part *part = AST_VECTOR_GET(body_parts, i); const struct body_part *part = AST_VECTOR_GET(body_parts, i);
add_rlmi_resource(pool, rlmi, part->cid, part->resource, part->uri, part->state); add_rlmi_resource(pool, rlmi, part->cid, S_OR(part->display_name, part->resource), part->uri, part->state);
} }
rlmi_part = pjsip_multipart_create_part(pool); rlmi_part = pjsip_multipart_create_part(pool);
@ -2243,6 +2268,7 @@ static struct body_part *allocate_body_part(pj_pool_t *pool, const struct ast_si
bp->resource = sub->resource; bp->resource = sub->resource;
bp->state = sub->subscription_state; bp->state = sub->subscription_state;
bp->uri = sub->uri; bp->uri = sub->uri;
bp->display_name = sub->display_name;
return bp; return bp;
} }
@ -4873,6 +4899,8 @@ static int apply_list_configuration(struct ast_sorcery *sorcery)
"0", OPT_UINT_T, 0, FLDSET(struct resource_list, notification_batch_interval)); "0", OPT_UINT_T, 0, FLDSET(struct resource_list, notification_batch_interval));
ast_sorcery_object_field_register_custom(sorcery, "resource_list", "list_item", ast_sorcery_object_field_register_custom(sorcery, "resource_list", "list_item",
"", list_item_handler, list_item_to_str, NULL, 0, 0); "", list_item_handler, list_item_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sorcery, "resource_list", "resource_display_name", "no",
OPT_BOOL_T, 1, FLDSET(struct resource_list, resource_display_name));
ast_sorcery_reload_object(sorcery, "resource_list"); ast_sorcery_reload_object(sorcery, "resource_list");

Loading…
Cancel
Save