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
This commit is contained in:
Alexei Gradinari 2022-02-01 10:59:02 -05:00 committed by Kevin Harwell
parent 4358c776b8
commit e2423c6f49
6 changed files with 124 additions and 9 deletions

View File

@ -1502,6 +1502,19 @@
; Time Asterisk should wait, in milliseconds,
; 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================================
; See https://wiki.asterisk.org/wiki/display/AST/Exchanging+Device+and+Mailbox+State+Using+PJSIP
; for more information.

View File

@ -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')

View File

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

View File

@ -292,6 +292,17 @@ struct ast_sip_notifier {
* \return An ao2 object that can be used to create a NOTIFY body.
*/
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 {

View File

@ -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 subscription_established(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,
struct ast_str **buf);
static int publisher_start(struct ast_sip_outbound_publish *configuration,
@ -128,6 +129,7 @@ struct ast_sip_notifier presence_notifier = {
.new_subscribe = new_subscribe,
.subscription_established = subscription_established,
.get_notify_data = get_notify_data,
.get_resource_display_name = get_resource_display_name,
};
struct ast_sip_notifier dialog_notifier = {
@ -135,6 +137,7 @@ struct ast_sip_notifier dialog_notifier = {
.new_subscribe = new_subscribe,
.subscription_established = subscription_established,
.get_notify_data = get_notify_data,
.get_resource_display_name = get_resource_display_name,
};
struct ast_sip_subscription_handler presence_handler = {
@ -424,6 +427,27 @@ static int new_subscribe(struct ast_sip_endpoint *endpoint,
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)
{
struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);

View File

@ -210,6 +210,15 @@
many notifications.</para>
</description>
</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 name="inbound-publication">
<synopsis>The configuration for inbound publications</synopsis>
@ -332,6 +341,8 @@ struct resource_list {
unsigned int full_state;
/*! Time, in milliseconds Asterisk waits before sending a batched notification.*/
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;
/*! Data to be persisted with the subscription */
struct ast_json *persistence_data;
/*! Display Name of resource */
char *display_name;
/*! Name of resource being subscribed to */
char resource[0];
};
@ -886,6 +899,7 @@ struct resource_tree;
struct tree_node {
AST_VECTOR(, struct tree_node *) children;
unsigned int full_state;
char *display_name;
char resource[0];
};
@ -929,7 +943,7 @@ static struct resource_list *retrieve_resource_list(const char *resource, const
* \retval NULL Allocation failure.
* \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;
@ -944,6 +958,7 @@ static struct tree_node *tree_node_alloc(const char *resource, struct resources
return NULL;
}
node->full_state = full_state;
node->display_name = ast_strdup(display_name);
if (visited) {
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));
}
AST_VECTOR_FREE(&node->children);
ast_free(node->display_name);
ast_free(node);
}
@ -1035,7 +1051,11 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
if (!child_list) {
int resp = handler->notifier->new_subscribe(endpoint, resource);
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) {
ast_debug(1,
"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 {
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) {
ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
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))) {
ast_debug(2, "Subscription '%s->%s' is not to a list\n",
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) {
return 500;
}
@ -1152,7 +1172,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
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) {
AST_VECTOR_FREE(&visited);
return 500;
@ -1207,6 +1227,7 @@ static void destroy_subscription(struct ast_sip_subscription *sub)
AST_VECTOR_FREE(&sub->children);
ao2_cleanup(sub->datastores);
ast_json_unref(sub->persistence_data);
ast_free(sub->display_name);
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,
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;
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 */
sub->display_name = ast_strdup(display_name);
sub->datastores = ast_datastores_alloc();
if (!sub->datastores) {
destroy_subscription(sub);
@ -1288,7 +1311,7 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as
int i;
struct ast_sip_subscription *sub;
sub = allocate_subscription(handler, resource, tree);
sub = allocate_subscription(handler, resource, current->display_name, tree);
if (!sub) {
return NULL;
}
@ -1880,7 +1903,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
return NULL;
}
sub = allocate_subscription(handler, resource, sub_tree);
sub = allocate_subscription(handler, resource, NULL, sub_tree);
if (!sub) {
ao2_cleanup(sub_tree);
return NULL;
@ -2088,6 +2111,8 @@ struct body_part {
pjsip_evsub_state state;
/*! The actual body part that will be present in the multipart body */
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) {
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);
@ -2243,6 +2268,7 @@ static struct body_part *allocate_body_part(pj_pool_t *pool, const struct ast_si
bp->resource = sub->resource;
bp->state = sub->subscription_state;
bp->uri = sub->uri;
bp->display_name = sub->display_name;
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));
ast_sorcery_object_field_register_custom(sorcery, "resource_list", "list_item",
"", 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");