diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index 6f0601dde401613677a155459d4d416aedee500b..8507b4ed8caa143c40535e5b5ce342a50ef245c5 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 0000000000000000000000000000000000000000..8af67bfc20a1353ad97cc5ffe9741c6f9df10ebb --- /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 0000000000000000000000000000000000000000..0d95b08fa3e07fb6d867642259ffdc9ab7267904 --- /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 354b0b2506040adcd2d3bc3cb5bbe6999cc35a90..aca1141d851c5702ab0a220f1e3102f324661363 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 df9a35f3326b36a01703c900d939478dfbce71e8..b51df87b622287a2c12607d4933e33c7b8191c09 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 31394e28a8f647bd1265b19a85cbb1179970ed7e..60506db5c9ee1c34e5b4db1b8c1e0b226f2483b1 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -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");