diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 6f0601dde4..8507b4ed8c 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -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.
diff --git a/contrib/ast-db-manage/config/versions/8f72185e437f_res_pjsip_pubsub_add_resource_list_.py b/contrib/ast-db-manage/config/versions/8f72185e437f_res_pjsip_pubsub_add_resource_list_.py
new file mode 100644
index 0000000000..8af67bfc20
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/8f72185e437f_res_pjsip_pubsub_add_resource_list_.py
@@ -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')
+
diff --git a/doc/CHANGES-staging/rls_display_name.txt b/doc/CHANGES-staging/rls_display_name.txt
new file mode 100644
index 0000000000..0d95b08fa3
--- /dev/null
+++ b/doc/CHANGES-staging/rls_display_name.txt
@@ -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.
diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index 354b0b2506..aca1141d85 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -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 {
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index df9a35f332..b51df87b62 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -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);
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 31394e28a8..60506db5c9 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -210,6 +210,15 @@
many notifications.
+
+ 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 non-empty HINT name will be set as the Display Name.
+ The message-summary is not supported yet.
+
+
The configuration for inbound publications
@@ -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");