diff --git a/CHANGES b/CHANGES index 91acc9e5c9..81ed1bc72c 100644 --- a/CHANGES +++ b/CHANGES @@ -98,6 +98,12 @@ ARI * The live recording object on recording events now contains a target_uri field which contains the URI of what is being recorded. +Core +------------------ + * Exposed sorcery-based configuration files like pjsip.conf to dialplans via + the new AST_SORCERY diaplan function. + + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 12.0.0 to Asterisk 12.1.0 ------------ ------------------------------------------------------------------------------ diff --git a/funcs/func_sorcery.c b/funcs/func_sorcery.c new file mode 100644 index 0000000000..1671b3f055 --- /dev/null +++ b/funcs/func_sorcery.c @@ -0,0 +1,221 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Fairview 5 Engineering, LLC + * + * George Joseph + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Get a field from a sorcery object + * + * \author \verbatim George Joseph \endverbatim + * + * \ingroup functions + * + */ + +/*** MODULEINFO + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "") + +#include "asterisk/app.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/sorcery.h" + +/*** DOCUMENTATION + + + Get a field from a sorcery object + + + + The name of the module owning the sorcery instance. + + + The type of object to query. + + + The id of the object to query. + + + The name of the field. + + + Fields that have multiple occurrences may be retrieved in two ways. + + Returns all matching fields concatenated + in a single string separated by separator + which defaults to ,. + + Returns the nth occurrence of the field + as specified by occurrence_number which defaults to 1. + + + The default is concat with separator ,. + + + Specifies either the separator for concat + or the occurrence number for single. + + + +***/ + +static int sorcery_function_read(struct ast_channel *chan, + const char *cmd, char *data, struct ast_str **buf, ssize_t len) +{ + char *parsed_data = ast_strdupa(data); + RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); + RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup); + struct ast_variable *change_set; + struct ast_variable *it_change_set; + int found, field_number = 1, ix, method; + char *separator = ","; + + enum methods { + CONCAT, + SINGLE, + }; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(module_name); + AST_APP_ARG(object_type); + AST_APP_ARG(object_id); + AST_APP_ARG(field_name); + AST_APP_ARG(method); + AST_APP_ARG(method_arg); + ); + + /* Check for zero arguments */ + if (ast_strlen_zero(parsed_data)) { + ast_log(AST_LOG_ERROR, "Cannot call %s without arguments\n", cmd); + return -1; + } + + AST_STANDARD_APP_ARGS(args, parsed_data); + + if (ast_strlen_zero(args.module_name)) { + ast_log(AST_LOG_ERROR, "Cannot call %s without a module name to query\n", cmd); + return -1; + } + + if (ast_strlen_zero(args.object_type)) { + ast_log(AST_LOG_ERROR, "Cannot call %s with an empty object type\n", cmd); + return -1; + } + + if (ast_strlen_zero(args.object_id)) { + ast_log(AST_LOG_ERROR, "Cannot call %s with an empty object name\n", cmd); + return -1; + } + + if (ast_strlen_zero(args.field_name)) { + ast_log(AST_LOG_ERROR, "Cannot call %s with an empty field name\n", cmd); + return -1; + } + + if (ast_strlen_zero(args.method)) { + method = CONCAT; + } else { + if (strcmp(args.method, "concat") == 0) { + method = CONCAT; + if (ast_strlen_zero(args.method_arg)) { + separator = ","; + } else { + separator = args.method_arg; + } + + } else if (strcmp(args.method, "single") == 0) { + method = SINGLE; + if (!ast_strlen_zero(args.method_arg)) { + if (sscanf(args.method_arg, "%30d", &field_number) <= 0 || field_number <= 0 ) { + ast_log(AST_LOG_ERROR, "occurrence_number must be a positive integer\n"); + return -1; + } + } + } else { + ast_log(AST_LOG_ERROR, "Retrieval method must be 'concat' or 'single'\n"); + return -1; + } + } + + sorcery = ast_sorcery_retrieve_by_module_name(args.module_name); + if (!sorcery) { + ast_log(AST_LOG_ERROR, "Failed to retrieve sorcery instance for module %s\n", args.module_name); + return -1; + } + + sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args.object_type, args.object_id); + if (!sorcery_obj) { + return -1; + } + + change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj); + if (!change_set) { + return -1; + } + + ix=1; + found = 0; + for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) { + + if (method == CONCAT && strcmp(it_change_set->name, args.field_name) == 0) { + ast_str_append(buf, 0, "%s%s", it_change_set->value, separator); + found = 1; + continue; + } + + if (method == SINGLE && strcmp(it_change_set->name, args.field_name) == 0 && ix++ == field_number) { + ast_str_set(buf, len, "%s", it_change_set->value); + found = 1; + break; + } + } + + ast_variables_destroy(change_set); + + if (!found) { + return -1; + } + + if (method == CONCAT) { + ast_str_truncate(*buf, -1); + } + + return 0; +} + +static struct ast_custom_function sorcery_function = { + .name = "AST_SORCERY", + .read2 = sorcery_function_read, +}; + +static int unload_module(void) +{ + return ast_custom_function_unregister(&sorcery_function); +} + +static int load_module(void) +{ + return ast_custom_function_register(&sorcery_function); +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get a field from a sorcery object"); + diff --git a/include/asterisk/config.h b/include/asterisk/config.h index 6d341db612..1c10d176bc 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -691,7 +691,34 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch void ast_variable_append(struct ast_category *category, struct ast_variable *variable); void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line); int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line); -struct ast_variable *ast_variable_list_sort(struct ast_variable *start); + +/*! + * \brief Performs an in-place sort on the variable list by ascending name + * + * \param head The variable list head + * + * \return The new list head + */ +struct ast_variable *ast_variable_list_sort(struct ast_variable *head); + +/*! + * \brief Appends a variable list to the end of another list + * + * \param head A pointer to an ast_variable * of the existing variable list head. May NOT be NULL + * but the content may be to initialize a new list. If so, upon return, this parameter will be updated + * with a pointer to the new list head. + * \param search_hint The place in the current list to start searching for the end of the list. + * Might help performance on longer lists. If NULL, it defaults to head. + * \param new_var The head of the new variable list to be appended + * + * \return The tail of the resulting list. + * + * \note If the existing *head is NULL, it will be updated to new_var. This allows you to call + * ast_variable_list_append in a loop or callback without initializing the list first. + */ +struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, + struct ast_variable *new_var); +#define ast_variable_list_append(head, new_var) ast_variable_list_append_hint(head, NULL, new_var) /*! * \brief Update variable value within a config diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index cf11528d1f..d4d62bfdb6 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -119,6 +119,24 @@ enum ast_sorcery_retrieve_flags { AST_RETRIEVE_FLAG_ALL = (1 << 1), }; +/*! + * \brief Field handler flags + */ +enum ast_sorcery_field_handler_flags { + /*! \brief Try both handlers, string first */ + AST_HANDLER_PREFER_STRING, + + /*! \brief Try both handlers, list first */ + AST_HANDLER_PREFER_LIST, + + /*! \brief Use string handler only */ + AST_HANDLER_ONLY_STRING, + + /*! \brief Use list handler only */ + AST_HANDLER_ONLY_LIST, +}; + + /*! \brief Forward declaration for the sorcery main structure */ struct ast_sorcery; @@ -465,15 +483,19 @@ int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char * * \param type Type of object * \param name Name of the field * \param default_val Default value of the field + * \param config_handler A custom handler for translating the string representation of the fields + * \param sorcery_handler A custom handler for translating the native representation of the fields + * \param multiple_handler A custom handler for translating the native representation of the fields * \param opt_type Option type * \param flags Option type specific flags * * \retval 0 success * \retval -1 failure */ -int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type, - aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, unsigned int no_doc, - size_t argc, ...); +int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, + const char *name, const char *default_val, enum aco_option_type opt_type, + aco_option_handler config_handler, sorcery_field_handler sorcery_handler, + sorcery_fields_handler multiple_handler, unsigned int flags, unsigned int no_doc, size_t argc, ...); /*! * \brief Register a field within an object @@ -489,7 +511,7 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char * \retval -1 failure */ #define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags, ...) \ - __ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, NULL, NULL, flags, 0, VA_NARGS(__VA_ARGS__), __VA_ARGS__) + __ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, NULL, NULL, NULL, flags, 0, VA_NARGS(__VA_ARGS__), __VA_ARGS__) /*! * \brief Register a field within an object without documentation @@ -505,7 +527,7 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char * \retval -1 failure */ #define ast_sorcery_object_field_register_nodoc(sorcery, type, name, default_val, opt_type, flags, ...) \ - __ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, NULL, NULL, flags, 1, VA_NARGS(__VA_ARGS__), __VA_ARGS__) + __ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, NULL, NULL, NULL, flags, 1, VA_NARGS(__VA_ARGS__), __VA_ARGS__) /*! * \brief Register a field within an object with custom handlers @@ -516,13 +538,14 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char * \param default_val Default value of the field * \param config_handler Custom configuration handler * \param sorcery_handler Custom sorcery handler + * \param multiple_handler Custom multiple handler * \param flags Option type specific flags * * \retval 0 success * \retval -1 failure */ -#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, flags, ...) \ - __ast_sorcery_object_field_register(sorcery, type, name, default_val, OPT_CUSTOM_T, config_handler, sorcery_handler, flags, 0, VA_NARGS(__VA_ARGS__), __VA_ARGS__); +#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags, ...) \ + __ast_sorcery_object_field_register(sorcery, type, name, default_val, OPT_CUSTOM_T, config_handler, sorcery_handler, multiple_handler, flags, 0, VA_NARGS(__VA_ARGS__), __VA_ARGS__); /*! * \brief Register a field within an object with custom handlers without documentation @@ -533,13 +556,14 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char * \param default_val Default value of the field * \param config_handler Custom configuration handler * \param sorcery_handler Custom sorcery handler + * \param multiple_handler Custom multiple handler * \param flags Option type specific flags * * \retval 0 success * \retval -1 failure */ -#define ast_sorcery_object_field_register_custom_nodoc(sorcery, type, name, default_val, config_handler, sorcery_handler, flags, ...) \ - __ast_sorcery_object_field_register(sorcery, type, name, default_val, OPT_CUSTOM_T, config_handler, sorcery_handler, flags, 1, VA_NARGS(__VA_ARGS__), __VA_ARGS__); +#define ast_sorcery_object_field_register_custom_nodoc(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags, ...) \ + __ast_sorcery_object_field_register(sorcery, type, name, default_val, OPT_CUSTOM_T, config_handler, sorcery_handler, multiple_handler, flags, 1, VA_NARGS(__VA_ARGS__), __VA_ARGS__); /*! * \brief Inform any wizards to load persistent objects @@ -578,6 +602,22 @@ void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *ty */ void ast_sorcery_ref(struct ast_sorcery *sorcery); + +/*! + * \brief Create an object set (KVP list) for an object + * + * \param sorcery Pointer to a sorcery structure + * \param object Pointer to a sorcery object + * \param flags Flags indicating which handler to use and in what order. + * + * \retval non-NULL success + * \retval NULL if error occurred + * + * \note The returned ast_variable list must be destroyed using ast_variables_destroy + */ +struct ast_variable *ast_sorcery_objectset_create2(const struct ast_sorcery *sorcery, + const void *object, enum ast_sorcery_field_handler_flags flags); + /*! * \brief Create an object set (KVP list) for an object * @@ -588,8 +628,14 @@ void ast_sorcery_ref(struct ast_sorcery *sorcery); * \retval NULL if error occurred * * \note The returned ast_variable list must be destroyed using ast_variables_destroy + * + * \note This function attempts to use a field's sorcery_fields_handler first and if that + * doesn't exist or fails, a field's sorcery_field_handler is used. The difference is + * that the former may return multiple list entries for the same field and the latter will only + * return 1. It's up to the field itself to determine what the appropriate content is. */ -struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object); +#define ast_sorcery_objectset_create(sorcery, object) \ + ast_sorcery_objectset_create2(sorcery, object, AST_HANDLER_PREFER_LIST) /*! * \brief Create an object set in JSON format for an object diff --git a/main/bucket.c b/main/bucket.c index b3a0d3ca94..f698c570b4 100644 --- a/main/bucket.c +++ b/main/bucket.c @@ -944,8 +944,8 @@ int ast_bucket_init(void) } ast_sorcery_object_field_register(bucket_sorcery, "bucket", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket, scheme)); - ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "created", "", timeval_str2struct, timeval_struct2str, 0, FLDSET(struct ast_bucket, created)); - ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "modified", "", timeval_str2struct, timeval_struct2str, 0, FLDSET(struct ast_bucket, modified)); + ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, created)); + ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, modified)); if (ast_sorcery_apply_default(bucket_sorcery, "file", "bucket_file", NULL)) { ast_log(LOG_ERROR, "Failed to apply intermediary for 'file' object type in Bucket sorcery\n"); @@ -958,8 +958,8 @@ int ast_bucket_init(void) } ast_sorcery_object_field_register(bucket_sorcery, "file", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket_file, scheme)); - ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "created", "", timeval_str2struct, timeval_struct2str, 0, FLDSET(struct ast_bucket_file, created)); - ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "modified", "", timeval_str2struct, timeval_struct2str, 0, FLDSET(struct ast_bucket_file, modified)); + ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, created)); + ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, modified)); return 0; } diff --git a/main/config.c b/main/config.c index 70ef6a8044..db9182a469 100644 --- a/main/config.c +++ b/main/config.c @@ -620,6 +620,26 @@ struct ast_variable *ast_variable_list_sort(struct ast_variable *start) return top.next; } +struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar) +{ + struct ast_variable *curr; + ast_assert(head != NULL); + + if (!*head) { + *head = newvar; + } else { + if (search_hint == NULL) { + search_hint = *head; + } + for (curr = search_hint; curr->next; curr = curr->next); + curr->next = newvar; + } + + for (curr = newvar; curr->next; curr = curr->next); + + return curr; +} + const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var) { const char *tmp; diff --git a/main/sorcery.c b/main/sorcery.c index bbdfa8cf2b..99d051ab24 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -811,7 +811,7 @@ int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char * } int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type, - aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, unsigned int no_doc, size_t argc, ...) + aco_option_handler config_handler, sorcery_field_handler sorcery_handler, sorcery_fields_handler multiple_handler, unsigned int flags, unsigned int no_doc, size_t argc, ...) { 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); @@ -832,6 +832,7 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char ast_copy_string(object_field->name, name, sizeof(object_field->name)); object_field->handler = sorcery_handler; + object_field->multiple_handler = multiple_handler; va_start(args, argc); for (pos = 0; pos < argc; pos++) { @@ -1015,13 +1016,47 @@ void ast_sorcery_ref(struct ast_sorcery *sorcery) ao2_ref(sorcery, +1); } -struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object) +static struct ast_variable *get_single_field_as_var_list(const void *object, struct ast_sorcery_object_field *object_field) +{ + struct ast_variable *tmp = NULL; + char *buf = NULL; + + if (!object_field->handler) { + return NULL; + } + + if (!(object_field->handler(object, object_field->args, &buf))) { + tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""); + } + ast_free(buf); + + return tmp; +} + +static struct ast_variable *get_multiple_fields_as_var_list(const void *object, struct ast_sorcery_object_field *object_field) +{ + struct ast_variable *tmp = NULL; + + if (!object_field->multiple_handler) { + return NULL; + } + + if (object_field->multiple_handler(object, &tmp)) { + ast_variables_destroy(tmp); + tmp = NULL; + } + + return tmp; +} + +struct ast_variable *ast_sorcery_objectset_create2(const struct ast_sorcery *sorcery, + const void *object, enum ast_sorcery_field_handler_flags flags) { const struct ast_sorcery_object_details *details = object; RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup); struct ao2_iterator i; struct ast_sorcery_object_field *object_field; - struct ast_variable *fields = NULL; + struct ast_variable *head = NULL, *tail = NULL; int res = 0; if (!object_type) { @@ -1033,38 +1068,46 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) { struct ast_variable *tmp = NULL; - if (object_field->multiple_handler) { - if ((res = object_field->multiple_handler(object, &tmp))) { - ast_variables_destroy(tmp); + switch (flags) { + case AST_HANDLER_PREFER_LIST: + if ((tmp = get_multiple_fields_as_var_list(object, object_field)) || + (tmp = get_single_field_as_var_list(object, object_field))) { + break; } - } else if (object_field->handler) { - char *buf = NULL; - - if ((res = object_field->handler(object, object_field->args, &buf)) || - !(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) { - res = -1; + continue; + case AST_HANDLER_PREFER_STRING: + if ((tmp = get_single_field_as_var_list(object, object_field)) || + (tmp = get_multiple_fields_as_var_list(object, object_field))) { + break; } - - ast_free(buf); - } else { + continue; + case AST_HANDLER_ONLY_LIST: + if ((tmp = get_multiple_fields_as_var_list(object, object_field))) { + break; + } + continue; + case AST_HANDLER_ONLY_STRING: + if ((tmp = get_single_field_as_var_list(object, object_field))) { + break; + } + continue; + default: continue; } - if (!res && tmp) { - tmp->next = fields; - fields = tmp; - } + tail = ast_variable_list_append_hint(&head, tail, tmp); + } ao2_iterator_destroy(&i); /* If any error occurs we destroy all fields handled before so a partial objectset is not returned */ if (res) { - ast_variables_destroy(fields); - fields = NULL; + ast_variables_destroy(head); + head = NULL; } - return fields; + return head; } struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sorcery, const void *object) diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c index 056449b60b..bfba26253e 100644 --- a/res/res_pjsip/config_auth.c +++ b/res/res_pjsip/config_auth.c @@ -323,7 +323,7 @@ int ast_sip_initialize_sorcery_auth(void) ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime", "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime)); ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type", - "userpass", auth_type_handler, auth_type_to_str, 0, 0); + "userpass", auth_type_handler, auth_type_to_str, NULL, 0, 0); ast_sip_register_endpoint_formatter(&endpoint_auth_formatter); ast_sip_register_cli_formatter(&cli_auth_formatter); diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c index 58d0f15775..797427b4a8 100644 --- a/res/res_pjsip/config_transport.c +++ b/res/res_pjsip/config_transport.c @@ -440,6 +440,29 @@ static int transport_localnet_handler(const struct aco_option *opt, struct ast_v return error; } +static int localnet_to_vl(const void *obj, struct ast_variable **fields) +{ + const struct ast_sip_transport *transport = obj; + + char str[MAX_OBJECT_FIELD]; + struct ast_variable *head = NULL; + struct ast_ha *ha = transport->localnet; + + for (; ha; ha = ha->next) { + const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr)); + snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "", + addr, ast_sockaddr_stringify_addr(&ha->netmask)); + + ast_variable_list_append(&head, ast_variable_new("local_net", str, "")); + } + + if (head) { + *fields = head; + } + + return 0; +} + static int localnet_to_str(const void *obj, const intptr_t *args, char **buf) { RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); @@ -599,8 +622,8 @@ int ast_sip_initialize_sorcery_transport(void) } ast_sorcery_object_field_register(sorcery, "transport", "type", "", OPT_NOOP_T, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, transport_protocol_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, transport_protocol_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations)); ast_sorcery_object_field_register(sorcery, "transport", "ca_list_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_file)); ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file)); @@ -610,13 +633,13 @@ int ast_sip_initialize_sorcery_transport(void) ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_port", "0", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, external_signaling_port), 0, 65535); ast_sorcery_object_field_register(sorcery, "transport", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_media_address)); ast_sorcery_object_field_register(sorcery, "transport", "domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, domain)); - ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, verify_server_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, verify_client_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, require_client_cert_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, tls_method_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, transport_tls_cipher_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "local_net", "", transport_localnet_handler, localnet_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, verify_server_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, verify_client_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, require_client_cert_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, tls_method_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, transport_tls_cipher_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "local_net", "", transport_localnet_handler, localnet_to_str, localnet_to_vl, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos)); ast_sip_register_endpoint_formatter(&endpoint_transport_formatter); diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index 6c77beefe7..eb12d8e745 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -279,6 +279,25 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab return 0; } +static int contact_to_vl(void *object, void *arg, int flags) +{ + struct ast_sip_contact *contact = object; + struct ast_variable **var = arg; + + ast_variable_list_append(&*var, ast_variable_new("contact", contact->uri, "")); + + return 0; +} + +static int contacts_to_vl(const void *obj, struct ast_variable **fields) +{ + const struct ast_sip_aor *aor = obj; + + ast_sip_for_each_contact(aor, contact_to_vl, fields); + + return 0; +} + int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg) { char *copy, *name; @@ -343,7 +362,46 @@ int ast_sip_contact_to_str(void *object, void *arg, int flags) static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf) { - return ast_sip_sorcery_object_to_ami(aor, buf); + RAII_VAR(struct ast_variable *, objset, ast_sorcery_objectset_create2( + ast_sip_get_sorcery(), aor, AST_HANDLER_ONLY_STRING), ast_variables_destroy); + struct ast_variable *i; + + if (!objset) { + return -1; + } + + ast_str_append(buf, 0, "ObjectType: %s\r\n", + ast_sorcery_object_get_type(aor)); + ast_str_append(buf, 0, "ObjectName: %s\r\n", + ast_sorcery_object_get_id(aor)); + + for (i = objset; i; i = i->next) { + char *camel = ast_to_camel_case(i->name); + if (strcmp(camel, "Contact") == 0) { + ast_free(camel); + camel = NULL; + } + ast_str_append(buf, 0, "%s: %s\r\n", S_OR(camel, "Contacts"), i->value); + ast_free(camel); + } + + return 0; +} + +static int contacts_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_aor *aor = obj; + RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); + + ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &str); + ast_str_truncate(str, -1); + + *buf = ast_strdup(ast_str_buffer(str)); + if (!*buf) { + return -1; + } + + return 0; } static int format_ami_aor_handler(void *obj, void *arg, int flags) @@ -364,11 +422,6 @@ static int format_ami_aor_handler(void *obj, void *arg, int flags) } sip_aor_to_ami(aor, &buf); - ast_str_append(&buf, 0, "Contacts: "); - ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &buf); - ast_str_truncate(buf, -1); - ast_str_append(&buf, 0, "\r\n"); - total_contacts = ao2_container_count(contacts); num_permanent = aor->permanent_contacts ? ao2_container_count(aor->permanent_contacts) : 0; @@ -670,7 +723,7 @@ int ast_sip_initialize_sorcery_location(void) ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0); ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri)); ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path)); - ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400); ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy)); @@ -684,7 +737,7 @@ int ast_sip_initialize_sorcery_location(void) ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify)); ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts)); ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing)); - ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, contacts_to_str, contacts_to_vl, 0, 0); ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes)); ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy)); ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path)); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 994987b7a9..110be29060 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -809,12 +809,12 @@ static int set_var_handler(const struct aco_option *opt, } *val++ = '\0'; + if (!(new_var = ast_variable_new(name, val, ""))) { return -1; } - new_var->next = endpoint->channel_vars; - endpoint->channel_vars = new_var; + ast_variable_list_append(&endpoint->channel_vars, new_var); return 0; } @@ -834,6 +834,16 @@ static int set_var_to_str(const void *obj, const intptr_t *args, char **buf) return 0; } +static int set_var_to_vl(const void *obj, struct ast_variable **fields) +{ + const struct ast_sip_endpoint *endpoint = obj; + if (endpoint->channel_vars) { + *fields = ast_variables_dup(endpoint->channel_vars); + } + return 0; +} + + static void *sip_nat_hook_alloc(const char *name) { return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL); @@ -1021,8 +1031,8 @@ static void sip_sorcery_object_ami_set_type_name(const void *obj, struct ast_str int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf) { - RAII_VAR(struct ast_variable *, objset, ast_sorcery_objectset_create( - ast_sip_get_sorcery(), obj), ast_variables_destroy); + RAII_VAR(struct ast_variable *, objset, ast_sorcery_objectset_create2( + ast_sip_get_sorcery(), obj, AST_HANDLER_ONLY_STRING), ast_variables_destroy); struct ast_variable *i; if (!objset) { @@ -1569,7 +1579,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disallow", "", OPT_CODEC_T, 0, FLDSET(struct ast_sip_endpoint, media.prefs, media.codecs)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow", "", OPT_CODEC_T, 1, FLDSET(struct ast_sip_endpoint, media.prefs, media.codecs)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmf_mode", "rfc4733", dtmf_handler, dtmf_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmf_mode", "rfc4733", dtmf_handler, dtmf_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ipv6)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.symmetric)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ice_support", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ice_support)); @@ -1579,23 +1589,23 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_suggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, timers_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, timers_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.min_se)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_sess_expires", "1800", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.sess_expires)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, inbound_auths_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, inbound_auths_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.address)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, ident_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, ident_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.enabled)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, direct_media_method_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, connected_line_method_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, direct_media_glare_mitigation_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, direct_media_method_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, connected_line_method_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, direct_media_glare_mitigation_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disable_direct_media_on_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.disable_on_nat)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid", "", caller_id_handler, caller_id_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_privacy", "", caller_id_privacy_handler, caller_id_privacy_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_tag", "", caller_id_tag_handler, caller_id_tag_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid", "", caller_id_handler, caller_id_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_privacy", "", caller_id_privacy_handler, caller_id_privacy_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_tag", "", caller_id_tag_handler, caller_id_tag_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_inbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_inbound)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_outbound)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_pai)); @@ -1603,17 +1613,17 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.mailboxes)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.aggregate)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, media_encryption_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, media_encryption_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "one_touch_recording", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, info.recording.enabled)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "inband_progress", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, inband_progress)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "call_group", "", group_handler, callgroup_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "pickup_group", "", group_handler, pickupgroup_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_call_group", "", named_groups_handler, named_callgroups_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_pickup_group", "", named_groups_handler, named_pickupgroups_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "call_group", "", group_handler, callgroup_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "pickup_group", "", group_handler, pickupgroup_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_call_group", "", named_groups_handler, named_callgroups_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_pickup_group", "", named_groups_handler, named_pickupgroups_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "device_state_busy_at", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, devicestate_busy_at)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.enabled)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat)); @@ -1625,8 +1635,8 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_transfer", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allowtransfer)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_owner", "-", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpowner)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_session", "Asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpsession)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_audio", "0", tos_handler, tos_audio_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_video", "0", tos_handler, tos_video_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_audio", "0", tos_handler, tos_audio_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_video", "0", tos_handler, tos_video_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_audio", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_audio)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_subscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow)); @@ -1635,17 +1645,17 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_verify", "", dtls_handler, dtlsverify_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_rekey", "", dtls_handler, dtlsrekey_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cert_file", "", dtls_handler, dtlscertfile_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_private_key", "", dtls_handler, dtlsprivatekey_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, dtlscipher_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_file", "", dtls_handler, dtlscafile_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_path", "", dtls_handler, dtlscapath_to_str, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_setup", "", dtls_handler, dtlssetup_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_verify", "", dtls_handler, dtlsverify_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_rekey", "", dtls_handler, dtlsrekey_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cert_file", "", dtls_handler, dtlscertfile_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_private_key", "", dtls_handler, dtlsprivatekey_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, dtlscipher_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_file", "", dtls_handler, dtlscafile_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_path", "", dtls_handler, dtlscapath_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_setup", "", dtls_handler, dtlssetup_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "redirect_method", "user", redirect_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "redirect_method", "user", redirect_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); diff --git a/res/res_pjsip_acl.c b/res/res_pjsip_acl.c index 48c2ea4b54..acb073d4bb 100644 --- a/res/res_pjsip_acl.c +++ b/res/res_pjsip_acl.c @@ -278,12 +278,12 @@ static int load_module(void) } ast_sorcery_object_field_register(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "type", "", OPT_NOOP_T, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "permit", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "deny", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "acl", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_permit", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_deny", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_acl", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "permit", "", acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "deny", "", acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "acl", "", acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_permit", "", acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_deny", "", acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_acl", "", acl_handler, NULL, NULL, 0, 0); ast_sip_register_service(&acl_module); return AST_MODULE_LOAD_SUCCESS; diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index d78901e66d..414f430a20 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -184,7 +184,8 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va return error; } -static int ip_identify_match_to_str(const void *obj, const intptr_t *args, char **buf) + +static int match_to_str(const void *obj, const intptr_t *args, char **buf) { RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); const struct ip_identify_match *identify = obj; @@ -194,6 +195,29 @@ static int ip_identify_match_to_str(const void *obj, const intptr_t *args, char return 0; } +static int match_to_var_list(const void *obj, struct ast_variable **fields) +{ + char str[MAX_OBJECT_FIELD]; + const struct ip_identify_match *identify = obj; + struct ast_variable *head = NULL; + struct ast_ha *ha = identify->matches; + + for (; ha; ha = ha->next) { + const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr)); + snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "", + addr, ast_sockaddr_stringify_addr(&ha->netmask)); + + ast_variable_list_append(&head, ast_variable_new("match", str, "")); + + } + + if (head) { + *fields = head; + } + + return 0; +} + static int sip_identify_to_ami(const struct ip_identify_match *identify, struct ast_str **buf) { @@ -374,7 +398,7 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name)); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, ip_identify_match_to_str, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0); ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify"); ast_sip_register_endpoint_identifier(&ip_identifier); diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 0ef9507110..019ac51cd7 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -891,9 +891,28 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf) { const struct sip_outbound_registration *registration = obj; + return ast_sip_auths_to_str(®istration->outbound_auths, buf); } +static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fields) +{ + const struct sip_outbound_registration *registration = obj; + int i; + struct ast_variable *head = NULL; + + for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths) ; i++) { + ast_variable_list_append(&head, ast_variable_new("outbound_auth", + AST_VECTOR_GET(®istration->outbound_auths, i), "")); + } + + if (head) { + *fields = head; + } + + return 0; +} + static struct sip_outbound_registration *retrieve_registration(const char *registration_name) { return ast_sorcery_retrieve_by_id( @@ -1083,7 +1102,7 @@ static int ami_outbound_registration_detail(void *obj, void *arg, int flags) static int ami_show_outbound_registrations(struct mansession *s, const struct message *m) { - struct ast_sip_ami ami = { s = s, m = m }; + struct ast_sip_ami ami = { .s = s, .m = m }; struct sip_ami_outbound ami_outbound = { .ami = &ami }; RAII_VAR(struct ao2_container *, regs, ast_sorcery_retrieve_by_fields( ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | @@ -1240,7 +1259,7 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent)); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path)); ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); sip_outbound_registration_perform_all(); diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c index fe1992bc09..ed4d604e6b 100644 --- a/tests/test_sorcery.c +++ b/tests/test_sorcery.c @@ -26,6 +26,7 @@ /*** MODULEINFO TEST_FRAMEWORK + func_sorcery core ***/ @@ -36,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "") #include "asterisk/test.h" #include "asterisk/module.h" #include "asterisk/astobj2.h" +#include "asterisk/pbx.h" #include "asterisk/sorcery.h" #include "asterisk/logger.h" #include "asterisk/json.h" @@ -45,12 +47,22 @@ struct test_sorcery_object { SORCERY_OBJECT(details); unsigned int bob; unsigned int joe; + struct ast_variable *jim; + struct ast_variable *jack; }; +/*! \brief Internal function to destroy a test object */ +static void test_sorcery_object_destroy(void *obj) +{ + struct test_sorcery_object *tobj = obj; + ast_variables_destroy(tobj->jim); + ast_variables_destroy(tobj->jack); +} + /*! \brief Internal function to allocate a test object */ static void *test_sorcery_object_alloc(const char *id) { - return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL); + return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), test_sorcery_object_destroy); } /*! \brief Internal function for object set transformation */ @@ -85,6 +97,8 @@ static int test_sorcery_copy(const void *src, void *dst) struct test_sorcery_object *obj = dst; obj->bob = 10; obj->joe = 20; + obj->jim = ast_variable_new("jim", "444", ""); + obj->jack = ast_variable_new("jack", "999,000", ""); return 0; } @@ -235,6 +249,55 @@ static const struct ast_sorcery_observer test_observer = { .loaded = sorcery_observer_loaded, }; +/* This handler takes a simple value and creates new list entry for it*/ +static int jim_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct test_sorcery_object *tobj = obj; + + ast_variable_list_append(&tobj->jim, ast_variables_dup(var)); + + return 0; +} + +/* This handler takes a CSV string and creates new a new list entry for each value */ +static int jack_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct test_sorcery_object *tobj = obj; + + char *jacks = ast_strdupa(var->value); + char *val; + + while ((val = strsep(&jacks, ","))) { + ast_variable_list_append(&tobj->jack, ast_variable_new("jack", val, "")); + } + return 0; +} + +static int jim_vl(const void *obj, struct ast_variable **fields) +{ + const struct test_sorcery_object *tobj = obj; + if (tobj->jim) { + *fields = ast_variables_dup(tobj->jim); + } + return 0; +} + +static int jack_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct test_sorcery_object *tobj = obj; + struct ast_variable *curr = tobj->jack; + RAII_VAR(struct ast_str *, str, ast_str_create(128), ast_free); + + while(curr) { + ast_str_append(&str, 0, "%s,", curr->value); + curr = curr->next; + } + ast_str_truncate(str, -1); + *buf = ast_strdup(ast_str_buffer(str)); + str = NULL; + return 0; +} + static struct ast_sorcery *alloc_and_initialize_sorcery(void) { struct ast_sorcery *sorcery; @@ -251,6 +314,8 @@ static struct ast_sorcery *alloc_and_initialize_sorcery(void) ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob)); ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe)); + ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jim", "444", jim_handler, NULL, jim_vl, 0, 0); + ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jack", "888,999", jack_handler, jack_str, NULL, 0, 0); return sorcery; } @@ -727,6 +792,8 @@ AST_TEST_DEFINE(object_copy) obj->bob = 50; obj->joe = 100; + jim_handler(NULL, ast_variable_new("jim", "444", ""), obj); + jim_handler(NULL, ast_variable_new("jim", "555", ""), obj); if (!(copy = ast_sorcery_copy(sorcery, obj))) { ast_test_status_update(test, "Failed to create a copy of a known valid object\n"); @@ -740,6 +807,22 @@ AST_TEST_DEFINE(object_copy) } else if (copy->joe != obj->joe) { ast_test_status_update(test, "Value of 'joe' on newly created copy is not the same as original\n"); res = AST_TEST_FAIL; + } else if (!copy->jim) { + ast_test_status_update(test, "A new ast_variable was not created for 'jim'\n"); + res = AST_TEST_FAIL; + } else if (copy->jim == obj->jim) { + ast_test_status_update(test, "Created copy of 'jim' is actually the ogirinal 'jim'\n"); + res = AST_TEST_FAIL; + } else if (strcmp(copy->jim->value, obj->jim->value)) { + ast_test_status_update(test, "Value of 1st 'jim' on newly created copy is not the same as original\n"); + res = AST_TEST_FAIL; + } else if (!copy->jim->next) { + ast_test_status_update(test, "A new ast_variable was not created for 2nd 'jim'\n"); + res = AST_TEST_FAIL; + } else if (strcmp(copy->jim->next->value, obj->jim->next->value)) { + ast_test_status_update(test, "Value of 2nd 'jim' (%s %s) on newly created copy is not the same as original (%s %s)\n", + copy->jim->value, copy->jim->next->value, obj->jim->value, obj->jim->next->value); + res = AST_TEST_FAIL; } return res; @@ -791,6 +874,12 @@ AST_TEST_DEFINE(object_copy_native) } else if (copy->joe != 20) { ast_test_status_update(test, "Value of 'joe' on newly created copy is not the predefined native copy value\n"); res = AST_TEST_FAIL; + } else if (!copy->jim) { + ast_test_status_update(test, "A new ast_variable was not created for 'jim'\n"); + res = AST_TEST_FAIL; + } else if (strcmp(copy->jim->value, "444")) { + ast_test_status_update(test, "Value of 'jim' on newly created copy is not the predefined native copy value\n"); + res = AST_TEST_FAIL; } return res; @@ -804,6 +893,7 @@ AST_TEST_DEFINE(object_diff) RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy); struct ast_variable *field; int res = AST_TEST_PASS; + int jims = 0; switch (cmd) { case TEST_INIT: @@ -829,6 +919,8 @@ AST_TEST_DEFINE(object_diff) obj1->bob = 99; obj1->joe = 55; + jim_handler(NULL, ast_variable_new("jim", "444", ""), obj1); + jim_handler(NULL, ast_variable_new("jim", "555", ""), obj1); if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) { ast_test_status_update(test, "Failed to allocate a second known object type\n"); @@ -837,6 +929,9 @@ AST_TEST_DEFINE(object_diff) obj2->bob = 99; obj2->joe = 42; + jim_handler(NULL, ast_variable_new("jim", "444", ""), obj2); + jim_handler(NULL, ast_variable_new("jim", "666", ""), obj2); + jim_handler(NULL, ast_variable_new("jim", "777", ""), obj2); if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) { ast_test_status_update(test, "Failed to diff obj1 and obj2\n"); @@ -845,16 +940,30 @@ AST_TEST_DEFINE(object_diff) return AST_TEST_FAIL; } - for (field = changes; field; field = field->next) { - if (!strcmp(field->name, "joe")) { - if (strcmp(field->value, "42")) { - ast_test_status_update(test, "Object diff produced unexpected value '%s' for joe\n", field->value); - res = AST_TEST_FAIL; - } - } else { - ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", field->name); - res = AST_TEST_FAIL; - } + for (field = changes; field; field = field->next) { + if (!strcmp(field->name, "joe")) { + if (strcmp(field->value, "42")) { + ast_test_status_update(test, + "Object diff produced unexpected value '%s' for joe\n", field->value); + res = AST_TEST_FAIL; + } + } else if (!strcmp(field->name, "jim")) { + jims++; + if (!strcmp(field->value, "555")) { + ast_test_status_update(test, + "Object diff produced unexpected value '%s' for jim\n", field->value); + res = AST_TEST_FAIL; + } + } else { + ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", + field->name); + res = AST_TEST_FAIL; + } + } + + if (jims != 2) { + ast_test_status_update(test, "Object diff didn't produce 2 jims\n"); + res = AST_TEST_FAIL; } return res; @@ -972,6 +1081,16 @@ AST_TEST_DEFINE(objectset_create) ast_test_status_update(test, "Object set failed to create proper value for 'joe'\n"); res = AST_TEST_FAIL; } + } else if (!strcmp(field->name, "jim")) { + if (strcmp(field->value, "444")) { + ast_test_status_update(test, "Object set failed to create proper value for 'jim'\n"); + res = AST_TEST_FAIL; + } + } else if (!strcmp(field->name, "jack")) { + if (strcmp(field->value, "888,999")) { + ast_test_status_update(test, "Object set failed to create proper value (%s) for 'jack'\n", field->value); + res = AST_TEST_FAIL; + } } else { ast_test_status_update(test, "Object set created field '%s' which is unknown\n", field->name); res = AST_TEST_FAIL; @@ -1029,6 +1148,16 @@ AST_TEST_DEFINE(objectset_json_create) ast_test_status_update(test, "Object set failed to create proper value for 'joe'\n"); res = AST_TEST_FAIL; } + } else if (!strcmp(ast_json_object_iter_key(field), "jim")) { + if (strcmp(ast_json_string_get(value), "444")) { + ast_test_status_update(test, "Object set failed to create proper value for 'jim'\n"); + res = AST_TEST_FAIL; + } + } else if (!strcmp(ast_json_object_iter_key(field), "jack")) { + if (strcmp(ast_json_string_get(value), "888,999")) { + ast_test_status_update(test, "Object set failed to create proper value for 'jack'\n"); + res = AST_TEST_FAIL; + } } else { ast_test_status_update(test, "Object set created field '%s' which is unknown\n", ast_json_object_iter_key(field)); res = AST_TEST_FAIL; @@ -2696,6 +2825,176 @@ AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple_all) return AST_TEST_PASS; } +AST_TEST_DEFINE(dialplan_function) +{ + RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); + RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup); + RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); + struct ast_str *buf; + char expression[256]; + + switch (cmd) { + case TEST_INIT: + info->name = "dialplan_function"; + info->category = "/main/sorcery/"; + info->summary = "AST_SORCERY dialplan function"; + info->description = + "Test the AST_SORCERY dialplan function"; + 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; + } + + if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) { + ast_test_status_update(test, "Failed to allocate a known object type\n"); + return AST_TEST_FAIL; + } + + if (ast_sorcery_create(sorcery, obj)) { + ast_test_status_update(test, "Failed to create a known object type\n"); + return AST_TEST_FAIL; + } + + if (!(buf = ast_str_create(16))) { + ast_test_status_update(test, "Failed to allocate return buffer\n"); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "notest_sorcery", "test", "blah", "bob"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Retrieved a non-existent module\n"); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "notest", "blah", "bob"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Retrieved a non-existent type\n"); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "noid", "bob"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Retrieved a non-existent id\n"); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "blah", "nobob"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Retrieved a non-existent field\n"); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "blah", "bob"); + if (ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve field 'bob'\n"); + return AST_TEST_FAIL; + } + if (strcmp(ast_str_buffer(buf), "5")) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve field. Got '%d', should be '5'\n", obj->bob); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,1)", "test_sorcery", "test", "blah", "bob"); + if (ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve field 'bob'\n"); + return AST_TEST_FAIL; + } + if (strcmp(ast_str_buffer(buf), "5")) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve field. Got '%d', should be '5'\n", obj->bob); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,2)", "test_sorcery", "test", "blah", "bob"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Got a second 'bob' and shouldn't have\n"); + return AST_TEST_FAIL; + } + + /* 444 is already the first item in the list */ + jim_handler(NULL, ast_variable_new("jim", "555", ""), obj); + jim_handler(NULL, ast_variable_new("jim", "666", ""), obj); + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "blah", "jim"); + if (ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Couldn't retrieve 'jim'\n"); + return AST_TEST_FAIL; + } + if (strcmp(ast_str_buffer(buf), "444,555,666")) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve jim. Got '%s', should be '444,555,666'\n", ast_str_buffer(buf)); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,2)", "test_sorcery", "test", "blah", "jim"); + if (ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Couldn't retrieve 2nd jim\n"); + return AST_TEST_FAIL; + } + if (strcmp(ast_str_buffer(buf), "555")) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve 2nd jim. Got '%s', should be '555'\n", ast_str_buffer(buf)); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,concat,|)", "test_sorcery", "test", "blah", "jim"); + if (ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Couldn't retrieve any 'jim'\n"); + return AST_TEST_FAIL; + } + if (strcmp(ast_str_buffer(buf), "444|555|666")) { + ast_free(buf); + ast_test_status_update(test, "Failed retrieve 'jim'. Got '%s', should be '444|555|666'\n", ast_str_buffer(buf)); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,noconcat,3)", "test_sorcery", "test", "blah", "jim"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Should have failed with invalid retrieval_type\n"); + return AST_TEST_FAIL; + } + + ast_str_reset(buf); + snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,|)", "test_sorcery", "test", "blah", "jim"); + if (!ast_func_read2(NULL, expression, &buf, 16)) { + ast_free(buf); + ast_test_status_update(test, "Should have failed with invalid occurrence_number\n"); + return AST_TEST_FAIL; + } + + ast_free(buf); + + return AST_TEST_PASS; +} + static int unload_module(void) { AST_TEST_UNREGISTER(wizard_registration); @@ -2741,6 +3040,7 @@ static int unload_module(void) AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_field); AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple); AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple_all); + AST_TEST_UNREGISTER(dialplan_function); return 0; } @@ -2789,6 +3089,7 @@ static int load_module(void) AST_TEST_REGISTER(configuration_file_wizard_retrieve_field); AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple); AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple_all); + AST_TEST_REGISTER(dialplan_function); return AST_MODULE_LOAD_SUCCESS; }