static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) { const char *family_prefix = data; size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */ char family[family_len + 1]; char tree[prefix_len + 1]; RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); struct ast_db_entry *entry; snprintf(tree, sizeof(tree), "%.*s", (int) prefix_len, prefix); snprintf(family, sizeof(family), "%s/%s", family_prefix, type); if (!(entries = ast_db_gettree_by_prefix(family, tree))) { return; } for (entry = entries; entry; entry = entry->next) { /* The key in the entry includes the family, so we need to strip it out */ const char *key = entry->key + family_len + 2; RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; RAII_VAR(void *, object, NULL, ao2_cleanup); RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); if (!(json = ast_json_load_string(entry->data, &error)) || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) || !(object = ast_sorcery_alloc(sorcery, type, key)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { return; } ao2_link(objects, object); } }
static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id) { const char *prefix = data; char family[strlen(prefix) + strlen(type) + 2]; RAII_VAR(char *, value, NULL, ast_free_ptr); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); void *object = NULL; snprintf(family, sizeof(family), "%s/%s", prefix, type); if (ast_db_get_allocated(family, id, &value) || !(json = ast_json_load_string(value, &error)) || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) || !(object = ast_sorcery_alloc(sorcery, type, id)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id); ao2_cleanup(object); return NULL; } return object; }
static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex) { const char *prefix = data; char family[strlen(prefix) + strlen(type) + 2]; char tree[strlen(regex) + 1]; RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); regex_t expression; struct ast_db_entry *entry; snprintf(family, sizeof(family), "%s/%s", prefix, type); if (regex[0] == '^') { /* * For performance reasons, try to create an astDB prefix * pattern from the regex to reduce the number of entries * retrieved from astDB for regex to then match. */ if (make_astdb_prefix_pattern(tree, regex)) { return; } } else { tree[0] = '\0'; } if (!(entries = ast_db_gettree(family, tree)) || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { return; } for (entry = entries; entry; entry = entry->next) { /* The key in the entry includes the family, so we need to strip it out for regex purposes */ const char *key = entry->key + strlen(family) + 2; RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; RAII_VAR(void *, object, NULL, ao2_cleanup); RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); if (regexec(&expression, key, 0, NULL, 0)) { continue; } else if (!(json = ast_json_load_string(entry->data, &error)) || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) || !(object = ast_sorcery_alloc(sorcery, type, key)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { regfree(&expression); return; } ao2_link(objects, object); } regfree(&expression); }
/*! \brief Internal helper function which retrieves an object, or multiple objects, using fields for criteria */ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects) { const char *prefix = data; char family[strlen(prefix) + strlen(type) + 2]; RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); struct ast_db_entry *entry; snprintf(family, sizeof(family), "%s/%s", prefix, type); if (!(entries = ast_db_gettree(family, NULL))) { return NULL; } for (entry = entries; entry; entry = entry->next) { const char *key = entry->key + strlen(family) + 2; RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; RAII_VAR(struct ast_variable *, existing, NULL, ast_variables_destroy); void *object = NULL; if (!(json = ast_json_load_string(entry->data, &error))) { return NULL; } if (ast_json_to_ast_variables(json, &existing) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) { return NULL; } existing = sorcery_astdb_filter_objectset(existing, sorcery, type); if (fields && !ast_variable_lists_match(existing, fields, 0)) { continue; } if (!(object = ast_sorcery_alloc(sorcery, type, key)) || ast_sorcery_objectset_apply(sorcery, object, existing)) { ao2_cleanup(object); return NULL; } if (!objects) { return object; } ao2_link(objects, object); ao2_cleanup(object); } return NULL; }
/*! \brief Internal helper function which retrieves an object, or multiple objects, using fields for criteria */ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects) { const char *prefix = data; char family[strlen(prefix) + strlen(type) + 2]; RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); RAII_VAR(struct ast_json *, criteria, NULL, ast_json_unref); struct ast_db_entry *entry; snprintf(family, sizeof(family), "%s/%s", prefix, type); if (!(entries = ast_db_gettree(family, NULL)) || (fields && !(criteria = sorcery_objectset_to_json(fields)))) { return NULL; } for (entry = entries; entry; entry = entry->next) { const char *key = entry->key + strlen(family) + 2; RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); void *object = NULL; if (!(json = ast_json_load_string(entry->data, &error))) { return NULL; } else if (criteria && !sorcery_json_equal(json, criteria)) { continue; } else if (!(objset = sorcery_json_to_objectset(json)) || !(object = ast_sorcery_alloc(sorcery, type, key)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { ao2_cleanup(object); return NULL; } if (!objects) { return object; } ao2_link(objects, object); ao2_cleanup(object); } return NULL; }
static void *sorcery_realtime_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields) { const char *family = data; RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, id, NULL, ast_variables_destroy); void *object = NULL; if (!(objectset = ast_load_realtime_fields(family, fields))) { return NULL; } objectset = sorcery_realtime_filter_objectset(objectset, &id, sorcery, type); if (!id || !(object = ast_sorcery_alloc(sorcery, type, id->value)) || ast_sorcery_objectset_apply(sorcery, object, objectset)) { return NULL; } return object; }
static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id) { const char *prefix = data; char family[strlen(prefix) + strlen(type) + 2]; RAII_VAR(char *, value, NULL, ast_free_ptr); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); void *object = NULL; snprintf(family, sizeof(family), "%s/%s", prefix, type); if (ast_db_get_allocated(family, id, &value) || !(json = ast_json_load_string(value, &error)) || !(objset = sorcery_json_to_objectset(json)) || !(object = ast_sorcery_alloc(sorcery, type, id)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { ao2_cleanup(object); return NULL; } return object; }
static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields) { const char *family = data; RAII_VAR(struct ast_config *, rows, NULL, ast_config_destroy); RAII_VAR(struct ast_variable *, all, NULL, ast_variables_destroy); struct ast_category *row = NULL; if (!fields) { char field[strlen(UUID_FIELD) + 6], value[2]; /* If no fields have been specified we want all rows, so trick realtime into doing it */ snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD); snprintf(value, sizeof(value), "%%"); if (!(all = ast_variable_new(field, value, ""))) { return; } fields = all; } if (!(rows = ast_load_realtime_multientry_fields(family, fields))) { return; } 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); objectset = sorcery_realtime_filter_objectset(objectset, &id, sorcery, type); if (id && (object = ast_sorcery_alloc(sorcery, type, id->value)) && !ast_sorcery_objectset_apply(sorcery, object, objectset)) { ao2_link(objects, object); } ast_variables_destroy(objectset); } }
void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup); RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup); struct ast_json *fields; struct ast_variable *update_set = NULL; int created = 0; sorcery = ast_sorcery_retrieve_by_module_name(args->config_class); if (!sorcery) { ast_ari_response_error( response, 404, "Not Found", "configClass '%s' not found", args->config_class); return; } object_type = ast_sorcery_get_object_type(sorcery, args->object_type); if (!object_type) { ast_ari_response_error( response, 404, "Not Found", "objectType '%s' not found", args->object_type); return; } sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id); if (!sorcery_obj) { ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id); sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id); if (!sorcery_obj) { ast_ari_response_alloc_failed(response); return; } created = 1; } else { void *copy; copy = ast_sorcery_copy(sorcery, sorcery_obj); if (!copy) { ast_ari_response_alloc_failed(response); return; } ao2_ref(sorcery_obj, -1); sorcery_obj = copy; } fields = ast_json_object_get(args->fields, "fields"); if (!fields && !created) { /* Whoops. We need data. */ ast_ari_response_error( response, 400, "Bad request", "Fields must be provided to update object '%s'", args->id); return; } else if (fields) { size_t i; for (i = 0; i < ast_json_array_size(fields); i++) { struct ast_variable *new_var; struct ast_json *json_value = ast_json_array_get(fields, i); if (!json_value) { continue; } new_var = ast_variable_new( ast_json_string_get(ast_json_object_get(json_value, "attribute")), ast_json_string_get(ast_json_object_get(json_value, "value")), ""); if (!new_var) { ast_variables_destroy(update_set); ast_ari_response_alloc_failed(response); return; } ast_variable_list_append(&update_set, new_var); } } /* APPLY! Note that a NULL update_set is fine (and necessary), as it * will force validation on a newly created object. */ if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) { ast_variables_destroy(update_set); ast_ari_response_error( response, 400, "Bad request", "%s of object '%s' failed field value validation", created ? "Creation" : "Update", args->id); return; } ast_variables_destroy(update_set); if (created) { if (ast_sorcery_create(sorcery, sorcery_obj)) { ast_ari_response_error( response, 403, "Forbidden", "Cannot create sorcery objects of type '%s'", args->object_type); return; } } else { if (ast_sorcery_update(sorcery, sorcery_obj)) { ast_ari_response_error( response, 403, "Forbidden", "Cannot update sorcery objects of type '%s'", args->object_type); return; } } return_sorcery_object(sorcery, sorcery_obj, response); }