summaryrefslogtreecommitdiffstats
path: root/src/config/config-request.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/config/config-request.c')
-rw-r--r--src/config/config-request.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/config/config-request.c b/src/config/config-request.c
new file mode 100644
index 0000000..7428367
--- /dev/null
+++ b/src/config/config-request.c
@@ -0,0 +1,524 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "hash.h"
+#include "ostream.h"
+#include "settings-parser.h"
+#include "master-service-settings.h"
+#include "all-settings.h"
+#include "config-parser.h"
+#include "config-request.h"
+#include "old-set-parser.h"
+
+struct config_export_context {
+ pool_t pool;
+ string_t *value;
+ string_t *prefix;
+ HASH_TABLE(char *, char *) keys;
+ enum config_dump_scope scope;
+
+ config_request_callback_t *callback;
+ void *context;
+
+ const char *const *modules;
+ const char *const *exclude_settings;
+ enum config_dump_flags flags;
+ const struct config_module_parser *parsers;
+ struct config_module_parser *dup_parsers;
+ struct master_service_settings_output output;
+
+ bool failed;
+};
+
+static void config_export_size(string_t *str, uoff_t size)
+{
+ static const char suffixes[] = { 'B', 'k', 'M', 'G', 'T' };
+ char suffix = suffixes[0];
+ unsigned int i;
+
+ if (size == 0) {
+ str_append_c(str, '0');
+ return;
+ }
+ for (i = 1; i < N_ELEMENTS(suffixes) && (size % 1024) == 0; i++) {
+ suffix = suffixes[i];
+ size /= 1024;
+ }
+ str_printfa(str, "%"PRIuUOFF_T" %c", size, suffix);
+}
+
+static void config_export_time(string_t *str, unsigned int stamp)
+{
+ const char *suffix = "secs";
+
+ if (stamp == 0) {
+ str_append_c(str, '0');
+ return;
+ }
+
+ if (stamp % 60 == 0) {
+ stamp /= 60;
+ suffix = "mins";
+ if (stamp % 60 == 0) {
+ stamp /= 60;
+ suffix = "hours";
+ if (stamp % 24 == 0) {
+ stamp /= 24;
+ suffix = "days";
+ if (stamp % 7 == 0) {
+ stamp /= 7;
+ suffix = "weeks";
+ }
+ }
+ }
+ }
+
+ str_printfa(str, "%u %s", stamp, suffix);
+}
+
+static void config_export_time_msecs(string_t *str, unsigned int stamp_msecs)
+{
+ if ((stamp_msecs % 1000) == 0)
+ config_export_time(str, stamp_msecs/1000);
+ else
+ str_printfa(str, "%u ms", stamp_msecs);
+}
+
+bool config_export_type(string_t *str, const void *value,
+ const void *default_value,
+ enum setting_type type, bool dump_default,
+ bool *dump_r)
+{
+ switch (type) {
+ case SET_BOOL: {
+ const bool *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval)
+ str_append(str, *val ? "yes" : "no");
+ break;
+ }
+ case SET_SIZE: {
+ const uoff_t *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval)
+ config_export_size(str, *val);
+ break;
+ }
+ case SET_UINT:
+ case SET_UINT_OCT:
+ case SET_TIME:
+ case SET_TIME_MSECS: {
+ const unsigned int *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval) {
+ switch (type) {
+ case SET_UINT_OCT:
+ str_printfa(str, "0%o", *val);
+ break;
+ case SET_TIME:
+ config_export_time(str, *val);
+ break;
+ case SET_TIME_MSECS:
+ config_export_time_msecs(str, *val);
+ break;
+ default:
+ str_printfa(str, "%u", *val);
+ break;
+ }
+ }
+ break;
+ }
+ case SET_IN_PORT: {
+ const in_port_t *val = value, *dval = default_value;
+
+ if (dump_default || dval == NULL || *val != *dval)
+ str_printfa(str, "%u", *val);
+ break;
+ }
+ case SET_STR_VARS: {
+ const char *const *val = value, *sval;
+ const char *const *_dval = default_value;
+ const char *dval = _dval == NULL ? NULL : *_dval;
+
+ i_assert(*val == NULL ||
+ **val == SETTING_STRVAR_UNEXPANDED[0]);
+
+ sval = *val == NULL ? NULL : (*val + 1);
+ if ((dump_default || null_strcmp(sval, dval) != 0) &&
+ sval != NULL) {
+ str_append(str, sval);
+ *dump_r = TRUE;
+ }
+ break;
+ }
+ case SET_STR: {
+ const char *const *val = value;
+ const char *const *_dval = default_value;
+ const char *dval = _dval == NULL ? NULL : *_dval;
+
+ if ((dump_default || null_strcmp(*val, dval) != 0) &&
+ *val != NULL) {
+ str_append(str, *val);
+ *dump_r = TRUE;
+ }
+ break;
+ }
+ case SET_ENUM: {
+ const char *const *val = value;
+ size_t len = strlen(*val);
+
+ if (dump_default)
+ str_append(str, *val);
+ else {
+ const char *const *_dval = default_value;
+ const char *dval = _dval == NULL ? NULL : *_dval;
+
+ i_assert(dval != NULL);
+ if (strncmp(*val, dval, len) != 0 ||
+ ((*val)[len] != ':' && (*val)[len] != '\0'))
+ str_append(str, *val);
+ }
+ break;
+ }
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+setting_export_section_name(string_t *str, const struct setting_define *def,
+ const void *set, unsigned int idx)
+{
+ const char *const *name;
+ size_t name_offset;
+
+ if (def->type != SET_DEFLIST_UNIQUE) {
+ /* not unique, use the index */
+ str_printfa(str, "%u", idx);
+ return;
+ }
+ name_offset = def->list_info->type_offset;
+ i_assert(name_offset != SIZE_MAX);
+
+ name = CONST_PTR_OFFSET(set, name_offset);
+ if (*name == NULL || **name == '\0') {
+ /* no name, this one isn't unique. use the index. */
+ str_printfa(str, "%u", idx);
+ } else T_BEGIN {
+ str_append(str, settings_section_escape(*name));
+ } T_END;
+}
+
+static void
+settings_export(struct config_export_context *ctx,
+ const struct setting_parser_info *info,
+ bool parent_unique_deflist,
+ const void *set, const void *change_set)
+{
+ const struct setting_define *def;
+ const void *value, *default_value, *change_value;
+ void *const *children, *const *change_children = NULL;
+ unsigned int i, count, count2;
+ size_t prefix_len;
+ const char *str;
+ char *key;
+ bool dump, dump_default = FALSE;
+
+ for (def = info->defines; def->key != NULL; def++) {
+ if (ctx->exclude_settings != NULL &&
+ str_array_find(ctx->exclude_settings, def->key))
+ continue;
+
+ value = CONST_PTR_OFFSET(set, def->offset);
+ default_value = info->defaults == NULL ? NULL :
+ CONST_PTR_OFFSET(info->defaults, def->offset);
+ change_value = CONST_PTR_OFFSET(change_set, def->offset);
+ switch (ctx->scope) {
+ case CONFIG_DUMP_SCOPE_ALL_WITH_HIDDEN:
+ dump_default = TRUE;
+ break;
+ case CONFIG_DUMP_SCOPE_ALL_WITHOUT_HIDDEN:
+ if ((def->flags & SET_FLAG_HIDDEN) == 0) {
+ /* not hidden - dump it */
+ dump_default = TRUE;
+ break;
+ }
+ /* hidden - dump default only if it's explicitly set */
+ /* fall through */
+ case CONFIG_DUMP_SCOPE_SET:
+ dump_default = *((const char *)change_value) != 0;
+ break;
+ case CONFIG_DUMP_SCOPE_CHANGED:
+ dump_default = FALSE;
+ break;
+ }
+ if (!parent_unique_deflist ||
+ (ctx->flags & CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS) == 0) {
+ /* .. */
+ } else if (*((const char *)change_value) == 0 &&
+ def->offset != info->type_offset) {
+ /* this is mainly for service {} blocks. if value
+ hasn't changed, it's the default. even if
+ info->defaults has a different value. */
+ default_value = value;
+ } else {
+ /* value is set explicitly, but we don't know the
+ default here. assume it's not the default. */
+ dump_default = TRUE;
+ }
+
+ dump = FALSE;
+ count = 0; children = NULL;
+ str_truncate(ctx->value, 0);
+ switch (def->type) {
+ case SET_BOOL:
+ case SET_SIZE:
+ case SET_UINT:
+ case SET_UINT_OCT:
+ case SET_TIME:
+ case SET_TIME_MSECS:
+ case SET_IN_PORT:
+ case SET_STR_VARS:
+ case SET_STR:
+ case SET_ENUM:
+ if (!config_export_type(ctx->value, value,
+ default_value, def->type,
+ dump_default, &dump))
+ i_unreached();
+ break;
+ case SET_DEFLIST:
+ case SET_DEFLIST_UNIQUE: {
+ const ARRAY_TYPE(void_array) *val = value;
+ const ARRAY_TYPE(void_array) *change_val = change_value;
+
+ if (!array_is_created(val))
+ break;
+
+ children = array_get(val, &count);
+ for (i = 0; i < count; i++) {
+ if (i > 0)
+ str_append_c(ctx->value, ' ');
+ setting_export_section_name(ctx->value, def, children[i], i);
+ }
+ change_children = array_get(change_val, &count2);
+ i_assert(count == count2);
+ break;
+ }
+ case SET_STRLIST: {
+ const ARRAY_TYPE(const_string) *val = value;
+ const char *const *strings;
+
+ if (!array_is_created(val))
+ break;
+
+ key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+ def->key, NULL);
+
+ if (hash_table_lookup(ctx->keys, key) != NULL) {
+ /* already added all of these */
+ break;
+ }
+ hash_table_insert(ctx->keys, key, key);
+ /* for doveconf -n to see this KEY_LIST */
+ ctx->callback(key, "", CONFIG_KEY_LIST, ctx->context);
+
+ strings = array_get(val, &count);
+ i_assert(count % 2 == 0);
+ for (i = 0; i < count; i += 2) {
+ str = p_strdup_printf(ctx->pool, "%s%s%c%s",
+ str_c(ctx->prefix),
+ def->key,
+ SETTINGS_SEPARATOR,
+ strings[i]);
+ ctx->callback(str, strings[i+1],
+ CONFIG_KEY_NORMAL, ctx->context);
+ }
+ count = 0;
+ break;
+ }
+ case SET_ALIAS:
+ break;
+ }
+ if (str_len(ctx->value) > 0 || dump) {
+ key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+ def->key, NULL);
+ if (hash_table_lookup(ctx->keys, key) == NULL) {
+ enum config_key_type type;
+
+ if (def->offset == info->type_offset &&
+ parent_unique_deflist)
+ type = CONFIG_KEY_UNIQUE_KEY;
+ else if (SETTING_TYPE_IS_DEFLIST(def->type))
+ type = CONFIG_KEY_LIST;
+ else
+ type = CONFIG_KEY_NORMAL;
+ ctx->callback(key, str_c(ctx->value), type,
+ ctx->context);
+ hash_table_insert(ctx->keys, key, key);
+ }
+ }
+
+ i_assert(count == 0 || children != NULL);
+ prefix_len = str_len(ctx->prefix);
+ for (i = 0; i < count; i++) {
+ str_append(ctx->prefix, def->key);
+ str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
+ setting_export_section_name(ctx->prefix, def, children[i], i);
+ str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
+ settings_export(ctx, def->list_info,
+ def->type == SET_DEFLIST_UNIQUE,
+ children[i], change_children[i]);
+
+ str_truncate(ctx->prefix, prefix_len);
+ }
+ }
+}
+
+struct config_export_context *
+config_export_init(const char *const *modules,
+ const char *const *exclude_settings,
+ enum config_dump_scope scope,
+ enum config_dump_flags flags,
+ config_request_callback_t *callback, void *context)
+{
+ struct config_export_context *ctx;
+ pool_t pool;
+
+ pool = pool_alloconly_create(MEMPOOL_GROWING"config export", 1024*64);
+ ctx = p_new(pool, struct config_export_context, 1);
+ ctx->pool = pool;
+
+ ctx->modules = modules == NULL ? NULL : p_strarray_dup(pool, modules);
+ ctx->exclude_settings = exclude_settings == NULL ? NULL :
+ p_strarray_dup(pool, exclude_settings);
+ ctx->flags = flags;
+ ctx->callback = callback;
+ ctx->context = context;
+ ctx->scope = scope;
+ ctx->value = str_new(pool, 256);
+ ctx->prefix = str_new(pool, 64);
+ hash_table_create(&ctx->keys, ctx->pool, 0, str_hash, strcmp);
+ return ctx;
+}
+
+void config_export_by_filter(struct config_export_context *ctx,
+ const struct config_filter *filter)
+{
+ const char *error;
+
+ if (config_filter_parsers_get(config_filter, ctx->pool,
+ ctx->modules, filter,
+ &ctx->dup_parsers, &ctx->output,
+ &error) < 0) {
+ i_error("%s", error);
+ ctx->failed = TRUE;
+ }
+ ctx->parsers = ctx->dup_parsers;
+}
+
+void config_export_parsers(struct config_export_context *ctx,
+ const struct config_module_parser *parsers)
+{
+ ctx->parsers = parsers;
+}
+
+void config_export_get_output(struct config_export_context *ctx,
+ struct master_service_settings_output *output_r)
+{
+ *output_r = ctx->output;
+}
+
+const char *
+config_export_get_import_environment(struct config_export_context *ctx)
+{
+ enum setting_type stype;
+ unsigned int i;
+
+ for (i = 0; ctx->parsers[i].root != NULL; i++) {
+ if (ctx->parsers[i].root == &master_service_setting_parser_info) {
+ const char *const *value =
+ settings_parse_get_value(ctx->parsers[i].parser,
+ "import_environment", &stype);
+ i_assert(value != NULL);
+ return *value;
+ }
+ }
+ i_unreached();
+}
+
+static void config_export_free(struct config_export_context *ctx)
+{
+ if (ctx->dup_parsers != NULL)
+ config_filter_parsers_free(ctx->dup_parsers);
+ hash_table_destroy(&ctx->keys);
+ pool_unref(&ctx->pool);
+}
+
+int config_export_finish(struct config_export_context **_ctx)
+{
+ struct config_export_context *ctx = *_ctx;
+ const struct config_module_parser *parser;
+ const char *error;
+ unsigned int i;
+ int ret = 0;
+
+ *_ctx = NULL;
+
+ if (ctx->failed) {
+ config_export_free(ctx);
+ return -1;
+ }
+
+ for (i = 0; ctx->parsers[i].root != NULL; i++) {
+ parser = &ctx->parsers[i];
+ if (!config_module_want_parser(config_module_parsers,
+ ctx->modules, parser->root))
+ continue;
+
+ T_BEGIN {
+ enum setting_type stype;
+ const char *const *value = settings_parse_get_value(parser->parser, "ssl", &stype);
+
+ if ((ctx->flags & CONFIG_DUMP_FLAG_IN_SECTION) == 0 &&
+ value != NULL && strcmp(*value, "no") != 0 &&
+ settings_parse_is_valid_key(parser->parser, "ssl_dh")) {
+ value = settings_parse_get_value(parser->parser,
+ "ssl_dh", &stype);
+
+ if (value == NULL || **value == '\0') {
+ const char *newval;
+ if (old_settings_ssl_dh_load(&newval, &error)) {
+ if (newval != NULL)
+ settings_parse_line(parser->parser, t_strdup_printf("%s=%s", "ssl_dh", newval));
+ } else {
+ i_error("%s", error);
+ ret = -1;
+ }
+ }
+ }
+ settings_export(ctx, parser->root, FALSE,
+ settings_parser_get(parser->parser),
+ settings_parser_get_changes(parser->parser));
+ } T_END;
+
+ if ((ctx->flags & CONFIG_DUMP_FLAG_CHECK_SETTINGS) != 0) {
+ settings_parse_var_skip(parser->parser);
+ if (!settings_parser_check(parser->parser, ctx->pool,
+ &error)) {
+ if ((ctx->flags & CONFIG_DUMP_FLAG_CALLBACK_ERRORS) != 0) {
+ ctx->callback(NULL, error, CONFIG_KEY_ERROR,
+ ctx->context);
+ } else {
+ i_error("%s", error);
+ ret = -1;
+ break;
+ }
+ }
+ }
+ }
+ config_export_free(ctx);
+ return ret;
+}