summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_directory.c2
-rw-r--r--apps/app_voicemail.c8
-rw-r--r--include/asterisk/config.h176
-rw-r--r--main/config.c346
-rw-r--r--main/manager.c378
-rw-r--r--pbx/pbx_realtime.c2
-rw-r--r--res/res_sorcery_config.c8
-rw-r--r--res/res_sorcery_realtime.c7
-rw-r--r--tests/test_config.c533
-rw-r--r--tests/test_sorcery.c4
-rw-r--r--tests/test_sorcery_realtime.c15
11 files changed, 1263 insertions, 216 deletions
diff --git a/apps/app_directory.c b/apps/app_directory.c
index 350401e46..267e2e8c3 100644
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -528,7 +528,7 @@ static struct ast_config *realtime_directory(char *context)
}
/* Does the context exist within the config file? If not, make one */
- if (!(cat = ast_category_get(cfg, ctx))) {
+ if (!(cat = ast_category_get(cfg, ctx, NULL))) {
if (!(cat = ast_category_new(ctx, "", 99999))) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_config_destroy(cfg);
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 45f9ebfea..c881e49d4 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -1821,7 +1821,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
sprintf(new, "%s%s", newpassword, value);
}
- if (!(cat = ast_category_get(cfg, category))) {
+ if (!(cat = ast_category_get(cfg, category, NULL))) {
ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
break;
}
@@ -1858,7 +1858,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
}
new = ast_alloca(strlen(newpassword) + 1);
sprintf(new, "%s", newpassword);
- if (!(cat = ast_category_get(cfg, category))) {
+ if (!(cat = ast_category_get(cfg, category, NULL))) {
ast_debug(4, "failed to get category!\n");
ast_free(var);
break;
@@ -7791,7 +7791,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
char duration_buf[12];
*duration += prepend_duration;
- msg_cat = ast_category_get(msg_cfg, "message");
+ msg_cat = ast_category_get(msg_cfg, "message", NULL);
snprintf(duration_buf, 11, "%ld", *duration);
if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
@@ -11930,7 +11930,7 @@ static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *
return -1;
}
- cat = ast_category_get(msg_cfg, "message");
+ cat = ast_category_get(msg_cfg, "message", NULL);
if (!cat) {
ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
ast_variables_destroy(var);
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 541fea4ff..98cd71445 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -203,10 +203,30 @@ void ast_config_sort_categories(struct ast_config *config, int descending,
int (*comparator)(struct ast_category *p, struct ast_category *q));
/*!
- * \brief Goes through categories
+ * \brief Browse categories with filters
*
* \param config Which config structure you wish to "browse"
- * \param prev A pointer to a previous category.
+ * \param category_name An optional category name.
+ * Pass NULL to not restrict by category name.
+ * \param prev A pointer to the starting category structure.
+ * Pass NULL to start at the beginning.
+ * \param filter An optional comma-separated list of <name_regex>=<value_regex>
+ * pairs. Only categories with matching variables will be returned.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
+ *
+ * \retval a category on success
+ * \retval NULL on failure/no-more-categories
+ */
+struct ast_category *ast_category_browse_filtered(struct ast_config *config,
+ const char *category_name, struct ast_category *prev, const char *filter);
+
+/*!
+ * \brief Browse categories
+ *
+ * \param config Which config structure you wish to "browse"
+ * \param prev_name A pointer to a previous category name.
*
* \details
* This function is kind of non-intuitive in it's use.
@@ -216,13 +236,20 @@ void ast_config_sort_categories(struct ast_config *config, int descending,
* as the second pointer, and it will return a pointer to the category name
* afterwards.
*
- * \retval a category on success
+ * \retval a category name on success
* \retval NULL on failure/no-more-categories
*/
-char *ast_category_browse(struct ast_config *config, const char *prev);
+char *ast_category_browse(struct ast_config *config, const char *prev_name);
/*!
- * \brief Goes through variables
+ * \brief Browse variables
+ * \param config Which config structure you wish to "browse"
+ * \param category_name Which category to "browse"
+ * \param filter an optional comma-separated list of <name_regex>=<value_regex>
+ * pairs. Only categories with matching variables will be browsed.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
*
* \details
* Somewhat similar in intent as the ast_category_browse.
@@ -231,7 +258,10 @@ char *ast_category_browse(struct ast_config *config, const char *prev);
* \retval ast_variable list on success
* \retval NULL on failure
*/
-struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
+struct ast_variable *ast_variable_browse_filtered(const struct ast_config *config,
+ const char *category_name, const char *filter);
+struct ast_variable *ast_variable_browse(const struct ast_config *config,
+ const char *category_name);
/*!
* \brief given a pointer to a category, return the root variable.
@@ -243,25 +273,50 @@ struct ast_variable *ast_variable_browse(const struct ast_config *config, const
struct ast_variable *ast_category_first(struct ast_category *cat);
/*!
- * \brief Gets a variable
+ * \brief Gets a variable by context and variable names
*
* \param config which (opened) config to use
* \param category category under which the variable lies
* \param variable which variable you wish to get the data for
+ * \param filter an optional comma-separated list of <name_regex>=<value_regex>
+ * pairs. Only categories with matching variables will be searched.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
+ *
+ * \retval The variable value on success
+ * \retval NULL if unable to find it.
+ */
+const char *ast_variable_retrieve_filtered(struct ast_config *config,
+ const char *category, const char *variable, const char *filter);
+const char *ast_variable_retrieve(struct ast_config *config,
+ const char *category, const char *variable);
+
+/*!
+ * \brief Gets a variable from a specific category structure
+ *
+ * \param category category structure under which the variable lies
+ * \param variable which variable you wish to get the data for
*
* \details
- * Goes through a given config file in the given category and searches for the given variable
+ * Goes through a given category and searches for the given variable
*
* \retval The variable value on success
* \retval NULL if unable to find it.
*/
-const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
+const char *ast_variable_find(const struct ast_category *category, const char *variable);
/*!
* \brief Retrieve a category if it exists
*
* \param config which config to use
* \param category_name name of the category you're looking for
+ * \param filter If a config contains more than 1 category with the same name,
+ * you can specify a filter to narrow the search. The filter is a comma-separated
+ * list of <name_regex>=<value_regex> pairs. Only a category with matching
+ * variables will be returned. The special name 'TEMPLATES' can be used with the
+ * special values 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
*
* \details
* This will search through the categories within a given config file for a match.
@@ -269,20 +324,57 @@ const char *ast_variable_retrieve(const struct ast_config *config, const char *c
* \retval pointer to category if found
* \retval NULL if not.
*/
-struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name);
+struct ast_category *ast_category_get(const struct ast_config *config,
+ const char *category_name, const char *filter);
+
+/*!
+ * \brief Return the name of the category
+ *
+ * \param category category structure
+ *
+ * \retval pointer to category name if found
+ * \retval NULL if not.
+ */
+const char *ast_category_get_name(const struct ast_category *category);
+
+/*!
+ * \brief Check if category is a template
+ *
+ * \param category category structure
+ *
+ * \retval 1 if a template.
+ * \retval 0 if not.
+ */
+int ast_category_is_template(const struct ast_category *category);
+
+/*!
+ * \brief Return the template names this category inherits from
+ *
+ * \param category category structure
+ *
+ * \return an ast_str (which must be freed after use) with a comma
+ * separated list of templates names or NULL if there were no templates.
+ */
+struct ast_str *ast_category_get_templates(const struct ast_category *category);
/*!
* \brief Check for category duplicates
*
* \param config which config to use
* \param category_name name of the category you're looking for
+ * \param filter an optional comma-separated list of <name_regex>=<value_regex>
+ * pairs. Only categories with matching variables will be returned.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
*
* \details
* This will search through the categories within a given config file for a match.
*
* \return non-zero if found
*/
-int ast_category_exist(const struct ast_config *config, const char *category_name);
+int ast_category_exist(const struct ast_config *config, const char *category_name,
+ const char *filter);
/*!
* \brief Retrieve realtime configuration
@@ -661,9 +753,23 @@ void ast_config_set_current_category(struct ast_config *cfg, const struct ast_ca
*/
const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var);
-/*! \brief Create a category structure */
+/*!
+ * \brief Create a category
+ *
+ * \param name name of new category
+ * \param in_file filename which contained the new config
+ * \param lineno line number
+ */
struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
-void ast_category_append(struct ast_config *config, struct ast_category *cat);
+
+/*!
+ * \brief Create a category making it a template
+ *
+ * \param name name of new template
+ * \param in_file filename which contained the new config
+ * \param lineno line number
+ */
+struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno);
/*!
* \brief Inserts new category
@@ -677,17 +783,49 @@ void ast_category_append(struct ast_config *config, struct ast_category *cat);
* matching the match parameter.
*
* \retval 0 if succeeded
- * \retval -1 if NULL parameters or match category was not found
+ * \retval -1 if the specified match category wasn't found
*/
int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match);
-int ast_category_delete(struct ast_config *cfg, const char *category);
/*!
- * \brief Removes and destroys all variables within a category
- * \retval 0 if the category was found and emptied
- * \retval -1 if the category was not found
+ * \brief Delete a category
+ *
+ * \param config which config to use
+ * \param category category to delete
+ *
+ * \return the category after the deleted one which could be NULL.
+ */
+struct ast_category *ast_category_delete(struct ast_config *cfg, struct ast_category *category);
+
+/*!
+ * \brief Appends a category to a config
+ *
+ * \param config which config to use
+ * \param cat category to insert
+ */
+void ast_category_append(struct ast_config *config, struct ast_category *cat);
+
+/*!
+ * \brief Applies base (template) to category.
+ *
+ * \param existing existing category
+ * \param base base category
+ *
+ * \details
+ * This function is used to apply a base (template) to an existing category
+ */
+void ast_category_inherit(struct ast_category *existing, const struct ast_category *base);
+
+/*!
+ * \brief Removes and destroys all variables in a category
+ *
+ * \param category category to empty
+ *
+ * \retval 0 if succeeded
+ * \retval -1 if categopry is NULL
*/
-int ast_category_empty(struct ast_config *cfg, const char *category);
+int ast_category_empty(struct ast_category *category);
+
void ast_category_destroy(struct ast_category *cat);
struct ast_variable *ast_category_detach_variables(struct ast_category *cat);
void ast_category_rename(struct ast_category *cat, const char *name);
diff --git a/main/config.c b/main/config.c
index 55c40a955..7009e7d08 100644
--- a/main/config.c
+++ b/main/config.c
@@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/stat.h>
#include <math.h> /* HUGE_VAL */
+#include <regex.h>
#define AST_INCLUDE_GLOB 1
@@ -71,6 +72,7 @@ static char *extconfig_conf = "extconfig.conf";
static struct ao2_container *cfg_hooks;
static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
+static int does_category_match(struct ast_category *cat, const char *category_name, const char *match);
/*! \brief Structure to keep comments for rewriting configuration files */
struct ast_comment {
@@ -232,6 +234,8 @@ struct ast_category {
struct ast_variable *root;
/*! Last category variable in the list. */
struct ast_variable *last;
+ /*! Previous node in the list. */
+ struct ast_category *prev;
/*! Next node in the list. */
struct ast_category *next;
};
@@ -599,16 +603,12 @@ void ast_variables_destroy(struct ast_variable *v)
struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
{
- struct ast_category *cat = NULL;
-
- if (!category) {
- return NULL;
- }
+ struct ast_category *cat;
if (config->last_browse && (config->last_browse->name == category)) {
cat = config->last_browse;
} else {
- cat = ast_category_get(config, category);
+ cat = ast_category_get(config, category, NULL);
}
return (cat) ? cat->root : NULL;
@@ -677,29 +677,37 @@ const char *ast_config_option(struct ast_config *cfg, const char *cat, const cha
return tmp;
}
+const char *ast_variable_retrieve(struct ast_config *config,
+ const char *category, const char *variable)
+{
+ return ast_variable_retrieve_filtered(config, category, variable, NULL);
+}
-const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+const char *ast_variable_retrieve_filtered(struct ast_config *config,
+ const char *category, const char *variable, const char *filter)
{
- struct ast_variable *v;
+ struct ast_category *cat = NULL;
+ const char *value;
- if (category) {
- for (v = ast_variable_browse(config, category); v; v = v->next) {
- if (!strcasecmp(variable, v->name)) {
- return v->value;
- }
+ while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
+ value = ast_variable_find(cat, variable);
+ if (value) {
+ return value;
}
- } else {
- struct ast_category *cat;
+ }
- for (cat = config->root; cat; cat = cat->next) {
- for (v = cat->root; v; v = v->next) {
- if (!strcasecmp(variable, v->name)) {
- return v->value;
- }
- }
+ return NULL;
+}
+
+const char *ast_variable_find(const struct ast_category *category, const char *variable)
+{
+ struct ast_variable *v;
+
+ for (v = category->root; v; v = v->next) {
+ if (!strcasecmp(variable, v->name)) {
+ return v->value;
}
}
-
return NULL;
}
@@ -726,7 +734,99 @@ static void move_variables(struct ast_category *old, struct ast_category *new)
ast_variable_append(new, var);
}
-struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+/*! \brief Returns true if ALL of the regex expressions and category name match.
+ * Both can be NULL (I.E. no predicate) which results in a true return;
+ */
+static int does_category_match(struct ast_category *cat, const char *category_name, const char *match)
+{
+ char *dupmatch;
+ char *nvp = NULL;
+ int match_found = 0, match_expressions = 0;
+ int template_ok = 0;
+
+ /* Only match on category name if it's not a NULL or empty string */
+ if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
+ return 0;
+ }
+
+ /* If match is NULL or empty, automatically match if not a template */
+ if (ast_strlen_zero(match)) {
+ return !cat->ignored;
+ }
+
+ dupmatch = ast_strdupa(match);
+
+ while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
+ struct ast_variable *v;
+ char *match_name;
+ char *match_value = NULL;
+ char *regerr;
+ int rc;
+ regex_t r_name, r_value;
+
+ match_expressions++;
+
+ match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
+ match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
+
+ /* an empty match value is OK. A NULL match value (no =) is NOT. */
+ if (match_value == NULL) {
+ break;
+ }
+
+ if (!strcmp("TEMPLATES", match_name)) {
+ if (!strcasecmp("include", match_value)) {
+ if (cat->ignored) {
+ template_ok = 1;
+ }
+ match_found++;
+ } else if (!strcasecmp("restrict", match_value)) {
+ if (cat->ignored) {
+ match_found++;
+ template_ok = 1;
+ } else {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
+ regerr = ast_alloca(128);
+ regerror(rc, &r_name, regerr, 128);
+ ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
+ match_name, regerr);
+ regfree(&r_name);
+ return 0;
+ }
+ if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
+ regerr = ast_alloca(128);
+ regerror(rc, &r_value, regerr, 128);
+ ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
+ match_value, regerr);
+ regfree(&r_name);
+ regfree(&r_value);
+ return 0;
+ }
+
+ for (v = cat->root; v; v = v->next) {
+ if (!regexec(&r_name, v->name, 0, NULL, 0)
+ && !regexec(&r_value, v->value, 0, NULL, 0)) {
+ match_found++;
+ break;
+ }
+ }
+ regfree(&r_name);
+ regfree(&r_value);
+ }
+ if (match_found == match_expressions && (!cat->ignored || template_ok)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
{
struct ast_category *category;
@@ -741,44 +841,85 @@ struct ast_category *ast_category_new(const char *name, const char *in_file, int
}
ast_copy_string(category->name, name, sizeof(category->name));
category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
+ category->ignored = template;
return category;
}
-static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
{
- struct ast_category *cat;
+ return new_category(name, in_file, lineno, 0);
+}
- /* try exact match first, then case-insensitive match */
- for (cat = config->root; cat; cat = cat->next) {
- if (cat->name == category_name && (ignored || !cat->ignored))
- return cat;
- }
+struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
+{
+ return new_category(name, in_file, lineno, 1);
+}
+
+struct ast_category *ast_category_get(const struct ast_config *config,
+ const char *category_name, const char *filter)
+{
+ struct ast_category *cat;
for (cat = config->root; cat; cat = cat->next) {
- if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ if (does_category_match(cat, category_name, filter)) {
return cat;
+ }
}
return NULL;
}
-struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+const char *ast_category_get_name(const struct ast_category *category)
{
- return category_get(config, category_name, 0);
+ return category->name;
}
-int ast_category_exist(const struct ast_config *config, const char *category_name)
+int ast_category_is_template(const struct ast_category *category)
{
- return !!ast_category_get(config, category_name);
+ return category->ignored;
+}
+
+struct ast_str *ast_category_get_templates(const struct ast_category *category)
+{
+ struct ast_category_template_instance *template;
+ struct ast_str *str;
+ int first = 1;
+
+ if (AST_LIST_EMPTY(&category->template_instances)) {
+ return NULL;
+ }
+
+ str = ast_str_create(128);
+ if (!str) {
+ return NULL;
+ }
+
+ AST_LIST_TRAVERSE(&category->template_instances, template, next) {
+ ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
+ first = 0;
+ }
+
+ return str;
+}
+
+int ast_category_exist(const struct ast_config *config, const char *category_name,
+ const char *filter)
+{
+ return !!ast_category_get(config, category_name, filter);
}
void ast_category_append(struct ast_config *config, struct ast_category *category)
{
- if (config->last)
+ if (config->last) {
config->last->next = category;
- else
+ category->prev = config->last;
+ } else {
config->root = category;
+ category->prev = NULL;
+ }
+ category->next = NULL;
category->include_level = config->include_level;
+
config->last = category;
config->current = category;
}
@@ -787,19 +928,26 @@ int ast_category_insert(struct ast_config *config, struct ast_category *cat, con
{
struct ast_category *cur_category;
- if (!config || !cat || !match) {
+ if (!config || !config->root || !cat || !match) {
return -1;
}
+
if (!strcasecmp(config->root->name, match)) {
cat->next = config->root;
+ cat->prev = NULL;
+ config->root->prev = cat;
config->root = cat;
return 0;
}
- for (cur_category = config->root; cur_category && cur_category->next;
- cur_category = cur_category->next) {
- if (!strcasecmp(cur_category->next->name, match)) {
- cat->next = cur_category->next;
- cur_category->next = cat;
+
+ for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
+ if (!strcasecmp(cur_category->name, match)) {
+ cat->prev = cur_category->prev;
+ cat->prev->next = cat;
+
+ cat->next = cur_category;
+ cur_category->prev = cat;
+
return 0;
}
}
@@ -841,9 +989,10 @@ static void ast_includes_destroy(struct ast_config_include *incls)
}
}
-static struct ast_category *next_available_category(struct ast_category *cat)
+static struct ast_category *next_available_category(struct ast_category *cat,
+ const char *name, const char *filter)
{
- for (; cat && cat->ignored; cat = cat->next);
+ for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
return cat;
}
@@ -856,7 +1005,7 @@ struct ast_variable *ast_category_first(struct ast_category *cat)
struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
{
- struct ast_category *category = ast_category_get(config, cat);
+ struct ast_category *category = ast_category_get(config, cat, NULL);
if (category)
return category->root;
@@ -1021,12 +1170,29 @@ char *ast_category_browse(struct ast_config *config, const char *prev)
}
if (cat)
- cat = next_available_category(cat);
+ cat = next_available_category(cat, NULL, NULL);
config->last_browse = cat;
return (cat) ? cat->name : NULL;
}
+struct ast_category *ast_category_browse_filtered(struct ast_config *config,
+ const char *category_name, struct ast_category *prev, const char *filter)
+{
+ struct ast_category *cat;
+
+ if (!prev) {
+ prev = config->root;
+ } else {
+ prev = prev->next;
+ }
+
+ cat = next_available_category(prev, category_name, filter);
+ config->last_browse = cat;
+
+ return cat;
+}
+
struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
{
struct ast_variable *v;
@@ -1043,7 +1209,7 @@ void ast_category_rename(struct ast_category *cat, const char *name)
ast_copy_string(cat->name, name, sizeof(cat->name));
}
-static void inherit_category(struct ast_category *new, const struct ast_category *base)
+void ast_category_inherit(struct ast_category *new, const struct ast_category *base)
{
struct ast_variable *var;
struct ast_category_template_instance *x;
@@ -1147,65 +1313,45 @@ int ast_variable_update(struct ast_category *category, const char *variable,
return -1;
}
-int ast_category_delete(struct ast_config *cfg, const char *category)
+struct ast_category *ast_category_delete(struct ast_config *config,
+ struct ast_category *category)
{
- struct ast_category *prev=NULL, *cat;
+ struct ast_category *prev;
- cat = cfg->root;
- while (cat) {
- if (cat->name == category) {
- if (prev) {
- prev->next = cat->next;
- if (cat == cfg->last)
- cfg->last = prev;
- } else {
- cfg->root = cat->next;
- if (cat == cfg->last)
- cfg->last = NULL;
- }
- ast_category_destroy(cat);
- return 0;
- }
- prev = cat;
- cat = cat->next;
+ if (!config || !category) {
+ return NULL;
}
- prev = NULL;
- cat = cfg->root;
- while (cat) {
- if (!strcasecmp(cat->name, category)) {
- if (prev) {
- prev->next = cat->next;
- if (cat == cfg->last)
- cfg->last = prev;
- } else {
- cfg->root = cat->next;
- if (cat == cfg->last)
- cfg->last = NULL;
- }
- ast_category_destroy(cat);
- return 0;
- }
- prev = cat;
- cat = cat->next;
+ if (category->prev) {
+ category->prev->next = category->next;
+ } else {
+ config->root = category->next;
}
- return -1;
+
+ if (category->next) {
+ category->next->prev = category->prev;
+ } else {
+ config->last = category->prev;
+ }
+
+ prev = category->prev;
+
+ ast_category_destroy(category);
+
+ return prev;
}
-int ast_category_empty(struct ast_config *cfg, const char *category)
+int ast_category_empty(struct ast_category *category)
{
- struct ast_category *cat;
-
- for (cat = cfg->root; cat; cat = cat->next) {
- if (strcasecmp(cat->name, category))
- continue;
- ast_variables_destroy(cat->root);
- cat->root = NULL;
- cat->last = NULL;
- return 0;
+ if (!category) {
+ return -1;
}
- return -1;
+ ast_variables_destroy(category->root);
+ category->root = NULL;
+ category->last = NULL;
+
+ return 0;
}
void ast_config_destroy(struct ast_config *cfg)
@@ -1489,7 +1635,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
if (!strcasecmp(cur, "!")) {
(*cat)->ignored = 1;
} else if (!strcasecmp(cur, "+")) {
- *cat = category_get(cfg, catname, 1);
+ *cat = ast_category_get(cfg, catname, NULL);
if (!(*cat)) {
if (newcat)
ast_category_destroy(newcat);
@@ -1504,12 +1650,12 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
} else {
struct ast_category *base;
- base = category_get(cfg, cur, 1);
+ base = ast_category_get(cfg, cur, "TEMPLATES=restrict");
if (!base) {
ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
return -1;
}
- inherit_category(*cat, base);
+ ast_category_inherit(*cat, base);
}
}
}
diff --git a/main/manager.c b/main/manager.c
index e535bca4d..3e0f0f8e1 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -354,10 +354,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Category">
<para>Category in configuration file.</para>
</parameter>
+ <parameter name="Filter">
+ <para>A comma separated list of
+ <replaceable>name_regex</replaceable>=<replaceable>value_regex</replaceable>
+ expressions which will cause only categories whose variables match all expressions
+ to be considered. The special variable name <literal>TEMPLATES</literal>
+ can be used to control whether templates are included. Passing
+ <literal>include</literal> as the value will include templates
+ along with normal categories. Passing
+ <literal>restrict</literal> as the value will restrict the operation to
+ ONLY templates. Not specifying a <literal>TEMPLATES</literal> expression
+ results in the default behavior which is to not include templates.</para>
+ </parameter>
</syntax>
<description>
<para>This action will dump the contents of a configuration
- file by category and contents or optionally by specified category only.</para>
+ file by category and contents or optionally by specified category only.
+ In the case where a category name is non-unique, a filter may be specified
+ to match only categories with matching variable values.</para>
</description>
</manager>
<manager name="GetConfigJSON" language="en_US">
@@ -369,11 +383,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Filename" required="true">
<para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
</parameter>
+ <parameter name="Category">
+ <para>Category in configuration file.</para>
+ </parameter>
+ <parameter name="Filter">
+ <xi:include xpointer="xpointer(/docs/manager[@name='GetConfig']/syntax/parameter[@name='Filter']/para[1])" />
+ </parameter>
</syntax>
<description>
<para>This action will dump the contents of a configuration file by category
- and contents in JSON format. This only makes sense to be used using rawman over
- the HTTP interface.</para>
+ and contents in JSON format or optionally by specified category only.
+ This only makes sense to be used using rawman over the HTTP interface.
+ In the case where a category name is non-unique, a filter may be specified
+ to match only categories with matching variable values.</para>
</description>
</manager>
<manager name="UpdateConfig" language="en_US">
@@ -391,9 +413,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Reload">
<para>Whether or not a reload should take place (or name of specific module).</para>
</parameter>
- <parameter name="Action-XXXXXX">
+ <parameter name="Action-000000">
<para>Action to take.</para>
- <para>X's represent 6 digit number beginning with 000000.</para>
+ <para>0's represent 6 digit number beginning with 000000.</para>
<enumlist>
<enum name="NewCat" />
<enum name="RenameCat" />
@@ -405,25 +427,58 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="Insert" />
</enumlist>
</parameter>
- <parameter name="Cat-XXXXXX">
+ <parameter name="Cat-000000">
<para>Category to operate on.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Var-XXXXXX">
+ <parameter name="Var-000000">
<para>Variable to work on.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Value-XXXXXX">
+ <parameter name="Value-000000">
<para>Value to work on.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Match-XXXXXX">
+ <parameter name="Match-000000">
<para>Extra match required to match line.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Line-XXXXXX">
+ <parameter name="Line-000000">
<para>Line in category to operate on (used with delete and insert actions).</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+ </parameter>
+ <parameter name="Options-000000">
+ <para>A comma separated list of action-specific options.</para>
+ <enumlist>
+ <enum name="NewCat"><para>One or more of the following... </para>
+ <enumlist>
+ <enum name="allowdups"><para>Allow duplicate category names.</para></enum>
+ <enum name="template"><para>This category is a template.</para></enum>
+ <enum name="inherit=&quot;template[,...]&quot;"><para>Templates from which to inherit.</para></enum>
+ </enumlist>
+ </enum>
+ </enumlist>
+ <para> </para>
+ <para>The following actions share the same options...</para>
+ <enumlist>
+ <enum name="RenameCat"/>
+ <enum name="DelCat"/>
+ <enum name="EmptyCat"/>
+ <enum name="Update"/>
+ <enum name="Delete"/>
+ <enum name="Append"/>
+ <enum name="Insert"><para> </para>
+ <enumlist>
+ <enum name="catfilter=&quot;&lt;expression&gt;[,...]&quot;"><para> </para>
+ <xi:include xpointer="xpointer(/docs/manager[@name='GetConfig']/syntax/parameter[@name='Filter']/para[1])" />
+ <para><literal>catfilter</literal> is most useful when a file
+ contains multiple categories with the same name and you wish to
+ operate on specific ones instead of all of them.</para>
+ </enum>
+ </enumlist>
+ </enum>
+ </enumlist>
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
</syntax>
<description>
@@ -1175,7 +1230,8 @@ enum error_type {
FAILURE_EMPTYCAT,
FAILURE_UPDATE,
FAILURE_DELETE,
- FAILURE_APPEND
+ FAILURE_APPEND,
+ FAILURE_TEMPLATE
};
enum add_filter_result {
@@ -3157,9 +3213,11 @@ static int action_getconfig(struct mansession *s, const struct message *m)
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
const char *category = astman_get_header(m, "Category");
+ const char *filter = astman_get_header(m, "Filter");
+ const char *category_name;
int catcount = 0;
int lineno = 0;
- char *cur_category = NULL;
+ struct ast_category *cur_category = NULL;
struct ast_variable *v;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
@@ -3167,6 +3225,7 @@ static int action_getconfig(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Filename not specified");
return 0;
}
+
cfg = ast_config_load2(fn, "manager", config_flags);
if (cfg == CONFIG_STATUS_FILEMISSING) {
astman_send_error(s, m, "Config file not found");
@@ -3177,19 +3236,34 @@ static int action_getconfig(struct mansession *s, const struct message *m)
}
astman_start_ack(s, m);
- while ((cur_category = ast_category_browse(cfg, cur_category))) {
- if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
- lineno = 0;
- astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
- for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
- astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
- }
- catcount++;
+ while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
+ struct ast_str *templates;
+
+ category_name = ast_category_get_name(cur_category);
+ lineno = 0;
+ astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
+
+ if (ast_category_is_template(cur_category)) {
+ astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
+ }
+
+ if ((templates = ast_category_get_templates(cur_category))
+ && ast_str_strlen(templates) > 0) {
+ astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
+ ast_free(templates);
}
+
+ for (v = ast_category_first(cur_category); v; v = v->next) {
+ astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
+ }
+
+ catcount++;
}
+
if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "No categories found\r\n");
}
+
ast_config_destroy(cfg);
astman_append(s, "\r\n");
@@ -3200,7 +3274,8 @@ static int action_listcategories(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
- char *category = NULL;
+ const char *match = astman_get_header(m, "Match");
+ struct ast_category *category = NULL;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
int catcount = 0;
@@ -3208,6 +3283,7 @@ static int action_listcategories(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Filename not specified");
return 0;
}
+
if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
astman_send_error(s, m, "Config file not found");
return 0;
@@ -3215,23 +3291,23 @@ static int action_listcategories(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Config file has invalid format");
return 0;
}
+
astman_start_ack(s, m);
- while ((category = ast_category_browse(cfg, category))) {
- astman_append(s, "Category-%06d: %s\r\n", catcount, category);
+ while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
+ astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
catcount++;
}
+
if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "Error: no categories found\r\n");
}
+
ast_config_destroy(cfg);
astman_append(s, "\r\n");
return 0;
}
-
-
-
/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
static void json_escape(char *out, const char *in)
{
@@ -3266,7 +3342,10 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
- char *category = NULL;
+ const char *filter = astman_get_header(m, "Filter");
+ const char *category = astman_get_header(m, "Category");
+ struct ast_category *cur_category = NULL;
+ const char *category_name;
struct ast_variable *v;
int comma1 = 0;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
@@ -3286,14 +3365,30 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
astman_start_ack(s, m);
astman_append(s, "JSON: {");
- while ((category = ast_category_browse(cfg, category))) {
+ while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
int comma2 = 0;
+ struct ast_str *templates;
+ category_name = ast_category_get_name(cur_category);
astman_append(s, "%s\"", comma1 ? "," : "");
- astman_append_json(s, category);
+ astman_append_json(s, category_name);
astman_append(s, "\":[");
comma1 = 1;
- for (v = ast_variable_browse(cfg, category); v; v = v->next) {
+
+ if (ast_category_is_template(cur_category)) {
+ astman_append(s, "istemplate:1");
+ comma2 = 1;
+ }
+
+ if ((templates = ast_category_get_templates(cur_category))
+ && ast_str_strlen(templates) > 0) {
+ astman_append(s, "%s", comma2 ? "," : "");
+ astman_append(s, "templates:\"%s\"", ast_str_buffer(templates));
+ ast_free(templates);
+ comma2 = 1;
+ }
+
+ for (v = ast_category_first(cur_category); v; v = v->next) {
astman_append(s, "%s\"", comma2 ? "," : "");
astman_append_json(s, v->name);
astman_append(s, "\":\"");
@@ -3301,6 +3396,7 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
astman_append(s, "\"");
comma2 = 1;
}
+
astman_append(s, "]");
}
astman_append(s, "}\r\n\r\n");
@@ -3315,19 +3411,28 @@ static enum error_type handle_updates(struct mansession *s, const struct message
{
int x;
char hdr[40];
- const char *action, *cat, *var, *value, *match, *line;
- struct ast_category *category;
+ const char *action, *cat, *var, *value, *match, *line, *options;
struct ast_variable *v;
struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
enum error_type result = 0;
for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
unsigned int object = 0;
+ char *dupoptions;
+ int allowdups = 0;
+ int istemplate = 0;
+ int ignoreerror = 0;
+ char *inherit = NULL;
+ char *catfilter = NULL;
+ char *token;
+ int foundvar = 0;
+ int foundcat = 0;
+ struct ast_category *category = NULL;
snprintf(hdr, sizeof(hdr), "Action-%06d", x);
action = astman_get_header(m, hdr);
if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
- break; /* this could cause problems if actions come in misnumbered */
+ break; /* this could cause problems if actions come in misnumbered */
snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
cat = astman_get_header(m, hdr);
@@ -3353,22 +3458,90 @@ static enum error_type handle_updates(struct mansession *s, const struct message
snprintf(hdr, sizeof(hdr), "Line-%06d", x);
line = astman_get_header(m, hdr);
+ snprintf(hdr, sizeof(hdr), "Options-%06d", x);
+ options = astman_get_header(m, hdr);
+ if (!ast_strlen_zero(options)) {
+ dupoptions = ast_strdupa(options);
+ while ((token = ast_strsep(&dupoptions, ',', AST_STRSEP_STRIP))) {
+ if (!strcasecmp("allowdups", token)) {
+ allowdups = 1;
+ continue;
+ }
+ if (!strcasecmp("template", token)) {
+ istemplate = 1;
+ continue;
+ }
+ if (!strcasecmp("ignoreerror", token)) {
+ ignoreerror = 1;
+ continue;
+ }
+ if (ast_begins_with(token, "inherit")) {
+ char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ if (c) {
+ inherit = ast_strdupa(c);
+ }
+ continue;
+ }
+ if (ast_begins_with(token, "catfilter")) {
+ char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ if (c) {
+ catfilter = ast_strdupa(c);
+ }
+ continue;
+ }
+ }
+ }
+
if (!strcasecmp(action, "newcat")) {
- if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
- result = FAILURE_NEWCAT; /* already exist */
- break;
+ struct ast_category *template;
+ char *tmpl_name = NULL;
+
+ if (!allowdups) {
+ if (ast_category_get(cfg, cat, "TEMPLATES=include")) {
+ if (ignoreerror) {
+ continue;
+ } else {
+ result = FAILURE_NEWCAT; /* already exist */
+ break;
+ }
+ }
+ }
+
+ if (istemplate) {
+ category = ast_category_new_template(cat, dfn, -1);
+ } else {
+ category = ast_category_new(cat, dfn, -1);
}
- if (!(category = ast_category_new(cat, dfn, -1))) {
+
+ if (!category) {
result = FAILURE_ALLOCATION;
break;
}
- if (ast_strlen_zero(match)) {
- ast_category_append(cfg, category);
- } else {
- if (ast_category_insert(cfg, category, match)) {
- result = FAILURE_NEWCAT;
- ast_category_destroy(category);
- break;
+
+ if (inherit) {
+ while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
+ if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
+ ast_category_inherit(category, template);
+ } else {
+ ast_category_destroy(category);
+ category = NULL;
+ result = FAILURE_TEMPLATE; /* template not found */
+ break;
+ }
+ }
+ }
+
+ if (category != NULL) {
+ if (ast_strlen_zero(match)) {
+ ast_category_append(cfg, category);
+ } else {
+ if (ast_category_insert(cfg, category, match)) {
+ ast_category_destroy(category);
+ result = FAILURE_NEWCAT;
+ break;
+ }
}
}
} else if (!strcasecmp(action, "renamecat")) {
@@ -3376,19 +3549,37 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
+
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ ast_category_rename(category, value);
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
- ast_category_rename(category, value);
} else if (!strcasecmp(action, "delcat")) {
- if (ast_category_delete(cfg, cat)) {
- result = FAILURE_DELCAT;
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ category = ast_category_delete(cfg, category);
+ foundcat = 1;
+ }
+
+ if (!foundcat && !ignoreerror) {
+ result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "emptycat")) {
- if (ast_category_empty(cfg, cat)) {
- result = FAILURE_EMPTYCAT;
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ ast_category_empty(category);
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
+ result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "update")) {
@@ -3396,11 +3587,22 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg,cat))) {
+
+ foundcat = 0;
+ foundvar = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!ast_variable_update(category, var, value, match, object)) {
+ foundvar = 1;
+ }
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
- if (ast_variable_update(category, var, value, match, object)) {
+
+ if (!foundvar) {
result = FAILURE_UPDATE;
break;
}
@@ -3409,12 +3611,23 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
+
+ foundcat = 0;
+ foundvar = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!ast_variable_delete(category, var, match, line)) {
+ foundvar = 1;
+ }
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
- if (ast_variable_delete(category, var, match, line)) {
- result = FAILURE_DELETE;
+
+ if (!foundvar && !ignoreerror) {
+ result = FAILURE_UPDATE;
break;
}
} else if (!strcasecmp(action, "append")) {
@@ -3422,32 +3635,44 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
- result = UNKNOWN_CATEGORY;
- break;
+
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!(v = ast_variable_new(var, value, dfn))) {
+ result = FAILURE_ALLOCATION;
+ break;
+ }
+ if (object || (match && !strcasecmp(match, "object"))) {
+ v->object = 1;
+ }
+ ast_variable_append(category, v);
+ foundcat = 1;
}
- if (!(v = ast_variable_new(var, value, dfn))) {
- result = FAILURE_ALLOCATION;
+
+ if (!foundcat) {
+ result = UNKNOWN_CATEGORY;
break;
}
- if (object || (match && !strcasecmp(match, "object"))) {
- v->object = 1;
- }
- ast_variable_append(category, v);
} else if (!strcasecmp(action, "insert")) {
if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
- result = UNKNOWN_CATEGORY;
- break;
+
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!(v = ast_variable_new(var, value, dfn))) {
+ result = FAILURE_ALLOCATION;
+ break;
+ }
+ ast_variable_insert(category, v, line);
+ foundcat = 1;
}
- if (!(v = ast_variable_new(var, value, dfn))) {
- result = FAILURE_ALLOCATION;
+
+ if (!foundcat) {
+ result = UNKNOWN_CATEGORY;
break;
}
- ast_variable_insert(category, v, line);
}
else {
ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
@@ -3533,6 +3758,9 @@ static int action_updateconfig(struct mansession *s, const struct message *m)
case FAILURE_APPEND:
astman_send_error(s, m, "Append did not complete successfully");
break;
+ case FAILURE_TEMPLATE:
+ astman_send_error(s, m, "Template category not found");
+ break;
}
}
return 0;
diff --git a/pbx/pbx_realtime.c b/pbx/pbx_realtime.c
index de62851e0..6c8e671ed 100644
--- a/pbx/pbx_realtime.c
+++ b/pbx/pbx_realtime.c
@@ -206,7 +206,7 @@ static struct ast_variable *realtime_switch_common(const char *table, const char
match = ast_extension_match(cat, exten);
}
if (match) {
- var = ast_category_detach_variables(ast_category_get(cfg, cat));
+ var = ast_category_detach_variables(ast_category_get(cfg, cat, NULL));
break;
}
cat = ast_category_browse(cfg, cat);
diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c
index d43a4958a..176c7fe68 100644
--- a/res/res_sorcery_config.c
+++ b/res/res_sorcery_config.c
@@ -234,6 +234,7 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
struct sorcery_config *config = data;
struct ast_flags flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_config *cfg = ast_config_load2(config->filename, config->uuid, flags);
+ struct ast_category *category = NULL;
RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
const char *id = NULL;
@@ -255,16 +256,17 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
return;
}
- while ((id = ast_category_browse(cfg, id))) {
+ while ((category = ast_category_browse_filtered(cfg, NULL, category, NULL))) {
RAII_VAR(void *, obj, NULL, ao2_cleanup);
+ id = ast_category_get_name(category);
/* If given criteria has not been met skip the category, it is not applicable */
- if (!sorcery_is_criteria_met(ast_variable_browse(cfg, id), config->criteria)) {
+ if (!sorcery_is_criteria_met(ast_category_first(category), config->criteria)) {
continue;
}
if (!(obj = ast_sorcery_alloc(sorcery, type, id)) ||
- ast_sorcery_objectset_apply(sorcery, obj, ast_variable_browse(cfg, id))) {
+ ast_sorcery_objectset_apply(sorcery, obj, ast_category_first(category))) {
if (config->file_integrity) {
ast_log(LOG_ERROR, "Config file '%s' could not be loaded due to error with object '%s' of type '%s'\n",
diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c
index 47cd73676..52b097f57 100644
--- a/res/res_sorcery_realtime.c
+++ b/res/res_sorcery_realtime.c
@@ -177,7 +177,7 @@ static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery
const char *family = data;
RAII_VAR(struct ast_config *, rows, NULL, ast_config_destroy);
RAII_VAR(struct ast_variable *, all, NULL, ast_variables_destroy);
- char *row = NULL;
+ struct ast_category *row = NULL;
if (!fields) {
char field[strlen(UUID_FIELD) + 6], value[2];
@@ -197,9 +197,8 @@ static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery
return;
}
- while ((row = ast_category_browse(rows, row))) {
- struct ast_category *cat = ast_category_get(rows, row);
- struct ast_variable *objectset = ast_category_detach_variables(cat);
+ while ((row = ast_category_browse_filtered(rows, NULL, row, NULL))) {
+ struct ast_variable *objectset = ast_category_detach_variables(row);
RAII_VAR(struct ast_variable *, id, NULL, ast_variables_destroy);
RAII_VAR(void *, object, NULL, ao2_cleanup);
diff --git a/tests/test_config.c b/tests/test_config.c
index a9a281ddc..b1f55c782 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -227,6 +227,533 @@ out:
return res;
}
+AST_TEST_DEFINE(config_basic_ops)
+{
+ enum ast_test_result_state res = AST_TEST_FAIL;
+ struct ast_config *cfg = NULL;
+ struct ast_category *cat = NULL;
+ struct ast_variable *var;
+ char temp[32];
+ const char *cat_name;
+ const char *var_value;
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "config_basic_ops";
+ info->category = "/main/config/";
+ info->summary = "Test basic config ops";
+ info->description = "Test basic config ops";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cfg = ast_config_new();
+ if (!cfg) {
+ return res;
+ }
+
+ /* load the config */
+ for(i = 0; i < 5; i++) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ ast_category_append(cfg, ast_category_new(temp, "dummy", -1));
+ }
+
+ /* test0 test1 test2 test3 test4 */
+ /* check the config has 5 elements */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* search for test2 */
+ cat = ast_category_get(cfg, "test2", NULL);
+ if (!cat || strcmp(ast_category_get_name(cat), "test2")) {
+ ast_test_status_update(test, "Get failed %s != %s\n", ast_category_get_name(cat), "test2");
+ goto out;
+ }
+
+ /* delete test2 */
+ cat = ast_category_delete(cfg, cat);
+
+ /* Now: test0 test1 test3 test4 */
+ /* make sure the curr category is test1 */
+ if (!cat || strcmp(ast_category_get_name(cat), "test1")) {
+ ast_test_status_update(test, "Delete failed %s != %s\n", ast_category_get_name(cat), "test1");
+ goto out;
+ }
+
+ /* Now: test0 test1 test3 test4 */
+ /* make sure the test2 is not found */
+ cat = ast_category_get(cfg, "test2", NULL);
+ if (cat) {
+ ast_test_status_update(test, "Should not have found test2\n");
+ goto out;
+ }
+
+ /* Now: test0 test1 test3 test4 */
+ /* make sure the sequence is correctly missing test2 */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ i++;
+ if (i == 2) {
+ i++;
+ }
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* insert test2 back in before test3 */
+ ast_category_insert(cfg, ast_category_new("test2", "dummy", -1), "test3");
+
+ /* Now: test0 test1 test2 test3 test4 */
+ /* make sure the sequence is correct again */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* Now: test0 test1 test2 test3 test4 */
+ /* make sure non filtered browse still works */
+ i = 0;
+ cat_name = NULL;
+ while ((cat_name = ast_category_browse(cfg, cat_name))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(cat_name, temp)) {
+ ast_test_status_update(test, "%s != %s\n", cat_name, temp);
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* append another test2 */
+ ast_category_append(cfg, ast_category_new("test2", "dummy", -1));
+ /* Now: test0 test1 test2 test3 test4 test2*/
+ /* make sure only test2's are returned */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, "test2", cat, NULL))) {
+ if (strcmp(ast_category_get_name(cat), "test2")) {
+ ast_test_status_update(test, "Should have returned test2 instead of %s\n", ast_category_get_name(cat));
+ goto out;
+ }
+ i++;
+ }
+ /* make sure 2 test2's were found */
+ if (i != 2) {
+ ast_test_status_update(test, "Should have found 2 test2's %d\n", i);
+ goto out;
+ }
+
+ /* Test in-flight deletion using ast_category_browse_filtered */
+ /* Now: test0 test1 test2 test3 test4 test2 */
+ /* Delete the middle test2 and continue */
+ cat = NULL;
+ for(i = 0; i < 5; i++) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
+ cat_name = ast_category_get_name(cat);
+ if (strcmp(cat_name, temp)) {
+ ast_test_status_update(test, "Should have returned %s instead of %s: %d\n", temp, cat_name, i);
+ goto out;
+ }
+ if (i == 2) {
+ cat = ast_category_delete(cfg, cat);
+ }
+ }
+
+ /* Now: test0 test1 test3 test4 test2 */
+ /* Test in-flight deletion using ast_category_browse */
+ /* Delete test1 and continue */
+ cat_name = NULL;
+ for(i = 0; i < 5; i++) {
+ if (i == 2) { /* 2 was already deleted above */
+ continue;
+ }
+ snprintf(temp, sizeof(temp), "test%d", i);
+ cat_name = ast_category_browse(cfg, cat_name);
+ cat = ast_category_get(cfg, cat_name, NULL);
+ if (strcmp(cat_name, temp)) {
+ ast_test_status_update(test, "Should have returned %s instead of %s: %d\n", temp, cat_name, i);
+ goto out;
+ }
+ if (i == 1) {
+ ast_category_delete(cfg, cat);
+ }
+ }
+
+ /* Now: test0 test3 test4 test2 */
+ /* delete the head item */
+ cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL);
+ cat_name = ast_category_get_name(cat);
+ if (strcmp(cat_name, "test0")) {
+ ast_test_status_update(test, "Should have returned test0 instead of %s\n", cat_name);
+ goto out;
+ }
+ ast_category_delete(cfg, cat);
+ /* Now: test3 test4 test2 */
+
+ /* make sure head got updated to the new first element */
+ cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL);
+ cat_name = ast_category_get_name(cat);
+ if (strcmp(cat_name, "test3")) {
+ ast_test_status_update(test, "Should have returned test3 instead of %s\n", cat_name);
+ goto out;
+ }
+
+ /* delete the tail item */
+ cat = ast_category_get(cfg, "test2", NULL);
+ cat_name = ast_category_get_name(cat);
+ if (strcmp(cat_name, "test2")) {
+ ast_test_status_update(test, "Should have returned test2 instead of %s\n", cat_name);
+ goto out;
+ }
+ ast_category_delete(cfg, cat);
+ /* Now: test3 test4 */
+
+ /* There should now only be 2 elements in the list */
+ cat = NULL;
+ cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
+ cat_name = ast_category_get_name(cat);
+ if (strcmp(cat_name, "test3")) {
+ ast_test_status_update(test, "Should have returned test3 instead of %s\n", cat_name);
+ goto out;
+ }
+
+ cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
+ cat_name = ast_category_get_name(cat);
+ if (strcmp(cat_name, "test4")) {
+ ast_test_status_update(test, "Should have returned test4 instead of %s\n", cat_name);
+ goto out;
+ }
+
+ /* There should be nothing more */
+ cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
+ if (cat) {
+ ast_test_status_update(test, "Should not have returned anything\n");
+ goto out;
+ }
+
+ /* Test ast_variable retrieve.
+ * Get the second category.
+ */
+ cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL);
+ cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
+ cat_name = ast_category_get_name(cat);
+ var = ast_variable_new("aaa", "bbb", "dummy");
+ if (!var) {
+ ast_test_status_update(test, "Couldn't allocate variable.\n");
+ goto out;
+ }
+ ast_variable_append(cat, var);
+
+ /* Make sure we can retrieve with specific category name */
+ var_value = ast_variable_retrieve(cfg, cat_name, "aaa");
+ if (!var_value || strcmp(var_value, "bbb")) {
+ ast_test_status_update(test, "Variable not found or wrong value.\n");
+ goto out;
+ }
+
+ /* Make sure we can retrieve with NULL category name */
+ var_value = ast_variable_retrieve(cfg, NULL, "aaa");
+ if (!var_value || strcmp(var_value, "bbb")) {
+ ast_test_status_update(test, "Variable not found or wrong value.\n");
+ goto out;
+ }
+
+ res = AST_TEST_PASS;
+
+out:
+ ast_config_destroy(cfg);
+ return res;
+}
+
+AST_TEST_DEFINE(config_filtered_ops)
+{
+ enum ast_test_result_state res = AST_TEST_FAIL;
+ struct ast_config *cfg = NULL;
+ struct ast_category *cat = NULL;
+ char temp[32];
+ const char *value;
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "config_filtered_ops";
+ info->category = "/main/config/";
+ info->summary = "Test filtered config ops";
+ info->description = "Test filtered config ops";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cfg = ast_config_new();
+ if (!cfg) {
+ return res;
+ }
+
+ /* load the config */
+ for(i = 0; i < 5; i++) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ cat = ast_category_new(temp, "dummy", -1);
+ ast_variable_insert(cat, ast_variable_new("type", "a", "dummy"), "0");
+ ast_category_append(cfg, cat);
+ }
+
+ for(i = 0; i < 5; i++) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ cat = ast_category_new(temp, "dummy", -1);
+ ast_variable_insert(cat, ast_variable_new("type", "b", "dummy"), "0");
+ ast_category_append(cfg, cat);
+ }
+
+ /* check the config has 5 elements for each type*/
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "type=a"))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ value = ast_variable_find(cat, "type");
+ if (!value || strcmp(value, "a")) {
+ ast_test_status_update(test, "Type %s != %s\n", "a", value);
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "type=b"))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (!cat || strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ value = ast_variable_find(cat, "type");
+ if (!value || strcmp(value, "b")) {
+ ast_test_status_update(test, "Type %s != %s\n", "b", value);
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* Delete b3 and make sure it's gone and a3 is still there.
+ * Really this is a test of get since delete takes a specific category structure.
+ */
+ cat = ast_category_get(cfg, "test3", "type=b");
+ value = ast_variable_find(cat, "type");
+ if (strcmp(value, "b")) {
+ ast_test_status_update(test, "Type %s != %s\n", "b", value);
+ goto out;
+ }
+ ast_category_delete(cfg, cat);
+
+ cat = ast_category_get(cfg, "test3", "type=b");
+ if (cat) {
+ ast_test_status_update(test, "Category b was not deleted.\n");
+ goto out;
+ }
+
+ cat = ast_category_get(cfg, "test3", "type=a");
+ if (!cat) {
+ ast_test_status_update(test, "Category a was deleted.\n");
+ goto out;
+ }
+
+ value = ast_variable_find(cat, "type");
+ if (strcmp(value, "a")) {
+ ast_test_status_update(test, "Type %s != %s\n", value, "a");
+ goto out;
+ }
+
+ /* Basic regex stuff is handled by regcomp/regexec so not testing here.
+ * Still need to test multiple name/value pairs though.
+ */
+ ast_category_empty(cat);
+ ast_variable_insert(cat, ast_variable_new("type", "bx", "dummy"), "0");
+ ast_variable_insert(cat, ast_variable_new("e", "z", "dummy"), "0");
+
+ cat = ast_category_get(cfg, "test3", "type=.,e=z");
+ if (!cat) {
+ ast_test_status_update(test, "Category not found.\n");
+ goto out;
+ }
+
+ cat = ast_category_get(cfg, "test3", "type=.,e=zX");
+ if (cat) {
+ ast_test_status_update(test, "Category found.\n");
+ goto out;
+ }
+
+ cat = ast_category_get(cfg, "test3", "TEMPLATE=restrict,type=.,e=z");
+ if (cat) {
+ ast_test_status_update(test, "Category found.\n");
+ goto out;
+ }
+
+ res = AST_TEST_PASS;
+
+out:
+ ast_config_destroy(cfg);
+ return res;
+}
+
+AST_TEST_DEFINE(config_template_ops)
+{
+ enum ast_test_result_state res = AST_TEST_FAIL;
+ struct ast_config *cfg = NULL;
+ struct ast_category *cat = NULL;
+ char temp[32];
+ const char *value;
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "config_template_ops";
+ info->category = "/main/config/";
+ info->summary = "Test template config ops";
+ info->description = "Test template config ops";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cfg = ast_config_new();
+ if (!cfg) {
+ return res;
+ }
+
+ /* load the config with 5 templates and 5 regular */
+ for(i = 0; i < 5; i++) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ cat = ast_category_new_template(temp, "dummy", -1);
+ ast_variable_insert(cat, ast_variable_new("type", "a", "dummy"), "0");
+ ast_category_append(cfg, cat);
+ }
+
+ for(i = 0; i < 5; i++) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ cat = ast_category_new(temp, "dummy", -1);
+ ast_variable_insert(cat, ast_variable_new("type", "b", "dummy"), "0");
+ ast_category_append(cfg, cat);
+ }
+
+ /* check the config has 5 template elements of type a */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=restrict,type=a"))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ value = ast_variable_find(cat, "type");
+ if (!value || strcmp(value, "a")) {
+ ast_test_status_update(test, "Type %s != %s\n", value, "a");
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* Test again with 'include'. There should still only be 5 (type a) */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=include,type=a"))) {
+ snprintf(temp, sizeof(temp), "test%d", i);
+ if (strcmp(ast_category_get_name(cat), temp)) {
+ ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
+ goto out;
+ }
+ value = ast_variable_find(cat, "type");
+ if (!value || strcmp(value, "a")) {
+ ast_test_status_update(test, "Type %s != %s\n", value, "a");
+ goto out;
+ }
+ i++;
+ }
+ if (i != 5) {
+ ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
+ goto out;
+ }
+
+ /* Test again with 'include' but no type. There should now be 10 (type a and type b) */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=include"))) {
+ i++;
+ }
+ if (i != 10) {
+ ast_test_status_update(test, "There were %d matches instead of 10.\n", i);
+ goto out;
+ }
+
+ /* Test again with 'restrict' and type b. There should 0 */
+ i = 0;
+ cat = NULL;
+ while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=restrict,type=b"))) {
+ i++;
+ }
+ if (i != 0) {
+ ast_test_status_update(test, "There were %d matches instead of 0.\n", i);
+ goto out;
+ }
+
+ res = AST_TEST_PASS;
+
+out:
+ ast_config_destroy(cfg);
+ return res;
+}
+
/*!
* \brief Write the config file to disk
*
@@ -937,6 +1464,9 @@ AST_TEST_DEFINE(config_options_test)
static int unload_module(void)
{
+ AST_TEST_UNREGISTER(config_basic_ops);
+ AST_TEST_UNREGISTER(config_filtered_ops);
+ AST_TEST_UNREGISTER(config_template_ops);
AST_TEST_UNREGISTER(copy_config);
AST_TEST_UNREGISTER(config_hook);
AST_TEST_UNREGISTER(ast_parse_arg_test);
@@ -946,6 +1476,9 @@ static int unload_module(void)
static int load_module(void)
{
+ AST_TEST_REGISTER(config_basic_ops);
+ AST_TEST_REGISTER(config_filtered_ops);
+ AST_TEST_REGISTER(config_template_ops);
AST_TEST_REGISTER(copy_config);
AST_TEST_REGISTER(config_hook);
AST_TEST_REGISTER(ast_parse_arg_test);
diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c
index 2d811b19e..cbdec0ce1 100644
--- a/tests/test_sorcery.c
+++ b/tests/test_sorcery.c
@@ -493,7 +493,7 @@ AST_TEST_DEFINE(apply_config)
return AST_TEST_NOT_RUN;
}
- if (!ast_category_get(config, "test_sorcery_section")) {
+ if (!ast_category_get(config, "test_sorcery_section", NULL)) {
ast_test_status_update(test, "Sorcery configuration file does not have test_sorcery section\n");
ast_config_destroy(config);
return AST_TEST_NOT_RUN;
@@ -2226,7 +2226,7 @@ AST_TEST_DEFINE(caching_wizard_behavior)
return AST_TEST_NOT_RUN;
}
- if (!ast_category_get(config, "test_sorcery_cache")) {
+ if (!ast_category_get(config, "test_sorcery_cache", NULL)) {
ast_test_status_update(test, "Sorcery configuration file does not contain 'test_sorcery_cache' section\n");
ast_config_destroy(config);
return AST_TEST_NOT_RUN;
diff --git a/tests/test_sorcery_realtime.c b/tests/test_sorcery_realtime.c
index b64ad9389..ab9c18814 100644
--- a/tests/test_sorcery_realtime.c
+++ b/tests/test_sorcery_realtime.c
@@ -139,15 +139,15 @@ static struct ast_config *realtime_sorcery_multi(const char *database, const cha
static int realtime_sorcery_update(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
{
- struct ast_category *object;
+ struct ast_category *object, *found;
- if (!ast_category_exist(realtime_objects, entity)) {
+ if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
return 0;
} else if (!(object = ast_category_new(entity, "", 0))) {
return -1;
}
- ast_category_delete(realtime_objects, entity);
+ ast_category_delete(realtime_objects, found);
ast_variable_append(object, ast_variables_dup((struct ast_variable*)fields));
ast_variable_append(object, ast_variable_new(keyfield, entity, ""));
ast_category_append(realtime_objects, object);
@@ -161,7 +161,7 @@ static int realtime_sorcery_store(const char *database, const char *table, const
const struct ast_variable *keyfield = realtime_find_variable(fields, "id");
struct ast_category *object;
- if (!keyfield || ast_category_exist(realtime_objects, keyfield->value) || !(object = ast_category_new(keyfield->value, "", 0))) {
+ if (!keyfield || ast_category_exist(realtime_objects, keyfield->value, NULL) || !(object = ast_category_new(keyfield->value, "", 0))) {
return -1;
}
@@ -173,11 +173,12 @@ static int realtime_sorcery_store(const char *database, const char *table, const
static int realtime_sorcery_destroy(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
{
- if (!ast_category_exist(realtime_objects, entity)) {
+ struct ast_category *found;
+ if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
return 0;
}
- ast_category_delete(realtime_objects, entity);
+ ast_category_delete(realtime_objects, found);
return 1;
}
@@ -570,7 +571,7 @@ AST_TEST_DEFINE(object_retrieve_regex)
ast_test_status_update(test, "Failed to retrieve a container of objects\n");
return AST_TEST_FAIL;
} else if (ao2_container_count(objects) != 2) {
- ast_test_status_update(test, "Received a container with incorrect number of objects in it\n");
+ ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 2\n", ao2_container_count(objects));
return AST_TEST_FAIL;
}