+++ /dev/null
-/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "str.h"
-#include "hash.h"
-#include "settings-legacy.h"
-#include "dict-sql-settings.h"
-
-#include <ctype.h>
-
-enum section_type {
- SECTION_ROOT = 0,
- SECTION_MAP,
- SECTION_FIELDS
-};
-
-struct dict_sql_map_field {
- struct dict_sql_field sql_field;
- const char *variable;
-};
-
-struct setting_parser_ctx {
- pool_t pool;
- struct dict_sql_legacy_settings *set;
- enum section_type type;
-
- struct dict_sql_map cur_map;
- ARRAY(struct dict_sql_map_field) cur_fields;
-};
-
-#define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map)
-#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_sql_map)
-
-static const struct setting_def dict_sql_map_setting_defs[] = {
- DEF_STR(pattern),
- DEF_STR(table),
- DEF_STR(username_field),
- DEF_STR(value_field),
- DEF_STR(value_type),
- DEF_STR(expire_field),
-
- { 0, NULL, 0 }
-};
-
-struct dict_sql_legacy_settings_cache {
- pool_t pool;
- const char *path;
- struct dict_sql_legacy_settings *set;
-};
-
-static HASH_TABLE(const char *, struct dict_sql_legacy_settings_cache *) dict_sql_legacy_settings_cache;
-
-static const char *pattern_read_name(const char **pattern)
-{
- const char *p = *pattern, *name;
-
- if (*p == '{') {
- /* ${name} */
- name = ++p;
- p = strchr(p, '}');
- if (p == NULL) {
- /* error, but allow anyway */
- *pattern += strlen(*pattern);
- return "";
- }
- *pattern = p + 1;
- } else {
- /* $name - ends at the first non-alnum_ character */
- name = p;
- for (; *p != '\0'; p++) {
- if (!i_isalnum(*p) && *p != '_')
- break;
- }
- *pattern = p;
- }
- name = t_strdup_until(name, p);
- return name;
-}
-
-static const char *dict_sql_fields_map(struct setting_parser_ctx *ctx)
-{
- struct dict_sql_map_field *fields;
- string_t *pattern;
- const char *p, *name;
- unsigned int i, count;
-
- /* go through the variables in the pattern, replace them with plain
- '$' character and add its sql field */
- pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1);
- fields = array_get_modifiable(&ctx->cur_fields, &count);
-
- p_array_init(&ctx->cur_map.pattern_fields, ctx->pool, count);
- for (p = ctx->cur_map.pattern; *p != '\0';) {
- if (*p != '$') {
- str_append_c(pattern, *p);
- p++;
- continue;
- }
- p++;
- str_append_c(pattern, '$');
-
- name = pattern_read_name(&p);
- for (i = 0; i < count; i++) {
- if (fields[i].variable != NULL &&
- strcmp(fields[i].variable, name) == 0)
- break;
- }
- if (i == count) {
- return t_strconcat("Missing SQL field for variable: ",
- name, NULL);
- }
-
- /* mark this field as used */
- fields[i].variable = NULL;
- array_push_back(&ctx->cur_map.pattern_fields,
- &fields[i].sql_field);
- }
-
- /* make sure there aren't any unused fields */
- for (i = 0; i < count; i++) {
- if (fields[i].variable != NULL) {
- return t_strconcat("Unused variable: ",
- fields[i].variable, NULL);
- }
- }
-
- ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern));
- return NULL;
-}
-
-static bool
-dict_sql_value_type_parse(const char *value_type, enum dict_sql_type *type_r)
-{
- for (enum dict_sql_type type = DICT_SQL_TYPE_STRING;
- type < DICT_SQL_TYPE_COUNT; type++) {
- if (strcmp(value_type, dict_sql_type_names[type]) == 0) {
- *type_r = type;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static const char *dict_sql_map_finish(struct setting_parser_ctx *ctx)
-{
- unsigned int i;
-
- if (ctx->cur_map.pattern == NULL)
- return "Missing setting: pattern";
- if (ctx->cur_map.table == NULL)
- return "Missing setting: table";
- if (ctx->cur_map.value_field == NULL)
- return "Missing setting: value_field";
-
- if (ctx->cur_map.expire_field != NULL &&
- ctx->cur_map.expire_field[0] == '\0')
- ctx->cur_map.expire_field = NULL;
- const char *const *value_fields =
- t_strsplit_spaces(ctx->cur_map.value_field, ",");
- ctx->cur_map.values_count = str_array_length(value_fields);
-
- enum dict_sql_type *value_types =
- p_new(ctx->pool, enum dict_sql_type, ctx->cur_map.values_count);
- if (ctx->cur_map.value_type != NULL) {
- const char *const *types =
- t_strsplit_spaces(ctx->cur_map.value_type, ",");
- if (str_array_length(types) != ctx->cur_map.values_count)
- return "Number of fields in value_fields doesn't match value_type";
- for (i = 0; i < ctx->cur_map.values_count; i++) {
- if (!dict_sql_value_type_parse(types[i], &value_types[i]))
- return t_strdup_printf("Invalid value %s in value_type", types[i]);
- }
- } else {
- for (i = 0; i < ctx->cur_map.values_count; i++)
- value_types[i] = DICT_SQL_TYPE_STRING;
- }
- ctx->cur_map.value_types = value_types;
-
- if (ctx->cur_map.username_field == NULL) {
- /* not all queries require this */
- ctx->cur_map.username_field = "'username_field not set'";
- }
-
- if (!array_is_created(&ctx->cur_map.pattern_fields)) {
- /* no fields besides value. allocate the array anyway. */
- p_array_init(&ctx->cur_map.pattern_fields, ctx->pool, 1);
- if (strchr(ctx->cur_map.pattern, '$') != NULL)
- return "Missing fields for pattern variables";
- }
- array_push_back(&ctx->set->map_set.maps, &ctx->cur_map);
- i_zero(&ctx->cur_map);
- return NULL;
-}
-
-static const char *
-parse_setting(const char *key, const char *value,
- struct setting_parser_ctx *ctx)
-{
- struct dict_sql_map_field *field;
-
- switch (ctx->type) {
- case SECTION_ROOT:
- if (strcmp(key, "connect") == 0) {
- ctx->set->connect = p_strdup(ctx->pool, value);
- return NULL;
- }
- break;
- case SECTION_MAP:
- return parse_setting_from_defs(ctx->pool,
- dict_sql_map_setting_defs,
- &ctx->cur_map, key, value);
- case SECTION_FIELDS:
- if (*value != '$') {
- return t_strconcat("Value is missing '$' for field: ",
- key, NULL);
- }
- field = array_append_space(&ctx->cur_fields);
- field->sql_field.name = p_strdup(ctx->pool, key);
-
- const char *arg, *last = value + strlen(value) - 1;
- if (str_begins(value, "${hexblob:", &arg) && *last == '}') {
- field->variable = p_strdup_until(ctx->pool, arg, last);
- field->sql_field.value_type = DICT_SQL_TYPE_HEXBLOB;
- } else if (str_begins(value, "${int:", &arg) && *last == '}') {
- field->variable = p_strdup_until(ctx->pool, arg, last);
- field->sql_field.value_type = DICT_SQL_TYPE_INT;
- } else if (str_begins(value, "${uint:", &arg) && *last == '}') {
- field->variable = p_strdup_until(ctx->pool, arg, last);
- field->sql_field.value_type = DICT_SQL_TYPE_UINT;
- } else if (str_begins(value, "${double:", &arg) && *last == '}') {
- field->variable = p_strdup_until(ctx->pool, arg, last);
- field->sql_field.value_type = DICT_SQL_TYPE_DOUBLE;
- } else if (str_begins(value, "${uuid:", &arg) && *last == '}') {
- field->variable = p_strdup_until(ctx->pool, arg, last);
- field->sql_field.value_type = DICT_SQL_TYPE_UUID;
- } else {
- field->variable = p_strdup(ctx->pool, value + 1);
- }
- return NULL;
- }
- return t_strconcat("Unknown setting: ", key, NULL);
-}
-
-static bool
-parse_section(const char *type, const char *name ATTR_UNUSED,
- struct setting_parser_ctx *ctx, const char **error_r)
-{
- switch (ctx->type) {
- case SECTION_ROOT:
- if (type == NULL)
- return FALSE;
- if (strcmp(type, "map") == 0) {
- array_clear(&ctx->cur_fields);
- ctx->type = SECTION_MAP;
- return TRUE;
- }
- break;
- case SECTION_MAP:
- if (type == NULL) {
- ctx->type = SECTION_ROOT;
- *error_r = dict_sql_map_finish(ctx);
- return FALSE;
- }
- if (strcmp(type, "fields") == 0) {
- ctx->type = SECTION_FIELDS;
- return TRUE;
- }
- break;
- case SECTION_FIELDS:
- if (type == NULL) {
- ctx->type = SECTION_MAP;
- *error_r = dict_sql_fields_map(ctx);
- return FALSE;
- }
- break;
- }
- *error_r = t_strconcat("Unknown section: ", type, NULL);
- return FALSE;
-}
-
-struct dict_sql_legacy_settings *
-dict_sql_legacy_settings_read(const char *path, const char **error_r)
-{
- struct setting_parser_ctx ctx;
- struct dict_sql_legacy_settings_cache *cache;
- pool_t pool;
-
- if (!hash_table_is_created(dict_sql_legacy_settings_cache)) {
- hash_table_create(&dict_sql_legacy_settings_cache, default_pool, 0,
- str_hash, strcmp);
- }
-
- cache = hash_table_lookup(dict_sql_legacy_settings_cache, path);
- if (cache != NULL)
- return cache->set;
-
- i_zero(&ctx);
- pool = pool_alloconly_create("dict sql settings", 1024);
- ctx.pool = pool;
- ctx.set = p_new(pool, struct dict_sql_legacy_settings, 1);
- t_array_init(&ctx.cur_fields, 16);
- p_array_init(&ctx.set->map_set.maps, pool, 8);
-
- if (!settings_read(path, NULL, parse_setting, parse_section,
- &ctx, error_r)) {
- pool_unref(&pool);
- return NULL;
- }
-
- if (ctx.set->connect == NULL) {
- *error_r = t_strdup_printf("Error in configuration file %s: "
- "Missing connect setting", path);
- pool_unref(&pool);
- return NULL;
- }
-
- cache = p_new(pool, struct dict_sql_legacy_settings_cache, 1);
- cache->pool = pool;
- cache->path = p_strdup(pool, path);
- cache->set = ctx.set;
-
- hash_table_insert(dict_sql_legacy_settings_cache, cache->path, cache);
-
- ctx.set->map_set.pool = pool;
- pool_ref(pool);
- return ctx.set;
-}
-
-void dict_sql_legacy_settings_deinit(void)
-{
- struct hash_iterate_context *iter;
- struct dict_sql_legacy_settings_cache *cache;
- const char *key;
-
- if (!hash_table_is_created(dict_sql_legacy_settings_cache))
- return;
-
- iter = hash_table_iterate_init(dict_sql_legacy_settings_cache);
- while (hash_table_iterate(iter, dict_sql_legacy_settings_cache, &key, &cache))
- pool_unref(&cache->pool);
- hash_table_iterate_deinit(&iter);
- hash_table_destroy(&dict_sql_legacy_settings_cache);
-}
#include "hash.h"
#include "str.h"
#include "sql-api-private.h"
-#include "sql-db-cache-legacy.h"
#include "dict-private.h"
#include "dict-sql-settings.h"
#include "dict-sql.h"
char *error;
};
-static struct sql_db_cache *dict_sql_db_cache;
-
static void sql_dict_prev_inc_flush(struct sql_dict_transaction_context *ctx);
static void sql_dict_prev_set_flush(struct sql_dict_transaction_context *ctx);
static void sql_dict_prev_inc_free(struct sql_dict_transaction_context *ctx);
return 0;
}
-static int
-sql_dict_init_legacy(struct dict *driver, const char *uri,
- const struct dict_legacy_settings *set,
- struct dict **dict_r, const char **error_r)
-{
- struct sql_legacy_settings sql_set;
- struct dict_sql_legacy_settings *dict_sql_set;
- struct sql_dict *dict;
- pool_t pool;
-
- pool = pool_alloconly_create("sql dict", 2048);
- dict_sql_set = dict_sql_legacy_settings_read(uri, error_r);
- if (dict_sql_set == NULL) {
- pool_unref(&pool);
- return -1;
- }
-
- dict = p_new(pool, struct sql_dict, 1);
- dict->pool = pool;
- dict->dict = *driver;
- dict->set = &dict_sql_set->map_set;
-
- i_zero(&sql_set);
- sql_set.driver = driver->name;
- sql_set.connect_string = dict_sql_set->connect;
- sql_set.event_parent = set->event_parent;
-
- if (sql_db_cache_new_legacy(dict_sql_db_cache, &sql_set,
- &dict->db, error_r) < 0) {
- pool_unref(&pool);
- return -1;
- }
-
- *dict_r = &dict->dict;
- return 0;
-}
-
static void sql_dict_deinit(struct dict *_dict)
{
struct sql_dict *dict = (struct sql_dict *)_dict;
.flags = DICT_DRIVER_FLAG_SUPPORT_EXPIRE_SECS,
.v = {
.init = sql_dict_init,
- .init_legacy = sql_dict_init_legacy,
.deinit = sql_dict_deinit,
.wait = sql_dict_wait,
.expire_scan = sql_dict_expire_scan,
}
};
-static struct dict *dict_sql_drivers;
-
void dict_sql_register(void)
{
- const struct sql_db *const *drivers;
- unsigned int i, count;
-
- dict_sql_db_cache = sql_db_cache_init_legacy(DICT_SQL_MAX_UNUSED_CONNECTIONS);
-
dict_driver_register(&sql_dict);
-
- /* @UNSAFE - FIXME: remove these when dict_legacy is dropped */
- drivers = array_get(&sql_drivers, &count);
- dict_sql_drivers = i_new(struct dict, count + 1);
-
- for (i = 0; i < count; i++) {
- dict_sql_drivers[i] = sql_dict;
- dict_sql_drivers[i].name = drivers[i]->name;
-
- dict_driver_register(&dict_sql_drivers[i]);
- }
}
void dict_sql_unregister(void)
{
- int i;
-
dict_driver_unregister(&sql_dict);
- for (i = 0; dict_sql_drivers[i].name != NULL; i++)
- dict_driver_unregister(&dict_sql_drivers[i]);
- i_free(dict_sql_drivers);
- sql_db_cache_deinit_legacy(&dict_sql_db_cache);
- dict_sql_legacy_settings_deinit();
}