From 4394e0431c81e80741273e4f3f63be4137716ff5 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Fri, 21 Nov 2014 17:49:39 +0000 Subject: [PATCH] sorcery: Make is_object_field_registered handle field names that are regexes. As a result of https://reviewboard.asterisk.org/r/3305, res_sorcery_realtime was tossing database fields that didn't have an exact match to a sorcery registered field. This broke the ability to use regexes as field names which manifested itself as a failure of res_pjsip_phoneprov_provider which uses this capability. It also broke handling of fields that start with '@' in realtime but I don't think anyone noticed. This patch does the following... * Modifies ast_sorcery_fields_register to pre-compile the name regex. * Modifies ast_sorcery_is_object_field_registered to test the regex if it exists instead of doing an exact strcmp. * Modifies res_pjsip_phoneprov_provider with a few tweaks to get it to work with realtime. Tested-by: George Joseph Review: https://reviewboard.asterisk.org/r/4185/ ........ Merged revisions 428543 from http://svn.asterisk.org/svn/asterisk/branches/12 ........ Merged revisions 428544 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@428545 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- main/sorcery.c | 47 +++++++++++++++++++++++++++++- res/res_pjsip_phoneprov_provider.c | 9 ++---- tests/test_sorcery.c | 46 +++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/main/sorcery.c b/main/sorcery.c index 9ddf77020b..f732d6ee44 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -140,6 +140,9 @@ struct ast_sorcery_object_field { /*! \brief Name of the field */ char name[MAX_OBJECT_FIELD]; + /*! \brief The compiled name regex if name is a regex */ + regex_t *name_regex; + /*! \brief Callback function for translation of a single value */ sorcery_field_handler handler; @@ -869,18 +872,41 @@ void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char object_type->diff = diff; } +static void sorcery_object_field_destructor(void *obj) +{ + struct ast_sorcery_object_field *object_field = obj; + + if (object_field->name_regex) { + regfree(object_field->name_regex); + } +} + int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *type, const char *regex, aco_option_handler config_handler, sorcery_fields_handler sorcery_handler) { +#define MAX_REGEX_ERROR_LEN 128 RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); RAII_VAR(struct ast_sorcery_object_field *, object_field, NULL, ao2_cleanup); + int rc; - if (!object_type || !object_type->type.item_alloc || !config_handler || !(object_field = ao2_alloc(sizeof(*object_field), NULL))) { + if (!object_type || !object_type->type.item_alloc || !config_handler + || !(object_field = ao2_alloc(sizeof(*object_field), sorcery_object_field_destructor))) { return -1; } ast_copy_string(object_field->name, regex, sizeof(object_field->name)); object_field->multiple_handler = sorcery_handler; + if (!(object_field->name_regex = ast_calloc(1, sizeof(regex_t)))) { + return -1; + } + + if ((rc = regcomp(object_field->name_regex, regex, REG_EXTENDED | REG_NOSUB))) { + char *regerr = ast_alloca(MAX_REGEX_ERROR_LEN); + regerror(rc, object_field->name_regex, regerr, MAX_REGEX_ERROR_LEN); + ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n", regex, regerr); + return -1; + } + ao2_link(object_type->fields, object_field); __aco_option_register(object_type->info, regex, ACO_REGEX, object_type->file->types, "", OPT_CUSTOM_T, config_handler, 0, 1, 0); @@ -1926,6 +1952,20 @@ struct ast_sorcery_object_type *ast_sorcery_get_object_type(const struct ast_sor return ao2_find(sorcery->types, type, OBJ_SEARCH_KEY); } +static int is_registered_cb(void *obj, void *arg, int flags) +{ + struct ast_sorcery_object_field *object_field = obj; + char *name = arg; + int rc = 0; + + if (object_field->name_regex + && !regexec(object_field->name_regex, name, 0, NULL, 0)) { + rc = CMP_MATCH | CMP_STOP; + } + + return rc; +} + int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type *object_type, const char *field_name) { @@ -1935,6 +1975,11 @@ int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type ast_assert(object_type != NULL); object_field = ao2_find(object_type->fields, field_name, OBJ_SEARCH_KEY); + + if (!object_field) { + object_field = ao2_callback(object_type->fields, 0, is_registered_cb, (char *)field_name); + } + if (!object_field) { res = 0; } diff --git a/res/res_pjsip_phoneprov_provider.c b/res/res_pjsip_phoneprov_provider.c index 0480f0bbd1..61b026f7cc 100644 --- a/res/res_pjsip_phoneprov_provider.c +++ b/res/res_pjsip_phoneprov_provider.c @@ -289,7 +289,7 @@ static int load_users(void) int user_count = 0; char port_string[6]; - c = ast_sorcery_retrieve_by_regex(sorcery, "phoneprov", ""); + c = ast_sorcery_retrieve_by_fields(sorcery, "phoneprov", AST_RETRIEVE_FLAG_MULTIPLE, NULL); if (!c) { ast_log(LOG_ERROR, "Retrieve by regex failed to allocate a container.\n"); return -1; @@ -377,12 +377,7 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } - if (ast_sorcery_apply_default(sorcery, "phoneprov", "config", - "pjsip.conf,criteria=type=phoneprov")) { - ast_log(LOG_ERROR, "Unable to register object phoneprov.\n"); - ast_sorcery_unref(sorcery); - return AST_MODULE_LOAD_DECLINE; - } + ast_sorcery_apply_default(sorcery, "phoneprov", "config", "pjsip.conf,criteria=type=phoneprov"); ast_sorcery_object_register(sorcery, "phoneprov", phoneprov_alloc, NULL, users_apply_handler); diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c index cbdec0ce14..8873b10f00 100644 --- a/tests/test_sorcery.c +++ b/tests/test_sorcery.c @@ -2995,6 +2995,48 @@ AST_TEST_DEFINE(dialplan_function) return AST_TEST_PASS; } +AST_TEST_DEFINE(object_field_registered) +{ + RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); + RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup); + + switch (cmd) { + case TEST_INIT: + info->name = "object_field_registered"; + info->category = "/main/sorcery/"; + info->summary = "ast_sorcery_is_object_field_registered unit test"; + info->description = + "Test ast_sorcery_is_object_field_registered in sorcery"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(sorcery = alloc_and_initialize_sorcery())) { + ast_test_status_update(test, "Failed to open sorcery structure\n"); + return AST_TEST_FAIL; + } + + object_type = ast_sorcery_get_object_type(sorcery, "test"); + + ast_sorcery_object_fields_register(sorcery, "test", "^prefix/.", test_sorcery_regex_handler, test_sorcery_regex_fields); + + ast_test_validate(test, ast_sorcery_is_object_field_registered(object_type, "joe")); + ast_test_validate(test, ast_sorcery_is_object_field_registered(object_type, "bob")); + ast_test_validate(test, ast_sorcery_is_object_field_registered(object_type, "@joebob")); + ast_test_validate(test, ast_sorcery_is_object_field_registered(object_type, "prefix/goober")); + + ast_test_validate(test, !ast_sorcery_is_object_field_registered(object_type, "joebob")); + ast_test_validate(test, !ast_sorcery_is_object_field_registered(object_type, "prefix/")); + ast_test_validate(test, !ast_sorcery_is_object_field_registered(object_type, "goober")); + + ast_sorcery_object_fields_register(sorcery, "test", "^", test_sorcery_regex_handler, test_sorcery_regex_fields); + + ast_test_validate(test, ast_sorcery_is_object_field_registered(object_type, "goober")); + + return AST_TEST_PASS; +} + static int unload_module(void) { AST_TEST_UNREGISTER(wizard_registration); @@ -3041,6 +3083,8 @@ static int unload_module(void) AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple); AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple_all); AST_TEST_UNREGISTER(dialplan_function); + AST_TEST_UNREGISTER(object_field_registered); + return 0; } @@ -3090,6 +3134,8 @@ static int load_module(void) AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple); AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple_all); AST_TEST_REGISTER(dialplan_function); + AST_TEST_REGISTER(object_field_registered); + return AST_MODULE_LOAD_SUCCESS; }