Mercurial > dovecot > core-2.2
changeset 9002:9d0037a997f4 HEAD
Initial commit for config rewrite.
line wrap: on
line diff
--- a/.hgignore Mon Jan 26 19:17:54 2009 -0500 +++ b/.hgignore Tue Jan 27 18:21:53 2009 -0500 @@ -51,6 +51,9 @@ doc/wiki/Makefile.am src/auth/checkpassword-reply src/auth/dovecot-auth +src/config/all-settings.c +src/config/all-settings.h +src/config/doveconf src/deliver/deliver src/dict/dict src/imap-login/imap-login
--- a/TODO Mon Jan 26 19:17:54 2009 -0500 +++ b/TODO Tue Jan 27 18:21:53 2009 -0500 @@ -1,3 +1,14 @@ + - config rewrite + - deliver: plugin var expanding + - master: expands only known vars, not all_settings.. should it expand + anything after all? use the 1/0 prefix to mark which vars have been + expanded (config vs userdb)? + - go through mail-process.c. nfs test? + - support !include and !include_try + - check plugins to make sure they're using only internal getenv()s, + especially fix DEBUG env checks + - add back all setting verification code from master + - put plugin settings to some plugins array, not envs, var expand - proxying: support fallbacking to local (or other?) server if the first one is down user_attrs {
--- a/configure.in Mon Jan 26 19:17:54 2009 -0500 +++ b/configure.in Tue Jan 27 18:21:53 2009 -0500 @@ -2336,6 +2336,14 @@ AM_CONDITIONAL(BUILD_SOLR, test "$have_solr" = "yes") dnl ** +dnl ** Settings +dnl ** + +dnl get a list of setting .[ch] files, but list .h files first +SETTING_FILES=`find $srcdir/src -name '*settings.[[ch]]' ! -name all-settings.[[ch]] | sed -e s,$srcdir/src,./src,g -e 's,./src,$(top_srcdir)/src,g' -e 's/^\(.*\)\(.\)$/\2 \1\2/' | sort -r | sed s/^..//|tr '\n' ' '` +AC_SUBST(SETTING_FILES) + +dnl ** dnl ** capabilities dnl ** @@ -2380,6 +2388,7 @@ src/lib-storage/index/shared/Makefile src/lib-storage/register/Makefile src/auth/Makefile +src/config/Makefile src/deliver/Makefile src/dict/Makefile src/imap/Makefile
--- a/src/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -20,6 +20,7 @@ lib-storage \ lib-auth \ auth \ + config \ dict \ master \ login-common \
--- a/src/auth/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -25,6 +25,7 @@ -I$(top_srcdir)/src/lib-otp \ -DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ + -DPKG_RUNDIR=\""$(rundir)"\" \ $(AUTH_CFLAGS) dovecot_auth_LDFLAGS = -export-dynamic @@ -64,6 +65,7 @@ auth-master-listener.c \ auth-request.c \ auth-request-handler.c \ + auth-settings.c \ auth-stream.c \ auth-worker-client.c \ auth-worker-server.c \ @@ -120,6 +122,7 @@ auth-master-listener.h \ auth-request.h \ auth-request-handler.h \ + auth-settings.h \ auth-stream.h \ auth-worker-client.h \ auth-worker-server.h \
--- a/src/auth/auth-client-connection.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth-client-connection.c Tue Jan 27 18:21:53 2009 -0500 @@ -58,8 +58,8 @@ io_remove(&conn->io); } - if (conn->auth->verbose_debug) { - i_info("client out: %s", conn->auth->verbose_debug_passwords ? + if (conn->auth->set->debug) { + i_info("client out: %s", conn->auth->set->debug_passwords ? cmd : reply_line_hide_pass(cmd)); } } @@ -117,7 +117,7 @@ auth_request_handler_set(conn->request_handler, conn->connect_uid, pid); conn->pid = pid; - if (conn->auth->verbose_debug) + if (conn->auth->set->debug) i_info("new auth connection: pid=%u", conn->pid); return TRUE; } @@ -166,18 +166,18 @@ auth_client_handle_line(struct auth_client_connection *conn, const char *line) { if (strncmp(line, "AUTH\t", 5) == 0) { - if (conn->auth->verbose_debug) { + if (conn->auth->set->debug) { i_info("client in: %s", - conn->auth->verbose_debug_passwords ? line : + conn->auth->set->debug_passwords ? line : auth_line_hide_pass(line)); } return auth_request_handler_auth_begin(conn->request_handler, line + 5); } if (strncmp(line, "CONT\t", 5) == 0) { - if (conn->auth->verbose_debug) { + if (conn->auth->set->debug) { i_info("client in: %s", - conn->auth->verbose_debug_passwords ? line : + conn->auth->set->debug_passwords ? line : cont_line_hide_pass(line)); } return auth_request_handler_auth_continue(conn->request_handler,
--- a/src/auth/auth-master-connection.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth-master-connection.c Tue Jan 27 18:21:53 2009 -0500 @@ -38,7 +38,7 @@ reply_str = auth_stream_reply_export(reply); - if (conn->listener->auth->verbose_debug) + if (conn->listener->auth->set->debug) i_info("master out: %s", reply_str); iov[0].iov_base = reply_str; @@ -105,7 +105,7 @@ break; } - if (conn->listener->auth->verbose_debug) + if (conn->listener->auth->set->debug) i_info("master out: %s", str_c(str)); str_append_c(str, '\n'); @@ -163,7 +163,7 @@ static bool auth_master_input_line(struct auth_master_connection *conn, const char *line) { - if (conn->listener->auth->verbose_debug) + if (conn->listener->auth->set->debug) i_info("master in: %s", line); if (strncmp(line, "REQUEST\t", 8) == 0)
--- a/src/auth/auth-request-handler.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth-request-handler.c Tue Jan 27 18:21:53 2009 -0500 @@ -14,7 +14,6 @@ #include <stdlib.h> -#define DEFAULT_AUTH_FAILURE_DELAY 2 #define AUTH_FAILURE_DELAY_CHECK_MSECS 500 struct auth_request_handler { @@ -34,7 +33,6 @@ static ARRAY_DEFINE(auth_failures_arr, struct auth_request *); static struct aqueue *auth_failures; static struct timeout *to_auth_failures; -static unsigned int auth_failure_delay; static void auth_failure_timeout(void *context); @@ -356,7 +354,7 @@ hash_table_insert(handler->requests, POINTER_CAST(id), request); - if (request->auth->ssl_require_client_cert && + if (request->auth->set->ssl_require_client_cert && !request->valid_client_cert) { /* we fail without valid certificate */ auth_request_handler_auth_fail(handler, request, @@ -546,8 +544,10 @@ for (i = 0; i < count; i++) { auth_request = auth_requests[aqueue_idx(auth_failures, 0)]; + /* FIXME: assumess that failure_delay is always the same. */ diff = ioloop_time - auth_request->last_access; - if (diff < (time_t)auth_failure_delay && !flush_all) + if (diff < (time_t)auth_request->auth->set->failure_delay && + !flush_all) break; aqueue_delete_tail(auth_failures); @@ -566,12 +566,6 @@ void auth_request_handler_init(void) { - const char *env; - - env = getenv("FAILURE_DELAY"); - auth_failure_delay = env != NULL ? atoi(env) : - DEFAULT_AUTH_FAILURE_DELAY; - i_array_init(&auth_failures_arr, 128); auth_failures = aqueue_init(&auth_failures_arr.arr); }
--- a/src/auth/auth-request.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth-request.c Tue Jan 27 18:21:53 2009 -0500 @@ -165,7 +165,7 @@ else if (strcmp(key, "original_username") == 0) request->original_username = p_strdup(request->pool, value); else if (strcmp(key, "cert_username") == 0) { - if (request->auth->ssl_username_from_cert) { + if (request->auth->set->ssl_username_from_cert) { /* get username from SSL certificate. it overrides the username given by the auth mechanism. */ request->user = p_strdup(request->pool, value); @@ -521,7 +521,7 @@ request->credentials_scheme, request->private_callback.lookup_credentials); } else { - if (request->auth->verbose_debug_passwords && + if (request->auth->set->debug_passwords && result == PASSDB_RESULT_OK) { auth_request_log_debug(request, "password", "Credentials: %s", @@ -772,10 +772,10 @@ unsigned char *p; char *user; - if (strchr(username, '@') == NULL && - request->auth->default_realm != NULL) { + if (*request->auth->set->default_realm != '\0' && + strchr(username, '@') == NULL) { user = p_strconcat(request->pool, username, "@", - request->auth->default_realm, NULL); + request->auth->set->default_realm, NULL); } else { user = p_strdup(request->pool, username); } @@ -791,7 +791,7 @@ } } - if (request->auth->username_format != NULL) { + if (*request->auth->set->username_format != '\0') { /* username format given, put it through variable expansion. we'll have to temporarily replace request->user to get %u to be the wanted username */ @@ -804,7 +804,7 @@ dest = t_str_new(256); table = auth_request_get_var_expand_table(request, NULL); - var_expand(dest, request->auth->username_format, table); + var_expand(dest, request->auth->set->username_format, table); user = p_strdup(request->pool, str_c(dest)); request->user = old_username; @@ -816,12 +816,12 @@ bool auth_request_set_username(struct auth_request *request, const char *username, const char **error_r) { + const struct auth_settings *set = request->auth->set; const char *p, *login_username = NULL; - if (request->auth->master_user_separator != '\0' && - !request->userdb_lookup) { + if (*set->master_user_separator != '\0' && !request->userdb_lookup) { /* check if the username contains a master user */ - p = strchr(username, request->auth->master_user_separator); + p = strchr(username, *set->master_user_separator); if (p != NULL) { /* it does, set it. */ login_username = t_strdup_until(username, p); @@ -1312,7 +1312,7 @@ if (ret == 0) { auth_request_log_info(request, subsystem, "Password mismatch"); - if (request->auth->verbose_debug_passwords) { + if (request->auth->set->debug_passwords) { auth_request_log_debug(request, subsystem, "%s(%s) != '%s'", scheme, plain_password, @@ -1436,7 +1436,7 @@ { va_list va; - if (!auth_request->auth->verbose_debug) + if (!auth_request->auth->set->debug) return; va_start(va, format); @@ -1452,7 +1452,7 @@ { va_list va; - if (!auth_request->auth->verbose) + if (!auth_request->auth->set->verbose) return; va_start(va, format);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,347 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "hostpid.h" +#include "settings-parser.h" +#include "auth-settings.h" + +#include <stddef.h> + +extern struct setting_parser_info auth_socket_setting_parser_info; +extern struct setting_parser_info auth_setting_parser_info; +extern struct setting_parser_info auth_root_setting_parser_info; + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_socket_unix_settings, name), NULL } + +static struct setting_define auth_socket_client_setting_defines[] = { + DEF(SET_STR, path), + DEF(SET_UINT, mode), + DEF(SET_STR, user), + DEF(SET_STR, group), + + SETTING_DEFINE_LIST_END +}; + +static struct auth_socket_unix_settings auth_socket_client_default_settings = { + MEMBER(path) "auth-client", + MEMBER(mode) 0660, + MEMBER(user) "", + MEMBER(group) "" +}; + +struct setting_parser_info auth_socket_client_setting_parser_info = { + MEMBER(defines) auth_socket_client_setting_defines, + MEMBER(defaults) &auth_socket_client_default_settings, + + MEMBER(parent) &auth_socket_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct auth_socket_unix_settings) +}; + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_socket_unix_settings, name), NULL } + +static struct setting_define auth_socket_master_setting_defines[] = { + DEF(SET_STR, path), + DEF(SET_UINT, mode), + DEF(SET_STR, user), + DEF(SET_STR, group), + + SETTING_DEFINE_LIST_END +}; + +static struct auth_socket_unix_settings auth_socket_master_default_settings = { + MEMBER(path) "auth-master", + MEMBER(mode) 0660, + MEMBER(user) "", + MEMBER(group) "" +}; + +struct setting_parser_info auth_socket_master_setting_parser_info = { + MEMBER(defines) auth_socket_master_setting_defines, + MEMBER(defaults) &auth_socket_master_default_settings, + + MEMBER(parent) &auth_socket_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct auth_socket_unix_settings) +}; + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_socket_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct auth_socket_settings, field), defines } + +static struct setting_define auth_socket_setting_defines[] = { + DEF(SET_STR, type), + + DEFLIST(clients, "client", &auth_socket_client_setting_parser_info), + DEFLIST(masters, "master", &auth_socket_master_setting_parser_info), + + SETTING_DEFINE_LIST_END +}; + +static struct auth_socket_settings auth_socket_default_settings = { + MEMBER(type) "listen" +}; + +struct setting_parser_info auth_socket_setting_parser_info = { + MEMBER(defines) auth_socket_setting_defines, + MEMBER(defaults) &auth_socket_default_settings, + + MEMBER(parent) &auth_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) offsetof(struct auth_socket_settings, type), + MEMBER(struct_size) sizeof(struct auth_socket_settings) +}; + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_passdb_settings, name), NULL } + +static struct setting_define auth_passdb_setting_defines[] = { + DEF(SET_STR, driver), + DEF(SET_STR, args), + DEF(SET_BOOL, deny), + + SETTING_DEFINE_LIST_END +}; + +struct setting_parser_info auth_passdb_setting_parser_info = { + MEMBER(defines) auth_passdb_setting_defines, + MEMBER(defaults) NULL, + + MEMBER(parent) &auth_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) offsetof(struct auth_passdb_settings, driver), + MEMBER(struct_size) sizeof(struct auth_passdb_settings) +}; + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_userdb_settings, name), NULL } + +static struct setting_define auth_userdb_setting_defines[] = { + DEF(SET_STR, driver), + DEF(SET_STR, args), + + SETTING_DEFINE_LIST_END +}; + +struct setting_parser_info auth_userdb_setting_parser_info = { + MEMBER(defines) auth_userdb_setting_defines, + MEMBER(defaults) NULL, + + MEMBER(parent) &auth_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) offsetof(struct auth_userdb_settings, driver), + MEMBER(struct_size) sizeof(struct auth_userdb_settings) +}; + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct auth_settings, field), defines } + +static struct setting_define auth_setting_defines[] = { + DEF(SET_STR, name), + DEF(SET_STR, mechanisms), + DEF(SET_STR, realms), + DEF(SET_STR, default_realm), + DEF(SET_UINT, cache_size), + DEF(SET_UINT, cache_ttl), + DEF(SET_UINT, cache_negative_ttl), + DEF(SET_STR, username_chars), + DEF(SET_STR, username_translation), + DEF(SET_STR, username_format), + DEF(SET_STR, master_user_separator), + DEF(SET_STR, anonymous_username), + DEF(SET_STR, krb5_keytab), + DEF(SET_STR, gssapi_hostname), + DEF(SET_STR, winbind_helper_path), + DEF(SET_UINT, failure_delay), + + DEF(SET_BOOL, verbose), + DEF(SET_BOOL, debug), + DEF(SET_BOOL, debug_passwords), + DEF(SET_BOOL, ssl_require_client_cert), + DEF(SET_BOOL, ssl_username_from_cert), + DEF(SET_BOOL, use_winbind), + + DEF(SET_UINT, worker_max_count), + + DEFLIST(sockets, "socket", &auth_socket_setting_parser_info), + DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info), + DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info), + + SETTING_DEFINE_LIST_END +}; + +static struct auth_settings auth_default_settings = { + MEMBER(name) NULL, + MEMBER(root) NULL, + + MEMBER(mechanisms) "plain", + MEMBER(realms) "", + MEMBER(default_realm) "", + MEMBER(cache_size) 0, + MEMBER(cache_ttl) 3600, + MEMBER(cache_negative_ttl) 0, + MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@", + MEMBER(username_translation) "", + MEMBER(username_format) "", + MEMBER(master_user_separator) "", + MEMBER(anonymous_username) "anonymous", + MEMBER(krb5_keytab) "", + MEMBER(gssapi_hostname) "", + MEMBER(winbind_helper_path) "/usr/bin/ntlm_auth", + MEMBER(failure_delay) 2, + + MEMBER(verbose) FALSE, + MEMBER(debug) FALSE, + MEMBER(debug_passwords) FALSE, + MEMBER(ssl_require_client_cert) FALSE, + MEMBER(ssl_username_from_cert) FALSE, + MEMBER(use_winbind) FALSE, + + MEMBER(worker_max_count) 30, + + MEMBER(sockets) ARRAY_INIT, + MEMBER(passdbs) ARRAY_INIT, + MEMBER(userdbs) ARRAY_INIT +}; + +struct setting_parser_info auth_setting_parser_info = { + MEMBER(defines) auth_setting_defines, + MEMBER(defaults) &auth_default_settings, + + MEMBER(parent) &auth_root_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) offsetof(struct auth_settings, root), + MEMBER(type_offset) offsetof(struct auth_settings, name), + MEMBER(struct_size) sizeof(struct auth_settings) +}; + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct auth_root_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct auth_root_settings, field), defines } + +static struct setting_define auth_root_setting_defines[] = { + DEF(SET_STR, base_dir), + DEFLIST(auths, "auth", &auth_setting_parser_info), + + SETTING_DEFINE_LIST_END +}; + +static struct auth_root_settings auth_root_default_settings = { + MEMBER(base_dir) PKG_RUNDIR, + MEMBER(auths) ARRAY_INIT +}; + +struct setting_parser_info auth_root_setting_parser_info = { + MEMBER(defines) auth_root_setting_defines, + MEMBER(defaults) &auth_root_default_settings, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct auth_root_settings) +}; + +static pool_t settings_pool = NULL; + +static void fix_base_path(struct auth_settings *set, const char **str) +{ + if (*str != NULL && **str != '\0' && **str != '/') { + *str = p_strconcat(settings_pool, + set->root->base_dir, "/", *str, NULL); + } +} + +static void auth_settings_check(struct auth_settings *set) +{ + struct auth_socket_unix_settings *const *u; + struct auth_socket_settings *const *sockets; + unsigned int i, j, count, count2; + + if (!array_is_created(&set->sockets)) + return; + + sockets = array_get(&set->sockets, &count); + for (i = 0; i < count; i++) { + if (array_is_created(&sockets[i]->masters)) { + u = array_get(&sockets[i]->masters, &count2); + for (j = 0; j < count2; j++) + fix_base_path(set, &u[j]->path); + } + if (array_is_created(&sockets[i]->clients)) { + u = array_get(&sockets[i]->clients, &count2); + for (j = 0; j < count2; j++) + fix_base_path(set, &u[j]->path); + } + } +} + +struct auth_settings *auth_settings_read(const char *name) +{ + struct setting_parser_context *parser; + struct auth_root_settings *set; + struct auth_settings *const *auths; + unsigned int i, count; + + if (settings_pool == NULL) + settings_pool = pool_alloconly_create("auth settings", 1024); + else + p_clear(settings_pool); + + parser = settings_parser_init(settings_pool, + &auth_root_setting_parser_info, + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); + + auth_default_settings.gssapi_hostname = my_hostname; + + if (settings_parse_environ(parser) < 0) { + i_fatal("Error reading configuration: %s", + settings_parser_get_error(parser)); + } + + set = settings_parser_get(parser); + settings_parser_deinit(&parser); + + if (array_is_created(&set->auths)) { + auths = array_get(&set->auths, &count); + for (i = 0; i < count; i++) { + if (strcmp(auths[i]->name, name) == 0) { + auth_settings_check(auths[i]); + return auths[i]; + } + } + } + i_fatal("Error reading configuration: No auth section: %s", name); + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,71 @@ +#ifndef AUTH_SETTINGS_H +#define AUTH_SETTINGS_H + +struct auth_socket_unix_settings { + const char *path; + unsigned int mode; + const char *user; + const char *group; +}; + +struct auth_socket_settings { + const char *type; + + ARRAY_DEFINE(clients, struct auth_socket_unix_settings *); + ARRAY_DEFINE(masters, struct auth_socket_unix_settings *); +}; + +struct auth_passdb_settings { + const char *driver; + const char *args; + bool deny; + bool pass; + bool master; +}; + +struct auth_userdb_settings { + const char *driver; + const char *args; +}; + +struct auth_settings { + const char *name; + struct auth_root_settings *root; + + const char *mechanisms; + const char *realms; + const char *default_realm; + unsigned int cache_size; + unsigned int cache_ttl; + unsigned int cache_negative_ttl; + const char *username_chars; + const char *username_translation; + const char *username_format; + const char *master_user_separator; + const char *anonymous_username; + const char *krb5_keytab; + const char *gssapi_hostname; + const char *winbind_helper_path; + unsigned int failure_delay; + + bool verbose, debug, debug_passwords; + bool ssl_require_client_cert; + bool ssl_username_from_cert; + bool use_winbind; + + unsigned int worker_max_count; + + ARRAY_DEFINE(sockets, struct auth_socket_settings *); + ARRAY_DEFINE(passdbs, struct auth_passdb_settings *); + ARRAY_DEFINE(userdbs, struct auth_userdb_settings *); +}; + +struct auth_root_settings { + const char *base_dir; + + ARRAY_DEFINE(auths, struct auth_settings *); +}; + +struct auth_settings *auth_settings_read(const char *name); + +#endif
--- a/src/auth/auth-worker-server.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth-worker-server.c Tue Jan 27 18:21:53 2009 -0500 @@ -28,6 +28,7 @@ }; struct auth_worker_connection { + struct auth *auth; int fd; struct io *io; @@ -43,8 +44,6 @@ static ARRAY_DEFINE(connections, struct auth_worker_connection *) = ARRAY_INIT; static unsigned int idle_count; -static unsigned int auth_workers_max; - static ARRAY_DEFINE(worker_request_array, struct auth_worker_request *); static struct aqueue *worker_request_queue; static time_t auth_worker_last_warn; @@ -120,12 +119,12 @@ auth_worker_request_send(conn, request); } -static struct auth_worker_connection *auth_worker_create(void) +static struct auth_worker_connection *auth_worker_create(struct auth *auth) { struct auth_worker_connection *conn; int fd, try; - if (array_count(&connections) >= auth_workers_max) + if (array_count(&connections) >= auth->set->worker_max_count) return NULL; for (try = 0;; try++) { @@ -154,6 +153,7 @@ } conn = i_new(struct auth_worker_connection, 1); + conn->auth = auth; conn->fd = fd; conn->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH, FALSE); @@ -172,6 +172,7 @@ const char *reason, bool restart) { struct auth_worker_connection *conn = *_conn; + struct auth *auth = conn->auth; struct auth_worker_connection **connp; unsigned int i, count; @@ -208,7 +209,7 @@ i_free(conn); if (idle_count == 0 && restart) { - conn = auth_worker_create(); + conn = auth_worker_create(auth); if (conn != NULL) auth_worker_request_send_next(conn); } @@ -323,7 +324,7 @@ conn = auth_worker_find_free(); if (conn == NULL) { /* no free connections, create a new one */ - conn = auth_worker_create(); + conn = auth_worker_create(auth_request->auth); } } if (conn != NULL) @@ -334,7 +335,7 @@ } } -void auth_worker_server_init(void) +void auth_worker_server_init(struct auth *auth) { const char *env; @@ -348,16 +349,11 @@ i_fatal("AUTH_WORKER_PATH environment not set"); worker_socket_path = i_strdup(env); - env = getenv("AUTH_WORKER_MAX_COUNT"); - if (env == NULL) - i_fatal("AUTH_WORKER_MAX_COUNT environment not set"); - auth_workers_max = atoi(env); - i_array_init(&worker_request_array, 128); worker_request_queue = aqueue_init(&worker_request_array.arr); i_array_init(&connections, 16); - (void)auth_worker_create(); + (void)auth_worker_create(auth); } void auth_worker_server_deinit(void)
--- a/src/auth/auth-worker-server.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth-worker-server.h Tue Jan 27 18:21:53 2009 -0500 @@ -11,7 +11,7 @@ struct auth_stream_reply *data, auth_worker_callback_t *callback); -void auth_worker_server_init(void); +void auth_worker_server_init(struct auth *auth); void auth_worker_server_deinit(void); #endif
--- a/src/auth/auth.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth.c Tue Jan 27 18:21:53 2009 -0500 @@ -2,9 +2,9 @@ #include "common.h" #include "network.h" -#include "buffer.h" +#include "array.h" #include "str.h" -#include "hostpid.h" +#include "env-util.h" #include "mech.h" #include "userdb.h" #include "passdb.h" @@ -15,72 +15,65 @@ #include <stdlib.h> #include <unistd.h> -struct auth *auth_preinit(void) +struct auth_userdb_settings userdb_dummy_set = { + MEMBER(driver) "static", + MEMBER(args) "" +}; + +struct auth *auth_preinit(struct auth_settings *set) { + struct auth_passdb_settings *const *passdbs; + struct auth_userdb_settings *const *userdbs; struct auth *auth; - struct auth_passdb *auth_passdb, **passdb_p, **masterdb_p; - const char *driver, *args; pool_t pool; - unsigned int i; + unsigned int i, count, db_count, passdb_count, last_passdb = 0; pool = pool_alloconly_create("auth", 2048); auth = p_new(pool, struct auth, 1); auth->pool = pool; - - auth->verbose_debug_passwords = - getenv("VERBOSE_DEBUG_PASSWORDS") != NULL; - auth->verbose_debug = getenv("VERBOSE_DEBUG") != NULL || - auth->verbose_debug_passwords; - auth->verbose = getenv("VERBOSE") != NULL || auth->verbose_debug; + auth->set = set; - passdb_p = &auth->passdbs; - masterdb_p = &auth->masterdbs; - auth_passdb = NULL; - for (i = 1; ; i++) { - driver = getenv(t_strdup_printf("PASSDB_%u_DRIVER", i)); - if (driver == NULL) - break; + if (array_is_created(&set->passdbs)) + passdbs = array_get(&set->passdbs, &db_count); + else { + passdbs = NULL; + db_count = 0; + } - args = getenv(t_strdup_printf("PASSDB_%u_ARGS", i)); - auth_passdb = passdb_preinit(auth, driver, args, i); + /* initialize passdbs first and count them */ + for (passdb_count = 0, i = 0; i < db_count; i++) { + if (passdbs[i]->master) + continue; - auth_passdb->deny = - getenv(t_strdup_printf("PASSDB_%u_DENY", i)) != NULL; - auth_passdb->pass = - getenv(t_strdup_printf("PASSDB_%u_PASS", i)) != NULL; + passdb_preinit(auth, passdbs[i]); + passdb_count++; + last_passdb = i; + } + if (passdb_count != 0 && passdbs[last_passdb]->pass) + i_fatal("Last passdb can't have pass=yes"); - if (getenv(t_strdup_printf("PASSDB_%u_MASTER", i)) == NULL) { - *passdb_p = auth_passdb; - passdb_p = &auth_passdb->next; - } else { - if (auth_passdb->deny) - i_fatal("Master passdb can't have deny=yes"); + for (passdb_count = 0, i = 0; i < db_count; i++) { + if (!passdbs[i]->master) + continue; - *masterdb_p = auth_passdb; - masterdb_p = &auth_passdb->next; - } - } - if (auth_passdb != NULL && auth_passdb->pass) { - if (masterdb_p != &auth_passdb->next) - i_fatal("Last passdb can't have pass=yes"); - else if (auth->passdbs == NULL) { + if (passdbs[i]->deny) + i_fatal("Master passdb can't have deny=yes"); + if (passdbs[i]->pass && passdb_count == 0) { i_fatal("Master passdb can't have pass=yes " "if there are no passdbs"); } + passdb_preinit(auth, passdbs[i]); } - for (i = 1; ; i++) { - driver = getenv(t_strdup_printf("USERDB_%u_DRIVER", i)); - if (driver == NULL) - break; - - args = getenv(t_strdup_printf("USERDB_%u_ARGS", i)); - userdb_preinit(auth, driver, args); + if (array_is_created(&set->userdbs)) { + userdbs = array_get(&set->userdbs, &count); + for (i = 0; i < count; i++) + userdb_preinit(auth, userdbs[i]); } if (auth->userdbs == NULL) { /* use a dummy userdb static. */ - userdb_preinit(auth, "static", ""); + userdb_preinit(auth, &userdb_dummy_set); } return auth; } @@ -209,7 +202,7 @@ struct auth_userdb *userdb; const struct mech_module *mech; const char *const *mechanisms; - const char *env; + const char *p; for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next) passdb_init(passdb); @@ -219,26 +212,17 @@ userdb_init(userdb); /* caching is handled only by the main auth process */ if (!worker) - passdb_cache_init(); + passdb_cache_init(auth->set); auth->mech_handshake = str_new(auth->pool, 512); - auth->anonymous_username = getenv("ANONYMOUS_USERNAME"); - if (auth->anonymous_username != NULL && - *auth->anonymous_username == '\0') - auth->anonymous_username = NULL; - /* register wanted mechanisms */ - env = getenv("MECHANISMS"); - if (env == NULL) - i_fatal("MECHANISMS environment is unset"); - - mechanisms = t_strsplit_spaces(env, " "); + mechanisms = t_strsplit_spaces(auth->set->mechanisms, " "); while (*mechanisms != NULL) { if (strcasecmp(*mechanisms, "ANONYMOUS") == 0) { - if (auth->anonymous_username == NULL) { + if (*auth->set->anonymous_username == '\0') { i_fatal("ANONYMOUS listed in mechanisms, " - "but anonymous_username not given"); + "but anonymous_username not set"); } } mech = mech_module_find(*mechanisms); @@ -255,48 +239,22 @@ i_fatal("No authentication mechanisms configured"); auth_mech_list_verify_passdb(auth); - env = getenv("REALMS"); - if (env == NULL) - env = ""; - auth->auth_realms = p_strsplit_spaces(auth->pool, env, " "); + auth->auth_realms = (const char *const *) + p_strsplit_spaces(auth->pool, auth->set->realms, " "); - env = getenv("DEFAULT_REALM"); - if (env != NULL && *env != '\0') - auth->default_realm = env; - - env = getenv("USERNAME_CHARS"); - if (env == NULL || *env == '\0') { + if (*auth->set->username_chars == '\0') { /* all chars are allowed */ memset(auth->username_chars, 1, sizeof(auth->username_chars)); } else { - for (; *env != '\0'; env++) - auth->username_chars[(int)(uint8_t)*env] = 1; - } - - env = getenv("USERNAME_TRANSLATION"); - if (env != NULL) { - for (; *env != '\0' && env[1] != '\0'; env += 2) - auth->username_translation[(int)(uint8_t)*env] = env[1]; + for (p = auth->set->username_chars; *p != '\0'; p++) + auth->username_chars[(int)(uint8_t)*p] = 1; } - env = getenv("USERNAME_FORMAT"); - if (env != NULL && *env != '\0') - auth->username_format = env; - - env = getenv("GSSAPI_HOSTNAME"); - if (env != NULL && *env != '\0') - auth->gssapi_hostname = env; - else - auth->gssapi_hostname = my_hostname; - - env = getenv("MASTER_USER_SEPARATOR"); - if (env != NULL) - auth->master_user_separator = env[0]; - - auth->ssl_require_client_cert = - getenv("SSL_REQUIRE_CLIENT_CERT") != NULL; - auth->ssl_username_from_cert = - getenv("SSL_USERNAME_FROM_CERT") != NULL; + if (*auth->set->username_translation != '\0') { + p = auth->set->username_translation; + for (; *p != '\0' && p[1] != '\0'; p += 2) + auth->username_translation[(int)(uint8_t)*p] = p[1]; + } } void auth_deinit(struct auth **_auth)
--- a/src/auth/auth.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/auth.h Tue Jan 27 18:21:53 2009 -0500 @@ -1,6 +1,8 @@ #ifndef AUTH_H #define AUTH_H +#include "auth-settings.h" + #define PASSWORD_HIDDEN_STR "<hidden>" struct auth_passdb { @@ -29,6 +31,7 @@ struct auth { pool_t pool; + const struct auth_settings *set; struct mech_module_list *mech_modules; buffer_t *mech_handshake; @@ -37,23 +40,14 @@ struct auth_passdb *passdbs; struct auth_userdb *userdbs; - char *const *auth_realms; - const char *default_realm; - const char *anonymous_username; - const char *username_format; - const char *gssapi_hostname; + const char *const *auth_realms; char username_chars[256]; char username_translation[256]; - char master_user_separator; - bool ssl_require_client_cert; - bool ssl_username_from_cert; - - bool verbose, verbose_debug, verbose_debug_passwords; }; const string_t *auth_mechanisms_get_list(struct auth *auth); -struct auth *auth_preinit(void); +struct auth *auth_preinit(struct auth_settings *set); void auth_init(struct auth *auth); void auth_deinit(struct auth **auth);
--- a/src/auth/db-ldap.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/db-ldap.c Tue Jan 27 18:21:53 2009 -0500 @@ -997,7 +997,7 @@ ctx->static_attrs = t_strsplit(str_c(str), ","); } - if (auth_request->auth->verbose_debug) + if (auth_request->auth->set->debug) ctx->debug = t_str_new(256); ctx->attr = ldap_first_attribute(conn->ld, entry, &ctx->ber); @@ -1069,7 +1069,7 @@ if (ctx->debug != NULL) { if (!first) str_append_c(ctx->debug, '/'); - if (ctx->auth_request->auth->verbose_debug_passwords || + if (ctx->auth_request->auth->set->debug_passwords || strcmp(ctx->name, "password") != 0) str_append(ctx->debug, ctx->value); else
--- a/src/auth/main.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/main.c Tue Jan 27 18:21:53 2009 -0500 @@ -175,7 +175,7 @@ static void drop_privileges(void) { - const char *version; + const char *version, *name; version = getenv("DOVECOT_VERSION"); if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) { @@ -191,6 +191,9 @@ "it standalone, you'll need to set AUTH_* " "environment variables (AUTH_1 isn't set)."); } + name = getenv("AUTH_NAME"); + if (name == NULL) + i_fatal("Missing AUTH_NAME environment"); open_logfile(); @@ -207,7 +210,7 @@ userdbs_init(); modules = module_dir_load(AUTH_MODULE_DIR, NULL, TRUE, version); module_dir_init(modules); - auth = auth_preinit(); + auth = auth_preinit(auth_settings_read(name)); auth_master_listeners_init(); if (!worker) add_extra_listeners(); @@ -233,7 +236,7 @@ lib_signals_ignore(SIGUSR2, TRUE); child_wait_init(); - mech_init(); + mech_init(auth->set); password_schemes_init(); auth_init(auth); auth_request_handler_init(); @@ -283,11 +286,11 @@ auth_worker_server_deinit(); auth_master_listeners_deinit(); - auth_deinit(&auth); module_dir_unload(&modules); userdbs_deinit(); passdbs_deinit(); - mech_deinit(); + mech_deinit(auth->set); + auth_deinit(&auth); password_schemes_deinit(); sql_drivers_deinit();
--- a/src/auth/mech-anonymous.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech-anonymous.c Tue Jan 27 18:21:53 2009 -0500 @@ -7,9 +7,9 @@ mech_anonymous_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { - i_assert(request->auth->anonymous_username != NULL); + i_assert(*request->auth->set->anonymous_username != '\0'); - if (request->auth->verbose) { + if (request->auth->set->verbose) { /* temporarily set the user to the one that was given, so that the log message goes right */ request->user = @@ -18,7 +18,7 @@ } request->user = p_strdup(request->pool, - request->auth->anonymous_username); + request->auth->set->anonymous_username); auth_request_success(request, NULL, 0); }
--- a/src/auth/mech-digest-md5.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech-digest-md5.c Tue Jan 27 18:21:53 2009 -0500 @@ -60,7 +60,7 @@ struct auth *auth = request->auth_request.auth; buffer_t *buf; string_t *str; - char *const *tmp; + const char *const *tmp; unsigned char nonce[16]; int i; bool first_qop; @@ -220,7 +220,7 @@ static bool verify_realm(struct digest_auth_request *request, const char *realm) { - char *const *tmp; + const char *const *tmp; if (*realm == '\0') return TRUE;
--- a/src/auth/mech-gssapi.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech-gssapi.c Tue Jan 27 18:21:53 2009 -0500 @@ -13,13 +13,13 @@ */ #include "common.h" -#include "mech.h" -#include "passdb.h" +#include "env-util.h" #include "str.h" #include "str-sanitize.h" -#include "buffer.h" #include "hex-binary.h" #include "safe-memset.h" +#include "mech.h" +#include "passdb.h" #include <stdlib.h> @@ -98,12 +98,13 @@ } while (message_context != 0); } -static void mech_gssapi_initialize(void) +static void mech_gssapi_initialize(struct auth *auth) { - const char *path; + const char *path = auth->set->krb5_keytab; - path = getenv("KRB5_KTNAME"); - if (path != NULL) { + if (*path != '\0') { + /* environment may be used by Kerberos 5 library directly */ + env_put(t_strconcat("KRB5_KTNAME=", path, NULL)); #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY gsskrb5_register_acceptor_identity(path); #elif defined (HAVE_KRB5_GSS_REGISTER_ACCEPTOR_IDENTITY) @@ -117,11 +118,6 @@ struct gssapi_auth_request *request; pool_t pool; - if (!gssapi_initialized) { - gssapi_initialized = TRUE; - mech_gssapi_initialize(); - } - pool = pool_alloconly_create("gssapi_auth_request", 1024); request = p_new(pool, struct gssapi_auth_request, 1); request->pool = pool; @@ -141,7 +137,12 @@ gss_name_t gss_principal; const char *service_name; - if (strcmp(request->auth->gssapi_hostname, "$ALL") == 0) { + if (!gssapi_initialized) { + gssapi_initialized = TRUE; + mech_gssapi_initialize(request->auth); + } + + if (strcmp(request->auth->set->gssapi_hostname, "$ALL") == 0) { auth_request_log_info(request, "gssapi", "Using all keytab entries"); *ret = GSS_C_NO_CREDENTIAL; @@ -159,7 +160,7 @@ principal_name = t_str_new(128); str_append(principal_name, service_name); str_append_c(principal_name, '@'); - str_append(principal_name, request->auth->gssapi_hostname); + str_append(principal_name, request->auth->set->gssapi_hostname); auth_request_log_info(request, "gssapi", "Obtaining credentials for %s", str_c(principal_name));
--- a/src/auth/mech-rpa.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech-rpa.c Tue Jan 27 18:21:53 2009 -0500 @@ -238,18 +238,17 @@ static bool rpa_verify_realm(struct rpa_auth_request *request, const char *realm) { + const struct auth *auth = request->auth_request.auth; const char *default_realm; - char *const *tmp; + const char *const *tmp; - tmp = request->auth_request.auth->auth_realms; - for (; *tmp != NULL; tmp++) { + for (tmp = auth->auth_realms; *tmp != NULL; tmp++) { if (strcasecmp(realm, *tmp) == 0) return TRUE; } - default_realm = request->auth_request.auth->default_realm != NULL ? - request->auth_request.auth->default_realm : - my_hostname; + default_realm = *auth->set->default_realm != '\0' ? + auth->set->default_realm : my_hostname; return strcasecmp(realm, default_realm) == 0; } @@ -353,7 +352,7 @@ string_t *realms; buffer_t *buf; unsigned char timestamp[RPA_TIMESTAMP_LEN / 2]; - char *const *tmp; + const char *const *tmp; realms = t_str_new(64); for (tmp = auth->auth_realms; *tmp != NULL; tmp++) { @@ -361,8 +360,8 @@ } if (str_len(realms) == 0) { - rpa_add_realm(realms, auth->default_realm != NULL ? - auth->default_realm : my_hostname, + rpa_add_realm(realms, *auth->set->default_realm != '\0' ? + auth->set->default_realm : my_hostname, request->auth_request.service); }
--- a/src/auth/mech-winbind.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech-winbind.c Tue Jan 27 18:21:53 2009 -0500 @@ -95,7 +95,8 @@ winbind_wait_pid(&winbind_spnego_context); } -static void winbind_helper_connect(struct winbind_helper *winbind) +static void +winbind_helper_connect(struct auth *auth, struct winbind_helper *winbind) { int infd[2], outfd[2]; pid_t pid; @@ -122,7 +123,7 @@ if (pid == 0) { /* child */ - const char *helper_path, *args[3]; + const char *args[3]; (void)close(infd[0]); (void)close(outfd[1]); @@ -131,11 +132,7 @@ dup2(infd[1], STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); - helper_path = getenv("WINBIND_HELPER_PATH"); - if (helper_path == NULL) - helper_path = DEFAULT_WINBIND_HELPER_PATH; - - args[0] = helper_path; + args[0] = auth->set->winbind_helper_path; args[1] = winbind->param; args[2] = NULL; execv(args[0], (void *)args); @@ -281,6 +278,17 @@ } static void +mech_winbind_auth_initial(struct auth_request *auth_request, + const unsigned char *data, size_t data_size) +{ + struct winbind_auth_request *request = + (struct winbind_auth_request *)auth_request; + + winbind_helper_connect(auth_request->auth, request->winbind); + mech_generic_auth_initial(auth_request, data, data_size); +} + +static void mech_winbind_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size) { @@ -306,7 +314,6 @@ request->auth_request.pool = pool; request->winbind = winbind; - winbind_helper_connect(request->winbind); return &request->auth_request; } @@ -339,7 +346,7 @@ MEMBER(passdb_need) MECH_PASSDB_NEED_NOTHING, mech_winbind_spnego_auth_new, - mech_generic_auth_initial, + mech_winbind_auth_initial, mech_winbind_auth_continue, mech_generic_auth_free };
--- a/src/auth/mech.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech.c Tue Jan 27 18:21:53 2009 -0500 @@ -81,14 +81,14 @@ extern const struct mech_module mech_winbind_ntlm; extern const struct mech_module mech_winbind_spnego; -void mech_init(void) +void mech_init(const struct auth_settings *set) { mech_register_module(&mech_plain); mech_register_module(&mech_login); mech_register_module(&mech_apop); mech_register_module(&mech_cram_md5); mech_register_module(&mech_digest_md5); - if (getenv("USE_WINBIND") != NULL) { + if (set->use_winbind) { mech_register_module(&mech_winbind_ntlm); mech_register_module(&mech_winbind_spnego); } else { @@ -106,14 +106,14 @@ #endif } -void mech_deinit(void) +void mech_deinit(const struct auth_settings *set) { mech_unregister_module(&mech_plain); mech_unregister_module(&mech_login); mech_unregister_module(&mech_apop); mech_unregister_module(&mech_cram_md5); mech_unregister_module(&mech_digest_md5); - if (getenv("NTLM_USE_WINBIND") != NULL) { + if (set->use_winbind) { mech_unregister_module(&mech_winbind_ntlm); mech_unregister_module(&mech_winbind_spnego); } else {
--- a/src/auth/mech.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/mech.h Tue Jan 27 18:21:53 2009 -0500 @@ -63,7 +63,7 @@ const unsigned char *data, size_t data_size); void mech_generic_auth_free(struct auth_request *request); -void mech_init(void); -void mech_deinit(void); +void mech_init(const struct auth_settings *set); +void mech_deinit(const struct auth_settings *set); #endif
--- a/src/auth/passdb-cache.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/passdb-cache.c Tue Jan 27 18:21:53 2009 -0500 @@ -14,7 +14,7 @@ { const char *p; - if (!request->auth->verbose_debug_passwords && + if (!request->auth->set->debug_passwords && *value != '\0' && *value != '\t') { /* hide the password */ p = strchr(value, '\t'); @@ -125,31 +125,13 @@ return TRUE; } -void passdb_cache_init(void) +void passdb_cache_init(const struct auth_settings *set) { - const char *env; - size_t max_size; - unsigned int cache_ttl, neg_cache_ttl; - - env = getenv("CACHE_SIZE"); - if (env == NULL) + if (set->cache_size == 0 || set->cache_ttl == 0) return; - max_size = (size_t)strtoul(env, NULL, 10) * 1024; - if (max_size == 0) - return; - - env = getenv("CACHE_TTL"); - if (env == NULL) - return; - - cache_ttl = (unsigned int)strtoul(env, NULL, 10); - if (cache_ttl == 0) - return; - - env = getenv("CACHE_NEGATIVE_TTL"); - neg_cache_ttl = env == NULL ? 0 : (unsigned int)strtoul(env, NULL, 10); - passdb_cache = auth_cache_new(max_size, cache_ttl, neg_cache_ttl); + passdb_cache = auth_cache_new(set->cache_size * 1024UL, set->cache_ttl, + set->cache_negative_ttl); } void passdb_cache_deinit(void)
--- a/src/auth/passdb-cache.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/passdb-cache.h Tue Jan 27 18:21:53 2009 -0500 @@ -14,7 +14,7 @@ enum passdb_result *result_r, bool use_expired); -void passdb_cache_init(void); +void passdb_cache_init(const struct auth_settings *set); void passdb_cache_deinit(void); #endif
--- a/src/auth/passdb-passwd-file.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/passdb-passwd-file.c Tue Jan 27 18:21:53 2009 -0500 @@ -147,7 +147,7 @@ struct passwd_file_passdb_module, 1); module->auth = auth_passdb->auth; module->pwf = db_passwd_file_init(args, format, FALSE, - module->auth->verbose_debug); + module->auth->set->debug); if (!module->pwf->vars) module->module.cache_key = format;
--- a/src/auth/passdb.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/passdb.c Tue Jan 27 18:21:53 2009 -0500 @@ -81,7 +81,7 @@ const char *error = t_strdup_printf( "Requested %s scheme, but we have only %s", wanted_scheme, input_scheme); - if (auth_request->auth->verbose_debug_passwords) { + if (auth_request->auth->set->debug_passwords) { error = t_strdup_printf("%s (input: %s)", error, input); } @@ -92,7 +92,7 @@ /* we can generate anything out of plaintext passwords */ plaintext = t_strndup(*credentials_r, *size_r); - if (auth_request->auth->verbose_debug_passwords) { + if (auth_request->auth->set->debug_passwords) { auth_request_log_info(auth_request, "password", "Generating %s from user %s password %s", wanted_scheme, auth_request->original_username, @@ -131,31 +131,34 @@ callback(result, credentials, size, auth_request); } -struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver, - const char *args, unsigned int id) +struct auth_passdb * +passdb_preinit(struct auth *auth, struct auth_passdb_settings *set) { struct passdb_module_interface *iface; - struct auth_passdb *auth_passdb; - - if (args == NULL) args = ""; + struct auth_passdb *auth_passdb, **dest; auth_passdb = p_new(auth->pool, struct auth_passdb, 1); auth_passdb->auth = auth; - auth_passdb->args = p_strdup(auth->pool, args); - auth_passdb->id = id; + auth_passdb->args = set->args == NULL ? "" : + p_strdup(auth->pool, set->args); + auth_passdb->deny = set->deny; - iface = passdb_interface_find(driver); + for (dest = &auth->passdbs; *dest != NULL; dest = &(*dest)->next) + auth_passdb->id++; + *dest = auth_passdb; + + iface = passdb_interface_find(set->driver); if (iface == NULL) - i_fatal("Unknown passdb driver '%s'", driver); + i_fatal("Unknown passdb driver '%s'", set->driver); if (iface->verify_plain == NULL) { i_fatal("Support not compiled in for passdb driver '%s'", - driver); + set->driver); } if (iface->preinit == NULL && iface->init == NULL && *auth_passdb->args != '\0') { i_fatal("passdb %s: No args are supported: %s", - driver, auth_passdb->args); + set->driver, auth_passdb->args); } if (iface->preinit == NULL) { @@ -179,7 +182,7 @@ if (passdb->passdb->blocking && !worker) { /* blocking passdb - we need an auth server */ - auth_worker_server_init(); + auth_worker_server_init(passdb->auth); } }
--- a/src/auth/passdb.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/passdb.h Tue Jan 27 18:21:53 2009 -0500 @@ -80,8 +80,9 @@ lookup_credentials_callback_t *callback, struct auth_request *auth_request); -struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver, - const char *args, unsigned int id); +struct auth_passdb * + passdb_preinit(struct auth *auth, struct auth_passdb_settings *set); + void passdb_init(struct auth_passdb *passdb); void passdb_deinit(struct auth_passdb *passdb);
--- a/src/auth/userdb-passwd-file.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/userdb-passwd-file.c Tue Jan 27 18:21:53 2009 -0500 @@ -97,7 +97,7 @@ struct passwd_file_userdb_module, 1); module->auth = auth_userdb->auth; module->pwf = db_passwd_file_init(args, format, TRUE, - module->auth->verbose_debug); + module->auth->set->debug); if (!module->pwf->vars) module->module.cache_key = PASSWD_FILE_CACHE_KEY;
--- a/src/auth/userdb-prefetch.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/userdb-prefetch.c Tue Jan 27 18:21:53 2009 -0500 @@ -26,7 +26,7 @@ "passdb didn't return userdb entries"); } } else if (!auth_request->userdb_lookup || - auth_request->auth->verbose_debug) { + auth_request->auth->set->debug) { /* more userdbs, they may know the user */ auth_request_log_info(auth_request, "prefetch", "passdb didn't return userdb entries, "
--- a/src/auth/userdb.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/userdb.c Tue Jan 27 18:21:53 2009 -0500 @@ -100,33 +100,32 @@ return gr->gr_gid; } -void userdb_preinit(struct auth *auth, const char *driver, const char *args) +void userdb_preinit(struct auth *auth, struct auth_userdb_settings *set) { struct userdb_module_interface *iface; struct auth_userdb *auth_userdb, **dest; - if (args == NULL) args = ""; - auth_userdb = p_new(auth->pool, struct auth_userdb, 1); auth_userdb->auth = auth; - auth_userdb->args = p_strdup(auth->pool, args); + auth_userdb->args = set->args == NULL ? "" : + p_strdup(auth->pool, set->args); for (dest = &auth->userdbs; *dest != NULL; dest = &(*dest)->next) auth_userdb->num++; *dest = auth_userdb; - iface = userdb_interface_find(driver); + iface = userdb_interface_find(set->driver); if (iface == NULL) - i_fatal("Unknown userdb driver '%s'", driver); + i_fatal("Unknown userdb driver '%s'", set->driver); if (iface->lookup == NULL) { i_fatal("Support not compiled in for userdb driver '%s'", - driver); + set->driver); } if (iface->preinit == NULL && iface->init == NULL && *auth_userdb->args != '\0') { i_fatal("userdb %s: No args are supported: %s", - driver, auth_userdb->args); + set->driver, auth_userdb->args); } if (iface->preinit == NULL) { @@ -146,7 +145,7 @@ if (userdb->userdb->blocking && !worker) { /* blocking userdb - we need an auth server */ - auth_worker_server_init(); + auth_worker_server_init(userdb->auth); } }
--- a/src/auth/userdb.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/auth/userdb.h Tue Jan 27 18:21:53 2009 -0500 @@ -41,7 +41,7 @@ uid_t userdb_parse_uid(struct auth_request *request, const char *str); gid_t userdb_parse_gid(struct auth_request *request, const char *str); -void userdb_preinit(struct auth *auth, const char *driver, const char *args); +void userdb_preinit(struct auth *auth, struct auth_userdb_settings *set); void userdb_init(struct auth_userdb *userdb); void userdb_deinit(struct auth_userdb *userdb);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,33 @@ +bin_PROGRAMS = doveconf + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DPKG_RUNDIR=\""$(rundir)"\" \ + -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ + -DMODULEDIR=\""$(moduledir)"\" \ + -DSSLDIR=\""$(ssldir)\"" + +doveconf_LDADD = \ + ../lib-settings/libsettings.a \ + ../lib/liblib.a \ + $(RAND_LIBS) + +doveconf_SOURCES = \ + all-settings.c \ + config-connection.c \ + config-parser.c \ + main.c + +noinst_HEADERS = \ + all-settings.h \ + common.h \ + config-connection.h \ + config-parser.h + +all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl + $(top_srcdir)/src/config/settings-get.pl $(SETTING_FILES) > all-settings.c || rm -f all-settings.c + +EXTRA_DIST = \ + settings-get.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/common.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,9 @@ +#ifndef __COMMON_H +#define __COMMON_H + +#include "lib.h" + +extern struct master_service *service; +extern string_t *config_string; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/config-connection.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,123 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "common.h" +#include "str.h" +#include "ioloop.h" +#include "network.h" +#include "istream.h" +#include "ostream.h" +#include "config-connection.h" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#define MAX_INBUF_SIZE 1024 + +#define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 1 +#define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0 + +struct config_connection { + int fd; + struct istream *input; + struct ostream *output; + struct io *io; + + unsigned int version_received:1; + unsigned int handshaked:1; +}; + +static const char *const * +config_connection_next_line(struct config_connection *conn) +{ + const char *line; + + line = i_stream_next_line(conn->input); + if (line == NULL) + return NULL; + + return t_strsplit(line, "\t"); +} + +static void config_connection_request(struct config_connection *conn, + const char *const *args) +{ + /* <process> [<args>] */ + // FIXME + o_stream_send(conn->output, str_data(config_string), + str_len(config_string)); + o_stream_flush(conn->output); +} + +static void config_connection_input(void *context) +{ + struct config_connection *conn = context; + const char *const *args, *line; + + switch (i_stream_read(conn->input)) { + case -2: + i_error("BUG: Config client connection sent too much data"); + config_connection_destroy(conn); + return; + case -1: + config_connection_destroy(conn); + return; + } + + if (!conn->version_received) { + line = i_stream_next_line(conn->input); + if (line == NULL) + return; + + if (strncmp(line, "VERSION\t", 8) != 0 || + atoi(t_strcut(line + 8, '\t')) != + CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION) { + i_error("Config client not compatible with this server " + "(mixed old and new binaries?)"); + config_connection_destroy(conn); + return; + } + conn->version_received = TRUE; + } + + t_push(); + while ((args = config_connection_next_line(conn)) != NULL) { + if (args[0] == NULL) + continue; + if (strcmp(args[0], "REQ") == 0) + config_connection_request(conn, args + 1); + } + t_pop(); +} + +struct config_connection *config_connection_create(int fd) +{ + struct config_connection *conn; + + conn = i_new(struct config_connection, 1); + conn->fd = fd; + conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); + conn->io = io_add(fd, IO_READ, config_connection_input, conn); + return conn; +} + +void config_connection_destroy(struct config_connection *conn) +{ + io_remove(&conn->io); + i_stream_destroy(&conn->input); + o_stream_destroy(&conn->output); + if (close(conn->fd) < 0) + i_error("close(config conn) failed: %m"); + i_free(conn); +} + +void config_connection_dump_request(int fd, const char *service) +{ + struct config_connection *conn; + const char *args[2] = { service, NULL }; + + conn = config_connection_create(fd); + config_connection_request(conn, args); + config_connection_destroy(conn); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/config-connection.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,9 @@ +#ifndef __CONFIG_CONNECTION_H +#define __CONFIG_CONNECTION_H + +struct config_connection *config_connection_create(int fd); +void config_connection_destroy(struct config_connection *conn); + +void config_connection_dump_request(int fd, const char *service); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/config-parser.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,534 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "hash.h" +#include "strescape.h" +#include "istream.h" +#include "settings-parser.h" +#include "all-settings.h" +#include "config-parser.h" + +#include <unistd.h> +#include <fcntl.h> + +#define IS_WHITE(c) ((c) == ' ' || (c) == '\t') + +void config_parsers_fix_parents(pool_t pool) +{ +#if 0 + struct config_setting_parser_list *l; + const struct setting_define *d; + ARRAY_DEFINE(parents, ARRAY_TYPE(dynamic_settings_parsers)); + ARRAY_TYPE(dynamic_settings_parsers) *parsers; + struct dynamic_settings_parsers *parser; + unsigned int i, count; + + /* FIXME: currently we assume everyone are under a single parent */ + t_push(); + t_array_init(&parents, 4); + for (l = config_setting_parsers; l->module_name != NULL; l++) { + if (l->root->parent == NULL) + continue; + + for (d = l->root->parent->defines; d->key != NULL; d++) { + if (d->list_info == l->root) + break; + } + + if (d->key == NULL) { + parsers = array_get_modifiable(&parents, &count); + for (i = 0; i < count; i++) { + parser = array_idx_modifiable(&parsers[i], 0); + if (parser->info->parent == l->root->parent) + break; + } + if (i == count) { + parsers = array_append_space(&parents); + t_array_init(parsers, 16); + } + + parser = array_append_space(parsers); + parser->name = l->module_name; + parser->info = l->root; + } + } + + parsers = array_get_modifiable(&parents, &count); + for (i = 0; i < count; i++) { + (void)array_append_space(&parsers[i]); /* NULL-terminate */ + parser = array_idx_modifiable(&parsers[i], 0); + + settings_parser_info_update(pool, parser->info->parent, parser); + } + + t_pop(); +#endif +} + +static const char * +config_parse_line(pool_t pool, const char *key, const char *line, + const struct setting_parser_info **info_r) +{ + enum settings_parser_flags parser_flags = + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS; + struct config_setting_parser_list *l; + bool found = FALSE; + int ret; + + *info_r = NULL; + for (l = config_setting_parsers; l->module_name != NULL; l++) { + if (l->parser == NULL) { + l->parser = settings_parser_init(pool, l->root, + parser_flags); + } + + ret = settings_parse_line(l->parser, line); + if (ret > 0) { + found = TRUE; + *info_r = settings_parse_get_prev_info(l->parser); + } else if (ret < 0) + return settings_parser_get_error(l->parser); + } + + return found ? NULL : t_strconcat("Unknown setting: ", key, NULL); +} + +struct settings_export_context { + string_t *dest; + string_t *value; + string_t *prefix; + struct hash_table *keys; + pool_t pool; + bool export_defaults; +}; + +static void settings_export(struct settings_export_context *ctx, + const struct setting_parser_info *info, + const void *set) +{ + const struct setting_define *def; + const void *value, *default_value; + void *const *children = NULL; + unsigned int i, count, prefix_len; + char *key; + + for (def = info->defines; def->key != NULL; def++) { + value = CONST_PTR_OFFSET(set, def->offset); + default_value = info->defaults == NULL ? NULL : + CONST_PTR_OFFSET(info->defaults, def->offset); + + count = 0; + str_truncate(ctx->value, 0); + switch (def->type) { + case SET_INTERNAL: + break; + case SET_BOOL: { + const bool *val = value, *dval = default_value; + if (ctx->export_defaults || + dval == NULL || *val != *dval) { + str_append(ctx->value, + *val ? "yes" : "no"); + } + break; + } + case SET_UINT: { + const unsigned int *val = value, *dval = default_value; + if (ctx->export_defaults || + dval == NULL || *val != *dval) + str_printfa(ctx->value, "%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 ((ctx->export_defaults || + null_strcmp(sval, dval) != 0) && sval != NULL) + str_append(ctx->value, sval); + break; + } + case SET_STR: { + const char *const *val = value; + const char *const *_dval = default_value; + const char *dval = _dval == NULL ? NULL : *_dval; + + if ((ctx->export_defaults || + null_strcmp(*val, dval) != 0) && *val != NULL) + str_append(ctx->value, *val); + break; + } + case SET_ENUM: { + const char *const *val = value; + const char *const *_dval = default_value; + const char *dval = _dval == NULL ? NULL : *_dval; + unsigned int len = strlen(*val); + + if (ctx->export_defaults || + strncmp(*val, dval, len) != 0 || + ((*val)[len] != ':' && (*val)[len] != '\0')) + str_append(ctx->value, *val); + break; + } + case SET_DEFLIST: { + const ARRAY_TYPE(void_array) *val = 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, ' '); + str_printfa(ctx->value, "%u", i); + } + break; + } + case SET_STRLIST: { + const ARRAY_TYPE(const_string) *val = value; + unsigned int pos = str_len(ctx->dest); + const char *const *strings; + + if (!array_is_created(val)) + break; + + str_append_str(ctx->dest, ctx->prefix); + str_append(ctx->dest, def->key); + str_append(ctx->dest, "=0\n"); + + if (hash_table_lookup(ctx->keys, + str_c(ctx->dest) + pos) != NULL) { + /* already added all of these */ + str_truncate(ctx->dest, pos); + break; + } + key = p_strdup(ctx->pool, str_c(ctx->dest) + pos); + hash_table_insert(ctx->keys, key, key); + + strings = array_get(val, &count); + i_assert(count % 2 == 0); + for (i = 0; i < count; i += 2) { + str_append_str(ctx->dest, ctx->prefix); + str_append(ctx->dest, def->key); + str_append_c(ctx->dest, SETTINGS_SEPARATOR); + str_append_c(ctx->dest, '0'); + str_append_c(ctx->dest, SETTINGS_SEPARATOR); + str_append(ctx->dest, strings[i+0]); + str_append_c(ctx->dest, '='); + str_append(ctx->dest, strings[i+1]); + str_append_c(ctx->dest, '\n'); + } + count = 0; + break; + } + } + if (str_len(ctx->value) > 0) { + unsigned int pos = str_len(ctx->dest); + str_append_str(ctx->dest, ctx->prefix); + str_append(ctx->dest, def->key); + + if (hash_table_lookup(ctx->keys, + str_c(ctx->dest) + pos) != NULL) { + /* already exists */ + str_truncate(ctx->dest, pos); + } else { + str_append_c(ctx->dest, '='); + str_append_str(ctx->dest, ctx->value); + str_append_c(ctx->dest, '\n'); + key = p_strconcat(ctx->pool, str_c(ctx->prefix), + def->key, NULL); + hash_table_insert(ctx->keys, key, key); + } + } + + 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); + str_printfa(ctx->prefix, "%u", i); + str_append_c(ctx->prefix, SETTINGS_SEPARATOR); + settings_export(ctx, def->list_info, children[i]); + + str_truncate(ctx->prefix, prefix_len); + } + } +} + +static void config_export(string_t *dest) +{ + struct config_setting_parser_list *l; + struct settings_export_context ctx; + const void *set; + + memset(&ctx, 0, sizeof(ctx)); + ctx.dest = dest; + ctx.export_defaults = FALSE; + ctx.value = t_str_new(256); + ctx.prefix = t_str_new(64); + ctx.pool = pool_alloconly_create("config keys", 10240); + ctx.keys = hash_table_create(default_pool, ctx.pool, 0, + str_hash, (hash_cmp_callback_t *)strcmp); + + for (l = config_setting_parsers; l->module_name != NULL; l++) { + if (l->parser != NULL) { + set = settings_parser_get(l->parser); + settings_export(&ctx, l->root, set); + } + } + hash_table_destroy(&ctx.keys); + pool_unref(&ctx.pool); +} + +static const char *info_type_name_find(const struct setting_parser_info *info) +{ + unsigned int i; + + for (i = 0; info->defines[i].key != NULL; i++) { + if (info->defines[i].offset == info->type_offset) + return info->defines[i].key; + } + i_panic("setting parser: Invalid type_offset value"); + return NULL; +} + +void config_parse_file(string_t *dest, const char *path, const char *service) +{ + ARRAY_DEFINE(pathlen_stack, unsigned int); + ARRAY_TYPE(const_string) auth_defaults; + const struct setting_parser_info *info; + unsigned int pathlen = 0; + unsigned int counter = 0, auth_counter = 0, cur_counter; + struct istream *input; + const char *errormsg, *name, *type_name; + char *line, *key, *p; + int fd, linenum, ret, ignore; + string_t *str, *full_line; + size_t len; + pool_t pool; + bool asis; + + pool = pool_alloconly_create("config file parser", 10240); + + fd = open(path, O_RDONLY); + if (fd < 0) + i_fatal("open(%s) failed: %m", path); + + t_array_init(&pathlen_stack, 10); + t_array_init(&auth_defaults, 32); + + errormsg = config_parse_line(pool, "0", "auth=0", &info); + i_assert(errormsg == NULL); + + str = t_str_new(256); + full_line = t_str_new(512); + linenum = 0; errormsg = NULL; ignore = 0; asis = FALSE; + input = i_stream_create_fd(fd, (size_t)-1, FALSE); + while ((line = i_stream_read_next_line(input)) != NULL) { + linenum++; + + /* @UNSAFE: line is modified */ + + /* skip whitespace */ + while (IS_WHITE(*line)) + line++; + + /* ignore comments or empty lines */ + if (*line == '#' || *line == '\0') + continue; + + /* strip away comments. pretty kludgy way really.. */ + for (p = line; *p != '\0'; p++) { + if (*p == '\'' || *p == '"') { + char quote = *p; + for (p++; *p != quote && *p != '\0'; p++) { + if (*p == '\\' && p[1] != '\0') + p++; + } + if (*p == '\0') + break; + } else if (*p == '#') { + *p = '\0'; + break; + } + } + + /* remove whitespace from end of line */ + len = strlen(line); + while (IS_WHITE(line[len-1])) + len--; + line[len] = '\0'; + + if (len > 0 && line[len-1] == '\\') { + /* continues in next line */ + line[len-1] = '\0'; + str_append(full_line, line); + continue; + } + if (str_len(full_line) > 0) { + str_append(full_line, line); + line = str_c_modifiable(full_line); + } + + /* a) key = value + b) section_type [section_name] { + c) } */ + key = line; + while (!IS_WHITE(*line) && *line != '\0' && *line != '=') + line++; + if (IS_WHITE(*line)) { + *line++ = '\0'; + while (IS_WHITE(*line)) line++; + } + + ret = 1; + if (*line == '=') { + /* a) */ + *line++ = '\0'; + while (IS_WHITE(*line)) line++; + + len = strlen(line); + if (len > 0 && + ((*line == '"' && line[len-1] == '"') || + (*line == '\'' && line[len-1] == '\''))) { + line[len-1] = '\0'; + line = str_unescape(line+1); + } + + str_truncate(str, pathlen); + str_append(str, key); + str_append_c(str, '='); + str_append(str, line); + if (ignore > 0) { + /* ignore this setting */ + } else if (pathlen == 0 && + strncmp(str_c(str), "auth_", 5) == 0) { + /* verify that the setting is valid, + but delay actually adding it */ + const char *s = t_strdup(str_c(str) + 5); + + str_truncate(str, 0); + str_printfa(str, "auth/0/%s=%s", key + 5, line); + errormsg = config_parse_line(pool, key + 5, str_c(str), &info); + array_append(&auth_defaults, &s, 1); + } else if (asis) { + /* don't do any parsing, just add it */ + str_append(dest, str_c(str)); + str_append_c(dest, '\n'); + } else { + errormsg = config_parse_line(pool, key, str_c(str), &info); + } + } else if (strcmp(key, "}") != 0 || *line != '\0') { + /* b) + errors */ + line[-1] = '\0'; + + if (*line == '{') + name = ""; + else { + name = line; + while (!IS_WHITE(*line) && *line != '\0') + line++; + + if (*line != '\0') { + *line++ = '\0'; + while (IS_WHITE(*line)) + line++; + } + } + + if (*line != '{') + errormsg = "Expecting '='"; + else if (ignore > 0) { + ignore++; + } else if (strcmp(key, "protocol") == 0) { + array_append(&pathlen_stack, &pathlen, 1); + ignore = strcmp(name, service) != 0; + } else { + array_append(&pathlen_stack, &pathlen, 1); + + str_truncate(str, pathlen); + str_append(str, key); + pathlen = str_len(str); + + if (strcmp(key, "auth") == 0) + cur_counter = auth_counter++; + else + cur_counter = counter++; + + str_append_c(str, '='); + str_printfa(str, "%u", cur_counter); + if (cur_counter == 0 && strcmp(key, "auth") == 0) { + /* already added this */ + } else { + errormsg = config_parse_line(pool, key, + str_c(str), &info); + } + + str_truncate(str, pathlen); + str_append_c(str, SETTINGS_SEPARATOR); + str_printfa(str, "%u", cur_counter); + str_append_c(str, SETTINGS_SEPARATOR); + pathlen = str_len(str); + + if (errormsg == NULL && info->type_offset != (size_t)-1) { + type_name = info_type_name_find(info); + str_append(str, type_name); + str_append_c(str, '='); + str_append(str, name); + errormsg = config_parse_line(pool, type_name, + str_c(str), &info); + + str_truncate(str, pathlen); + } + + if (strcmp(key, "auth") == 0 && errormsg == NULL) { + /* add auth default settings */ + const char *const *lines; + unsigned int i, count; + + lines = array_get(&auth_defaults, &count); + for (i = 0; i < count; i++) { + str_truncate(str, pathlen); + + p = strchr(lines[i], '='); + str_append(str, lines[i]); + + errormsg = config_parse_line(pool, t_strdup_until(lines[i], p), str_c(str), &info); + i_assert(errormsg == NULL); + } + } + } + } else { + /* c) */ + unsigned int pathlen_count; + const unsigned int *arr; + + if (ignore > 0) + ignore--; + asis = FALSE; + + arr = array_get(&pathlen_stack, &pathlen_count); + if (pathlen_count == 0) + errormsg = "Unexpected '}'"; + else { + pathlen = arr[pathlen_count - 1]; + array_delete(&pathlen_stack, + pathlen_count - 1, 1); + } + } + + if (errormsg != NULL) { + i_fatal("Error in configuration file %s line %d: %s", + path, linenum, errormsg); + break; + } + str_truncate(full_line, 0); + } + i_stream_unref(&input); + config_export(dest); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/config-parser.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,8 @@ +#ifndef __CONFIG_PARSER_H +#define __CONFIG_PARSER_H + +void config_parsers_fix_parents(pool_t pool); + +void config_parse_file(string_t *dest, const char *path, const char *service); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/main.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,69 @@ +/* Copyright (C) 2005-2008 Timo Sirainen */ + +#include "common.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "str.h" +#include "config-connection.h" +#include "config-parser.h" + +#include <stdlib.h> +#include <unistd.h> + +string_t *config_string; +pool_t parsers_pool; + +static const char *config_path = SYSCONFDIR "/" PACKAGE ".conf"; + +static void main_init(const char *service) +{ + i_set_failure_internal(); + + parsers_pool = pool_alloconly_create("parent parsers", 2048); + config_parsers_fix_parents(parsers_pool); + + config_string = str_new(default_pool, 10240); + config_parse_file(config_string, config_path, service); + str_append_c(config_string, '\n'); +} + +int main(int argc, char *argv[]) +{ + struct ioloop *ioloop; + const char *path, *service = ""; + bool dump_nondefaults = FALSE, human_readable = FALSE; + int c; + + lib_init(); + + path = getenv("CONFIG_FILE_PATH"); + if (path != NULL) + config_path = path; + + while ((c = getopt(argc, argv, "c:s:na")) > 0) { + switch (c) { + case 'c': + config_path = optarg; + break; + case 's': + service = optarg; + break; + case 'n': + dump_nondefaults = TRUE; + /* fall through */ + case 'a': + /* FIXME: make it work */ + human_readable = TRUE; + break; + default: + i_fatal("Unknown parameter: %c", c); + } + } + + main_init(service); + ioloop = io_loop_create(); + config_connection_dump_request(STDOUT_FILENO, "master"); + io_loop_destroy(&ioloop); + lib_deinit(); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config/settings-get.pl Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,87 @@ +#!/usr/bin/env perl +use strict; + +print '#include "lib.h"'."\n"; +print '#include "settings-parser.h"'."\n"; +print '#include "all-settings.h"'."\n"; +print '#include <stddef.h>'."\n"; +print '#define CONFIG_BINARY'."\n"; + +my %parsers = {}; + +foreach my $file (@ARGV) { + my $f; + open($f, $file) || die "Can't open $file: $@"; + + my $state = 0; + my $file_contents = ""; + my $externs = ""; + + while (<$f>) { + my $write = 0; + if ($state == 0) { + if (/struct .*_settings {/ || + /struct setting_define.*{/ || + /struct .*_default_settings = {/) { + $state++; + } elsif (/^(static )?struct setting_parser_info (.*) = {/) { + $state++; + $parsers{$2} = 1; + } elsif (/^extern struct setting_parser_info (.*);/) { + $externs .= "extern struct setting_parser_info $1;\n"; + } + + if (/#define.*DEF/ || /^#undef.*DEF/) { + $write = 1; + $state = 2 if (/\\$/); + } + } elsif ($state == 2) { + $write = 1; + $state = 0 if (!/\\$/); + } + + if ($state == 1 || $state == 3) { + if ($state == 1) { + if (/DEFLIST.*".*",(.*)$/) { + my $value = $1; + if ($value =~ /.*&(.*)\)/) { + $parsers{$1} = 0; + $externs .= "extern struct setting_parser_info $1;\n"; + } else { + $state = 3; + } + } + } elsif ($state == 3) { + if (/.*&(.*)\)/) { + $parsers{$1} = 0; + } + } + + $write = 1; + if (/};/) { + $state = 0; + } + } + + $file_contents .= $_ if ($write); + } + + print "/* $file */\n"; + print $externs; + print $file_contents; + + close $f; +} + +print "struct config_setting_parser_list config_setting_parsers[] = {\n"; +foreach my $name (keys %parsers) { + next if (!$parsers{$name}); + + my $module = ""; + if ($name =~ /^([^_]*)/) { + $module = $1; + } + print " { \"$module\", &".$name.", NULL, NULL }, \n"; +} +print " { NULL, NULL, NULL, NULL }\n"; +print "};\n";
--- a/src/deliver/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/deliver/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ @@ -13,6 +14,7 @@ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/raw \ + -DBINDIR=\""$(bindir)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DMODULEDIR=\""$(moduledir)"\" @@ -28,6 +30,7 @@ libs = \ $(STORAGE_LIBS) \ ../lib-dict/libdict.a \ + ../lib-settings/libsettings.a \ $(unused_objects) deliver_LDADD = \ @@ -40,6 +43,7 @@ deliver_SOURCES = \ auth-client.c \ deliver.c \ + deliver-settings.c \ duplicate.c \ mail-send.c \ smtp-client.c @@ -47,6 +51,7 @@ headers = \ auth-client.h \ deliver.h \ + deliver-settings.h \ duplicate.h \ mail-send.h \ smtp-client.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/deliver/deliver-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,159 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "deliver.h" +#include "array.h" +#include "hostpid.h" +#include "istream.h" +#include "settings-parser.h" +#include "mail-storage-settings.h" +#include "deliver-settings.h" + +#include <stddef.h> + +#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf" + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct deliver_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct deliver_settings, field), defines } + +static struct setting_define deliver_setting_defines[] = { + DEF(SET_STR, base_dir), + DEF(SET_STR, log_path), + DEF(SET_STR, info_log_path), + DEF(SET_STR, log_timestamp), + DEF(SET_STR, syslog_facility), + DEF(SET_BOOL, version_ignore), + DEF(SET_UINT, umask), + + DEF(SET_STR, mail_plugins), + DEF(SET_STR, mail_plugin_dir), + DEF(SET_STR_VARS, mail_log_prefix), + + DEF(SET_STR, postmaster_address), + DEF(SET_STR, hostname), + DEF(SET_STR, sendmail_path), + DEF(SET_STR, rejection_subject), + DEF(SET_STR, rejection_reason), + DEF(SET_STR, auth_socket_path), + DEF(SET_STR, deliver_log_format), + DEF(SET_BOOL, quota_full_tempfail), + + { SET_STRLIST, "plugin", offsetof(struct deliver_settings, plugin_envs), NULL }, + + SETTING_DEFINE_LIST_END +}; + +static struct deliver_settings deliver_default_settings = { + MEMBER(base_dir) PKG_RUNDIR, + MEMBER(log_path) "", + MEMBER(info_log_path) "", + MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT, + MEMBER(syslog_facility) "mail", + MEMBER(version_ignore) FALSE, + MEMBER(umask) 0077, + + MEMBER(mail_plugins) "", + MEMBER(mail_plugin_dir) MODULEDIR"/lda", + MEMBER(mail_log_prefix) "%Us(%u): ", + + MEMBER(postmaster_address) "", + MEMBER(hostname) "", + MEMBER(sendmail_path) "/usr/lib/sendmail", + MEMBER(rejection_subject) "Rejected: %s", + MEMBER(rejection_reason) + "Your message to <%t> was automatically rejected:%n%r", + MEMBER(auth_socket_path) "auth-master", + MEMBER(deliver_log_format) "msgid=%m: %$", + MEMBER(quota_full_tempfail) FALSE +}; + +struct setting_parser_info deliver_setting_parser_info = { + MEMBER(defines) deliver_setting_defines, + MEMBER(defaults) &deliver_default_settings, + + MEMBER(parent) NULL, + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct deliver_settings) +}; + +static pool_t settings_pool = NULL; + +static void fix_base_path(struct deliver_settings *set, const char **str) +{ + if (*str != NULL && **str != '\0' && **str != '/') { + *str = p_strconcat(settings_pool, + set->base_dir, "/", *str, NULL); + } +} + +struct setting_parser_context * +deliver_settings_read(const char *path, + struct deliver_settings **set_r, + struct mail_user_settings **user_set_r) +{ + static const struct setting_parser_info *roots[] = { + &deliver_setting_parser_info, + &mail_user_setting_parser_info + }; + void **sets; + struct deliver_settings *deliver_set; + struct setting_parser_context *parser; + + if (settings_pool == NULL) + settings_pool = pool_alloconly_create("deliver settings", 1024); + else + p_clear(settings_pool); + + mail_storage_namespace_defines_init(settings_pool); + + parser = settings_parser_init_list(settings_pool, + roots, N_ELEMENTS(roots), + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); + if (settings_parse_exec(parser, DOVECOT_CONFIG_BIN_PATH, + path, "lda") < 0) { + i_fatal_status(EX_CONFIG, "Error reading configuration: %s", + settings_parser_get_error(parser)); + } + + sets = settings_parser_get_list(parser); + + deliver_set = sets[0]; + if (*deliver_set->hostname == '\0') + deliver_set->hostname = my_hostname; + fix_base_path(deliver_set, &deliver_set->auth_socket_path); + + if (*deliver_set->postmaster_address == '\0') { + i_fatal_status(EX_CONFIG, + "postmaster_address setting not given"); + } + + *set_r = deliver_set; + *user_set_r = sets[1]; + return parser; +} + +void deliver_settings_add(struct setting_parser_context *parser, + const ARRAY_TYPE(const_string) *extra_fields) +{ + const char *const *str, *p, *line; + unsigned int i, count; + + str = array_get(extra_fields, &count); + for (i = 0; i < count; i++) T_BEGIN { + p = strchr(str[i], '='); + if (p != NULL) + line = str[i]; + else + line = t_strconcat(str[i], "=yes", NULL); + if (settings_parse_line(parser, str[i]) < 0) { + i_fatal_status(EX_CONFIG, + "Invalid userdb input '%s': %s", str[i], + settings_parser_get_error(parser)); + } + } T_END; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/deliver/deliver-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,39 @@ +#ifndef DELIVER_SETTINGS_H +#define DELIVER_SETTINGS_H + +struct mail_user_settings; + +struct deliver_settings { + const char *base_dir; + const char *log_path; + const char *info_log_path; + const char *log_timestamp; + const char *syslog_facility; + bool version_ignore; + unsigned int umask; + + const char *mail_plugins; + const char *mail_plugin_dir; + const char *mail_log_prefix; + + /* deliver: */ + const char *postmaster_address; + const char *hostname; + const char *sendmail_path; + const char *rejection_subject; + const char *rejection_reason; + const char *auth_socket_path; + const char *deliver_log_format; + bool quota_full_tempfail; + + ARRAY_DEFINE(plugin_envs, const char *); +}; + +struct setting_parser_context * +deliver_settings_read(const char *path, + struct deliver_settings **set_r, + struct mail_user_settings **user_set_r); +void deliver_settings_add(struct setting_parser_context *parser, + const ARRAY_TYPE(const_string) *extra_fields); + +#endif
--- a/src/deliver/deliver.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/deliver/deliver.c Tue Jan 27 18:21:53 2009 -0500 @@ -25,6 +25,7 @@ #include "mail-namespace.h" #include "raw-storage.h" #include "imap-utf7.h" +#include "settings-parser.h" #include "dict.h" #include "auth-client.h" #include "mail-send.h" @@ -56,6 +57,8 @@ struct deliver_settings *deliver_set; deliver_mail_func_t *deliver_mail = NULL; +bool mailbox_autosubscribe; +bool mailbox_autocreate; bool tried_default_save = FALSE; /* FIXME: these two should be in some context struct instead of as globals.. */ @@ -66,10 +69,6 @@ static struct module *modules; static struct ioloop *ioloop; -static pool_t plugin_pool; -static ARRAY_DEFINE(lda_envs, const char *); -static ARRAY_DEFINE(plugin_envs, const char *); - static void sig_die(int signo, void *context ATTR_UNUSED) { /* warn about being killed because of some signal, except SIGINT (^C) @@ -135,7 +134,7 @@ msg = t_strdup_vprintf(fmt, args); str = t_str_new(256); - var_expand(str, deliver_set->log_format, + var_expand(str, deliver_set->deliver_log_format, get_log_var_expand_table(mail, msg)); i_info("%s", str_c(str)); va_end(args); @@ -173,7 +172,7 @@ } box = mailbox_open(storage_r, name, NULL, open_flags); - if (box != NULL || !deliver_set->mailbox_autocreate) + if (box != NULL || !mailbox_autocreate) return box; (void)mail_storage_get_last_error(*storage_r, &error); @@ -183,7 +182,7 @@ /* try creating it. */ if (mail_storage_mailbox_create(*storage_r, name, FALSE) < 0) return NULL; - if (deliver_set->mailbox_autosubscribe) { + if (mailbox_autosubscribe) { /* (try to) subscribe to it */ (void)mailbox_list_set_subscribed(ns->list, name, TRUE); } @@ -276,294 +275,6 @@ count++, deliver_set->hostname); } -#include "settings.h" -#include "../master/master-settings.h" -#include "../master/master-settings-defs.c" - -#define IS_WHITE(c) ((c) == ' ' || (c) == '\t') - -static bool setting_is_bool(const char *name) -{ - const struct setting_def *def; - - for (def = setting_defs; def->name != NULL; def++) { - if (strcmp(def->name, name) == 0) - return def->type == SET_BOOL; - } - if (strncmp(name, "NAMESPACE_", 10) == 0) { - return strstr(name, "_list") != NULL || - strstr(name, "_inbox") != NULL || - strstr(name, "_hidden") != NULL || - strstr(name, "_subscriptions") != NULL; - } - if (strcmp(name, "quota_full_tempfail") == 0) - return TRUE; - return FALSE; -} - -/* more ugly kludging because we have our own config parsing code. - hopefully this goes away in v1.2. */ -static struct { - const char *name; - bool set; -} default_yes_settings[] = { - { "dotlock_use_excl", TRUE }, - { "maildir_copy_with_hardlinks", TRUE }, - { "mbox_dirty_syncs", TRUE }, - { "mbox_lazy_writes", TRUE } -}; - -static void config_file_init(const char *path) -{ - struct istream *input; - const char *key, *value, *str, *ukey; - char *line, *p, quote; - int fd, sections = 0; - bool lda_section = FALSE, pop3_section = FALSE, plugin_section = FALSE; - bool ns_section = FALSE, ns_location = FALSE, ns_list = FALSE; - bool ns_subscriptions = FALSE; - unsigned int i, ns_idx = 0; - size_t len; - - plugin_pool = pool_alloconly_create("Plugin strings", 512); - i_array_init(&lda_envs, 16); - i_array_init(&plugin_envs, 16); - - fd = open(path, O_RDONLY); - if (fd < 0) - i_fatal_status(EX_CONFIG, "open(%s) failed: %m", path); - - input = i_stream_create_fd(fd, 1024, TRUE); - i_stream_set_return_partial_line(input, TRUE); - while ((line = i_stream_read_next_line(input)) != NULL) { - /* @UNSAFE: line is modified */ - - /* skip whitespace */ - while (IS_WHITE(*line)) - line++; - - /* ignore comments or empty lines */ - if (*line == '#' || *line == '\0') - continue; - - /* strip away comments. pretty kludgy way really.. */ - for (p = line; *p != '\0'; p++) { - if (*p == '\'' || *p == '"') { - quote = *p; - for (p++; *p != quote && *p != '\0'; p++) { - if (*p == '\\' && p[1] != '\0') - p++; - } - if (*p == '\0') - break; - } else if (*p == '#') { - *p = '\0'; - break; - } - } - - /* remove whitespace from end of line */ - len = strlen(line); - while (IS_WHITE(line[len-1])) - len--; - line[len] = '\0'; - - if (strncmp(line, "!include_try ", 13) == 0) - continue; - if (strncmp(line, "!include ", 9) == 0) { - i_fatal_status(EX_CONFIG, "Error in config file %s: " - "deliver doesn't support !include directive", path); - } - - value = p = strchr(line, '='); - if (value == NULL) { - if (strchr(line, '{') != NULL) { - if (strcmp(line, "protocol lda {") == 0) - lda_section = TRUE; - else if (strcmp(line, "plugin {") == 0) - plugin_section = TRUE; - else if (strcmp(line, "protocol pop3 {") == 0) - pop3_section = TRUE; - else if (strncmp(line, "namespace ", 10) == 0) { - ns_section = TRUE; - ns_idx++; - line += 10; - env_put(t_strdup_printf( - "NAMESPACE_%u_TYPE=%s", ns_idx, - t_strcut(line, ' '))); - } - sections++; - } - if (*line == '}') { - sections--; - lda_section = FALSE; - plugin_section = FALSE; - pop3_section = FALSE; - if (ns_section) { - ns_section = FALSE; - if (ns_location) - ns_location = FALSE; - else { - env_put(t_strdup_printf( - "NAMESPACE_%u=", ns_idx)); - } - if (ns_list) - ns_list = FALSE; - else { - env_put(t_strdup_printf( - "NAMESPACE_%u_LIST=1", ns_idx)); - } - if (ns_subscriptions) - ns_subscriptions = FALSE; - else { - env_put(t_strdup_printf( - "NAMESPACE_%u_SUBSCRIPTIONS=1", - ns_idx)); - } - } - } - continue; - } - - while (p > line && IS_WHITE(p[-1])) p--; - key = t_strdup_until(line, p); - - if (sections > 0 && !lda_section && !plugin_section) { - if (pop3_section) { - if (strcmp(key, "pop3_uidl_format") != 0) - continue; - } else if (ns_section) { - if (strcmp(key, "separator") == 0) { - key = t_strdup_printf( - "NAMESPACE_%u_SEP", ns_idx); - } else if (strcmp(key, "location") == 0) { - ns_location = TRUE; - key = t_strdup_printf("NAMESPACE_%u", - ns_idx); - } else { - if (strcmp(key, "list") == 0) - ns_list = TRUE; - if (strcmp(key, "subscriptions") == 0) - ns_subscriptions = TRUE; - key = t_strdup_printf("NAMESPACE_%u_%s", - ns_idx, key); - } - } else { - /* unwanted section */ - continue; - } - } - - do { - value++; - } while (IS_WHITE(*value)); - - len = strlen(value); - if (len > 0 && - ((*value == '"' && value[len-1] == '"') || - (*value == '\'' && value[len-1] == '\''))) { - value = str_unescape(p_strndup(unsafe_data_stack_pool, - value+1, len - 2)); - } - ukey = t_str_ucase(key); - if (setting_is_bool(key) && strcasecmp(value, "yes") != 0) { - for (i = 0; i < N_ELEMENTS(default_yes_settings); i++) { - if (strcmp(default_yes_settings[i].name, - key) == 0) - default_yes_settings[i].set = FALSE; - } - env_remove(ukey); - continue; - } - - if (lda_section) { - str = p_strconcat(plugin_pool, ukey, "=", value, NULL); - array_append(&lda_envs, &str, 1); - } - if (!plugin_section) { - env_put(t_strconcat(ukey, "=", value, NULL)); - } else { - /* %variables need to be expanded. - store these for later. */ - value = p_strconcat(plugin_pool, - ukey, "=", value, NULL); - array_append(&plugin_envs, &value, 1); - } - } - i_stream_unref(&input); - - for (i = 0; i < N_ELEMENTS(default_yes_settings); i++) { - if (default_yes_settings[i].set) { - key = default_yes_settings[i].name; - env_put(t_strconcat(t_str_ucase(key), "=1", NULL)); - } - } -} - -static const struct var_expand_table * -get_var_expand_table(const char *user, const char *home) -{ - static struct var_expand_table static_tab[] = { - { 'u', NULL, "user" }, - { 'n', NULL, "username" }, - { 'd', NULL, "domain" }, - { 's', NULL, "service" }, - { 'h', NULL, "home" }, - { 'l', NULL, "lip" }, - { 'r', NULL, "rip" }, - { 'p', NULL, "pid" }, - { 'i', NULL, "uid" }, - { '\0', NULL, NULL } - }; - struct var_expand_table *tab; - - tab = t_malloc(sizeof(static_tab)); - memcpy(tab, static_tab, sizeof(static_tab)); - - tab[0].value = user; - tab[1].value = t_strcut(user, '@'); - tab[2].value = strchr(user, '@'); - if (tab[2].value != NULL) tab[2].value++; - tab[3].value = "DELIVER"; - tab[4].value = home != NULL ? home : - "/HOME_DIRECTORY_USED_BUT_NOT_GIVEN_BY_USERDB"; - tab[5].value = NULL; - tab[6].value = NULL; - tab[7].value = my_pid; - tab[8].value = dec2str(geteuid()); - - return tab; -} - -static const char * -expand_mail_env(const char *env, const struct var_expand_table *table) -{ - string_t *str; - const char *p; - - str = t_str_new(256); - - /* it's either type:data or just data */ - p = strchr(env, ':'); - if (p != NULL) { - while (env != p) { - str_append_c(str, *env); - env++; - } - - str_append_c(str, *env++); - } - - if (env[0] == '~' && env[1] == '/') { - /* expand home */ - env = t_strconcat("%h", env+1, NULL); - } - - /* expand %vars */ - var_expand(str, env, table); - return str_c(str); -} - static const char *escape_local_part(const char *local_part) { const char *p; @@ -685,15 +396,15 @@ static void open_logfile(const char *username) { - const char *prefix, *log_path, *stamp; + const char *prefix, *log_path; prefix = t_strdup_printf("deliver(%s): ", username); - log_path = home_expand(getenv("LOG_PATH")); - if (log_path == NULL || *log_path == '\0') { - const char *env = getenv("SYSLOG_FACILITY"); + log_path = home_expand(deliver_set->log_path); + if (*log_path == '\0') { int facility; - if (env == NULL || !syslog_facility_find(env, &facility)) + if (!syslog_facility_find(deliver_set->syslog_facility, + &facility)) facility = LOG_MAIL; i_set_failure_prefix(prefix); i_set_failure_syslog("dovecot", LOG_NDELAY, facility); @@ -702,14 +413,11 @@ i_set_failure_file(log_path, prefix); } - log_path = home_expand(getenv("INFO_LOG_PATH")); - if (log_path != NULL && *log_path != '\0') + log_path = home_expand(deliver_set->info_log_path); + if (*log_path != '\0') i_set_info_file(log_path); - stamp = getenv("LOG_TIMESTAMP"); - if (stamp == NULL) - stamp = DEFAULT_FAILURE_STAMP_FORMAT; - i_set_failure_timestamp_format(stamp); + i_set_failure_timestamp_format(deliver_set->log_timestamp); } static void print_help(void) @@ -739,72 +447,20 @@ if (home != NULL) env_put(home); } -static void expand_envs(const char *user) +static void plugin_get_home(void) { - const struct var_expand_table *table; - const char *value, *const *envs, *home, *env_name; - unsigned int i, count; - string_t *str; - - home = getenv("HOME"); - - str = t_str_new(256); - table = get_var_expand_table(user, home); - envs = array_get(&plugin_envs, &count); - for (i = 0; i < count; i++) { - str_truncate(str, 0); - var_expand(str, envs[i], table); - env_put(str_c(str)); - } - /* add LDA envs again to make sure they override plugin settings */ - envs = array_get(&lda_envs, &count); - for (i = 0; i < count; i++) - env_put(envs[i]); - - /* get the table again in case plugin envs provided the home - directory (yea, kludgy) */ - if (home == NULL) - home = getenv("HOME"); - table = get_var_expand_table(user, home); - - value = getenv("MAIL_LOCATION"); - if (value != NULL) - value = expand_mail_env(value, table); - env_put(t_strconcat("MAIL=", value, NULL)); - - for (i = 1;; i++) { - env_name = t_strdup_printf("NAMESPACE_%u", i); - value = getenv(env_name); - if (value == NULL) - break; - - value = expand_mail_env(value, table); - env_put(t_strconcat(env_name, "=", value, NULL)); - - env_name = t_strdup_printf("NAMESPACE_%u_PREFIX", i); - value = getenv(env_name); - if (value != NULL) { - str_truncate(str, 0); - var_expand(str, value, table); - env_put(t_strconcat(env_name, "=", str_c(str), NULL)); - } - } -} - -static void putenv_extra_fields(const ARRAY_TYPE(const_string) *extra_fields) -{ - const char *const *fields; - const char *key, *p; + const char *const *envs; unsigned int i, count; - fields = array_get(extra_fields, &count); - for (i = 0; i < count; i++) { - p = strchr(fields[i], '='); - if (p == NULL) - env_put(t_strconcat(fields[i], "=1", NULL)); - else { - key = t_str_ucase(t_strdup_until(fields[i], p)); - env_put(t_strconcat(key, p, NULL)); + /* kludgy. this should be removed some day, but for now don't break + existing setups that rely on it. */ + if (array_is_created(&deliver_set->plugin_envs)) { + envs = array_get(&deliver_set->plugin_envs, &count); + for (i = 0; i < count; i++) { + if (strncmp(envs[i], "home=", 5) == 0) { + env_put(t_strconcat("HOME=", envs[i]+5, NULL)); + break; + } } } } @@ -813,17 +469,20 @@ { const char *config_path = DEFAULT_CONFIG_FILE; const char *mailbox = "INBOX"; - const char *auth_socket; - const char *home, *destaddr, *user, *value, *errstr, *path, *orig_user; + const char *home, *destaddr, *user, *error, *path, *orig_user; ARRAY_TYPE(const_string) extra_fields = ARRAY_INIT; + struct setting_parser_context *parser; struct mail_user *mail_user, *raw_mail_user; struct mail_namespace *raw_ns; + struct mail_namespace_settings raw_ns_set; struct mail_storage *storage; struct mailbox *box; struct raw_mailbox *raw_box; struct istream *input; struct mailbox_transaction_context *t; struct mailbox_header_lookup_ctx *headers_ctx; + struct mail_user_settings *user_set; + const struct mail_storage_settings *mail_set; struct mail *mail; uid_t process_euid; bool stderr_rejection = FALSE; @@ -867,7 +526,7 @@ #endif deliver_set = i_new(struct deliver_settings, 1); - deliver_set->mailbox_autocreate = TRUE; + mailbox_autocreate = TRUE; destaddr = user = path = NULL; for (i = 1; i < argc; i++) { @@ -918,9 +577,9 @@ mailbox = str_c(str); } } else if (strcmp(argv[i], "-n") == 0) { - deliver_set->mailbox_autocreate = FALSE; + mailbox_autocreate = FALSE; } else if (strcmp(argv[i], "-s") == 0) { - deliver_set->mailbox_autosubscribe = TRUE; + mailbox_autosubscribe = TRUE; } else if (strcmp(argv[i], "-f") == 0) { /* envelope sender address */ i++; @@ -964,42 +623,29 @@ "destination user parameter (-d user) not given"); } - T_BEGIN { - config_file_init(config_path); - } T_END; + mail_storage_init(); + mail_storage_register_all(); + mailbox_list_register_all(); + + parser = deliver_settings_read(config_path, &deliver_set, &user_set); open_logfile(user); - if (getenv("MAIL_DEBUG") != NULL) - env_put("DEBUG=1"); - - if (getenv("MAIL_PLUGINS") == NULL) + mail_set = mail_user_set_get_driver_settings(user_set, "MAIL"); + if (deliver_set->mail_plugins == '\0') modules = NULL; else { - const char *plugin_dir = getenv("MAIL_PLUGIN_DIR"); const char *version; - if (plugin_dir == NULL) - plugin_dir = MODULEDIR"/lda"; - - version = getenv("VERSION_IGNORE") != NULL ? - NULL : PACKAGE_VERSION; - modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"), + version = deliver_set->version_ignore ? NULL : PACKAGE_VERSION; + modules = module_dir_load(deliver_set->mail_plugin_dir, + deliver_set->mail_plugins, TRUE, version); } if (user_auth) { - auth_socket = getenv("AUTH_SOCKET_PATH"); - if (auth_socket == NULL) { - const char *base_dir = getenv("BASE_DIR"); - if (base_dir == NULL) - base_dir = PKG_RUNDIR; - auth_socket = t_strconcat(base_dir, "/auth-master", - NULL); - } - userdb_pool = pool_alloconly_create("userdb lookup replys", 512); orig_user = user; - ret = auth_client_lookup_and_restrict(auth_socket, + ret = auth_client_lookup_and_restrict(deliver_set->auth_socket_path, &user, process_euid, userdb_pool, &extra_fields); @@ -1008,7 +654,7 @@ if (strcmp(user, orig_user) != 0) { /* auth lookup changed the user. */ - if (getenv("DEBUG") != NULL) + if (mail_set->mail_debug) i_info("userdb changed username to %s", user); i_set_failure_prefix(t_strdup_printf("deliver(%s): ", user)); @@ -1018,90 +664,62 @@ user = t_strdup(user); } - expand_envs(user); if (userdb_pool != NULL) { - putenv_extra_fields(&extra_fields); + settings_parse_set_expanded(parser, TRUE); + deliver_settings_add(parser, &extra_fields); pool_unref(&userdb_pool); } - /* Fix namespaces with empty locations */ - for (i = 1;; i++) { - value = getenv(t_strdup_printf("NAMESPACE_%u", i)); - if (value == NULL) - break; - - if (*value == '\0') { - env_put(t_strdup_printf("NAMESPACE_%u=%s", i, - getenv("MAIL"))); - } + home = getenv("HOME"); + if (home == NULL) { + plugin_get_home(); + home = getenv("HOME"); } /* If possible chdir to home directory, so that core file could be written in case we crash. */ - home = getenv("HOME"); if (home != NULL) { if (chdir(home) < 0) { if (errno != ENOENT) i_error("chdir(%s) failed: %m", home); - else if (getenv("DEBUG") != NULL) + else if (mail_set->mail_debug) i_info("Home dir not found: %s", home); } } env_put(t_strconcat("USER=", user, NULL)); - - value = getenv("UMASK"); - if (value == NULL || sscanf(value, "%i", &i) != 1 || i < 0) - i = 0077; - (void)umask(i); - - deliver_set->hostname = getenv("HOSTNAME"); - if (deliver_set->hostname == NULL) - deliver_set->hostname = my_hostname; - deliver_set->postmaster_address = getenv("POSTMASTER_ADDRESS"); - if (deliver_set->postmaster_address == NULL) { - i_fatal_status(EX_CONFIG, - "postmaster_address setting not given"); - } - deliver_set->sendmail_path = getenv("SENDMAIL_PATH"); - if (deliver_set->sendmail_path == NULL) - deliver_set->sendmail_path = DEFAULT_SENDMAIL_PATH; - deliver_set->rejection_subject = getenv("REJECTION_SUBJECT"); - if (deliver_set->rejection_subject == NULL) - deliver_set->rejection_subject = DEFAULT_MAIL_REJECTION_SUBJECT; - deliver_set->rejection_reason = getenv("REJECTION_REASON"); - if (deliver_set->rejection_reason == NULL) { - deliver_set->rejection_reason = - DEFAULT_MAIL_REJECTION_HUMAN_REASON; - } - deliver_set->log_format = getenv("DELIVER_LOG_FORMAT"); - if (deliver_set->log_format == NULL) - deliver_set->log_format = DEFAULT_LOG_FORMAT; + (void)umask(deliver_set->umask); dict_drivers_register_builtin(); duplicate_init(); - mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL); - mail_storage_init(); - mail_storage_register_all(); - mailbox_list_register_all(); + mail_users_init(deliver_set->auth_socket_path, mail_set->mail_debug); module_dir_init(modules); - mail_user = mail_user_init(user); + mail_user = mail_user_alloc(user, user_set); mail_user_set_home(mail_user, home); - if (mail_namespaces_init(mail_user) < 0) - i_fatal("Namespace initialization failed"); + mail_user_set_vars(mail_user, geteuid(), "deliver", NULL, NULL); + if (mail_user_init(mail_user, &error) < 0) + i_fatal("Mail user initialization failed: %s", error); + if (mail_namespaces_init(mail_user, &error) < 0) + i_fatal("Namespace initialization failed: %s", error); /* create a separate mail user for the internal namespace */ - raw_mail_user = mail_user_init(user); - mail_user_set_home(raw_mail_user, NULL); + raw_mail_user = mail_user_alloc(user, user_set); + mail_user_set_home(raw_mail_user, "/"); + if (mail_user_init(raw_mail_user, &error) < 0) + i_fatal("Raw user initialization failed: %s", error); + + settings_parser_deinit(&parser); + + memset(&raw_ns_set, 0, sizeof(raw_ns_set)); + raw_ns_set.location = "/tmp"; + raw_ns = mail_namespaces_init_empty(raw_mail_user); raw_ns->flags |= NAMESPACE_FLAG_INTERNAL; - - if (mail_storage_create(raw_ns, "raw", "/tmp", - MAIL_STORAGE_FLAG_FULL_FS_ACCESS, - FILE_LOCK_METHOD_FCNTL, &errstr) < 0) - i_fatal("Couldn't create internal raw storage: %s", errstr); + raw_ns->set = &raw_ns_set; + if (mail_storage_create(raw_ns, "raw", 0, &error) < 0) + i_fatal("Couldn't create internal raw storage: %s", error); if (path == NULL) { input = create_raw_stream(0, &mtime); box = mailbox_open(&raw_ns->storage, "Dovecot Delivery Mail", @@ -1186,7 +804,7 @@ } if (error != MAIL_ERROR_NOSPACE || - getenv("QUOTA_FULL_TEMPFAIL") != NULL) { + deliver_set->quota_full_tempfail) { /* Saving to INBOX should always work unless we're over quota. If it didn't, it's probably a configuration problem. */
--- a/src/deliver/deliver.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/deliver/deliver.h Tue Jan 27 18:21:53 2009 -0500 @@ -9,25 +9,11 @@ #include "lib.h" #include "mail-storage.h" - -#define DEFAULT_MAIL_REJECTION_SUBJECT \ - "Rejected: %s" -#define DEFAULT_MAIL_REJECTION_HUMAN_REASON \ - "Your message to <%t> was automatically rejected:%n%r" -#define DEFAULT_LOG_FORMAT "msgid=%m: %$" - -struct deliver_settings { - const char *hostname; - const char *postmaster_address; - const char *sendmail_path; - const char *rejection_subject; - const char *rejection_reason; - const char *log_format; - bool mailbox_autosubscribe; - bool mailbox_autocreate; -}; +#include "deliver-settings.h" extern struct deliver_settings *deliver_set; +extern bool mailbox_autosubscribe; +extern bool mailbox_autocreate; extern bool tried_default_save; typedef int deliver_mail_func_t(struct mail_namespace *namespaces,
--- a/src/imap-login/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap-login/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -12,6 +12,7 @@ ../login-common/liblogin-common.a \ ../lib-imap/libimap.a \ ../lib-auth/libauth.a \ + ../lib-settings/libsettings.a \ ../lib/liblib.a \ $(SSL_LIBS)
--- a/src/imap-login/client-authenticate.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap-login/client-authenticate.c Tue Jan 27 18:21:53 2009 -0500 @@ -39,7 +39,7 @@ c) we allow insecure authentication */ if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && - (secured || !disable_plaintext_auth || + (secured || !login_settings->disable_plaintext_auth || (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) { str_append_c(str, ' '); str_append(str, "AUTH="); @@ -158,7 +158,7 @@ master_user = *args + 7; else if (strncmp(*args, "user=", 5) == 0) { /* already handled in login-common */ - } else if (auth_debug) { + } else if (login_settings->auth_debug) { i_info("Ignoring unknown passdb extra field: %s", *args); } @@ -347,8 +347,9 @@ init_resp = IMAP_ARG_STR(&args[1]); } - if (!client->common.secured && ssl_required) { - if (verbose_auth) { + if (!client->common.secured && + strcmp(login_settings->ssl, "required") == 0) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "Login failed: " "SSL required for authentication"); } @@ -381,8 +382,8 @@ user = IMAP_ARG_STR(&args[0]); pass = IMAP_ARG_STR(&args[1]); - if (!client->common.secured && disable_plaintext_auth) { - if (verbose_auth) { + if (!client->common.secured && login_settings->disable_plaintext_auth) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "Login failed: " "Plaintext authentication disabled"); }
--- a/src/imap-login/client.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap-login/client.c Tue Jan 27 18:21:53 2009 -0500 @@ -52,7 +52,8 @@ { const char *addr; - if (!verbose_proctitle || !process_per_connection) + if (!login_settings->verbose_proctitle || + !login_settings->login_process_per_connection) return; addr = net_ip2addr(&client->common.ip); @@ -99,7 +100,8 @@ return t_strconcat(full ? capability_string : CAPABILITY_BANNER_STRING, (ssl_initialized && !client->common.tls) ? " STARTTLS" : "", - disable_plaintext_auth && !client->common.secured ? + login_settings->disable_plaintext_auth && + !client->common.secured ? " LOGINDISABLED" : "", auths, NULL); } @@ -423,6 +425,7 @@ void client_destroy_oldest(void) { + unsigned int max_connections = login_settings->login_max_connections; struct client *client; struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT]; unsigned int i, destroy_count; @@ -465,7 +468,7 @@ greet = t_str_new(128); str_append(greet, "* OK "); str_printfa(greet, "[CAPABILITY %s] ", get_capability(client, FALSE)); - str_append(greet, greeting); + str_append(greet, login_settings->login_greeting); client_send_line(client, str_c(greet)); client->greeting_sent = TRUE;
--- a/src/imap-login/imap-proxy.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap-login/imap-proxy.c Tue Jan 27 18:21:53 2009 -0500 @@ -201,7 +201,7 @@ be using only Dovecot as their backend :) */ client_send_tagline(client, line + 2); - if (verbose_auth) { + if (login_settings->verbose_auth) { str = t_str_new(128); str_printfa(str, "proxy(%s): Login failed to %s:%u", client->common.virtual_user,
--- a/src/imap/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -4,6 +4,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ @@ -21,6 +22,7 @@ libs = \ $(STORAGE_LIBS) \ ../lib-dict/libdict.a \ + ../lib-settings/libsettings.a \ $(unused_objects) imap_LDADD = \ @@ -72,6 +74,7 @@ imap-fetch.c \ imap-fetch-body.c \ imap-search.c \ + imap-settings.c \ imap-sort.c \ imap-status.c \ imap-sync.c \ @@ -87,6 +90,7 @@ imap-expunge.h \ imap-fetch.h \ imap-search.h \ + imap-settings.h \ imap-sort.h \ imap-status.h \ imap-sync.h
--- a/src/imap/client.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/client.c Tue Jan 27 18:21:53 2009 -0500 @@ -26,7 +26,8 @@ client_destroy(client, "Disconnected for inactivity"); } -struct client *client_create(int fd_in, int fd_out, struct mail_user *user) +struct client *client_create(int fd_in, int fd_out, struct mail_user *user, + const struct imap_settings *set) { struct client *client; struct mail_namespace *ns; @@ -36,9 +37,11 @@ net_set_nonblock(fd_out, TRUE); client = i_new(struct client, 1); + client->set = set; client->fd_in = fd_in; client->fd_out = fd_out; - client->input = i_stream_create_fd(fd_in, imap_max_line_length, FALSE); + client->input = i_stream_create_fd(fd_in, + set->imap_max_line_length, FALSE); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); o_stream_set_flush_callback(client->output, client_output, client); @@ -56,6 +59,11 @@ &mail_storage_callbacks, client); } + client->capability_string = + str_new(default_pool, sizeof(CAPABILITY_STRING)+32); + str_append(client->capability_string, *set->imap_capability != '\0' ? + set->imap_capability : CAPABILITY_STRING); + i_assert(my_client == NULL); my_client = client; @@ -114,7 +122,7 @@ tab[1].value = dec2str(client->output->offset); str = t_str_new(128); - var_expand(str, logout_format, tab); + var_expand(str, client->set->imap_logout_format, tab); return str_c(str); } @@ -186,6 +194,7 @@ array_free(&client->search_saved_uidset); if (array_is_created(&client->search_updates)) array_free(&client->search_updates); + str_free(&client->capability_string); pool_unref(&client->command_pool); i_free(client); @@ -431,8 +440,9 @@ cmd->parser = client->free_parser; client->free_parser = NULL; } else { - cmd->parser = imap_parser_create(client->input, client->output, - imap_max_line_length); + cmd->parser = + imap_parser_create(client->input, client->output, + client->set->imap_max_line_length); } DLLIST_PREPEND(&client->command_queue, cmd);
--- a/src/imap/client.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/client.h Tue Jan 27 18:21:53 2009 -0500 @@ -74,7 +74,11 @@ struct ostream *output; struct timeout *to_idle, *to_idle_output; - struct mail_user *user; + const struct imap_settings *set; + enum client_workarounds workarounds; + string_t *capability_string; + + struct mail_user *user; struct mailbox *mailbox; struct mailbox_keywords keywords; unsigned int select_counter; /* increased when mailbox is changed */ @@ -124,7 +128,8 @@ /* Create new client with specified input/output handles. socket specifies if the handle is a socket. */ -struct client *client_create(int fd_in, int fd_out, struct mail_user *user); +struct client *client_create(int fd_in, int fd_out, struct mail_user *user, + const struct imap_settings *set); void client_destroy(struct client *client, const char *reason); /* Disconnect client connection */
--- a/src/imap/cmd-append.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/cmd-append.c Tue Jan 27 18:21:53 2009 -0500 @@ -499,7 +499,7 @@ o_stream_unset_flush_callback(client->output); ctx->save_parser = imap_parser_create(client->input, client->output, - imap_max_line_length); + client->set->imap_max_line_length); cmd->func = cmd_append_continue_parsing; cmd->context = ctx;
--- a/src/imap/cmd-capability.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/cmd-capability.c Tue Jan 27 18:21:53 2009 -0500 @@ -6,9 +6,8 @@ bool cmd_capability(struct client_command_context *cmd) { - client_send_line(cmd->client, - t_strconcat("* CAPABILITY ", - str_c(capability_string), NULL)); + client_send_line(cmd->client, t_strconcat( + "* CAPABILITY ", str_c(cmd->client->capability_string), NULL)); client_send_tagline(cmd, "OK Capability completed."); return TRUE;
--- a/src/imap/cmd-delete.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/cmd-delete.c Tue Jan 27 18:21:53 2009 -0500 @@ -36,7 +36,7 @@ return TRUE; } - if ((client_workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 && + if ((client->workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 && *name != '\0' && name[strlen(name)-1] == mail_storage_get_hierarchy_sep(storage)) { /* drop the extra trailing hierarchy separator */
--- a/src/imap/cmd-id.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/cmd-id.c Tue Jan 27 18:21:53 2009 -0500 @@ -5,6 +5,7 @@ bool cmd_id(struct client_command_context *cmd) { + const struct imap_settings *set = cmd->client->set; const struct imap_arg *args; const char *value; @@ -13,13 +14,13 @@ if (!cmd->client->id_logged) { cmd->client->id_logged = TRUE; - value = imap_id_args_get_log_reply(args, imap_id_log); + value = imap_id_args_get_log_reply(args, set->imap_id_log); if (value != NULL) i_info("ID sent: %s", value); } client_send_line(cmd->client, t_strdup_printf( - "* ID %s", imap_id_reply_generate(imap_id_send))); + "* ID %s", imap_id_reply_generate(set->imap_id_send))); client_send_tagline(cmd, "OK ID completed."); return TRUE; }
--- a/src/imap/cmd-idle.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/cmd-idle.c Tue Jan 27 18:21:53 2009 -0500 @@ -4,12 +4,12 @@ #include "ioloop.h" #include "istream.h" #include "ostream.h" +#include "mail-storage-settings.h" #include "commands.h" #include "imap-sync.h" #include <stdlib.h> -#define DEFAULT_IDLE_CHECK_INTERVAL 30 /* Send some noice to client every few minutes to avoid NATs and stateful firewalls from closing the connection */ #define KEEPALIVE_TIMEOUT (2*60) @@ -197,8 +197,6 @@ { struct client *client = cmd->client; struct cmd_idle_context *ctx; - const char *str; - unsigned int interval; ctx = p_new(cmd->pool, struct cmd_idle_context, 1); ctx->cmd = cmd; @@ -207,13 +205,12 @@ ctx->keepalive_to = timeout_add(KEEPALIVE_TIMEOUT * 1000, keepalive_timeout, ctx); - str = getenv("MAILBOX_IDLE_CHECK_INTERVAL"); - interval = str == NULL ? 0 : (unsigned int)strtoul(str, NULL, 10); - if (interval == 0) - interval = DEFAULT_IDLE_CHECK_INTERVAL; + if (client->mailbox != NULL) { + const struct mail_storage_settings *set; - if (client->mailbox != NULL) { - mailbox_notify_changes(client->mailbox, interval, + set = mailbox_get_settings(client->mailbox); + mailbox_notify_changes(client->mailbox, + set->mailbox_idle_check_interval, idle_callback, ctx); } client_send_line(client, "+ idling");
--- a/src/imap/cmd-subscribe.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/cmd-subscribe.c Tue Jan 27 18:21:53 2009 -0500 @@ -60,7 +60,7 @@ mailbox += strlen(ns->prefix); } - if ((client_workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 && + if ((cmd->client->workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 && *mailbox != '\0' && mailbox[strlen(mailbox)-1] == mail_storage_get_hierarchy_sep(ns->storage)) { /* verify the validity without the trailing '/' */
--- a/src/imap/common.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/common.h Tue Jan 27 18:21:53 2009 -0500 @@ -1,9 +1,6 @@ #ifndef COMMON_H #define COMMON_H -#include "lib.h" -#include "client.h" - /* Disconnect client after idling this many milliseconds */ #define CLIENT_IDLE_TIMEOUT_MSECS (60*30*1000) @@ -16,24 +13,17 @@ /* Disconnect client when it sends too many bad commands in a row */ #define CLIENT_MAX_BAD_COMMANDS 20 -/* RFC-2683 recommends at least 8000 bytes. Some clients however don't - break large message sets to multiple commands, so we're pretty liberal - by default. */ -#define DEFAULT_IMAP_MAX_LINE_LENGTH 65536 - enum client_workarounds { WORKAROUND_DELAY_NEWMAIL = 0x01, WORKAROUND_NETSCAPE_EOH = 0x04, WORKAROUND_TB_EXTRA_MAILBOX_SEP = 0x08 }; +#include "lib.h" +#include "client.h" +#include "imap-settings.h" + extern struct ioloop *ioloop; -extern unsigned int imap_max_line_length; -extern enum client_workarounds client_workarounds; -extern const char *logout_format; -extern const char *imap_id_send, *imap_id_log; - -extern string_t *capability_string; extern void (*hook_client_created)(struct client **client);
--- a/src/imap/imap-fetch-body.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/imap-fetch-body.c Tue Jan 27 18:21:53 2009 -0500 @@ -431,7 +431,7 @@ i_stream_seek(ctx->cur_input, old_offset); if (!ctx->cur_have_eoh && - (client_workarounds & WORKAROUND_NETSCAPE_EOH) != 0) { + (ctx->client->workarounds & WORKAROUND_NETSCAPE_EOH) != 0) { /* Netscape 4.x doesn't like if end of headers line is missing. */ msg_size.virtual_size += 2;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/imap/imap-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,102 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "mail-storage-settings.h" +#include "imap-settings.h" + +#include <stddef.h> +#include <stdlib.h> + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct imap_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct imap_settings, field), defines } + +static struct setting_define imap_setting_defines[] = { + DEF(SET_BOOL, mail_debug), + DEF(SET_BOOL, shutdown_clients), + DEF(SET_BOOL, verbose_proctitle), + + DEF(SET_STR, mail_plugins), + DEF(SET_STR, mail_plugin_dir), + DEF(SET_STR_VARS, mail_log_prefix), + + DEF(SET_UINT, imap_max_line_length), + DEF(SET_STR, imap_capability), + DEF(SET_STR, imap_client_workarounds), + DEF(SET_STR, imap_logout_format), + DEF(SET_STR, imap_id_send), + DEF(SET_STR, imap_id_log), + + SETTING_DEFINE_LIST_END +}; + +static struct imap_settings imap_default_settings = { + MEMBER(mail_debug) FALSE, + MEMBER(shutdown_clients) FALSE, + MEMBER(verbose_proctitle) FALSE, + + MEMBER(mail_plugins) "", + MEMBER(mail_plugin_dir) MODULEDIR"/imap", + MEMBER(mail_log_prefix) "%Us(%u): ", + + /* RFC-2683 recommends at least 8000 bytes. Some clients however don't + break large message sets to multiple commands, so we're pretty + liberal by default. */ + MEMBER(imap_max_line_length) 65536, + MEMBER(imap_capability) "", + MEMBER(imap_client_workarounds) "outlook-idle", + MEMBER(imap_logout_format) "bytes=%i/%o", + MEMBER(imap_id_send) "", + MEMBER(imap_id_log) "" +}; + +struct setting_parser_info imap_setting_parser_info = { + MEMBER(defines) imap_setting_defines, + MEMBER(defaults) &imap_default_settings, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct imap_settings) +}; + +static pool_t settings_pool = NULL; + +void imap_settings_read(const struct imap_settings **set_r, + const struct mail_user_settings **user_set_r) +{ + static const struct setting_parser_info *roots[] = { + &imap_setting_parser_info, + &mail_user_setting_parser_info + }; + struct setting_parser_context *parser; + void **sets; + + if (settings_pool == NULL) + settings_pool = pool_alloconly_create("imap settings", 2048); + else + p_clear(settings_pool); + + mail_storage_namespace_defines_init(settings_pool); + + parser = settings_parser_init_list(settings_pool, + roots, N_ELEMENTS(roots), + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); + + settings_parse_set_expanded(parser, TRUE); + if (settings_parse_environ(parser) < 0) { + i_fatal("Error reading configuration: %s", + settings_parser_get_error(parser)); + } + + sets = settings_parser_get_list(parser); + *set_r = sets[0]; + *user_set_r = sets[1]; + settings_parser_deinit(&parser); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/imap/imap-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,27 @@ +#ifndef IMAP_SETTINGS_H +#define IMAP_SETTINGS_H + +struct mail_user_settings; + +struct imap_settings { + bool mail_debug; + bool shutdown_clients; + bool verbose_proctitle; + + const char *mail_plugins; + const char *mail_plugin_dir; + const char *mail_log_prefix; + + /* imap: */ + unsigned int imap_max_line_length; + const char *imap_capability; + const char *imap_client_workarounds; + const char *imap_logout_format; + const char *imap_id_send; + const char *imap_id_log; +}; + +void imap_settings_read(const struct imap_settings **set_r, + const struct mail_user_settings **user_set_r); + +#endif
--- a/src/imap/imap-sync.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/imap-sync.c Tue Jan 27 18:21:53 2009 -0500 @@ -562,7 +562,7 @@ get_common_sync_flags(client, &flags, &imap_flags); client->sync_counter++; - no_newmail = (client_workarounds & WORKAROUND_DELAY_NEWMAIL) != 0 && + no_newmail = (client->workarounds & WORKAROUND_DELAY_NEWMAIL) != 0 && (imap_flags & IMAP_SYNC_FLAG_SAFE) == 0; if (no_newmail) { /* expunges might break the client just as badly as new mail
--- a/src/imap/main.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/imap/main.c Tue Jan 27 18:21:53 2009 -0500 @@ -39,10 +39,6 @@ }; struct ioloop *ioloop; -unsigned int imap_max_line_length; -enum client_workarounds client_workarounds = 0; -const char *logout_format; -const char *imap_id_send, *imap_id_log; static struct io *log_io = NULL; static struct module *modules = NULL; @@ -50,8 +46,6 @@ void (*hook_client_created)(struct client **client) = NULL; -string_t *capability_string; - static void sig_die(int signo, void *context ATTR_UNUSED) { /* warn about being killed because of some signal, except SIGINT (^C) @@ -66,16 +60,15 @@ io_loop_stop(ioloop); } -static void parse_workarounds(void) +static enum client_workarounds +parse_workarounds(const struct imap_settings *set) { + enum client_workarounds client_workarounds = 0; struct client_workaround_list *list; - const char *env, *const *str; + const char *const *str; - env = getenv("IMAP_CLIENT_WORKAROUNDS"); - if (env == NULL) - return; - - for (str = t_strsplit_spaces(env, " ,"); *str != NULL; str++) { + str = t_strsplit_spaces(set->imap_client_workarounds, " ,"); + for (; *str != NULL; str++) { list = client_workaround_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { @@ -86,6 +79,8 @@ if (list->name == NULL) i_fatal("Unknown client workaround: %s", *str); } + + return client_workarounds; } static void open_logfile(void) @@ -129,7 +124,8 @@ i_set_failure_timestamp_format(getenv("LOGSTAMP")); } -static void drop_privileges(void) +static void main_preinit(const struct imap_settings **set_r, + const struct mail_user_settings **user_set_r) { const char *version; @@ -143,25 +139,30 @@ /* Log file or syslog opening probably requires roots */ open_logfile(); - /* Load the plugins before chrooting. Their init() is called later. */ - if (getenv("MAIL_PLUGINS") != NULL) { - const char *plugin_dir = getenv("MAIL_PLUGIN_DIR"); + mail_storage_init(); + mail_storage_register_all(); + mailbox_list_register_all(); - if (plugin_dir == NULL) - plugin_dir = MODULEDIR"/imap"; - modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"), - TRUE, version); - } + /* read settings after registering storages so they can have their + own setting definitions too */ + imap_settings_read(set_r, user_set_r); + + /* Load the plugins before chrooting. Their init() is called later. */ + modules = *(*set_r)->mail_plugins == '\0' ? NULL : + module_dir_load((*set_r)->mail_plugin_dir, + (*set_r)->mail_plugins, TRUE, version); restrict_access_by_env(!IS_STANDALONE()); } -static void main_init(void) +static void main_init(const struct imap_settings *set, + const struct mail_user_settings *user_set) { struct client *client; struct ostream *output; struct mail_user *user; - const char *username, *home, *str, *tag; + const char *username, *home, *str, *tag, *error; + bool dump_capability; lib_signals_init(); lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); @@ -169,6 +170,8 @@ lib_signals_ignore(SIGPIPE, TRUE); lib_signals_ignore(SIGALRM, FALSE); + dump_capability = getenv("DUMP_CAPABILITY") != NULL; + username = getenv("USER"); if (username == NULL) { if (IS_STANDALONE()) @@ -178,63 +181,40 @@ } home = getenv("HOME"); - if (getenv("DEBUG") != NULL) { + if (set->mail_debug) { + home = getenv("HOME"); i_info("Effective uid=%s, gid=%s, home=%s", dec2str(geteuid()), dec2str(getegid()), home != NULL ? home : "(none)"); } - if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) { + if (set->shutdown_clients && !dump_capability) { /* If master dies, the log fd gets closed and we'll quit */ log_io = io_add(STDERR_FILENO, IO_ERROR, log_error_callback, NULL); } - capability_string = str_new(default_pool, sizeof(CAPABILITY_STRING)+32); - str_append(capability_string, CAPABILITY_STRING); - dict_drivers_register_builtin(); mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL); - mail_storage_init(); - mail_storage_register_all(); - mailbox_list_register_all(); clients_init(); commands_init(); module_dir_init(modules); - if (getenv("DUMP_CAPABILITY") != NULL) { - printf("%s\n", str_c(capability_string)); + user = mail_user_alloc(username, user_set); + mail_user_set_home(user, home); + if (mail_user_init(user, &error) < 0) + i_fatal("Mail user initialization failed: %s", error); + if (mail_namespaces_init(user, &error) < 0) + i_fatal("Namespace initialization failed: %s", error); + client = client_create(0, 1, user, set); + client->workarounds = parse_workarounds(set); + + if (dump_capability) { + printf("%s\n", str_c(client->capability_string)); exit(0); } - str = getenv("IMAP_CAPABILITY"); - if (str != NULL && *str != '\0') { - /* Overrides all capabilities */ - str_truncate(capability_string, 0); - str_append(capability_string, str); - } - - str = getenv("IMAP_MAX_LINE_LENGTH"); - imap_max_line_length = str != NULL ? - (unsigned int)strtoul(str, NULL, 10) : - DEFAULT_IMAP_MAX_LINE_LENGTH; - - logout_format = getenv("IMAP_LOGOUT_FORMAT"); - if (logout_format == NULL) - logout_format = "bytes=%i/%o"; - - imap_id_send = getenv("IMAP_ID_SEND"); - imap_id_log = getenv("IMAP_ID_LOG"); - - parse_workarounds(); - - user = mail_user_init(username); - mail_user_set_home(user, home); - if (mail_namespaces_init(user) < 0) - i_fatal("Namespace initialization failed"); - client = client_create(0, 1, user); - output = client->output; o_stream_ref(output); o_stream_cork(output); @@ -244,12 +224,12 @@ if (tag == NULL) { client_send_line(client, t_strconcat( "* PREAUTH [CAPABILITY ", - str_c(capability_string), "] " + str_c(client->capability_string), "] " "Logged in as ", user->username, NULL)); } else { client_send_line(client, t_strconcat( tag, " OK [CAPABILITY ", - str_c(capability_string), "] Logged in", NULL)); + str_c(client->capability_string), "] Logged in", NULL)); } str = getenv("CLIENT_INPUT"); if (str != NULL) T_BEGIN { @@ -277,14 +257,15 @@ mail_users_deinit(); dict_drivers_unregister_builtin(); - str_free(&capability_string); - lib_signals_deinit(); closelog(); } int main(int argc ATTR_UNUSED, char *argv[], char *envp[]) { + const struct imap_settings *set; + const struct mail_user_settings *user_set; + #ifdef DEBUG if (!IS_STANDALONE() && getenv("GDB") == NULL) fd_debug_verify_leaks(3, 1024); @@ -299,7 +280,7 @@ /* NOTE: we start rooted, so keep the code minimal until restrict_access_by_env() is called */ lib_init(); - drop_privileges(); + main_preinit(&set, &user_set); process_title_init(argv, envp); ioloop = io_loop_create(); @@ -307,7 +288,7 @@ /* fake that we're running, so we know if client was destroyed while initializing */ io_loop_set_running(ioloop); - main_init(); + main_init(set, user_set); if (io_loop_is_running(ioloop)) io_loop_run(ioloop); main_deinit();
--- a/src/lib-settings/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-settings/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -4,10 +4,12 @@ -I$(top_srcdir)/src/lib libsettings_a_SOURCES = \ - settings.c + settings.c \ + settings-parser.c headers = \ - settings.h + settings.h \ + settings-parser.h if INSTALL_HEADERS pkginc_libdir=$(pkgincludedir)/src/lib-settings
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-settings/settings-parser.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,903 @@ +/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "hash.h" +#include "network.h" +#include "istream.h" +#include "str.h" +#include "strescape.h" +#include "var-expand.h" +#include "settings-parser.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#define IS_WHITE(c) ((c) == ' ' || (c) == '\t') + +struct setting_link { + struct setting_link *parent; + const struct setting_parser_info *info; + + ARRAY_TYPE(void_array) *array; + void *set_struct; +}; + +struct setting_parser_context { + pool_t set_pool, parser_pool; + enum settings_parser_flags flags; + bool str_vars_are_expanded; + + struct setting_link *roots; + unsigned int root_count; + struct hash_table *links; + string_t *save_input_str; + + unsigned int linenum; + const char *error; + const struct setting_parser_info *prev_info; +}; + +static const struct setting_parser_info strlist_info = { + MEMBER(defines) NULL, + MEMBER(defaults) NULL, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) 0 +}; + +struct setting_parser_context * +settings_parser_init(pool_t set_pool, const struct setting_parser_info *root, + enum settings_parser_flags flags) +{ + return settings_parser_init_list(set_pool, &root, 1, flags); +} + +static void +setting_parser_copy_defaults(const struct setting_parser_info *info, + pool_t pool, void *dest) +{ + const struct setting_define *def; + const char *p, **strp; + + if (info->defaults == NULL) + return; + + memcpy(dest, info->defaults, info->struct_size); + for (def = info->defines; def->key != NULL; def++) { + switch (def->type) { + case SET_ENUM: { + /* fix enums by dropping everything after the + first ':' */ + strp = STRUCT_MEMBER_P(dest, def->offset); + p = strchr(*strp, ':'); + if (p != NULL) + *strp = p_strdup_until(pool, *strp, p); + break; + } + case SET_STR_VARS: { + /* insert the unexpanded-character */ + strp = STRUCT_MEMBER_P(dest, def->offset); + if (*strp != NULL) { + *strp = p_strconcat(pool, + SETTING_STRVAR_UNEXPANDED, + *strp, NULL); + } + break; + } + default: + break; + } + } +} + +struct setting_parser_context * +settings_parser_init_list(pool_t set_pool, + const struct setting_parser_info *const *roots, + unsigned int count, enum settings_parser_flags flags) +{ + struct setting_parser_context *ctx; + unsigned int i; + pool_t parser_pool; + + i_assert(count > 0); + + parser_pool = pool_alloconly_create("settings parser", 8192); + ctx = p_new(parser_pool, struct setting_parser_context, 1); + ctx->set_pool = set_pool; + ctx->parser_pool = parser_pool; + ctx->flags = flags; + + ctx->root_count = count; + ctx->roots = p_new(ctx->set_pool, struct setting_link, count); + for (i = 0; i < count; i++) { + ctx->roots[i].info = roots[i]; + ctx->roots[i].set_struct = + p_malloc(ctx->set_pool, roots[i]->struct_size); + setting_parser_copy_defaults(roots[i], ctx->set_pool, + ctx->roots[i].set_struct); + } + + ctx->links = hash_table_create(default_pool, ctx->parser_pool, 0, + str_hash, (hash_cmp_callback_t *)strcmp); + pool_ref(ctx->set_pool); + return ctx; +} + +void settings_parser_deinit(struct setting_parser_context **_ctx) +{ + struct setting_parser_context *ctx = *_ctx; + + *_ctx = NULL; + hash_table_destroy(&ctx->links); + pool_unref(&ctx->set_pool); + pool_unref(&ctx->parser_pool); +} + +void *settings_parser_get(struct setting_parser_context *ctx) +{ + i_assert(ctx->root_count == 1); + + return ctx->roots[0].set_struct; +} + +void **settings_parser_get_list(struct setting_parser_context *ctx) +{ + unsigned int i; + void **sets; + + sets = t_new(void *, ctx->root_count); + for (i = 0; i < ctx->root_count; i++) + sets[i] = ctx->roots[i].set_struct; + return sets; +} + +const char *settings_parser_get_error(struct setting_parser_context *ctx) +{ + return ctx->error; +} + +static const struct setting_define * +setting_define_find(const struct setting_parser_info *info, const char *key) +{ + const struct setting_define *list; + + for (list = info->defines; list->key != NULL; list++) { + if (strcmp(list->key, key) == 0 && list->type != SET_INTERNAL) + return list; + } + return NULL; +} + +static int +get_bool(struct setting_parser_context *ctx, const char *value, bool *result_r) +{ + /* FIXME: eventually we'd want to support only yes/no */ + if (strcasecmp(value, "yes") == 0 || + strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0) + *result_r = TRUE; + else if (strcasecmp(value, "no") == 0) + *result_r = FALSE; + else { + ctx->error = p_strconcat(ctx->parser_pool, "Invalid boolean: ", + value, NULL); + return -1; + } + + return 0; +} + +static int +get_uint(struct setting_parser_context *ctx, const char *value, + unsigned int *result_r) +{ + int num; + + /* use %i so we can handle eg. 0600 as octal value with umasks */ + if (!sscanf(value, "%i", &num) || num < 0) { + ctx->error = p_strconcat(ctx->parser_pool, "Invalid number: ", + value, NULL); + return -1; + } + *result_r = num; + return 0; +} + +static int get_enum(struct setting_parser_context *ctx, const char *value, + char **result_r, const char *allowed_values) +{ + const char *p; + + while (allowed_values != NULL) { + p = strchr(allowed_values, ':'); + if (p == NULL) { + if (strcmp(allowed_values, value) == 0) + break; + + ctx->error = p_strconcat(ctx->parser_pool, + "Invalid value: ", + value, NULL); + return -1; + } + + if (strncmp(allowed_values, value, p - allowed_values) == 0 && + value[p - allowed_values] == '\0') + break; + + allowed_values = p + 1; + } + + *result_r = p_strdup(ctx->set_pool, value); + return 0; +} + +static int +get_deflist(struct setting_parser_context *ctx, struct setting_link *parent, + const struct setting_parser_info *info, + const char *key, const char *value, ARRAY_TYPE(void_array) *result) +{ + struct setting_link *link; + const char *const *list; + char *full_key; + + i_assert(info->defines != NULL || info == &strlist_info); + + if (!array_is_created(result)) + p_array_init(result, ctx->set_pool, 5); + + list = t_strsplit(value, "\t "); + for (; *list != NULL; list++) { + if (**list == '\0') + continue; + + full_key = p_strconcat(ctx->parser_pool, key, + SETTINGS_SEPARATOR_S, *list, NULL); + if (hash_table_lookup(ctx->links, full_key) != NULL) { + ctx->error = p_strconcat(ctx->parser_pool, full_key, + " already exists", NULL); + return -1; + } + + link = p_new(ctx->parser_pool, struct setting_link, 1); + link->parent = parent; + link->info = info; + link->array = result; + hash_table_insert(ctx->links, full_key, link); + } + return 0; +} + +static int +settings_parse(struct setting_parser_context *ctx, struct setting_link *link, + const struct setting_define *def, + const char *key, const char *value) +{ + void *ptr, *ptr2; + + ctx->prev_info = link->info; + + if (link->set_struct == NULL) { + link->set_struct = + p_malloc(ctx->set_pool, link->info->struct_size); + setting_parser_copy_defaults(link->info, ctx->set_pool, + link->set_struct); + array_append(link->array, &link->set_struct, 1); + + if (link->info->parent_offset != (size_t)-1 && + link->parent != NULL) { + ptr = STRUCT_MEMBER_P(link->set_struct, + link->info->parent_offset); + *((void **)ptr) = link->parent->set_struct; + } + } + + ptr = STRUCT_MEMBER_P(link->set_struct, def->offset); + switch (def->type) { + case SET_INTERNAL: + i_unreached(); + case SET_BOOL: + return get_bool(ctx, value, (bool *)ptr); + case SET_UINT: + return get_uint(ctx, value, (unsigned int *)ptr); + case SET_STR: + *((char **)ptr) = p_strdup(ctx->set_pool, value); + return 0; + case SET_STR_VARS: + *((char **)ptr) = p_strconcat(ctx->set_pool, + ctx->str_vars_are_expanded ? + SETTING_STRVAR_EXPANDED : + SETTING_STRVAR_UNEXPANDED, + value, NULL); + return 0; + case SET_ENUM: + /* get the available values from default string */ + i_assert(link->info->defaults != NULL); + ptr2 = STRUCT_MEMBER_P(link->info->defaults, def->offset); + return get_enum(ctx, value, (char **)ptr, *(const char **)ptr2); + case SET_DEFLIST: + ctx->prev_info = def->list_info; + return get_deflist(ctx, link, def->list_info, + key, value, (ARRAY_TYPE(void_array) *)ptr); + case SET_STRLIST: { + ctx->prev_info = &strlist_info; + return get_deflist(ctx, link, &strlist_info, + key, value, (ARRAY_TYPE(void_array) *)ptr); + } + } + + i_unreached(); + return -1; +} + +static int settings_parse_keyvalue(struct setting_parser_context *ctx, + const char *key, const char *value) +{ + const struct setting_define *def = NULL; + unsigned int i; + int ret = 1; + + /* try to find from roots */ + for (i = 0; i < ctx->root_count; i++) { + def = setting_define_find(ctx->roots[i].info, key); + if (def != NULL) + break; + } + if (def != NULL) { + if (settings_parse(ctx, &ctx->roots[i], def, key, value) < 0) + ret = -1; + } else { + /* try to find from links */ + const char *end = strrchr(key, SETTINGS_SEPARATOR); + struct setting_link *link; + + link = end == NULL ? NULL : + hash_table_lookup(ctx->links, t_strdup_until(key, end)); + if (link == NULL) + def = NULL; + else if (link->info == &strlist_info) { + void *vkey, *vvalue; + + vkey = p_strdup(ctx->set_pool, end + 1); + vvalue = p_strdup(ctx->set_pool, value); + array_append(link->array, &vkey, 1); + array_append(link->array, &vvalue, 1); + return 1; + } else { + def = setting_define_find(link->info, end + 1); + } + if (def != NULL) { + if (settings_parse(ctx, link, def, key, value) < 0) + ret = -1; + } else { + ctx->error = p_strconcat(ctx->parser_pool, + "Unknown setting: ", key, NULL); + ret = 0; + } + } + return ret; +} + +int settings_parse_line(struct setting_parser_context *ctx, const char *line) +{ + const char *key, *value; + int ret; + + ctx->error = NULL; + ctx->prev_info = NULL; + + key = line; + value = strchr(line, '='); + if (value == NULL) { + ctx->error = "Missing '='"; + return -1; + } + + if (key == value) { + ctx->error = "Missing key name ('=' at the beginning of line)"; + return -1; + } + + T_BEGIN { + key = t_strdup_until(key, value); + ret = settings_parse_keyvalue(ctx, key, value + 1); + } T_END; + return ret; +} + +const struct setting_parser_info * +settings_parse_get_prev_info(struct setting_parser_context *ctx) +{ + return ctx->prev_info; +} + +int settings_parse_stream(struct setting_parser_context *ctx, + struct istream *input) +{ + const char *line; + string_t *full_line; + size_t len; + int ret = 1; + + full_line = str_new(default_pool, 512); + while ((line = i_stream_next_line(input)) != NULL) { + if (*line == '\0') { + /* empty line finishes it */ + ret = 0; + break; + } + + ctx->linenum++; + while (IS_WHITE(*line == ' ')) + line++; + if (*line == '\0' || *line == '#') + continue; + + len = strlen(line); + while (len > 0 && IS_WHITE(line[len-1])) + len--; + if (line[len] == '\\') { + /* line continues */ + str_append_n(full_line, line, len - 1); + } else { + /* full line */ + if (str_len(full_line) > 0) { + str_append_n(full_line, line, len); + line = str_c(full_line); + } + + ret = settings_parse_line(ctx, line); + if (ret == 0 && + (ctx->flags & + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS) == 0) + ret = -1; + + if (ret < 0) { + ctx->error = p_strdup_printf(ctx->parser_pool, + "Line %u: %s", + ctx->linenum, + ctx->error); + ret = -1; + break; + } + str_truncate(full_line, 0); + } + } + str_free(&full_line); + return ret; +} + +int settings_parse_stream_read(struct setting_parser_context *ctx, + struct istream *input) +{ + const unsigned char *data; + size_t size; + int ret; + + while ((ret = i_stream_read(input)) > 0) { + if (ctx->save_input_str != NULL) { + data = i_stream_get_data(input, &size); + str_append_n(ctx->save_input_str, data, size); + } + if ((ret = settings_parse_stream(ctx, input)) < 0) + return -1; + if (ret == 0) { + /* empty line read */ + return 0; + } + } + + switch (ret) { + case -1: + if (input->stream_errno != 0) { + ctx->error = p_strdup_printf(ctx->parser_pool, + "read() failed: %m"); + } else { + ctx->error = "input is missing end-of-settings line"; + } + break; + case -2: + ctx->error = p_strdup_printf(ctx->parser_pool, + "Line %u: line too long", + ctx->linenum); + break; + case 0: + /* blocks */ + return 1; + default: + i_unreached(); + } + return -1; +} + +int settings_parse_file(struct setting_parser_context *ctx, + const char *path, size_t max_line_length) +{ + struct istream *input; + int fd, ret; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ctx->error = p_strdup_printf(ctx->parser_pool, + "open(%s) failed: %m", path); + return -1; + } + + input = i_stream_create_fd(fd, max_line_length, TRUE); + ret = settings_parse_stream_read(ctx, input); + i_stream_unref(&input); + + return ret; +} + +int settings_parse_environ(struct setting_parser_context *ctx) +{ + extern char **environ; + const char *key, *value; + unsigned int i; + int ret = 0; + + for (i = 0; environ[i] != NULL && ret == 0; i++) { + value = strchr(environ[i], '='); + if (value != NULL) T_BEGIN { + key = t_strdup_until(environ[i], value++); + key = t_str_lcase(key); + if (settings_parse_keyvalue(ctx, key, value) < 0) { + ctx->error = p_strdup_printf(ctx->parser_pool, + "Invalid setting %s: %s", + key, ctx->error); + ret = -1; + } + } T_END; + } + return ret; +} + +int settings_parse_exec(struct setting_parser_context *ctx, + const char *bin_path, const char *config_path, + const char *service) +{ + struct istream *input; + pid_t pid; + int ret, fd[2], status; + + if (pipe(fd) < 0) { + i_error("pipe() failed: %m"); + return -1; + } + + pid = fork(); + if (pid == (pid_t)-1) { + i_error("fork() failed: %m"); + (void)close(fd[0]); + (void)close(fd[1]); + return -1; + } + if (pid == 0) { + /* child */ + static const char *argv[] = { + NULL, + "-c", NULL, + "-s", NULL, + NULL + }; + argv[0] = bin_path; + argv[2] = config_path; + argv[4] = service; + (void)close(fd[0]); + if (dup2(fd[1], STDOUT_FILENO) < 0) + i_fatal("dup2() failed: %m"); + + execv(argv[0], (void *)argv); + i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", bin_path); + return -1; + } + (void)close(fd[1]); + + input = i_stream_create_fd(fd[0], (size_t)-1, TRUE); + ret = settings_parse_stream_read(ctx, input); + i_stream_destroy(&input); + + if (waitpid(pid, &status, 0) < 0) { + i_error("waitpid() failed: %m"); + ret = -1; + } else if (status != 0) { + i_error("%s returned failure: %d", bin_path, status); + ret = -1; + } + return ret; +} + +void settings_parse_set_expanded(struct setting_parser_context *ctx, + bool is_expanded) +{ + ctx->str_vars_are_expanded = is_expanded; +} + +static void +settings_var_expand_info(const struct setting_parser_info *info, + pool_t pool, void *set, + const struct var_expand_table *table, string_t *str) +{ + const struct setting_define *def; + void *value, *const *children; + unsigned int i, count; + + for (def = info->defines; def->key != NULL; def++) { + value = PTR_OFFSET(set, def->offset); + switch (def->type) { + case SET_STR_VARS: { + const char **val = value; + + if (*val == NULL) + break; + + if (**val == SETTING_STRVAR_UNEXPANDED[0]) { + str_truncate(str, 0); + var_expand(str, *val + 1, table); + *val = p_strdup(pool, str_c(str)); + } else { + i_assert(**val == SETTING_STRVAR_EXPANDED[0]); + *val += 1; + } + break; + } + case SET_DEFLIST: { + const ARRAY_TYPE(void_array) *val = value; + + if (!array_is_created(val)) + break; + + children = array_get(val, &count); + for (i = 0; i < count; i++) { + settings_var_expand_info(def->list_info, + pool, children[i], + table, str); + } + break; + } + default: + break; + } + } +} + +void settings_var_expand(const struct setting_parser_info *info, + void *set, pool_t pool, + const struct var_expand_table *table) +{ + string_t *str; + + T_BEGIN { + str = t_str_new(256); + settings_var_expand_info(info, pool, set, table, str); + } T_END; +} + +bool settings_vars_have_key(const struct setting_parser_info *info, void *set, + char var_key, const char *long_var_key, + const char **key_r, const char **value_r) +{ + const struct setting_define *def; + const void *value; + void *const *children; + unsigned int i, count; + + for (def = info->defines; def->key != NULL; def++) { + value = CONST_PTR_OFFSET(set, def->offset); + switch (def->type) { + case SET_STR_VARS: { + const char *const *val = value; + + if (*val == NULL) + break; + + if (**val == SETTING_STRVAR_UNEXPANDED[0]) { + if (var_has_key(*val + 1, var_key, + long_var_key)) { + *key_r = def->key; + *value_r = *val + 1; + return TRUE; + } + } else { + i_assert(**val == SETTING_STRVAR_EXPANDED[0]); + } + break; + } + case SET_DEFLIST: { + const ARRAY_TYPE(void_array) *val = value; + + if (!array_is_created(val)) + break; + + children = array_get(val, &count); + for (i = 0; i < count; i++) { + if (settings_vars_have_key(def->list_info, + children[i], var_key, + long_var_key, + key_r, value_r)) + return TRUE; + } + break; + } + default: + break; + } + } + return FALSE; +} + +void *settings_dup(const struct setting_parser_info *info, + const void *set, pool_t pool) +{ + const struct setting_define *def; + const void *src; + void *dest_set, *dest, *const *children; + unsigned int i, count; + + dest_set = p_malloc(pool, info->struct_size); + memcpy(dest_set, set, info->struct_size); + for (def = info->defines; def->key != NULL; def++) { + src = CONST_PTR_OFFSET(set, def->offset); + dest = PTR_OFFSET(dest_set, def->offset); + + switch (def->type) { + case SET_INTERNAL: + case SET_BOOL: + case SET_UINT: + break; + case SET_STR_VARS: + case SET_STR: + case SET_ENUM: { + const char *const *src_str = src; + const char **dest_str = dest; + + *dest_str = p_strdup(pool, *src_str); + break; + } + case SET_DEFLIST: { + const ARRAY_TYPE(void_array) *src_arr = src; + ARRAY_TYPE(void_array) *dest_arr = dest; + void *child_set; + + if (!array_is_created(src_arr)) + break; + + children = array_get(src_arr, &count); + p_array_init(dest_arr, pool, count); + for (i = 0; i < count; i++) { + child_set = settings_dup(def->list_info, + children[i], pool); + array_append(dest_arr, &child_set, 1); + } + break; + } + case SET_STRLIST: { + const ARRAY_TYPE(const_string) *src_arr = src; + ARRAY_TYPE(const_string) *dest_arr = dest; + const char *const *strings, *dup; + + if (!array_is_created(src_arr)) + break; + + strings = array_get(src_arr, &count); + p_array_init(dest_arr, pool, count); + for (i = 0; i < count; i += 2) + dup = p_strdup(pool, strings[i]); + break; + } + } + } + return dest_set; +} + +void settings_parse_save_input(struct setting_parser_context *ctx, + string_t *dest) +{ + ctx->save_input_str = dest; +} + +static void +info_update_real(pool_t pool, struct setting_parser_info *parent, + const struct dynamic_settings_parser *parsers) +{ + /* @UNSAFE */ + ARRAY_DEFINE(defines, struct setting_define); + ARRAY_TYPE(dynamic_settings_parser) dynamic_parsers; + struct dynamic_settings_parser new_parser; + const struct setting_define *cur_defines; + struct setting_define *new_defines, new_define; + void *parent_defaults; + unsigned int i, j; + size_t offset, new_struct_size; + + t_array_init(&defines, 128); + /* add existing defines */ + for (j = 0; parent->defines[j].key != NULL; j++) + array_append(&defines, &parent->defines[j], 1); + new_struct_size = parent->struct_size; + + /* add new dynamic defines */ + for (i = 0; parsers[i].name != NULL; i++) { + i_assert(parsers[i].info->parent == parent); + cur_defines = parsers[i].info->defines; + for (j = 0; cur_defines[j].key != NULL; j++) { + new_define = cur_defines[j]; + new_define.offset += new_struct_size; + array_append(&defines, &new_define, 1); + } + new_struct_size += MEM_ALIGN(parsers[i].info->struct_size); + } + new_defines = p_new(pool, struct setting_define, + array_count(&defines) + 1); + memcpy(new_defines, array_idx(&defines, 0), + sizeof(*parent->defines) * array_count(&defines)); + parent->defines = new_defines; + + /* update defaults */ + parent_defaults = p_malloc(pool, new_struct_size); + memcpy(parent_defaults, parent->defaults, parent->struct_size); + offset = parent->struct_size; + for (i = 0; parsers[i].name != NULL; i++) { + memcpy(PTR_OFFSET(parent_defaults, offset), + parsers[i].info->defaults, parsers[i].info->struct_size); + offset += MEM_ALIGN(parsers[i].info->struct_size); + } + parent->defaults = parent_defaults; + + /* update dynamic parsers list */ + t_array_init(&dynamic_parsers, 32); + if (parent->dynamic_parsers != NULL) { + for (i = 0; parent->dynamic_parsers[i].name != NULL; i++) { + array_append(&dynamic_parsers, + &parent->dynamic_parsers[i], 1); + } + } + offset = parent->struct_size; + for (i = 0; parsers[i].name != NULL; i++) { + new_parser = parsers[i]; + new_parser.name = p_strdup(pool, new_parser.name); + new_parser.struct_offset = offset; + array_append(&dynamic_parsers, &new_parser, 1); + offset += MEM_ALIGN(parsers[i].info->struct_size); + } + parent->dynamic_parsers = + p_new(pool, struct dynamic_settings_parser, + array_count(&dynamic_parsers) + 1); + memcpy(parent->dynamic_parsers, array_idx(&dynamic_parsers, 0), + sizeof(*parent->dynamic_parsers) * + array_count(&dynamic_parsers)); + parent->struct_size = new_struct_size; +} + +void +settings_parser_info_update(pool_t pool, struct setting_parser_info *parent, + const struct dynamic_settings_parser *parsers) +{ + T_BEGIN { + info_update_real(pool, parent, parsers); + } T_END; +} + +const void *settings_find_dynamic(struct setting_parser_info *info, + const void *base_set, const char *name) +{ + unsigned int i; + + if (info->dynamic_parsers == NULL) + return NULL; + + for (i = 0; info->dynamic_parsers[i].name != NULL; i++) { + if (strcmp(info->dynamic_parsers[i].name, name) == 0) { + return CONST_PTR_OFFSET(base_set, + info->dynamic_parsers[i].struct_offset); + } + } + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-settings/settings-parser.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,147 @@ +#ifndef SETTINGS_PARSER_H +#define SETTINGS_PARSER_H + +struct var_expand_table; + +#define SETTINGS_SEPARATOR '/' +#define SETTINGS_SEPARATOR_S "/" + +/* STR_VARS pointer begins with either of these initially. Before actually + using the variables all variables in all unexpanded strings need to be + expanded. Afterwards the string pointers should be increased to skip + the initial '1' so it'll be easy to use them. */ +#define SETTING_STRVAR_UNEXPANDED "0" +#define SETTING_STRVAR_EXPANDED "1" + +enum setting_type { + SET_INTERNAL, /* don't set this variable */ + SET_BOOL, + SET_UINT, + SET_STR, + SET_STR_VARS, /* string with %variables */ + SET_ENUM, + SET_DEFLIST, /* of type array_t */ + SET_STRLIST /* of type ARRAY_TYPE(const_string) */ +}; + +#define SETTING_DEFINE_LIST_END { 0, NULL, 0, NULL } + +struct setting_define { + enum setting_type type; + const char *key; + + size_t offset; + const struct setting_parser_info *list_info; +}; + +#define SETTING_DEFINE_STRUCT_BOOL(name, struct_name) \ + { SET_BOOL + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ + ((struct struct_name *)0)->name, bool), \ + #name, offsetof(struct struct_name, name), NULL } +#define SETTING_DEFINE_STRUCT_UINT(name, struct_name) \ + { SET_UINT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ + ((struct struct_name *)0)->name, unsigned int), \ + #name, offsetof(struct struct_name, name), NULL } +#define SETTING_DEFINE_STRUCT_STR(name, struct_name) \ + { SET_STR + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \ + ((struct struct_name *)0)->name, const char *), \ + #name, offsetof(struct struct_name, name), NULL } + +struct setting_parser_info { + const struct setting_define *defines; + const void *defaults; + + struct setting_parser_info *parent; + struct dynamic_settings_parser *dynamic_parsers; + + size_t parent_offset; + size_t type_offset; + size_t struct_size; +}; +ARRAY_DEFINE_TYPE(setting_parser_info, struct setting_parser_info); + +/* name=NULL-terminated list of parsers. These follow the static settings. + After this list follows the actual settings. */ +struct dynamic_settings_parser { + const char *name; + const struct setting_parser_info *info; + size_t struct_offset; +}; +ARRAY_DEFINE_TYPE(dynamic_settings_parser, struct dynamic_settings_parser); + +enum settings_parser_flags { + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS = 0x01 +}; + +struct setting_parser_context; + +struct setting_parser_context * +settings_parser_init(pool_t set_pool, const struct setting_parser_info *root, + enum settings_parser_flags flags); +struct setting_parser_context * +settings_parser_init_list(pool_t set_pool, + const struct setting_parser_info *const *roots, + unsigned int count, enum settings_parser_flags flags); +void settings_parser_deinit(struct setting_parser_context **ctx); + +/* Return pointer to root setting structure. */ +void *settings_parser_get(struct setting_parser_context *ctx); +/* If there are multiple roots, return list to all of their settings. */ +void **settings_parser_get_list(struct setting_parser_context *ctx); + +/* Return the last error. */ +const char *settings_parser_get_error(struct setting_parser_context *ctx); +/* Return the parser info used for the previously parsed line. */ +const struct setting_parser_info * +settings_parse_get_prev_info(struct setting_parser_context *ctx); +/* Save all parsed input to given string. */ +void settings_parse_save_input(struct setting_parser_context *ctx, + string_t *dest); + +/* Parse a single line. Returns 1 if OK, 0 if key is unknown, -1 if error. */ +int settings_parse_line(struct setting_parser_context *ctx, const char *line); +/* Parse data already read in input stream. */ +int settings_parse_stream(struct setting_parser_context *ctx, + struct istream *input); +/* Read data from input stream and parser it. returns -1 = error, + 0 = eof/stream error, 1 = not finished yet (stream is non-blocking) */ +int settings_parse_stream_read(struct setting_parser_context *ctx, + struct istream *input); +/* Open file and parse it. */ +int settings_parse_file(struct setting_parser_context *ctx, + const char *path, size_t max_line_length); +int settings_parse_environ(struct setting_parser_context *ctx); +/* Execute the given binary and wait for it to return the configuration. */ +int settings_parse_exec(struct setting_parser_context *ctx, + const char *bin_path, const char *config_path, + const char *service); + +/* While parsing values, specifies if STR_VARS strings are already expanded. */ +void settings_parse_set_expanded(struct setting_parser_context *ctx, + bool is_expanded); +/* Expand all unexpanded variables using the given table. Update the string + pointers so that they can be used without skipping over the '1'. */ +void settings_var_expand(const struct setting_parser_info *info, + void *set, pool_t pool, + const struct var_expand_table *table); +/* Go through all the settings and return the first one that has an unexpanded + setting containing the given %key. */ +bool settings_vars_have_key(const struct setting_parser_info *info, void *set, + char var_key, const char *long_var_key, + const char **key_r, const char **value_r); +/* Duplicate the entire settings structure. */ +void *settings_dup(const struct setting_parser_info *info, + const void *set, pool_t pool); +/* parsers is a name=NULL -terminated list. The parsers are appended as + dynamic_settings_list structures to parent. The new structures are allocated + from the given pool. */ +void +settings_parser_info_update(pool_t pool, struct setting_parser_info *parent, + const struct dynamic_settings_parser *parsers); + +/* Return pointer to beginning of settings for given name, or NULL if there is + no such registered name. */ +const void *settings_find_dynamic(struct setting_parser_info *info, + const void *base_set, const char *name); + +#endif
--- a/src/lib-storage/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-auth \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ @@ -18,6 +19,7 @@ mail-search.c \ mail-search-build.c \ mail-storage.c \ + mail-storage-settings.c \ mail-user.c \ mailbox-list.c \ mailbox-search-result.c \ @@ -33,6 +35,7 @@ mail-thread.h \ mail-storage.h \ mail-storage-private.h \ + mail-storage-settings.h \ mail-user.h \ mailbox-list.h \ mailbox-list-private.h \
--- a/src/lib-storage/index/cydir/cydir-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/cydir/cydir-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -37,7 +37,7 @@ const char *data, struct mail_storage *storage, const char **layout_r, const char **error_r) { - bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; *layout_r = "fs"; @@ -125,8 +125,7 @@ storage, &storage->list_module_ctx); /* finish list init after we've overridden vfuncs */ - mailbox_list_init(_storage->list, _storage->ns, &list_set, - mail_storage_get_list_flags(_storage->flags)); + mailbox_list_init(_storage->list, _storage->ns, &list_set, 0); return 0; } @@ -409,6 +408,7 @@ MEMBER(mailbox_is_file) FALSE, { + NULL, cydir_class_init, cydir_class_deinit, cydir_alloc,
--- a/src/lib-storage/index/dbox/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/dbox/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ @@ -14,6 +15,7 @@ dbox-index.c \ dbox-mail.c \ dbox-save.c \ + dbox-settings.c \ dbox-sync.c \ dbox-sync-file.c \ dbox-sync-rebuild.c \ @@ -24,6 +26,7 @@ dbox-file.h \ dbox-file-maildir.h \ dbox-index.h \ + dbox-settings.h \ dbox-storage.h \ dbox-sync.h
--- a/src/lib-storage/index/dbox/dbox-file.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/dbox/dbox-file.c Tue Jan 27 18:21:53 2009 -0500 @@ -154,6 +154,7 @@ struct dbox_file * dbox_file_init(struct dbox_mailbox *mbox, unsigned int file_id) { + const struct dbox_settings *set = mbox->storage->set; struct dbox_file *file; unsigned int count; bool maildir; @@ -166,8 +167,8 @@ } count = array_count(&mbox->open_files); - if (count > mbox->max_open_files) - dbox_close_open_files(mbox, count - mbox->max_open_files); + if (count > set->dbox_max_open_files) + dbox_close_open_files(mbox, count - set->dbox_max_open_files); file = i_new(struct dbox_file, 1); file->refcount = 1; @@ -254,7 +255,8 @@ if (file->file_id != 0) { files = array_get(&file->mbox->open_files, &count); - if (!file->deleted && count <= file->mbox->max_open_files) { + if (!file->deleted && + count <= file->mbox->storage->set->dbox_max_open_files) { /* we can leave this file open for now */ return; } @@ -292,6 +294,8 @@ bool dbox_file_can_append(struct dbox_file *file, uoff_t mail_size) { + const struct dbox_settings *set = file->mbox->storage->set; + if (file->nonappendable) return FALSE; @@ -300,12 +304,12 @@ return FALSE; } - if (file->append_offset < file->mbox->rotate_min_size || + if (file->append_offset < set->dbox_rotate_min_size || file->append_offset == file->file_header_size) return TRUE; - if (file->append_offset + mail_size >= file->mbox->rotate_size) + if (file->append_offset + mail_size >= set->dbox_rotate_size) return FALSE; - return file->create_time >= day_begin_stamp(file->mbox->rotate_days); + return file->create_time >= day_begin_stamp(set->dbox_rotate_days); } static int dbox_file_parse_header(struct dbox_file *file, const char *line)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/index/dbox/dbox-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,45 @@ +/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "mail-storage-settings.h" +#include "dbox-settings.h" + +#include <stddef.h> + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct dbox_settings, name), NULL } + +static struct setting_define dbox_setting_defines[] = { + DEF(SET_UINT, dbox_rotate_size), + DEF(SET_UINT, dbox_rotate_min_size), + DEF(SET_UINT, dbox_rotate_days), + DEF(SET_UINT, dbox_max_open_files), + + SETTING_DEFINE_LIST_END +}; + +static struct dbox_settings dbox_default_settings = { + MEMBER(dbox_rotate_size) 2048*1024, + MEMBER(dbox_rotate_min_size) 16*1024, + MEMBER(dbox_rotate_days) 0, + MEMBER(dbox_max_open_files) 64 +}; + +static struct setting_parser_info dbox_setting_parser_info = { + MEMBER(defines) dbox_setting_defines, + MEMBER(defaults) &dbox_default_settings, + + MEMBER(parent) &mail_user_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct dbox_settings) +}; + +const struct setting_parser_info *dbox_get_setting_parser_info(void) +{ + return &dbox_setting_parser_info; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/index/dbox/dbox-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,13 @@ +#ifndef DBOX_SETTINGS_H +#define DBOX_SETTINGS_H + +struct dbox_settings { + unsigned int dbox_rotate_size; + unsigned int dbox_rotate_min_size; + unsigned int dbox_rotate_days; + unsigned int dbox_max_open_files; +}; + +const struct setting_parser_info *dbox_get_setting_parser_info(void); + +#endif
--- a/src/lib-storage/index/dbox/dbox-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/dbox/dbox-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -50,7 +50,7 @@ const char **layout_r, const char **alt_dir_r, const char **error_r) { - bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; *layout_r = "fs"; @@ -132,6 +132,8 @@ return -1; } + storage->set = mail_storage_get_driver_settings(_storage); + if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0) return -1; storage->list_module_ctx.super = _storage->list->v; @@ -145,8 +147,7 @@ storage, &storage->list_module_ctx); /* finish list init after we've overridden vfuncs */ - mailbox_list_init(_storage->list, _storage->ns, &list_set, - mail_storage_get_list_flags(_storage->flags)); + mailbox_list_init(_storage->list, _storage->ns, &list_set, 0); return 0; } @@ -194,7 +195,7 @@ struct mail_storage *_storage = &storage->storage; struct dbox_mailbox *mbox; struct mail_index *index; - const char *path, *value; + const char *path; pool_t pool; path = mailbox_list_get_path(_storage->list, name, @@ -216,31 +217,8 @@ mbox->storage = storage; mbox->last_interactive_change = ioloop_time; - value = getenv("DBOX_ROTATE_SIZE"); - if (value != NULL) - mbox->rotate_size = (uoff_t)strtoul(value, NULL, 10) * 1024; - else - mbox->rotate_size = DBOX_DEFAULT_ROTATE_SIZE; - mbox->rotate_size = 0; /* FIXME: currently anything else doesn't work */ - value = getenv("DBOX_ROTATE_MIN_SIZE"); - if (value != NULL) - mbox->rotate_min_size = (uoff_t)strtoul(value, NULL, 10) * 1024; - else - mbox->rotate_min_size = DBOX_DEFAULT_ROTATE_MIN_SIZE; - if (mbox->rotate_min_size > mbox->rotate_size) - mbox->rotate_min_size = mbox->rotate_size; - value = getenv("DBOX_ROTATE_DAYS"); - if (value != NULL) - mbox->rotate_days = (unsigned int)strtoul(value, NULL, 10); - else - mbox->rotate_days = DBOX_DEFAULT_ROTATE_DAYS; - - value = getenv("DBOX_MAX_OPEN_FILES"); - if (value != NULL) - mbox->max_open_files = (unsigned int)strtoul(value, NULL, 10); - else - mbox->max_open_files = DBOX_DEFAULT_MAX_OPEN_FILES; - i_array_init(&mbox->open_files, I_MIN(mbox->max_open_files, 128)); + i_array_init(&mbox->open_files, + I_MIN(storage->set->dbox_max_open_files, 128)); mbox->dbox_ext_id = mail_index_ext_register(index, "dbox", 0, @@ -679,6 +657,7 @@ MEMBER(mailbox_is_file) FALSE, { + dbox_get_setting_parser_info, dbox_class_init, dbox_class_deinit, dbox_alloc,
--- a/src/lib-storage/index/dbox/dbox-storage.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/dbox/dbox-storage.h Tue Jan 27 18:21:53 2009 -0500 @@ -3,6 +3,7 @@ #include "index-storage.h" #include "mailbox-list-private.h" +#include "dbox-settings.h" #define DBOX_STORAGE_NAME "dbox" #define DBOX_SUBSCRIPTION_FILE_NAME ".dbox-subscriptions" @@ -21,12 +22,6 @@ /* Delete temp files having ctime older than this. */ #define DBOX_TMP_DELETE_SECS (36*60*60) -/* Default rotation settings */ -#define DBOX_DEFAULT_ROTATE_SIZE (2*1024*1024) -#define DBOX_DEFAULT_ROTATE_MIN_SIZE (1024*16) -#define DBOX_DEFAULT_ROTATE_DAYS 0 -#define DBOX_DEFAULT_MAX_OPEN_FILES 64 - /* Flag specifies if the message should be in primary or alternative storage */ #define DBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND @@ -37,6 +32,7 @@ struct dbox_storage { struct mail_storage storage; union mailbox_list_module_context list_module_ctx; + const struct dbox_settings *set; const char *alt_dir; }; @@ -56,11 +52,7 @@ /* set while rebuilding indexes with converted maildir files */ struct maildir_keywords_sync_ctx *maildir_sync_keywords; - uoff_t rotate_size, rotate_min_size; - unsigned int rotate_days; - ARRAY_DEFINE(open_files, struct dbox_file *); - unsigned int max_open_files; const char *path, *alt_path; };
--- a/src/lib-storage/index/index-mail.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/index-mail.c Tue Jan 27 18:21:53 2009 -0500 @@ -418,13 +418,14 @@ void index_mail_cache_add_idx(struct index_mail *mail, unsigned int field_idx, const void *data, size_t data_size) { + const struct mail_storage_settings *set = mail->ibox->box.storage->set; const struct mail_index_header *hdr; - if (mail->ibox->mail_cache_min_mail_count > 0) { + if (set->mail_cache_min_mail_count > 0) { /* First check if we've configured caching not to be used with low enough message count. */ hdr = mail_index_get_header(mail->ibox->view); - if (hdr->messages_count < mail->ibox->mail_cache_min_mail_count) + if (hdr->messages_count < set->mail_cache_min_mail_count) return; }
--- a/src/lib-storage/index/index-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/index-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -17,9 +17,6 @@ #include <unistd.h> #include <sys/stat.h> -#define DEFAULT_CACHE_FIELDS "" -#define DEFAULT_NEVER_CACHE_FIELDS "imap.envelope" - /* How many seconds to keep index opened for reuse after it's been closed */ #define INDEX_CACHE_TIMEOUT 10 /* How many closed indexes to keep */ @@ -305,29 +302,20 @@ static void index_cache_register_defaults(struct index_mailbox *ibox) { + const struct mail_storage_settings *set = ibox->box.storage->set; static bool initialized = FALSE; struct mail_cache *cache = ibox->cache; - const char *cache_env, *never_env, *env; if (!initialized) { initialized = TRUE; - cache_env = getenv("MAIL_CACHE_FIELDS"); - if (cache_env == NULL) - cache_env = DEFAULT_CACHE_FIELDS; - never_env = getenv("MAIL_NEVER_CACHE_FIELDS"); - if (never_env == NULL) - never_env = DEFAULT_NEVER_CACHE_FIELDS; - - set_cache_decisions("mail_cache_fields", cache_env, + set_cache_decisions("mail_cache_fields", + set->mail_cache_fields, MAIL_CACHE_DECISION_TEMP); - set_cache_decisions("mail_never_cache_fields", never_env, + set_cache_decisions("mail_never_cache_fields", + set->mail_never_cache_fields, MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED); - - env = getenv("MAIL_CACHE_MIN_MAIL_COUNT"); - if (env != NULL) - ibox->mail_cache_min_mail_count = atoi(env); } ibox->cache_fields = i_malloc(sizeof(global_cache_fields)); @@ -399,20 +387,9 @@ i_assert(!ibox->box.opened); + index_flags = mail_storage_settings_to_index_flags(storage->set); if (!ibox->move_to_memory) index_flags |= MAIL_INDEX_OPEN_FLAG_CREATE; -#ifndef MMAP_CONFLICTS_WRITE - if ((storage->flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0) -#endif - index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; - if ((storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0) - index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL; - if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX) != 0) - index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH; - if ((storage->flags & MAIL_STORAGE_FLAG_FSYNC_DISABLE) != 0) { - index_flags |= MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE; - ibox->fsync_disable = TRUE; - } ret = mail_index_open(ibox->index, index_flags, storage->lock_method); if (ret <= 0 || ibox->move_to_memory) { @@ -563,7 +540,7 @@ return FALSE; } } - if (i > ibox->box.storage->keyword_max_len) { + if (i > ibox->box.storage->set->mail_max_keyword_length) { *error_r = "Keyword length too long"; return FALSE; }
--- a/src/lib-storage/index/index-storage.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/index-storage.h Tue Jan 27 18:21:53 2009 -0500 @@ -47,7 +47,6 @@ const ARRAY_TYPE(keywords) *keyword_names; struct mail_cache_field *cache_fields; - unsigned int mail_cache_min_mail_count; ARRAY_TYPE(seq_range) recent_flags; uint32_t recent_flags_prev_uid;
--- a/src/lib-storage/index/maildir/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ @@ -14,6 +15,7 @@ maildir-keywords.c \ maildir-mail.c \ maildir-save.c \ + maildir-settings.c \ maildir-storage.c \ maildir-sync.c \ maildir-sync-index.c \ @@ -25,6 +27,7 @@ maildir-filename.h \ maildir-keywords.h \ maildir-storage.h \ + maildir-settings.h \ maildir-sync.h \ maildir-uidlist.h
--- a/src/lib-storage/index/maildir/maildir-copy.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/maildir-copy.c Tue Jan 27 18:21:53 2009 -0500 @@ -94,8 +94,7 @@ do_save_mail_vsize(path, ctx); } - if ((mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) + if (mbox->storage->storage.set->mail_nfs_storage) ret = nfs_safe_link(path, str_c(ctx->dest_path), FALSE); else ret = link(path, str_c(ctx->dest_path)); @@ -162,7 +161,8 @@ memset(&do_ctx, 0, sizeof(do_ctx)); do_ctx.dest_path = str_new(default_pool, 512); - if (dest_mbox->storage->copy_preserve_filename && src_mbox != NULL) { + if (dest_mbox->storage->set->maildir_copy_preserve_filename && + src_mbox != NULL) { enum maildir_uidlist_rec_flag src_flags; const char *src_fname; @@ -268,7 +268,7 @@ struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->ictx.ibox; int ret; - if (mbox->storage->copy_with_hardlinks && + if (mbox->storage->set->maildir_copy_with_hardlinks && maildir_compatible_file_modes(&mbox->ibox.box, mail->box)) { T_BEGIN { ret = maildir_copy_hardlink(t, mail, flags,
--- a/src/lib-storage/index/maildir/maildir-keywords.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/maildir-keywords.c Tue Jan 27 18:21:53 2009 -0500 @@ -75,10 +75,9 @@ strcase_hash, (hash_cmp_callback_t *)strcasecmp); mk->dotlock_settings.use_excl_lock = - (box->storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0; + box->storage->set->dotlock_use_excl; mk->dotlock_settings.nfs_flush = - (box->storage->flags & - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0; + box->storage->set->mail_nfs_storage; mk->dotlock_settings.timeout = KEYWORDS_LOCK_STALE_TIMEOUT + 2; mk->dotlock_settings.stale_timeout = KEYWORDS_LOCK_STALE_TIMEOUT; mk->dotlock_settings.temp_prefix = @@ -117,7 +116,7 @@ we rely on stat()'s timestamp and don't bother handling ESTALE errors. */ - if ((mk->storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) { + if (mk->storage->set->mail_nfs_storage) { /* file is updated only by replacing it, no need to flush attribute cache */ nfs_flush_file_handle_cache(mk->path);
--- a/src/lib-storage/index/maildir/maildir-save.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/maildir-save.c Tue Jan 27 18:21:53 2009 -0500 @@ -378,11 +378,10 @@ if (ctx->fd == -1) ctx->failed = TRUE; else { - ctx->input = (ctx->mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ? - i_stream_create_crlf(input) : - i_stream_create_lf(input); - + if (ctx->mbox->storage->storage.set->mail_save_crlf) + ctx->input = i_stream_create_crlf(input); + else + ctx->input = i_stream_create_lf(input); maildir_save_add(t, fname, _ctx->flags, _ctx->keywords, _ctx->dest_mail); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/index/maildir/maildir-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,44 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "mail-storage-settings.h" +#include "maildir-settings.h" + +#include <stddef.h> + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct maildir_settings, name), NULL } + +static struct setting_define maildir_setting_defines[] = { + DEF(SET_BOOL, maildir_stat_dirs), + DEF(SET_BOOL, maildir_copy_with_hardlinks), + DEF(SET_BOOL, maildir_copy_preserve_filename), + + SETTING_DEFINE_LIST_END +}; + +static struct maildir_settings maildir_default_settings = { + MEMBER(maildir_stat_dirs) FALSE, + MEMBER(maildir_copy_with_hardlinks) TRUE, + MEMBER(maildir_copy_preserve_filename) FALSE +}; + +static struct setting_parser_info maildir_setting_parser_info = { + MEMBER(defines) maildir_setting_defines, + MEMBER(defaults) &maildir_default_settings, + + MEMBER(parent) &mail_user_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct maildir_settings) +}; + +const struct setting_parser_info *maildir_get_setting_parser_info(void) +{ + return &maildir_setting_parser_info; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/index/maildir/maildir-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,12 @@ +#ifndef MAILDIR_SETTINGS_H +#define MAILDIR_SETTINGS_H + +struct maildir_settings { + bool maildir_stat_dirs; + bool maildir_copy_with_hardlinks; + bool maildir_copy_preserve_filename; +}; + +const struct setting_parser_info *maildir_get_setting_parser_info(void); + +#endif
--- a/src/lib-storage/index/maildir/maildir-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/maildir-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -63,9 +63,8 @@ const char *data, struct mail_storage *storage, const char **layout_r, const char **error_r) { - enum mail_storage_flags flags = storage->flags; struct mail_user *user = storage->ns->user; - bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; const char *path, *home; *layout_r = MAILDIR_PLUSPLUS_DRIVER_NAME; @@ -75,7 +74,8 @@ list_set->maildir_name = ""; if (data == NULL || *data == '\0') { - if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) { + if ((storage->flags & + MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) { *error_r = "Root mail directory not given"; return -1; } @@ -229,6 +229,8 @@ if (mailbox_list_alloc(layout, &list, error_r) < 0) return -1; + storage->set = mail_storage_get_driver_settings(_storage); + _storage->list = list; storage->list_module_ctx.super = list->v; if (strcmp(layout, MAILDIR_PLUSPLUS_DRIVER_NAME) == 0) { @@ -248,14 +250,7 @@ storage, &storage->list_module_ctx); /* finish list init after we've overridden vfuncs */ - mailbox_list_init(list, _storage->ns, &list_set, - mail_storage_get_list_flags(flags)); - - storage->copy_with_hardlinks = - getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL; - storage->copy_preserve_filename = - getenv("MAILDIR_COPY_PRESERVE_FILENAME") != NULL; - storage->stat_dirs = getenv("MAILDIR_STAT_DIRS") != NULL; + mailbox_list_init(list, _storage->ns, &list_set, 0); storage->temp_prefix = mailbox_list_get_temp_prefix(list); if (list_set.control_dir == NULL) { @@ -267,15 +262,13 @@ return 0; } -static bool maildir_autodetect(const char *data, enum mail_storage_flags flags) +static bool maildir_autodetect(const struct mail_namespace *ns) { - bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = ns->mail_set->mail_debug; struct stat st; const char *path; - data = t_strcut(data, ':'); - - path = t_strconcat(data, "/cur", NULL); + path = t_strconcat(t_strcut(ns->set->location, ':'), "/cur", NULL); if (stat(path, &st) < 0) { if (debug) i_info("maildir autodetect: stat(%s) failed: %m", path); @@ -629,7 +622,7 @@ const char *root_dir; char sep; - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 && + if (list->mail_set->mail_full_filesystem_access && (*name == '/' || *name == '~')) return NULL; @@ -1014,7 +1007,7 @@ /* Check files beginning with .nfs always because they may be temporary files created by the kernel */ - if (storage->stat_dirs || *fname == '\0' || + if (storage->set->maildir_stat_dirs || *fname == '\0' || strncmp(fname, ".nfs", 4) == 0) { const char *path; struct stat st; @@ -1071,6 +1064,7 @@ MEMBER(mailbox_is_file) FALSE, { + maildir_get_setting_parser_info, maildir_class_init, maildir_class_deinit, maildir_alloc,
--- a/src/lib-storage/index/maildir/maildir-storage.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/maildir-storage.h Tue Jan 27 18:21:53 2009 -0500 @@ -1,6 +1,8 @@ #ifndef MAILDIR_STORAGE_H #define MAILDIR_STORAGE_H +#include "maildir-settings.h" + #define MAILDIR_STORAGE_NAME "maildir" #define MAILDIR_SUBSCRIPTION_FILE_NAME "subscriptions" #define MAILDIR_INDEX_PREFIX "dovecot.index" @@ -69,14 +71,12 @@ struct mail_storage storage; union mailbox_list_module_context list_module_ctx; + const struct maildir_settings *set; const char *temp_prefix; uint32_t maildir_list_ext_id; - unsigned int copy_with_hardlinks:1; - unsigned int copy_preserve_filename:1; unsigned int save_size_in_filename:1; - unsigned int stat_dirs:1; }; struct maildir_mailbox {
--- a/src/lib-storage/index/maildir/maildir-uidlist.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/maildir/maildir-uidlist.c Tue Jan 27 18:21:53 2009 -0500 @@ -236,10 +236,9 @@ uidlist->dotlock_settings.use_io_notify = TRUE; uidlist->dotlock_settings.use_excl_lock = - (box->storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0; + box->storage->set->dotlock_use_excl; uidlist->dotlock_settings.nfs_flush = - (box->storage->flags & - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0; + box->storage->set->mail_nfs_storage; uidlist->dotlock_settings.timeout = MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT + 2; uidlist->dotlock_settings.stale_timeout = @@ -711,7 +710,7 @@ { struct mail_storage *storage = uidlist->ibox->box.storage; - if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) { + if (storage->set->mail_nfs_storage) { nfs_flush_file_handle_cache(uidlist->path); nfs_flush_attr_cache_unlocked(uidlist->path); } @@ -745,7 +744,7 @@ return 1; } - if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) { + if (storage->set->mail_nfs_storage) { /* NFS: either the file hasn't been changed, or it has already been deleted and the inodes just happen to be the same. check if the fd is still valid. */ @@ -1432,7 +1431,6 @@ if (ctx->failed) return -1; - for (p = filename; *p != '\0'; p++) { if (*p == 13 || *p == 10) { i_warning("Maildir %s: Ignoring a file with #0x%x: %s",
--- a/src/lib-storage/index/mbox/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ @@ -15,6 +16,7 @@ mbox-mail.c \ mbox-md5.c \ mbox-save.c \ + mbox-settings.c \ mbox-sync-parse.c \ mbox-sync-rewrite.c \ mbox-sync-update.c \ @@ -27,6 +29,7 @@ mbox-file.h \ mbox-lock.h \ mbox-md5.h \ + mbox-settings.h \ mbox-storage.h \ mbox-sync-private.h
--- a/src/lib-storage/index/mbox/mbox-lock.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/mbox-lock.c Tue Jan 27 18:21:53 2009 -0500 @@ -22,14 +22,6 @@ /* 0.1 .. 0.2msec */ #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) -/* lock methods to use in wanted order */ -#define DEFAULT_READ_LOCK_METHODS "fcntl" -#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl" -/* lock timeout */ -#define MBOX_DEFAULT_LOCK_TIMEOUT (5*60) -/* assume stale dotlock if mbox file hasn't changed for n seconds */ -#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT (120) - enum mbox_lock_type { MBOX_LOCK_DOTLOCK, MBOX_LOCK_DOTLOCK_TRY, @@ -92,11 +84,6 @@ { 0, NULL, NULL } }; -static bool lock_settings_initialized = FALSE; -static enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1]; -static enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1]; -static int lock_timeout, dotlock_change_timeout; - static int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type, time_t max_wait_time, int idx); static int mbox_unlock_files(struct mbox_lock_context *ctx); @@ -133,18 +120,16 @@ locks[dest] = (enum mbox_lock_type)-1; } -static void mbox_init_lock_settings(void) +static void mbox_init_lock_settings(struct mbox_storage *storage) { - const char *str; + enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1]; + enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1]; int r, w; - str = getenv("MBOX_READ_LOCKS"); - if (str == NULL) str = DEFAULT_READ_LOCK_METHODS; - mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks); - - str = getenv("MBOX_WRITE_LOCKS"); - if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS; - mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks); + mbox_read_lock_methods(storage->set->mbox_read_locks, + "mbox_read_locks", read_locks); + mbox_read_lock_methods(storage->set->mbox_write_locks, + "mbox_write_locks", write_locks); /* check that read/write list orders match. write_locks must contain at least read_locks and possibly more. */ @@ -161,14 +146,17 @@ "(and possibly more)"); } - str = getenv("MBOX_LOCK_TIMEOUT"); - lock_timeout = str == NULL ? MBOX_DEFAULT_LOCK_TIMEOUT : atoi(str); + storage->read_locks = p_new(storage->storage.pool, + enum mbox_lock_type, MBOX_LOCK_COUNT+1); + memcpy(storage->read_locks, read_locks, + sizeof(*storage->read_locks) * (MBOX_LOCK_COUNT+1)); - str = getenv("MBOX_DOTLOCK_CHANGE_TIMEOUT"); - dotlock_change_timeout = str == NULL ? - DEFAULT_DOTLOCK_CHANGE_TIMEOUT : atoi(str); + storage->write_locks = p_new(storage->storage.pool, + enum mbox_lock_type, MBOX_LOCK_COUNT+1); + memcpy(storage->write_locks, write_locks, + sizeof(*storage->write_locks) * (MBOX_LOCK_COUNT+1)); - lock_settings_initialized = TRUE; + storage->lock_settings_initialized = TRUE; } static int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type) @@ -221,7 +209,8 @@ lock_types = ctx->lock_type == F_WRLCK || (ctx->lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ? - write_locks : read_locks; + ctx->mbox->storage->write_locks : + ctx->mbox->storage->read_locks; for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) { if (lock_types[i] == MBOX_LOCK_DOTLOCK) @@ -370,12 +359,10 @@ ctx->dotlock_last_stale = -1; memset(&set, 0, sizeof(set)); - set.use_excl_lock = (mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0; - set.nfs_flush = (mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0; - set.timeout = lock_timeout; - set.stale_timeout = dotlock_change_timeout; + set.use_excl_lock = mbox->storage->storage.set->dotlock_use_excl; + set.nfs_flush = mbox->storage->storage.set->mail_nfs_storage; + set.timeout = mbox->storage->set->mbox_lock_timeout; + set.stale_timeout = mbox->storage->set->mbox_dotlock_change_timeout; set.callback = dotlock_callback; set.context = ctx; @@ -588,7 +575,8 @@ lock_types = lock_type == F_WRLCK || (lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ? - write_locks : read_locks; + ctx->mbox->storage->write_locks : + ctx->mbox->storage->read_locks; for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) { type = lock_types[i]; lock_status = lock_type != F_UNLCK; @@ -616,8 +604,8 @@ index_storage_lock_notify_reset(&mbox->ibox); - if (!lock_settings_initialized) - mbox_init_lock_settings(); + if (!mbox->storage->lock_settings_initialized) + mbox_init_lock_settings(mbox->storage); if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) { /* read-only mbox stream. no need to lock. */ @@ -626,7 +614,7 @@ return 1; } - max_wait_time = time(NULL) + lock_timeout; + max_wait_time = time(NULL) + mbox->storage->set->mbox_lock_timeout; memset(&ctx, 0, sizeof(ctx)); ctx.mbox = mbox; @@ -634,6 +622,9 @@ if (mbox->mbox_lock_type == F_WRLCK) { /* dropping to shared lock. first drop those that we don't remove completely. */ + const enum mbox_lock_type *read_locks = + mbox->storage->read_locks; + for (i = 0; i < MBOX_LOCK_COUNT; i++) ctx.lock_status[i] = 1; for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++) @@ -658,6 +649,11 @@ if (drop_locks) { /* dropping to shared lock: drop the locks that are only in write list */ + const enum mbox_lock_type *read_locks = + mbox->storage->read_locks; + const enum mbox_lock_type *write_locks = + mbox->storage->write_locks; + memset(ctx.lock_status, 0, sizeof(ctx.lock_status)); for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++) ctx.lock_status[write_locks[i]] = 1; @@ -691,8 +687,7 @@ if (ret <= 0) return ret; - if ((mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) { + if (mbox->storage->storage.set->mail_nfs_storage) { if (fcntl_locked) { nfs_flush_attr_cache_fd_locked(mbox->path, mbox->mbox_fd);
--- a/src/lib-storage/index/mbox/mbox-mail.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/mbox-mail.c Tue Jan 27 18:21:53 2009 -0500 @@ -208,8 +208,8 @@ if (seq == hdr->messages_count) { /* last message, use the synced mbox size */ - trailer_size = (mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ? 2 : 1; + trailer_size = + mbox->storage->storage.set->mail_save_crlf ? 2 : 1; *next_offset_r = mbox->mbox_hdr.sync_size - trailer_size; } else { if (mbox_file_lookup_offset(mbox, view, seq + 1,
--- a/src/lib-storage/index/mbox/mbox-save.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/mbox-save.c Tue Jan 27 18:21:53 2009 -0500 @@ -393,8 +393,7 @@ } /* convert linefeeds to wanted format */ - ret = (ctx->mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ? + ret = ctx->mbox->storage->storage.set->mail_save_crlf ? i_stream_create_crlf(filter) : i_stream_create_lf(filter); i_stream_unref(&filter); @@ -542,8 +541,7 @@ this makes it impossible to save a mail that doesn't end with LF though. */ const char *linefeed = - (ctx->mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ? + ctx->mbox->storage->storage.set->mail_save_crlf ? "\r\n" : "\n"; if (o_stream_send_str(ctx->output, linefeed) < 0) return write_error(ctx);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/index/mbox/mbox-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,53 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "mail-storage-settings.h" +#include "mbox-settings.h" + +#include <stddef.h> + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct mbox_settings, name), NULL } + +static struct setting_define mbox_setting_defines[] = { + DEF(SET_STR, mbox_read_locks), + DEF(SET_STR, mbox_write_locks), + DEF(SET_UINT, mbox_lock_timeout), + DEF(SET_UINT, mbox_dotlock_change_timeout), + DEF(SET_UINT, mbox_min_index_size), + DEF(SET_BOOL, mbox_dirty_syncs), + DEF(SET_BOOL, mbox_very_dirty_syncs), + DEF(SET_BOOL, mbox_lazy_writes), + + SETTING_DEFINE_LIST_END +}; + +static struct mbox_settings mbox_default_settings = { + MEMBER(mbox_read_locks) "fcntl", + MEMBER(mbox_write_locks) "dotlock fcntl", + MEMBER(mbox_lock_timeout) 5*60, + MEMBER(mbox_dotlock_change_timeout) 2*60, + MEMBER(mbox_min_index_size) 0, + MEMBER(mbox_dirty_syncs) TRUE, + MEMBER(mbox_very_dirty_syncs) FALSE, + MEMBER(mbox_lazy_writes) TRUE +}; + +static struct setting_parser_info mbox_setting_parser_info = { + MEMBER(defines) mbox_setting_defines, + MEMBER(defaults) &mbox_default_settings, + + MEMBER(parent) &mail_user_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct mbox_settings) +}; + +const struct setting_parser_info *mbox_get_setting_parser_info(void) +{ + return &mbox_setting_parser_info; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/index/mbox/mbox-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,17 @@ +#ifndef MBOX_SETTINGS_H +#define MBOX_SETTINGS_H + +struct mbox_settings { + const char *mbox_read_locks; + const char *mbox_write_locks; + unsigned int mbox_lock_timeout; + unsigned int mbox_dotlock_change_timeout; + unsigned int mbox_min_index_size; + bool mbox_dirty_syncs; + bool mbox_very_dirty_syncs; + bool mbox_lazy_writes; +}; + +const struct setting_parser_info *mbox_get_setting_parser_info(void); + +#endif
--- a/src/lib-storage/index/mbox/mbox-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/mbox-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -155,13 +155,13 @@ return TRUE; } -static bool mbox_autodetect(const char *data, enum mail_storage_flags flags) +static bool mbox_autodetect(const struct mail_namespace *ns) { - bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = ns->mail_set->mail_debug; + const char *data = ns->set->location; const char *path; path = t_strcut(data, ':'); - if (debug) { if (strchr(data, ':') != NULL) { i_info("mbox autodetect: data=%s, splitting ':' -> %s", @@ -187,8 +187,10 @@ static const char *get_root_dir(struct mail_storage *storage) { + struct mail_namespace auto_ns; + struct mail_namespace_settings ns_set; const char *home, *path; - bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; if (mail_user_get_home(storage->ns->user, &home) > 0) { path = t_strconcat(home, "/mail", NULL); @@ -212,12 +214,17 @@ if (debug) i_info("mbox: checking if we are chrooted:"); - if (mbox_autodetect("", storage->flags)) + + memset(&ns_set, 0, sizeof(ns_set)); + ns_set.location = ""; + memset(&auto_ns, 0, sizeof(auto_ns)); + auto_ns.set = &ns_set; + auto_ns.mail_set = storage->set; + if (mbox_autodetect(&auto_ns)) return "/"; if (debug) i_info("mbox: root mail directory not found"); - return NULL; } @@ -270,7 +277,7 @@ return NULL; } - if ((storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0) + if (storage->set->mail_debug) i_info("mbox: root directory created: %s", path); return path; } @@ -281,7 +288,7 @@ const char **layout_r, const char **error_r) { enum mail_storage_flags flags = storage->flags; - bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; const char *p; struct stat st; bool autodetect; @@ -453,6 +460,8 @@ if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0) return -1; + storage->set = mail_storage_get_driver_settings(_storage); + storage->list_module_ctx.super = _storage->list->v; if (strcmp(layout, "fs") == 0 && *list_set.maildir_name == '\0') { /* have to use .imap/ directories */ @@ -468,7 +477,6 @@ /* finish list init after we've overridden vfuncs */ mailbox_list_init(_storage->list, _storage->ns, &list_set, - mail_storage_get_list_flags(_storage->flags) | MAILBOX_LIST_FLAG_MAILBOX_FILES); return 0; } @@ -515,16 +523,9 @@ static bool want_memory_indexes(struct mbox_storage *storage, const char *path) { - const char *env; struct stat st; - unsigned int min_size; - env = getenv("MBOX_MIN_INDEX_SIZE"); - if (env == NULL) - return FALSE; - - min_size = strtoul(env, NULL, 10); - if (min_size == 0) + if (storage->set->mbox_min_index_size == 0) return FALSE; if (stat(path, &st) < 0) { @@ -536,7 +537,7 @@ return FALSE; } } - return st.st_size / 1024 < min_size; + return st.st_size / 1024 < storage->set->mbox_min_index_size; } static void mbox_lock_touch_timeout(struct mbox_mailbox *mbox) @@ -569,10 +570,6 @@ sizeof(mbox->mbox_hdr), sizeof(uint64_t), sizeof(uint64_t)); - mbox->mbox_very_dirty_syncs = getenv("MBOX_VERY_DIRTY_SYNCS") != NULL; - mbox->mbox_do_dirty_syncs = mbox->mbox_very_dirty_syncs || - getenv("MBOX_DIRTY_SYNCS") != NULL; - if ((storage->storage.flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0) mbox->mbox_save_md5 = TRUE; @@ -989,6 +986,7 @@ MEMBER(mailbox_is_file) TRUE, { + mbox_get_setting_parser_info, mbox_class_init, mbox_class_deinit, mbox_alloc,
--- a/src/lib-storage/index/mbox/mbox-storage.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/mbox-storage.h Tue Jan 27 18:21:53 2009 -0500 @@ -1,6 +1,10 @@ #ifndef MBOX_STORAGE_H #define MBOX_STORAGE_H +#include "index-storage.h" +#include "mbox-settings.h" +#include "mailbox-list-private.h" + /* Padding to leave in X-Keywords header when rewriting mbox */ #define MBOX_HEADER_PADDING 50 /* Don't write Content-Length header unless it's value is larger than this. */ @@ -11,9 +15,6 @@ #define MBOX_INDEX_PREFIX "dovecot.index" #define MBOX_INDEX_DIR_NAME ".imap" -#include "index-storage.h" -#include "mailbox-list-private.h" - struct mbox_index_header { uint64_t sync_size; uint32_t sync_mtime; @@ -23,6 +24,11 @@ struct mbox_storage { struct mail_storage storage; + const struct mbox_settings *set; + enum mbox_lock_type *read_locks; + enum mbox_lock_type *write_locks; + unsigned int lock_settings_initialized:1; + union mailbox_list_module_context list_module_ctx; }; @@ -49,8 +55,6 @@ unsigned int no_mbox_file:1; unsigned int invalid_mbox_file:1; unsigned int mbox_broken_offsets:1; - unsigned int mbox_do_dirty_syncs:1; - unsigned int mbox_very_dirty_syncs:1; unsigned int mbox_save_md5:1; unsigned int mbox_dotlocked:1; unsigned int mbox_used_privileges:1;
--- a/src/lib-storage/index/mbox/mbox-sync.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/mbox/mbox-sync.c Tue Jan 27 18:21:53 2009 -0500 @@ -1675,9 +1675,10 @@ delay_writes = mbox->ibox.backend_readonly || ((flags & MBOX_SYNC_REWRITE) == 0 && - getenv("MBOX_LAZY_WRITES") != NULL); + mbox->storage->set->mbox_lazy_writes); - if (!mbox->mbox_do_dirty_syncs) + if (!mbox->storage->set->mbox_dirty_syncs && + !mbox->storage->set->mbox_very_dirty_syncs) flags |= MBOX_SYNC_UNDIRTY; if ((flags & MBOX_SYNC_LOCK_READING) != 0) { @@ -1865,8 +1866,8 @@ i_assert(*lock_id != 0); - if ((mbox->storage->storage.flags & - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0 && mbox->mbox_fd != -1) { + if (mbox->storage->storage.set->mail_nfs_storage && + mbox->mbox_fd != -1) { if (fdatasync(mbox->mbox_fd) < 0) { mbox_set_syscall_error(mbox, "fdatasync()"); ret = -1; @@ -1923,7 +1924,7 @@ if (index_mailbox_want_full_sync(&mbox->ibox, flags)) { if ((flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0 && - !mbox->mbox_very_dirty_syncs) + !mbox->storage->set->mbox_very_dirty_syncs) mbox_sync_flags |= MBOX_SYNC_UNDIRTY; if ((flags & MAILBOX_SYNC_FLAG_FULL_WRITE) != 0) mbox_sync_flags |= MBOX_SYNC_REWRITE;
--- a/src/lib-storage/index/raw/raw-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/raw/raw-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -30,7 +30,7 @@ const char *data, struct mail_storage *storage, const char **layout_r, const char **error_r) { - bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; *layout_r = "fs"; @@ -102,7 +102,7 @@ /* finish list init after we've overridden vfuncs */ mailbox_list_init(_storage->list, _storage->ns, &list_set, - mail_storage_get_list_flags(_storage->flags)); + MAILBOX_LIST_FLAG_MAILBOX_FILES); return 0; } @@ -258,6 +258,7 @@ MEMBER(mailbox_is_file) TRUE, { + NULL, raw_class_init, raw_class_deinit, raw_alloc,
--- a/src/lib-storage/index/shared/shared-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/index/shared/shared-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -69,7 +69,6 @@ return -1; } storage->ns_prefix_pattern = p_strdup(_storage->pool, wildcardp); - *wildcardp = '\0'; have_username = FALSE; for (p = storage->ns_prefix_pattern; *p != '\0'; p++) { @@ -91,6 +90,10 @@ return -1; } + /* truncate prefix after the above checks are done, so they can log + the full prefix in error conditions */ + *wildcardp = '\0'; + if (mailbox_list_alloc("shared", &_storage->list, error_r) < 0) return -1; MODULE_CONTEXT_SET_FULL(_storage->list, shared_mailbox_list_module, @@ -99,8 +102,7 @@ memset(&list_set, 0, sizeof(list_set)); list_set.mail_storage_flags = &_storage->flags; list_set.lock_method = &_storage->lock_method; - mailbox_list_init(_storage->list, _storage->ns, &list_set, - mail_storage_get_list_flags(_storage->flags)); + mailbox_list_init(_storage->list, _storage->ns, &list_set, 0); return 0; } @@ -134,6 +136,7 @@ }; struct var_expand_table *tab; struct mail_namespace *ns; + struct mail_namespace_settings *ns_set; struct mail_user *owner; const char *domain = NULL, *username = NULL, *userdomain = NULL; const char *name, *p, *next, **dest, *error; @@ -207,7 +210,7 @@ return 0; } - owner = mail_user_init(userdomain); + owner = mail_user_alloc(userdomain, user->unexpanded_set); if (!var_has_key(storage->location, 'h', "home")) ret = 1; else { @@ -220,6 +223,13 @@ return -1; } } + if (mail_user_init(owner, &error) < 0) { + mail_storage_set_critical(_storage, + "Couldn't create namespace '%s' for user %s: %s", + _storage->ns->prefix, userdomain, error); + mail_user_unref(&owner); + return -1; + } /* create the new namespace */ ns = i_new(struct mail_namespace, 1); @@ -230,14 +240,24 @@ ns->flags = NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_HIDDEN | NAMESPACE_FLAG_AUTOCREATED; ns->sep = _storage->ns->sep; + ns->mail_set = _storage->set; location = t_str_new(256); if (ret > 0) var_expand(location, storage->location, tab); else get_nonexisting_user_location(storage, userdomain, location); - if (mail_storage_create(ns, NULL, str_c(location), _storage->flags, - _storage->lock_method, &error) < 0) { + + ns_set = p_new(user->pool, struct mail_namespace_settings, 1); + ns_set->type = "shared"; + ns_set->separator = p_strdup_printf(user->pool, "%c", ns->sep); + ns_set->prefix = ns->prefix; + ns_set->location = p_strdup(user->pool, str_c(location)); + ns_set->hidden = TRUE; + ns_set->list = "yes"; + ns->set = ns_set; + + if (mail_storage_create(ns, NULL, _storage->flags, &error) < 0) { mail_storage_set_critical(_storage, "Namespace '%s': %s", ns->prefix, error); mail_namespace_destroy(ns); @@ -310,6 +330,7 @@ { NULL, NULL, + NULL, shared_alloc, shared_create, index_storage_destroy,
--- a/src/lib-storage/list/index-mailbox-list.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/list/index-mailbox-list.c Tue Jan 27 18:21:53 2009 -0500 @@ -457,16 +457,17 @@ struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list); const char *path; enum mail_index_open_flags index_flags; - enum mail_storage_flags storage_flags; + enum file_lock_method lock_method; int ret; - /* FIXME: a bit ugly way to get the flags, but this will do for now.. */ - index_flags = MAIL_INDEX_OPEN_FLAG_CREATE; - storage_flags = *list->set.mail_storage_flags; -#ifndef MMAP_CONFLICTS_WRITE - if ((storage_flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0) -#endif - index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; + index_flags = MAIL_INDEX_OPEN_FLAG_CREATE | + mail_storage_settings_to_index_flags(list->mail_set); + + if (!file_lock_method_parse(list->mail_set->lock_method, + &lock_method)) { + i_error("Unknown lock_method: %s", list->mail_set->lock_method); + return -1; + } if (mail_index_open(ilist->mail_index, index_flags, *list->set.lock_method) < 0) { @@ -507,7 +508,7 @@ /* FIXME: always disabled for now */ dir = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX); - if (*dir == '\0' || getenv("MAILBOX_LIST_INDEX_DISABLE") != NULL || + if (*dir == '\0' || list->mail_set->mailbox_list_index_disable || strcmp(list->name, "maildir++") != 0 || 1) { /* reserve the module context anyway, so syncing code knows that the index is disabled */
--- a/src/lib-storage/list/mailbox-list-fs-iter.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/list/mailbox-list-fs-iter.c Tue Jan 27 18:21:53 2009 -0500 @@ -178,7 +178,7 @@ { const char *const *patterns, *name, *p, *last; - if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) == 0) + if (!ctx->ctx.list->mail_set->mail_full_filesystem_access) return NULL; /* see if there are any absolute paths in patterns */
--- a/src/lib-storage/list/mailbox-list-fs.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/list/mailbox-list-fs.c Tue Jan 27 18:21:53 2009 -0500 @@ -97,7 +97,7 @@ static bool fs_is_valid_pattern(struct mailbox_list *list, const char *pattern) { - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) + if (list->mail_set->mail_full_filesystem_access) return TRUE; return fs_list_is_valid_common_nonfs(list, pattern); @@ -111,7 +111,7 @@ if (!fs_list_is_valid_common(name, &len)) return FALSE; - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) + if (list->mail_set->mail_full_filesystem_access) return TRUE; return fs_list_is_valid_common_nonfs(list, name); @@ -127,7 +127,7 @@ if (len > FS_MAX_CREATE_MAILBOX_NAME_LENGTH) return FALSE; - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) + if (list->mail_set->mail_full_filesystem_access) return TRUE; if (mailbox_list_name_is_too_large(name, '/'))
--- a/src/lib-storage/list/mailbox-list-maildir.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/list/mailbox-list-maildir.c Tue Jan 27 18:21:53 2009 -0500 @@ -137,7 +137,7 @@ if (!maildir_list_is_valid_common(list, name, &len)) return FALSE; - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) + if (list->mail_set->mail_full_filesystem_access) return TRUE; return maildir_list_is_valid_common_nonfs(name); @@ -153,7 +153,7 @@ if (len > MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH) return FALSE; - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) + if (list->mail_set->mail_full_filesystem_access) return TRUE; if (!maildir_list_is_valid_common_nonfs(name)) @@ -168,9 +168,6 @@ maildir_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type) { - struct maildir_mailbox_list *list = - (struct maildir_mailbox_list *)_list; - if (name == NULL) { /* return root directories */ switch (type) { @@ -187,7 +184,7 @@ i_unreached(); } - if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 && + if (_list->mail_set->mail_full_filesystem_access && (*name == '/' || *name == '~')) return maildir_list_get_absolute_path(_list, name);
--- a/src/lib-storage/list/subscription-file.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/list/subscription-file.c Tue Jan 27 18:21:53 2009 -0500 @@ -27,8 +27,7 @@ static void subsread_set_syscall_error(struct mailbox_list *list, const char *function, const char *path) { - if (errno == EACCES && - (list->flags & MAILBOX_LIST_FLAG_DEBUG) == 0) { + if (errno == EACCES && !list->mail_set->mail_debug) { mailbox_list_set_error(list, MAIL_ERROR_PERM, "No permission to read subscriptions"); } else { @@ -41,8 +40,7 @@ static void subswrite_set_syscall_error(struct mailbox_list *list, const char *function, const char *path) { - if (errno == EACCES && - (list->flags & MAILBOX_LIST_FLAG_DEBUG) == 0) { + if (errno == EACCES && !list->mail_set->mail_debug) { mailbox_list_set_error(list, MAIL_ERROR_PERM, "No permission to modify subscriptions"); } else { @@ -103,10 +101,8 @@ name = "INBOX"; memset(&dotlock_set, 0, sizeof(dotlock_set)); - dotlock_set.use_excl_lock = - (list->flags & MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL) != 0; - dotlock_set.nfs_flush = - (list->flags & MAILBOX_LIST_FLAG_NFS_FLUSH) != 0; + dotlock_set.use_excl_lock = list->mail_set->dotlock_use_excl; + dotlock_set.nfs_flush = list->mail_set->mail_nfs_storage; dotlock_set.temp_prefix = temp_prefix; dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT; dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
--- a/src/lib-storage/mail-namespace.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-namespace.c Tue Jan 27 18:21:53 2009 -0500 @@ -1,9 +1,11 @@ /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "array.h" #include "str.h" #include "file-lock.h" #include "mail-storage.h" +#include "mail-storage-settings.h" #include "mail-namespace.h" #include <stdlib.h> @@ -35,82 +37,85 @@ i_free(ns); } -static struct mail_namespace * -namespace_add_env(const char *data, unsigned int num, - struct mail_user *user, enum mail_storage_flags flags, - enum file_lock_method lock_method, - struct mail_namespace *prev_namespaces) +static int +namespace_add(struct mail_user *user, + struct mail_namespace_settings *ns_set, + const struct mail_storage_settings *mail_set, + struct mail_namespace *prev_namespaces, + struct mail_namespace **ns_p, const char **error_r) { struct mail_namespace *ns; - const char *sep, *type, *prefix, *driver, *error, *list, *alias_for; + const char *driver, *error; ns = i_new(struct mail_namespace, 1); + if (strncmp(ns_set->type, "private", 7) == 0) { + ns->owner = user; + ns->type = NAMESPACE_PRIVATE; + } else if (strncmp(ns_set->type, "shared", 6) == 0) + ns->type = NAMESPACE_SHARED; + else if (strncmp(ns_set->type, "public", 6) == 0) + ns->type = NAMESPACE_PUBLIC; + else { + *error_r = t_strdup_printf("Unknown namespace type: %s", + ns_set->type); + mail_namespace_free(ns); + return -1; + } - sep = getenv(t_strdup_printf("NAMESPACE_%u_SEP", num)); - type = getenv(t_strdup_printf("NAMESPACE_%u_TYPE", num)); - prefix = getenv(t_strdup_printf("NAMESPACE_%u_PREFIX", num)); - list = getenv(t_strdup_printf("NAMESPACE_%u_LIST", num)); - alias_for = getenv(t_strdup_printf("NAMESPACE_%u_ALIAS", num)); - if (getenv(t_strdup_printf("NAMESPACE_%u_INBOX", num)) != NULL) + if (strcmp(ns_set->list, "children") == 0) + ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN; + else if (strcmp(ns_set->list, "yes") == 0) + ns->flags |= NAMESPACE_FLAG_LIST_PREFIX; + else if (strcmp(ns_set->list, "no") != 0) { + *error_r = t_strdup_printf("Invalid list setting value: %s", + ns_set->list); + mail_namespace_free(ns); + return -1; + } + + if (ns_set->inbox) ns->flags |= NAMESPACE_FLAG_INBOX; - if (getenv(t_strdup_printf("NAMESPACE_%u_HIDDEN", num)) != NULL) + if (ns_set->hidden) ns->flags |= NAMESPACE_FLAG_HIDDEN; - if (list != NULL) { - if (strcmp(list, "children") == 0) - ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN; - else - ns->flags |= NAMESPACE_FLAG_LIST_PREFIX; - } - if (getenv(t_strdup_printf("NAMESPACE_%u_SUBSCRIPTIONS", num)) != NULL) + if (ns_set->subscriptions) ns->flags |= NAMESPACE_FLAG_SUBSCRIPTIONS; - if (type == NULL || *type == '\0' || strncmp(type, "private", 7) == 0) { - ns->type = NAMESPACE_PRIVATE; - ns->owner = user; - } else if (strncmp(type, "shared", 6) == 0) - ns->type = NAMESPACE_SHARED; - else if (strncmp(type, "public", 6) == 0) - ns->type = NAMESPACE_PUBLIC; - else { - i_error("Unknown namespace type: %s", type); - mail_namespace_free(ns); - return NULL; - } - - if (alias_for != NULL) { + if (ns_set->alias_for != NULL) { ns->alias_for = mail_namespace_find_prefix(prev_namespaces, - alias_for); + ns_set->alias_for); if (ns->alias_for == NULL) { - i_error("Invalid namespace alias_for: %s", alias_for); + *error_r = t_strdup_printf("Invalid namespace alias_for: %s", + ns_set->alias_for); mail_namespace_free(ns); - return NULL; + return -1; } if (ns->alias_for->alias_for != NULL) { - i_error("Chained namespace alias_for: %s", alias_for); + *error_r = t_strdup_printf("Chained namespace alias_for: %s", + ns_set->alias_for); mail_namespace_free(ns); - return NULL; + return -1; } ns->alias_chain_next = ns->alias_for->alias_chain_next; ns->alias_for->alias_chain_next = ns; } - if (prefix == NULL) - prefix = ""; - - if ((flags & MAIL_STORAGE_FLAG_DEBUG) != 0) { + if (mail_set->mail_debug) { i_info("Namespace: type=%s, prefix=%s, sep=%s, " "inbox=%s, hidden=%s, list=%s, subscriptions=%s", - type == NULL ? "" : type, prefix, sep == NULL ? "" : sep, - (ns->flags & NAMESPACE_FLAG_INBOX) ? "yes" : "no", - (ns->flags & NAMESPACE_FLAG_HIDDEN) ? "yes" : "no", - list, - (ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) ? - "yes" : "no"); + ns_set->type, ns_set->prefix, + ns_set->separator == NULL ? "" : ns_set->separator, + ns_set->inbox ? "yes" : "no", + ns_set->hidden ? "yes" : "no", + ns_set->list ? "yes" : "no", + ns_set->subscriptions ? "yes" : "no"); } - if (sep != NULL) - ns->sep = *sep; - ns->prefix = i_strdup(prefix); + if (*ns_set->location == '\0') + ns_set->location = mail_set->mail_location; + + ns->set = ns_set; + ns->mail_set = mail_set; + ns->prefix = i_strdup(ns_set->prefix); ns->user = user; if (ns->type == NAMESPACE_SHARED && strchr(ns->prefix, '%') != NULL) { @@ -121,16 +126,21 @@ driver = NULL; } - if (mail_storage_create(ns, driver, data, flags, lock_method, - &error) < 0) { - i_error("Namespace '%s': %s", ns->prefix, error); + if (mail_storage_create(ns, driver, 0, &error) < 0) { + *error_r = t_strdup_printf("Namespace '%s': %s", + ns->prefix, error); mail_namespace_free(ns); - return NULL; + return -1; } - return ns; + if (ns_set->separator != NULL) + ns->sep = *ns_set->separator; + + *ns_p = ns; + return 0; } -static bool namespaces_check(struct mail_namespace *namespaces) +static bool +namespaces_check(struct mail_namespace *namespaces, const char **error_r) { struct mail_namespace *ns, *inbox_ns = NULL, *private_ns = NULL; unsigned int private_ns_count = 0; @@ -140,9 +150,9 @@ for (ns = namespaces; ns != NULL; ns = ns->next) { if ((ns->flags & NAMESPACE_FLAG_INBOX) != 0) { if (inbox_ns != NULL) { - i_error("namespace configuration error: " + *error_r = "namespace configuration error: " "There can be only one namespace with " - "inbox=yes"); + "inbox=yes"; return FALSE; } inbox_ns = ns; @@ -154,7 +164,8 @@ if (*ns->prefix != '\0' && (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 && ns->prefix[strlen(ns->prefix)-1] != ns->sep) { - i_error("namespace configuration error: " + *error_r = t_strdup_printf( + "namespace configuration error: " "list=yes requires prefix=%s " "to end with separator", ns->prefix); return FALSE; @@ -164,16 +175,16 @@ if (list_sep == '\0') list_sep = ns->sep; else if (list_sep != ns->sep) { - i_error("namespace configuration error: " + *error_r = "namespace configuration error: " "All list=yes namespaces must use " - "the same separator"); + "the same separator"; return FALSE; } } if (*ns->prefix == '\0' && (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0) { - i_error("namespace configuration error: " - "Empty prefix requires list=yes"); + *error_r = "namespace configuration error: " + "Empty prefix requires list=yes"; return FALSE; } if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) @@ -186,57 +197,53 @@ the INBOX namespace. */ private_ns->flags |= NAMESPACE_FLAG_INBOX; } else { - i_error("namespace configuration error: " - "inbox=yes namespace missing"); + *error_r = "namespace configuration error: " + "inbox=yes namespace missing"; return FALSE; } } if (list_sep == '\0') { - i_error("namespace configuration error: " - "no list=yes namespaces"); + *error_r = "namespace configuration error: " + "no list=yes namespaces"; return FALSE; } if (subscriptions_count == 0) { - i_error("namespace configuration error: " - "no subscriptions=yes namespaces"); + *error_r = "namespace configuration error: " + "no subscriptions=yes namespaces"; return FALSE; } return TRUE; } -int mail_namespaces_init(struct mail_user *user) +int mail_namespaces_init(struct mail_user *user, const char **error_r) { + const struct mail_storage_settings *mail_set; + struct mail_namespace_settings *const *ns_set; struct mail_namespace *namespaces, *ns, **ns_p; - enum mail_storage_flags flags; - enum file_lock_method lock_method; - const char *mail, *data, *error; - unsigned int i; + struct mail_namespace_settings *inbox_set; + const char *error, *driver, *env; + unsigned int i, count; - mail_storage_parse_env(&flags, &lock_method); + i_assert(user->initialized); + namespaces = NULL; ns_p = &namespaces; - /* first try NAMESPACE_* environments */ - for (i = 1; ; i++) { - T_BEGIN { - data = getenv(t_strdup_printf("NAMESPACE_%u", i)); - } T_END; - - if (data == NULL) - break; - - T_BEGIN { - *ns_p = namespace_add_env(data, i, user, flags, - lock_method, namespaces); - } T_END; - - if (*ns_p == NULL) + mail_set = mail_user_set_get_driver_settings(user->set, "MAIL"); + if (array_is_created(&user->set->namespaces)) + ns_set = array_get(&user->set->namespaces, &count); + else { + ns_set = NULL; + count = 0; + } + for (i = 0; i < count; i++) { + if (namespace_add(user, ns_set[i], mail_set, namespaces, + ns_p, error_r) < 0) return -1; - ns_p = &(*ns_p)->next; } if (namespaces != NULL) { - if (!namespaces_check(namespaces)) { + if (!namespaces_check(namespaces, error_r)) { while (namespaces != NULL) { ns = namespaces; namespaces = ns->next; @@ -254,35 +261,50 @@ return 0; } - /* fallback to MAIL */ - mail = getenv("MAIL"); - if (mail == NULL) { - /* support also maildir-specific environment */ - mail = getenv("MAILDIR"); - if (mail != NULL) - mail = t_strconcat("maildir:", mail, NULL); - } - + /* fallback to using environment variables */ ns = i_new(struct mail_namespace, 1); ns->type = NAMESPACE_PRIVATE; ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_SUBSCRIPTIONS; - ns->prefix = i_strdup(""); - ns->user = user; ns->owner = user; - if (mail_storage_create(ns, NULL, mail, flags, lock_method, - &error) < 0) { - if (mail != NULL && *mail != '\0') - i_error("mail_location: %s", error); + inbox_set = p_new(user->pool, struct mail_namespace_settings, 1); + *inbox_set = mail_namespace_default_settings; + inbox_set->inbox = TRUE; + + driver = NULL; + env = "MAIL"; + inbox_set->location = getenv("MAIL"); + if (inbox_set->location == NULL) { + /* support also maildir-specific environment */ + inbox_set->location = getenv("MAILDIR"); + if (inbox_set->location == NULL) + inbox_set->location = ""; else { - i_error("mail_location not set and " - "autodetection failed: %s", error); + driver = "maildir"; + env = "MAILDIR"; + } + } + + ns->set = inbox_set; + ns->mail_set = mail_set; + ns->prefix = i_strdup(ns->set->prefix); + ns->user = user; + + if (mail_storage_create(ns, driver, 0, &error) < 0) { + if (*inbox_set->location != '\0') { + *error_r = t_strdup_printf( + "Initializing mail storage from environment %s " + "failed: %s", env, error); + } else { + *error_r = t_strdup_printf("mail_location not set and " + "autodetection failed: %s", error); } mail_namespace_free(ns); return -1; } user->namespaces = ns; + mail_namespace_init_storage(ns); if (hook_mail_namespaces_created != NULL) { T_BEGIN { @@ -292,8 +314,7 @@ return 0; } -struct mail_namespace * -mail_namespaces_init_empty(struct mail_user *user) +struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user) { struct mail_namespace *ns; @@ -303,6 +324,7 @@ ns->prefix = i_strdup(""); ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_SUBSCRIPTIONS; + ns->mail_set = mail_user_set_get_driver_settings(user->set, "MAIL"); user->namespaces = ns; return ns; }
--- a/src/lib-storage/mail-namespace.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-namespace.h Tue Jan 27 18:21:53 2009 -0500 @@ -3,6 +3,8 @@ #include "mail-user.h" +struct mail_storage_root_settings; + enum namespace_type { NAMESPACE_PRIVATE, NAMESPACE_SHARED, @@ -55,12 +57,15 @@ struct mailbox_list *list; /* FIXME: we should support multiple storages in one namespace */ struct mail_storage *storage; + + const struct mail_namespace_settings *set; + const struct mail_storage_settings *mail_set; }; /* Called after namespaces has been created */ extern void (*hook_mail_namespaces_created)(struct mail_namespace *namespaces); -int mail_namespaces_init(struct mail_user *user); +int mail_namespaces_init(struct mail_user *user, const char **error_r); struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user); void mail_namespaces_deinit(struct mail_namespace **namespaces);
--- a/src/lib-storage/mail-storage-private.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-storage-private.h Tue Jan 27 18:21:53 2009 -0500 @@ -4,6 +4,7 @@ #include "module-context.h" #include "file-lock.h" #include "mail-storage.h" +#include "mail-storage-settings.h" #include "mail-index-private.h" /* Called after mail storage has been created */ @@ -22,6 +23,8 @@ }; struct mail_storage_vfuncs { + const struct setting_parser_info *(*get_setting_parser_info)(void); + void (*class_init)(void); void (*class_deinit)(void); @@ -30,7 +33,7 @@ const char **error_r); void (*destroy)(struct mail_storage *storage); - bool (*autodetect)(const char *data, enum mail_storage_flags flags); + bool (*autodetect)(const struct mail_namespace *ns); struct mailbox *(*mailbox_open)(struct mail_storage *storage, const char *name, @@ -61,10 +64,10 @@ const struct mail_storage *storage_class; struct mail_namespace *ns; struct mailbox_list *list; + const struct mail_storage_settings *set; enum mail_storage_flags flags; enum file_lock_method lock_method; - unsigned int keyword_max_len; struct mail_storage_callbacks *callbacks; void *callback_context; @@ -380,7 +383,5 @@ void mail_set_expunged(struct mail *mail); void mailbox_set_deleted(struct mailbox *box); -enum mailbox_list_flags -mail_storage_get_list_flags(enum mail_storage_flags storage_flags); #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mail-storage-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,208 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "settings-parser.h" +#include "mail-index.h" +#include "mail-user.h" +#include "mail-namespace.h" +#include "mail-storage-private.h" +#include "mail-storage-settings.h" + +#include <stddef.h> + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct mail_storage_settings, name), NULL } + +static struct setting_define mail_storage_setting_defines[] = { + DEF(SET_STR_VARS, mail_location), + DEF(SET_STR, mail_cache_fields), + DEF(SET_STR, mail_never_cache_fields), + DEF(SET_UINT, mail_cache_min_mail_count), + DEF(SET_UINT, mailbox_idle_check_interval), + DEF(SET_UINT, mail_max_keyword_length), + DEF(SET_BOOL, mail_save_crlf), + DEF(SET_BOOL, fsync_disable), + DEF(SET_BOOL, mmap_disable), + DEF(SET_BOOL, dotlock_use_excl), + DEF(SET_BOOL, mail_nfs_storage), + DEF(SET_BOOL, mail_nfs_index), + DEF(SET_BOOL, mailbox_list_index_disable), + DEF(SET_BOOL, mail_debug), + DEF(SET_BOOL, mail_full_filesystem_access), + DEF(SET_ENUM, lock_method), + DEF(SET_STR, pop3_uidl_format), + + SETTING_DEFINE_LIST_END +}; + +struct mail_storage_settings mail_storage_default_settings = { + MEMBER(mail_location) "", + MEMBER(mail_cache_fields) "flags", + MEMBER(mail_never_cache_fields) "imap.envelope", + MEMBER(mail_cache_min_mail_count) 0, + MEMBER(mailbox_idle_check_interval) 30, + MEMBER(mail_max_keyword_length) 50, + MEMBER(mail_save_crlf) FALSE, + MEMBER(fsync_disable) FALSE, + MEMBER(mmap_disable) FALSE, + MEMBER(dotlock_use_excl) FALSE, + MEMBER(mail_nfs_storage) FALSE, + MEMBER(mail_nfs_index) FALSE, + MEMBER(mailbox_list_index_disable) FALSE, + MEMBER(mail_debug) FALSE, + MEMBER(mail_full_filesystem_access) FALSE, + MEMBER(lock_method) "fcntl:flock:dotlock", + MEMBER(pop3_uidl_format) "%08Xu%08Xv" +}; + +struct setting_parser_info mail_storage_setting_parser_info = { + MEMBER(defines) mail_storage_setting_defines, + MEMBER(defaults) &mail_storage_default_settings, + + MEMBER(parent) &mail_user_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct mail_storage_settings) +}; + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct mail_namespace_settings, name), NULL } + +static struct setting_define mail_namespace_setting_defines[] = { + DEF(SET_ENUM, type), + DEF(SET_STR, separator), + DEF(SET_STR_VARS, prefix), + DEF(SET_STR_VARS, location), + DEF(SET_STR_VARS, alias_for), + + DEF(SET_BOOL, inbox), + DEF(SET_BOOL, hidden), + DEF(SET_ENUM, list), + DEF(SET_BOOL, subscriptions), + + SETTING_DEFINE_LIST_END +}; + +struct mail_namespace_settings mail_namespace_default_settings = { + MEMBER(type) "private:shared:public", + MEMBER(separator) "", + MEMBER(prefix) "", + MEMBER(location) "", + MEMBER(alias_for) NULL, + + MEMBER(inbox) FALSE, + MEMBER(hidden) FALSE, + MEMBER(list) "yes:no:children", + MEMBER(subscriptions) TRUE +}; + +struct setting_parser_info mail_namespace_setting_parser_info = { + MEMBER(defines) mail_namespace_setting_defines, + MEMBER(defaults) &mail_namespace_default_settings, + + MEMBER(parent) &mail_user_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) offsetof(struct mail_namespace_settings, type), + MEMBER(struct_size) sizeof(struct mail_namespace_settings) +}; + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct mail_user_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, \ + offsetof(struct mail_user_settings, field), defines } + +static struct setting_define mail_user_setting_defines[] = { + DEF(SET_UINT, umask), + + DEFLIST(namespaces, "namespace", &mail_namespace_setting_parser_info), + + SETTING_DEFINE_LIST_END +}; + +static struct mail_user_settings mail_user_default_settings = { + MEMBER(umask) 0077, + + MEMBER(namespaces) ARRAY_INIT +}; + +struct setting_parser_info mail_user_setting_parser_info = { + MEMBER(defines) mail_user_setting_defines, + MEMBER(defaults) &mail_user_default_settings, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct mail_user_settings) +}; + +const void * +mail_user_set_get_driver_settings(const struct mail_user_settings *set, + const char *driver) +{ + const void *dset; + + dset = settings_find_dynamic(&mail_user_setting_parser_info, + set, driver); + if (dset == NULL) { + i_panic("Default settings not found for storage driver %s", + driver); + } + return dset; +} + +const void *mail_storage_get_driver_settings(struct mail_storage *storage) +{ + return mail_user_set_get_driver_settings(storage->ns->user->set, + storage->name); +} + +enum mail_index_open_flags +mail_storage_settings_to_index_flags(const struct mail_storage_settings *set) +{ + enum mail_index_open_flags index_flags = 0; + + if (set->fsync_disable) + index_flags |= MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE; +#ifndef MMAP_CONFLICTS_WRITE + if (set->mmap_disable) +#endif + index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; + if (set->dotlock_use_excl) + index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL; + return index_flags; +} + +void mail_storage_namespace_defines_init(pool_t pool) +{ + struct dynamic_settings_parser *parsers; + struct mail_storage *const *storages; + unsigned int i, j, count; + + storages = array_get(&mail_storage_classes, &count); + parsers = t_new(struct dynamic_settings_parser, count + 1); + parsers[0].name = "MAIL"; + parsers[0].info = &mail_storage_setting_parser_info; + + for (i = 0, j = 1; i < count; i++) { + if (storages[i]->v.get_setting_parser_info == NULL) + continue; + + parsers[j].name = storages[i]->name; + parsers[j].info = storages[i]->v.get_setting_parser_info(); + j++; + } + + settings_parser_info_update(pool, parsers[j-1].info->parent, parsers); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mail-storage-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,61 @@ +#ifndef MAIL_STORAGE_SETTINGS_H +#define MAIL_STORAGE_SETTINGS_H + +struct mail_user; +struct mail_storage; + +struct mail_storage_settings { + const char *mail_location; + const char *mail_cache_fields; + const char *mail_never_cache_fields; + unsigned int mail_cache_min_mail_count; + unsigned int mailbox_idle_check_interval; + unsigned int mail_max_keyword_length; + bool mail_save_crlf; + bool fsync_disable; + bool mmap_disable; + bool dotlock_use_excl; + bool mail_nfs_storage; + bool mail_nfs_index; + bool mailbox_list_index_disable; + bool mail_debug; + bool mail_full_filesystem_access; + const char *lock_method; + const char *pop3_uidl_format; +}; + +struct mail_namespace_settings { + const char *type; + const char *separator; + const char *prefix; + const char *location; + const char *alias_for; + + bool inbox; + bool hidden; + const char *list; + bool subscriptions; +}; + +struct mail_user_settings { + unsigned int umask; + + ARRAY_DEFINE(namespaces, struct mail_namespace_settings *); +}; + +extern struct setting_parser_info mail_user_setting_parser_info; +extern struct setting_parser_info mail_namespace_setting_parser_info; +extern struct setting_parser_info mail_storage_setting_parser_info; +extern struct mail_namespace_settings mail_namespace_default_settings; + +const void * +mail_user_set_get_driver_settings(const struct mail_user_settings *set, + const char *driver); +const void *mail_storage_get_driver_settings(struct mail_storage *storage); + +enum mail_index_open_flags +mail_storage_settings_to_index_flags(const struct mail_storage_settings *set); + +void mail_storage_namespace_defines_init(pool_t pool); + +#endif
--- a/src/lib-storage/mail-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -7,6 +7,7 @@ #include "mail-index-private.h" #include "mailbox-list-private.h" #include "mail-storage-private.h" +#include "mail-storage-settings.h" #include "mail-namespace.h" #include "mail-search.h" #include "mailbox-search-result-private.h" @@ -14,8 +15,6 @@ #include <stdlib.h> #include <ctype.h> -#define DEFAULT_MAX_KEYWORD_LENGTH 50 - struct mail_storage_module_register mail_storage_module_register = { 0 }; struct mail_module_register mail_module_register = { 0 }; @@ -26,28 +25,30 @@ void (*hook_mailbox_opened)(struct mailbox *box) = NULL; void (*hook_mailbox_index_opened)(struct mailbox *box) = NULL; -static ARRAY_DEFINE(storages, struct mail_storage *); +ARRAY_TYPE(mail_storage) mail_storage_classes; void mail_storage_init(void) { mailbox_lists_init(); - i_array_init(&storages, 8); + i_array_init(&mail_storage_classes, 8); } void mail_storage_deinit(void) { - if (array_is_created(&storages)) - array_free(&storages); + if (array_is_created(&mail_storage_classes)) + array_free(&mail_storage_classes); mailbox_lists_deinit(); } void mail_storage_class_register(struct mail_storage *storage_class) { + i_assert(mail_storage_find_class(storage_class->name) == NULL); + if (storage_class->v.class_init != NULL) storage_class->v.class_init(); /* append it after the list, so the autodetection order is correct */ - array_append(&storages, &storage_class, 1); + array_append(&mail_storage_classes, &storage_class, 1); } void mail_storage_class_unregister(struct mail_storage *storage_class) @@ -55,10 +56,10 @@ struct mail_storage *const *classes; unsigned int i, count; - classes = array_get(&storages, &count); + classes = array_get(&mail_storage_classes, &count); for (i = 0; i < count; i++) { if (classes[i] == storage_class) { - array_delete(&storages, i, 1); + array_delete(&mail_storage_classes, i, 1); break; } } @@ -66,48 +67,6 @@ storage_class->v.class_deinit(); } -void mail_storage_parse_env(enum mail_storage_flags *flags_r, - enum file_lock_method *lock_method_r) -{ - const char *str; - - *flags_r = 0; - if (getenv("FULL_FILESYSTEM_ACCESS") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS; - if (getenv("DEBUG") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_DEBUG; - if (getenv("MMAP_DISABLE") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_MMAP_DISABLE; - if (getenv("MMAP_NO_WRITE") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_MMAP_NO_WRITE; - if (getenv("DOTLOCK_USE_EXCL") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL; - if (getenv("MAIL_SAVE_CRLF") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_SAVE_CRLF; - if (getenv("FSYNC_DISABLE") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_FSYNC_DISABLE; - if (getenv("MAIL_NFS_STORAGE") != NULL) - *flags_r |= MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE; - if (getenv("MAIL_NFS_INDEX") != NULL) { - *flags_r |= MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX; - if ((*flags_r & MAIL_STORAGE_FLAG_MMAP_DISABLE) == 0) - i_fatal("mail_nfs_index=yes requires mmap_disable=yes"); - if ((*flags_r & MAIL_STORAGE_FLAG_FSYNC_DISABLE) != 0) - i_fatal("mail_nfs_index=yes requires fsync_disable=no"); - } - - str = getenv("POP3_UIDL_FORMAT"); - if (str != NULL && (str = strchr(str, '%')) != NULL && - str != NULL && var_get_key(str + 1) == 'm') - *flags_r |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5; - - str = getenv("LOCK_METHOD"); - if (str == NULL) - *lock_method_r = FILE_LOCK_METHOD_FCNTL; - else if (!file_lock_method_parse(str, lock_method_r)) - i_fatal("Unknown lock_method: %s", str); -} - struct mail_storage *mail_storage_find_class(const char *name) { struct mail_storage *const *classes; @@ -115,7 +74,7 @@ i_assert(name != NULL); - classes = array_get(&storages, &count); + classes = array_get(&mail_storage_classes, &count); for (i = 0; i < count; i++) { if (strcasecmp(classes[i]->name, name) == 0) return classes[i]; @@ -124,15 +83,15 @@ } static struct mail_storage * -mail_storage_autodetect(const char *data, enum mail_storage_flags flags) +mail_storage_autodetect(const struct mail_namespace *ns) { struct mail_storage *const *classes; unsigned int i, count; - classes = array_get(&storages, &count); + classes = array_get(&mail_storage_classes, &count); for (i = 0; i < count; i++) { if (classes[i]->v.autodetect != NULL && - classes[i]->v.autodetect(data, flags)) + classes[i]->v.autodetect(ns)) return classes[i]; } return NULL; @@ -158,15 +117,30 @@ } int mail_storage_create(struct mail_namespace *ns, const char *driver, - const char *data, enum mail_storage_flags flags, - enum file_lock_method lock_method, - const char **error_r) + enum mail_storage_flags flags, const char **error_r) { struct mail_storage *storage_class, *storage; struct mail_storage *const *classes; - const char *home, *value; + const char *data = ns->set->location; + const char *home, *p; unsigned int i, count; + if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 && + ns->mail_set->pop3_uidl_format != NULL) { + /* if pop3_uidl_format contains %m, we want to keep the + header MD5 sums stored even if we're not running POP3 + right now. */ + p = ns->mail_set->pop3_uidl_format; + while ((p = strchr(p, '%')) != NULL) { + if (p[1] == '%') + p += 2; + else if (var_get_key(++p) == 'm') { + flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5; + break; + } + } + } + if (data == NULL) data = ""; else if (driver == NULL) @@ -174,9 +148,9 @@ if (*data == '\0' && driver == NULL) { /* use the first driver that works */ - classes = array_get(&storages, &count); + classes = array_get(&mail_storage_classes, &count); } else if (driver == NULL) { - storage_class = mail_storage_autodetect(data, flags); + storage_class = mail_storage_autodetect(ns); if (storage_class == NULL) { *error_r = t_strdup_printf( "Ambiguous mail location setting, " @@ -200,8 +174,13 @@ for (i = 0; i < count; i++) { storage = classes[i]->v.alloc(); + storage->set = ns->mail_set; storage->flags = flags; - storage->lock_method = lock_method; + if (!file_lock_method_parse(storage->set->lock_method, + &storage->lock_method)) { + i_fatal("Unknown lock_method: %s", + storage->set->lock_method); + } storage->ns = ns; storage->callbacks = @@ -211,7 +190,7 @@ if (classes[i]->v.create(storage, data, error_r) == 0) break; - if ((flags & MAIL_STORAGE_FLAG_DEBUG) != 0 && count > 1) { + if (ns->mail_set->mail_debug && count > 1) { i_info("%s: Couldn't create mail storage %s: %s", classes[i]->name, data, *error_r); } @@ -234,10 +213,6 @@ return -1; } - value = getenv("MAIL_MAX_KEYWORD_LENGTH"); - storage->keyword_max_len = value != NULL ? - atoi(value) : DEFAULT_MAX_KEYWORD_LENGTH; - if (hook_mail_storage_created != NULL) { T_BEGIN { hook_mail_storage_created(storage); @@ -327,6 +302,12 @@ return storage->ns; } +const struct mail_storage_settings * +mail_storage_get_settings(struct mail_storage *storage) +{ + return storage->set; +} + void mail_storage_set_callbacks(struct mail_storage *storage, struct mail_storage_callbacks *callbacks, void *context) @@ -404,22 +385,6 @@ MAILBOX_LIST_PATH_TYPE_INDEX); } -enum mailbox_list_flags -mail_storage_get_list_flags(enum mail_storage_flags storage_flags) -{ - enum mailbox_list_flags list_flags = 0; - - if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0) - list_flags |= MAILBOX_LIST_FLAG_DEBUG; - if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0) - list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS; - if ((storage_flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0) - list_flags |= MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL; - if ((storage_flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) - list_flags |= MAILBOX_LIST_FLAG_NFS_FLUSH; - return list_flags; -} - bool mail_storage_set_error_from_errno(struct mail_storage *storage) { const char *error_string; @@ -427,8 +392,7 @@ if (!mail_error_from_errno(&error, &error_string)) return FALSE; - if ((storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0 && - error != MAIL_ERROR_NOTFOUND) { + if (storage->set->mail_debug && error != MAIL_ERROR_NOTFOUND) { /* debugging is enabled - admin may be debugging a (permission) problem, so return FALSE to get the caller to log the full error message. */ @@ -493,6 +457,11 @@ return box->storage; } +const struct mail_storage_settings *mailbox_get_settings(struct mailbox *box) +{ + return box->storage->set; +} + const char *mailbox_get_name(const struct mailbox *box) { return box->name;
--- a/src/lib-storage/mail-storage.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-storage.h Tue Jan 27 18:21:53 2009 -0500 @@ -13,33 +13,14 @@ #define MAIL_STORAGE_STAYALIVE_SECS 15 enum mail_storage_flags { - /* Print debugging information while initializing the storage */ - MAIL_STORAGE_FLAG_DEBUG = 0x01, - /* Allow full filesystem access with absolute or relative paths. */ - MAIL_STORAGE_FLAG_FULL_FS_ACCESS = 0x02, - /* Don't try to mmap() files */ - MAIL_STORAGE_FLAG_MMAP_DISABLE = 0x04, - /* Don't try to write() to mmap()ed files. Required for the few - OSes that don't have unified buffer cache - (currently OpenBSD <= 3.5) */ - MAIL_STORAGE_FLAG_MMAP_NO_WRITE = 0x08, /* Remember message headers' MD5 sum */ - MAIL_STORAGE_FLAG_KEEP_HEADER_MD5 = 0x10, - /* Use CRLF linefeeds when saving mails. */ - MAIL_STORAGE_FLAG_SAVE_CRLF = 0x40, + MAIL_STORAGE_FLAG_KEEP_HEADER_MD5 = 0x01, /* Don't try to autodetect anything, require that the given data contains all the necessary information. */ - MAIL_STORAGE_FLAG_NO_AUTODETECTION = 0x100, + MAIL_STORAGE_FLAG_NO_AUTODETECTION = 0x02, /* Don't autocreate any directories. If they don't exist, fail to create the storage. */ - MAIL_STORAGE_FLAG_NO_AUTOCREATE = 0x200, - /* Rely on O_EXCL when creating dotlocks */ - MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL = 0x400, - /* Flush NFS caches for mail storage / index */ - MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE = 0x800, - MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX = 0x1000, - /* Don't use fsync() or fdatasync() */ - MAIL_STORAGE_FLAG_FSYNC_DISABLE = 0x2000 + MAIL_STORAGE_FLAG_NO_AUTOCREATE = 0x04 }; enum mailbox_open_flags { @@ -239,8 +220,10 @@ const char *pattern; }; ARRAY_DEFINE_TYPE(mailbox_virtual_patterns, struct mailbox_virtual_pattern); +ARRAY_DEFINE_TYPE(mail_storage, struct mail_storage *); +ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *); -ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *); +extern ARRAY_TYPE(mail_storage) mail_storage_classes; typedef void mailbox_notify_callback_t(struct mailbox *box, void *context); @@ -257,25 +240,25 @@ /* Find mail storage class by name */ struct mail_storage *mail_storage_find_class(const char *name); -/* Returns flags and lock_method based on environment settings. */ -void mail_storage_parse_env(enum mail_storage_flags *flags_r, - enum file_lock_method *lock_method_r); - /* Create a new instance of registered mail storage class with given storage-specific data. If driver is NULL, it's tried to be autodetected - from data. If data is NULL, it uses the first storage that exists. - The storage is put into ns->storage. */ + from ns location. If ns location is NULL, it uses the first storage that + exists. The storage is put into ns->storage. */ int mail_storage_create(struct mail_namespace *ns, const char *driver, - const char *data, enum mail_storage_flags flags, - enum file_lock_method lock_method, - const char **error_r); + enum mail_storage_flags flags, const char **error_r); void mail_storage_destroy(struct mail_storage **storage); +/* Returns the storage's real hierarchy separator. */ char mail_storage_get_hierarchy_sep(struct mail_storage *storage); +/* Returns the storage's mailbox list backend. */ struct mailbox_list * mail_storage_get_list(const struct mail_storage *storage) ATTR_PURE; +/* Returns the storage's namespace. */ struct mail_namespace * mail_storage_get_namespace(const struct mail_storage *storage) ATTR_PURE; +/* Returns the mail storage settings. */ +const struct mail_storage_settings * +mail_storage_get_settings(struct mail_storage *storage) ATTR_PURE; /* Set storage callback functions to use. */ void mail_storage_set_callbacks(struct mail_storage *storage, @@ -326,10 +309,15 @@ /* Enable the given feature for the mailbox. */ int mailbox_enable(struct mailbox *box, enum mailbox_feature features); -enum mailbox_feature mailbox_get_enabled_features(struct mailbox *box); +/* Returns all enabled features. */ +enum mailbox_feature +mailbox_get_enabled_features(struct mailbox *box) ATTR_PURE; /* Returns storage of given mailbox */ struct mail_storage *mailbox_get_storage(const struct mailbox *box) ATTR_PURE; +/* Returns the storage's settings. */ +const struct mail_storage_settings * +mailbox_get_settings(struct mailbox *box) ATTR_PURE; /* Returns name of given mailbox */ const char *mailbox_get_name(const struct mailbox *box) ATTR_PURE;
--- a/src/lib-storage/mail-user.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-user.c Tue Jan 27 18:21:53 2009 -0500 @@ -2,7 +2,12 @@ #include "lib.h" #include "array.h" +#include "hostpid.h" +#include "network.h" +#include "var-expand.h" +#include "settings-parser.h" #include "auth-master.h" +#include "mail-storage-settings.h" #include "mail-namespace.h" #include "mail-user.h" @@ -19,7 +24,8 @@ pool_unref(&user->pool); } -struct mail_user *mail_user_init(const char *username) +struct mail_user *mail_user_alloc(const char *username, + const struct mail_user_settings *set) { struct mail_user *user; pool_t pool; @@ -27,17 +33,39 @@ i_assert(username != NULL); i_assert(*username != '\0'); - pool = pool_alloconly_create("mail user", 512); + pool = pool_alloconly_create("mail user", 2048); user = p_new(pool, struct mail_user, 1); user->pool = pool; user->refcount = 1; user->username = p_strdup(pool, username); + user->unexpanded_set = set; + user->set = settings_dup(&mail_user_setting_parser_info, set, pool); user->v.deinit = mail_user_deinit_base; p_array_init(&user->module_contexts, user->pool, 5); + return user; +} +int mail_user_init(struct mail_user *user, const char **error_r) +{ + const char *home, *key, *value; + + if (user->_home == NULL && + settings_vars_have_key(&mail_user_setting_parser_info, user->set, + 'h', "home", &key, &value) && + mail_user_get_home(user, &home) <= 0) { + *error_r = t_strdup_printf( + "userdb didn't return a home directory, " + "but %s used it (%%h): %s", key, value); + return -1; + } + + settings_var_expand(&mail_user_setting_parser_info, user->set, + user->pool, mail_user_var_expand_table(user)); + + user->initialized = TRUE; if (hook_mail_user_created != NULL) hook_mail_user_created(user); - return user; + return 0; } void mail_user_ref(struct mail_user *user) @@ -69,6 +97,62 @@ return NULL; } +void mail_user_set_vars(struct mail_user *user, uid_t uid, const char *service, + const struct ip_addr *local_ip, + const struct ip_addr *remote_ip) +{ + user->uid = uid; + user->service = p_strdup(user->pool, service); + if (local_ip != NULL) { + user->local_ip = p_new(user->pool, struct ip_addr, 1); + *user->local_ip = *local_ip; + } + if (remote_ip != NULL) { + user->remote_ip = p_new(user->pool, struct ip_addr, 1); + *user->remote_ip = *remote_ip; + } +} + +const struct var_expand_table * +mail_user_var_expand_table(struct mail_user *user) +{ + static struct var_expand_table static_tab[] = { + { 'u', NULL, "user" }, + { 'n', NULL, "username" }, + { 'd', NULL, "domain" }, + { 's', NULL, "service" }, + { 'h', NULL, "home" }, + { 'l', NULL, "lip" }, + { 'r', NULL, "rip" }, + { 'p', NULL, "pid" }, + { 'i', NULL, "uid" }, + { '\0', NULL, NULL } + }; + struct var_expand_table *tab; + + if (user->var_expand_table != NULL) + return user->var_expand_table; + + tab = p_malloc(user->pool, sizeof(static_tab)); + memcpy(tab, static_tab, sizeof(static_tab)); + + tab[0].value = user->username; + tab[1].value = p_strdup(user->pool, t_strcut(user->username, '@')); + tab[2].value = strchr(user->username, '@'); + if (tab[2].value != NULL) tab[2].value++; + tab[3].value = user->service; + tab[4].value = user->_home; /* don't look it up unless we need it */ + tab[5].value = user->local_ip == NULL ? NULL : + p_strdup(user->pool, net_ip2addr(user->local_ip)); + tab[6].value = user->remote_ip == NULL ? NULL : + p_strdup(user->pool, net_ip2addr(user->remote_ip)); + tab[7].value = my_pid; + tab[8].value = p_strdup(user->pool, dec2str(user->uid)); + + user->var_expand_table = tab; + return user->var_expand_table; +} + void mail_user_set_home(struct mail_user *user, const char *home) { user->_home = p_strdup(user->pool, home);
--- a/src/lib-storage/mail-user.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mail-user.h Tue Jan 27 18:21:53 2009 -0500 @@ -16,6 +16,15 @@ /* don't access the home directly. It may be set lazily. */ const char *_home; + uid_t uid; + const char *service; + struct ip_addr *local_ip, *remote_ip; + const struct var_expand_table *var_expand_table; + /* error during initialization */ + const char *error; + + const struct mail_user_settings *unexpanded_set; + struct mail_user_settings *set; struct mail_namespace *namespaces; /* Module-specific contexts. See mail_storage_module_id. */ @@ -26,6 +35,8 @@ /* User is an administrator. Allow operations not normally allowed for other people. */ unsigned int admin:1; + /* mail_user_init() has been called */ + unsigned int initialized:1; }; struct mail_user_module_register { @@ -44,13 +55,25 @@ void mail_users_init(const char *auth_socket_path, bool debug); void mail_users_deinit(void); -struct mail_user *mail_user_init(const char *username); +struct mail_user *mail_user_alloc(const char *username, + const struct mail_user_settings *set); +/* Returns -1 if settings were invalid. */ +int mail_user_init(struct mail_user *user, const char **error_r); + void mail_user_ref(struct mail_user *user); void mail_user_unref(struct mail_user **user); /* Find another user from the given user's namespaces. */ struct mail_user *mail_user_find(struct mail_user *user, const char *name); +/* Specify mail location %variable expansion data. */ +void mail_user_set_vars(struct mail_user *user, uid_t uid, const char *service, + const struct ip_addr *local_ip, + const struct ip_addr *remote_ip); +/* Return %variable expansion table for the user. */ +const struct var_expand_table * +mail_user_var_expand_table(struct mail_user *user); + /* Specify the user's home directory. This should be called also when it's known that the user doesn't have a home directory to avoid the internal lookup. */
--- a/src/lib-storage/mailbox-list-private.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mailbox-list-private.h Tue Jan 27 18:21:53 2009 -0500 @@ -3,6 +3,7 @@ #include "mail-namespace.h" #include "mailbox-list.h" +#include "mail-storage-settings.h" struct dirent; struct imap_match_glob; @@ -76,6 +77,7 @@ pool_t pool; struct mail_namespace *ns; struct mailbox_list_settings set; + const struct mail_storage_settings *mail_set; enum mailbox_list_flags flags; /* -1 if not set yet. use mailbox_list_get_permissions() to set them */
--- a/src/lib-storage/mailbox-list.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mailbox-list.c Tue Jan 27 18:21:53 2009 -0500 @@ -192,6 +192,7 @@ *set->subscription_fname != '\0'); list->ns = ns; + list->mail_set = ns->mail_set; list->flags = flags; list->file_create_mode = (mode_t)-1; list->file_create_gid = (gid_t)-1; @@ -215,7 +216,7 @@ list->set.mail_storage_flags = set->mail_storage_flags; list->set.lock_method = set->lock_method; - if ((flags & MAILBOX_LIST_FLAG_DEBUG) != 0) { + if (ns->mail_set->mail_debug) { i_info("%s: root=%s, index=%s, control=%s, inbox=%s", list->name, list->set.root_dir == NULL ? "" : list->set.root_dir, @@ -278,7 +279,7 @@ if (!ENOTFOUND(errno)) { mailbox_list_set_critical(list, "stat(%s) failed: %m", path); - } else if ((list->flags & MAILBOX_LIST_FLAG_DEBUG) != 0) { + } else if (list->mail_set->mail_debug) { i_info("Namespace %s: Permission lookup failed from %s", list->ns->prefix, path); } @@ -303,7 +304,7 @@ list->file_create_gid = st.st_gid; } - if ((list->flags & MAILBOX_LIST_FLAG_DEBUG) != 0) { + if (list->mail_set->mail_debug) { i_info("Namespace %s: Using permissions from %s: " "mode=0%o gid=%ld", list->ns->prefix, path, (int)list->file_create_mode, @@ -726,7 +727,7 @@ bool mailbox_list_try_get_absolute_path(struct mailbox_list *list, const char **name) { - if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) == 0) + if (!list->mail_set->mail_full_filesystem_access) return FALSE; if (**name == '/')
--- a/src/lib-storage/mailbox-list.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib-storage/mailbox-list.h Tue Jan 27 18:21:53 2009 -0500 @@ -13,16 +13,8 @@ }; enum mailbox_list_flags { - /* Print debugging information while initializing the driver */ - MAILBOX_LIST_FLAG_DEBUG = 0x01, - /* Allow full filesystem access with absolute or relative paths. */ - MAILBOX_LIST_FLAG_FULL_FS_ACCESS = 0x04, - /* Rely on O_EXCL when creating dotlocks */ - MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL = 0x08, /* Mailboxes are files, not directories. */ - MAILBOX_LIST_FLAG_MAILBOX_FILES = 0x10, - /* Flush NFS attribute cache when needed */ - MAILBOX_LIST_FLAG_NFS_FLUSH = 0x20 + MAILBOX_LIST_FLAG_MAILBOX_FILES = 0x01 }; enum mailbox_info_flags {
--- a/src/lib/array-decl.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/lib/array-decl.h Tue Jan 27 18:21:53 2009 -0500 @@ -17,5 +17,6 @@ ARRAY_DEFINE_TYPE(string, char *); ARRAY_DEFINE_TYPE(const_string, const char *); ARRAY_DEFINE_TYPE(uint32_t, uint32_t); +ARRAY_DEFINE_TYPE(void_array, void *); #endif
--- a/src/login-common/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -2,14 +2,17 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ - -DSBINDIR=\""$(sbindir)"\" + -DSBINDIR=\""$(sbindir)"\" \ + -DSSLDIR=\""$(ssldir)\"" liblogin_common_a_SOURCES = \ client-common.c \ login-proxy.c \ + login-settings.c \ main.c \ master.c \ sasl-server.c \ @@ -20,6 +23,7 @@ noinst_HEADERS = \ client-common.h \ login-proxy.h \ + login-settings.h \ common.h \ master.h \ sasl-server.h \
--- a/src/login-common/client-common.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/client-common.c Tue Jan 27 18:21:53 2009 -0500 @@ -128,7 +128,7 @@ memcpy(tab, static_tab, sizeof(static_tab)); str = t_str_new(256); - for (e = log_format_elements; *e != NULL; e++) { + for (e = login_settings->log_format_elements_split; *e != NULL; e++) { for (p = *e; *p != '\0'; p++) { if (*p != '%' || p[1] == '\0') continue; @@ -147,7 +147,7 @@ tab[1].value = msg; str_truncate(str, 0); - var_expand(str, log_format, tab); + var_expand(str, login_settings->login_log_format, tab); return str_c(str); } @@ -171,10 +171,10 @@ struct ip_addr net_ip; unsigned int bits; - if (trusted_networks == NULL) + if (login_settings->login_trusted_networks == NULL) return FALSE; - net = t_strsplit_spaces(trusted_networks, ", "); + net = t_strsplit_spaces(login_settings->login_trusted_networks, ", "); for (; *net != NULL; net++) { if (net_parse_range(*net, &net_ip, &bits) < 0) { i_error("login_trusted_networks: " @@ -190,7 +190,7 @@ const char *client_get_extra_disconnect_reason(struct client *client) { - if (ssl_require_client_cert && client->proxy != NULL) { + if (login_settings->ssl_require_client_cert && client->proxy != NULL) { if (ssl_proxy_has_broken_client_cert(client->proxy)) return "(client sent an invalid cert)"; if (!ssl_proxy_has_valid_client_cert(client->proxy)) @@ -203,7 +203,7 @@ /* some auth attempts without SSL/TLS */ if (client->auth_tried_disabled_plaintext) return "(tried to use disabled plaintext auth)"; - if (ssl_require_client_cert) + if (login_settings->ssl_require_client_cert) return "(cert required, client didn't start TLS)"; return t_strdup_printf("(auth failed, %u attempts)",
--- a/src/login-common/common.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/common.h Tue Jan 27 18:21:53 2009 -0500 @@ -2,6 +2,7 @@ #define COMMON_H #include "lib.h" +#include "login-settings.h" /* Used only for string sanitization */ #define MAX_MECH_NAME 64 @@ -13,17 +14,11 @@ extern const char *login_protocol; -extern bool disable_plaintext_auth, process_per_connection; -extern bool verbose_proctitle, verbose_ssl, verbose_auth, auth_debug; -extern bool ssl_required, ssl_require_client_cert; -extern const char *greeting, *log_format; -extern const char *const *log_format_elements; -extern const char *capability_string; -extern const char *trusted_networks; -extern unsigned int max_connections; -extern unsigned int login_process_uid; extern struct auth_client *auth_client; extern bool closing_down; +extern unsigned int login_process_uid; + +extern struct login_settings *login_settings; void main_ref(void); void main_unref(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/login-common/login-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,184 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "login-settings.h" + +#include <stddef.h> +#include <unistd.h> + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct login_settings, name), NULL } + +static struct setting_define login_setting_defines[] = { + DEF(SET_STR, login_dir), + DEF(SET_BOOL, login_chroot), + DEF(SET_STR, login_trusted_networks), + DEF(SET_STR, login_greeting), + DEF(SET_STR, login_log_format_elements), + DEF(SET_STR, login_log_format), + + DEF(SET_BOOL, login_process_per_connection), + DEF(SET_STR, capability_string), + + DEF(SET_ENUM, ssl), + DEF(SET_STR, ssl_ca_file), + DEF(SET_STR, ssl_cert_file), + DEF(SET_STR, ssl_key_file), + DEF(SET_STR, ssl_key_password), + DEF(SET_STR, ssl_parameters_file), + DEF(SET_STR, ssl_cipher_list), + DEF(SET_STR, ssl_cert_username_field), + DEF(SET_BOOL, ssl_verify_client_cert), + DEF(SET_BOOL, ssl_require_client_cert), + DEF(SET_BOOL, ssl_username_from_cert), + DEF(SET_BOOL, verbose_ssl), + + DEF(SET_BOOL, disable_plaintext_auth), + DEF(SET_BOOL, verbose_auth), + DEF(SET_BOOL, auth_debug), + DEF(SET_BOOL, verbose_proctitle), + + DEF(SET_UINT, login_max_connections), + + SETTING_DEFINE_LIST_END +}; + +static struct login_settings login_default_settings = { + MEMBER(login_dir) "login", + MEMBER(login_chroot) TRUE, + MEMBER(login_trusted_networks) "", + MEMBER(login_greeting) PACKAGE" ready.", + MEMBER(login_log_format_elements) "user=<%u> method=%m rip=%r lip=%l %c", + MEMBER(login_log_format) "%$: %s", + + MEMBER(login_process_per_connection) TRUE, + MEMBER(capability_string) NULL, + + MEMBER(ssl) "yes:no:required", + MEMBER(ssl_ca_file) "", + MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem", + MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem", + MEMBER(ssl_key_password) "", + MEMBER(ssl_parameters_file) "ssl-parameters.dat", + MEMBER(ssl_cipher_list) "ALL:!LOW:!SSLv2", + MEMBER(ssl_cert_username_field) "commonName", + MEMBER(ssl_verify_client_cert) FALSE, + MEMBER(ssl_require_client_cert) FALSE, + MEMBER(ssl_username_from_cert) FALSE, + MEMBER(verbose_ssl) FALSE, + + MEMBER(disable_plaintext_auth) TRUE, + MEMBER(verbose_auth) FALSE, + MEMBER(auth_debug) FALSE, + MEMBER(verbose_proctitle) FALSE, + + MEMBER(login_max_connections) 256 +}; + +struct setting_parser_info login_setting_parser_info = { + MEMBER(defines) login_setting_defines, + MEMBER(defaults) &login_default_settings, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct login_settings) +}; + +static pool_t settings_pool = NULL; + +static int ssl_settings_check(struct login_settings *set ATTR_UNUSED) +{ +#ifndef HAVE_SSL + i_error("SSL support not compiled in but ssl_disable=no"); + return FALSE; +#else + if (*set->ssl_cert_file == '\0') { + i_error("ssl_cert_file not set"); + return FALSE; + } + if (access(set->ssl_cert_file, R_OK) < 0) { + i_error("ssl_cert_file: access(%s) failed: %m", + set->ssl_cert_file); + return FALSE; + } + + if (*set->ssl_key_file == '\0') { + i_error("ssl_key_file not set"); + return FALSE; + } + if (access(set->ssl_key_file, R_OK) < 0) { + i_error("ssl_key_file: access(%s) failed: %m", + set->ssl_key_file); + return FALSE; + } + + if (*set->ssl_ca_file != '\0' && + access(set->ssl_ca_file, R_OK) < 0) { + i_error("ssl_ca_file: access(%s) failed: %m", + set->ssl_ca_file); + return FALSE; + } + + if (set->ssl_verify_client_cert && *set->ssl_ca_file == '\0') { + i_error("ssl_verify_client_cert set, but ssl_ca_file not"); + return FALSE; + } + return TRUE; +#endif +} + +static void login_settings_check(struct login_settings *set) +{ + if (strcmp(set->ssl, "no") == 0) { + /* disabled */ + } else if (strcmp(set->ssl, "yes") == 0) { + if (!ssl_settings_check(set)) + set->ssl = "no"; + } else if (strcmp(set->ssl, "required") == 0) { + if (!ssl_settings_check(set)) + i_fatal("Couldn't initialize ssl with ssl=required"); + set->disable_plaintext_auth = TRUE; + } else { + i_fatal("Unknown ssl setting value: %s", set->ssl); + } + + set->log_format_elements_split = + t_strsplit(set->login_log_format_elements, " "); + + if (set->ssl_require_client_cert || set->ssl_username_from_cert) { + /* if we require valid cert, make sure we also ask for it */ + set->ssl_verify_client_cert = TRUE; + } + if (set->login_max_connections < 1) + i_fatal("login_max_connections must be at least 1"); +} + +struct login_settings *login_settings_read(void) +{ + struct setting_parser_context *parser; + struct login_settings *set; + + if (settings_pool == NULL) + settings_pool = pool_alloconly_create("settings pool", 512); + else + p_clear(settings_pool); + + parser = settings_parser_init(settings_pool, + &login_setting_parser_info, + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); + + if (settings_parse_environ(parser) < 0) { + i_fatal("Error reading configuration: %s", + settings_parser_get_error(parser)); + } + + set = settings_parser_get(parser); + settings_parser_deinit(&parser); + login_settings_check(set); + return set; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/login-common/login-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,40 @@ +#ifndef __LOGIN_SETTINGS_H +#define __LOGIN_SETTINGS_H + +struct login_settings { + const char *login_dir; + bool login_chroot; + const char *login_trusted_networks; + const char *login_greeting; + const char *login_log_format_elements, *login_log_format; + + bool login_process_per_connection; + const char *capability_string; + + const char *ssl; + const char *ssl_ca_file; + const char *ssl_cert_file; + const char *ssl_key_file; + const char *ssl_key_password; + const char *ssl_parameters_file; + const char *ssl_cipher_list; + const char *ssl_cert_username_field; + bool ssl_verify_client_cert; + bool ssl_require_client_cert; + bool ssl_username_from_cert; + bool verbose_ssl; + + bool disable_plaintext_auth; + bool verbose_auth; + bool auth_debug; + bool verbose_proctitle; + + unsigned int login_max_connections; + + /* generated: */ + const char *const *log_format_elements_split; +}; + +struct login_settings *login_settings_read(void); + +#endif
--- a/src/login-common/main.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/main.c Tue Jan 27 18:21:53 2009 -0500 @@ -19,13 +19,7 @@ #include <unistd.h> #include <syslog.h> -bool disable_plaintext_auth, process_per_connection; -bool verbose_proctitle, verbose_ssl, verbose_auth, auth_debug; -bool ssl_required, ssl_require_client_cert; -const char *greeting, *log_format; -const char *const *log_format_elements; -const char *trusted_networks; -unsigned int max_connections; +struct login_settings *login_settings; unsigned int login_process_uid; struct auth_client *auth_client; bool closing_down; @@ -101,7 +95,7 @@ client->remote_port = remote_port; client->local_port = local_port; - if (process_per_connection) { + if (login_settings->login_process_per_connection) { closing_down = TRUE; main_listen_stop(); } @@ -139,7 +133,7 @@ client->local_port = local_port; } - if (process_per_connection) { + if (login_settings->login_process_per_connection) { closing_down = TRUE; main_listen_stop(); } @@ -161,7 +155,7 @@ } current_count = ssl_proxy_get_count() + login_proxy_get_count(); - if (current_count >= max_connections) { + if (current_count >= login_settings->login_max_connections) { /* can't accept any more connections until existing proxies get destroyed */ return; @@ -220,9 +214,10 @@ void connection_queue_add(unsigned int connection_count) { + unsigned int max_connections = login_settings->login_max_connections; unsigned int current_count; - if (process_per_connection) + if (login_settings->login_process_per_connection) return; current_count = clients_get_count() + ssl_proxy_get_count() + @@ -256,6 +251,8 @@ { const char *value; + login_settings = login_settings_read(); + if (!is_inetd) i_set_failure_internal(); else { @@ -271,6 +268,7 @@ if (chdir(value) < 0) i_error("chdir(%s) failed: %m", value); + /* Initialize SSL proxy so it can read certificate and private key file. */ random_init(); @@ -280,14 +278,13 @@ listen_count = value == NULL ? 0 : atoi(value); value = getenv("SSL_LISTEN_FDS"); ssl_listen_count = value == NULL ? 0 : atoi(value); - value = getenv("MAX_CONNECTIONS"); - max_connections = value == NULL ? 1 : strtoul(value, NULL, 10); /* set the number of fds we want to use. it may get increased or decreased. leave a couple of extra fds for auth sockets and such. normal connections each use one fd, but SSL connections use two */ *max_fds_r = LOGIN_MASTER_SOCKET_FD + 16 + - listen_count + ssl_listen_count + max_connections*2; + listen_count + ssl_listen_count + + login_settings->login_max_connections*2; restrict_fd_limit(*max_fds_r); /* Refuse to run as root - we should never need it and it's @@ -314,31 +311,6 @@ lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); lib_signals_ignore(SIGPIPE, TRUE); - process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL; - verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL; - verbose_ssl = getenv("VERBOSE_SSL") != NULL; - verbose_auth = getenv("VERBOSE_AUTH") != NULL; - auth_debug = getenv("AUTH_DEBUG") != NULL; - ssl_required = getenv("SSL_REQUIRED") != NULL; - ssl_require_client_cert = getenv("SSL_REQUIRE_CLIENT_CERT") != NULL; - disable_plaintext_auth = ssl_required || - getenv("DISABLE_PLAINTEXT_AUTH") != NULL; - - greeting = getenv("GREETING"); - if (greeting == NULL) - greeting = PACKAGE" ready."; - - value = getenv("LOG_FORMAT_ELEMENTS"); - if (value == NULL) - value = "user=<%u> method=%m rip=%r lip=%l %c : %$"; - log_format_elements = t_strsplit(value, " "); - - log_format = getenv("LOG_FORMAT"); - if (log_format == NULL) - log_format = "%$: %s"; - - trusted_networks = getenv("TRUSTED_NETWORKS"); - value = getenv("PROCESS_UID"); if (value == NULL) i_fatal("BUG: PROCESS_UID environment not given"); @@ -346,11 +318,6 @@ if (login_process_uid == 0) i_fatal("BUG: PROCESS_UID environment is 0"); - /* capability default is set in imap/pop3-login */ - value = getenv("CAPABILITY_STRING"); - if (value != NULL && *value != '\0') - capability_string = value; - closing_down = FALSE; main_refcount = 0;
--- a/src/login-common/master.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/master.c Tue Jan 27 18:21:53 2009 -0500 @@ -39,7 +39,7 @@ { struct client *client; - if (reply->tag == 0 && !process_per_connection) { + if (reply->tag == 0 && !login_settings->login_process_per_connection) { /* this means we have to start listening again. we've reached maximum number of login processes. */ main_listen_start();
--- a/src/login-common/sasl-server.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/sasl-server.c Tue Jan 27 18:21:53 2009 -0500 @@ -149,7 +149,7 @@ return; } - if (!client->secured && disable_plaintext_auth && + if (!client->secured && login_settings->disable_plaintext_auth && (mech->flags & MECH_SEC_PLAINTEXT) != 0) { sasl_server_auth_failed(client, "Plaintext authentication disabled."); @@ -182,7 +182,7 @@ { i_assert(client->authenticating); - if (verbose_auth && reason != NULL) { + if (login_settings->verbose_auth && reason != NULL) { const char *auth_name = str_sanitize(client->auth_mech_name, MAX_MECH_NAME); client_syslog(client,
--- a/src/login-common/ssl-proxy-openssl.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/login-common/ssl-proxy-openssl.c Tue Jan 27 18:21:53 2009 -0500 @@ -6,6 +6,7 @@ #include "network.h" #include "ostream.h" #include "read-full.h" +#include "safe-memset.h" #include "llist.h" #include "ssl-proxy.h" @@ -22,7 +23,6 @@ #include <openssl/err.h> #include <openssl/rand.h> -#define DOVECOT_SSL_DEFAULT_CIPHER_LIST "ALL:!LOW:!SSLv2" /* Check every 30 minutes if parameters file has been updated */ #define SSL_PARAMFILE_CHECK_INTERVAL (60*30) @@ -679,7 +679,8 @@ proxy = SSL_get_ex_data(ssl, extdata_index); proxy->cert_received = TRUE; - if (verbose_ssl || (verbose_auth && !preverify_ok)) { + if (login_settings->verbose_ssl || + (login_settings->verbose_auth && !preverify_ok)) { char buf[1024]; X509_NAME *subject; @@ -755,23 +756,13 @@ void ssl_proxy_init(void) { static char dovecot[] = "dovecot"; - const char *cafile, *certfile, *keyfile, *cipher_list, *username_field; + const struct login_settings *set = login_settings; + unsigned char buf; char *password; - unsigned char buf; unsigned long err; - memset(&ssl_params, 0, sizeof(ssl_params)); - - cafile = getenv("SSL_CA_FILE"); - certfile = getenv("SSL_CERT_FILE"); - keyfile = getenv("SSL_KEY_FILE"); - ssl_params.fname = getenv("SSL_PARAM_FILE"); - password = getenv("SSL_KEY_PASSWORD"); - - if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) { - /* SSL support is disabled */ + if (strcmp(set->ssl, "no") == 0) return; - } CRYPTO_set_mem_functions(ssl_clean_malloc, ssl_clean_realloc, ssl_clean_free); @@ -785,55 +776,56 @@ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); - cipher_list = getenv("SSL_CIPHER_LIST"); - if (cipher_list == NULL) - cipher_list = DOVECOT_SSL_DEFAULT_CIPHER_LIST; - if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list) != 1) { + if (SSL_CTX_set_cipher_list(ssl_ctx, set->ssl_cipher_list) != 1) { i_fatal("Can't set cipher list to '%s': %s", - cipher_list, ssl_last_error()); + set->ssl_cipher_list, ssl_last_error()); } - if (cafile != NULL) { - if (SSL_CTX_load_verify_locations(ssl_ctx, cafile, NULL) != 1) { + if (*set->ssl_ca_file != '\0') { + if (SSL_CTX_load_verify_locations(ssl_ctx, set->ssl_ca_file, + NULL) != 1) { i_fatal("Can't load CA file %s: %s", - cafile, ssl_last_error()); + set->ssl_ca_file, ssl_last_error()); } } - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) { + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, + set->ssl_cert_file) != 1) { err = ERR_peek_error(); if (ERR_GET_LIB(err) != ERR_LIB_PEM || ERR_GET_REASON(err) != PEM_R_NO_START_LINE) { i_fatal("Can't load certificate file %s: %s", - certfile, ssl_last_error()); - } else if (is_pem_key_file(certfile)) { + set->ssl_cert_file, ssl_last_error()); + } else if (is_pem_key_file(set->ssl_cert_file)) { i_fatal("Can't load certificate file %s: " "The file contains a private key " "(you've mixed ssl_cert_file and ssl_key_file settings)", - certfile); + set->ssl_cert_file); } else { i_fatal("Can't load certificate file %s: " "The file doesn't contain a certificate.", - certfile); + set->ssl_cert_file); } } + password = t_strdup_noconst(set->ssl_key_password); SSL_CTX_set_default_passwd_cb(ssl_ctx, pem_password_callback); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, password); - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, set->ssl_key_file, SSL_FILETYPE_PEM) != 1) { i_fatal("Can't load private key file %s: %s", - keyfile, ssl_last_error()); + set->ssl_key_file, ssl_last_error()); } + safe_memset(password, 0, strlen(password)); if (SSL_CTX_need_tmp_RSA(ssl_ctx)) SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback); - if (verbose_ssl) + if (set->verbose_ssl) SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback); - if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) { + if (set->ssl_verify_client_cert) { #if OPENSSL_VERSION_NUMBER >= 0x00907000L X509_STORE *store; @@ -845,18 +837,13 @@ SSL_VERIFY_CLIENT_ONCE, ssl_verify_client_cert); SSL_CTX_set_client_CA_list(ssl_ctx, - SSL_load_client_CA_file(cafile)); + SSL_load_client_CA_file(set->ssl_ca_file)); } - username_field = getenv("SSL_CERT_USERNAME_FIELD"); - if (username_field == NULL) - ssl_username_nid = NID_commonName; - else { - ssl_username_nid = OBJ_txt2nid(username_field); - if (ssl_username_nid == NID_undef) { - i_fatal("Invalid ssl_cert_username_field: %s", - username_field); - } + ssl_username_nid = OBJ_txt2nid(set->ssl_cert_username_field); + if (ssl_username_nid == NID_undef) { + i_fatal("Invalid ssl_cert_username_field: %s", + set->ssl_cert_username_field); } /* PRNG initialization might want to use /dev/urandom, make sure it
--- a/src/master/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -10,6 +10,7 @@ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ + -DBINDIR=\""$(bindir)"\" \ -DMODULEDIR=\""$(moduledir)"\" \ -DSSLDIR=\""$(ssldir)\"" @@ -29,8 +30,8 @@ log.c \ login-process.c \ mail-process.c \ + master-settings.c \ main.c \ - master-settings.c \ syslog-util.c \ ssl-init.c \ sysinfo-get.c @@ -53,9 +54,6 @@ ssl-init.h \ sysinfo-get.h -EXTRA_DIST = \ - master-settings-defs.c - ssl_build_param_SOURCES = \ ssl-init-main.c \ ssl-init-openssl.c \
--- a/src/master/auth-process.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/auth-process.c Tue Jan 27 18:21:53 2009 -0500 @@ -1,6 +1,7 @@ /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */ #include "common.h" +#include "array.h" #include "hash.h" #include "ioloop.h" #include "env-util.h" @@ -30,7 +31,8 @@ struct auth_process_group *next; int listen_fd; - struct auth_settings *set; + const struct master_settings *master_set; + const struct master_auth_settings *set; unsigned int process_count; struct auth_process *processes; @@ -323,7 +325,7 @@ path = t_strdup_printf("%s/auth-worker.%s", *group->set->chroot != '\0' ? group->set->chroot : - group->set->parent->defaults->base_dir, + group->master_set->base_dir, dec2str(pid)); p->worker_listen_fd = unix_socket_create(path, 0600, group->set->uid, @@ -371,7 +373,7 @@ path = t_strdup_printf("%s/auth-worker.%s", *p->group->set->chroot != '\0' ? p->group->set->chroot : - p->group->set->parent->defaults->base_dir, + p->group->master_set->base_dir, dec2str(p->pid)); (void)unlink(path); @@ -393,21 +395,6 @@ i_free(p); } -static void -socket_settings_env_put(const char *env_base, struct socket_settings *set) -{ - if (!set->used) - return; - - env_put(t_strdup_printf("%s=%s", env_base, set->path)); - if (set->mode != 0) - env_put(t_strdup_printf("%s_MODE=%o", env_base, set->mode)); - if (*set->user != '\0') - env_put(t_strdup_printf("%s_USER=%s", env_base, set->user)); - if (*set->group != '\0') - env_put(t_strdup_printf("%s_GROUP=%s", env_base, set->group)); -} - static int connect_auth_socket(struct auth_process_group *group, const char *path) { @@ -427,13 +414,10 @@ return 0; } -static void auth_set_environment(struct auth_settings *set) +static void auth_set_environment(const struct master_settings *master_set, + const struct master_auth_settings *set) { - struct auth_socket_settings *as; - struct auth_passdb_settings *ap; - struct auth_userdb_settings *au; - const char *str; - int i; + master_settings_export_to_env(master_set); /* setup access environment */ restrict_access_set_env(set->user, set->uid, set->gid, @@ -442,93 +426,42 @@ /* set other environment */ env_put("DOVECOT_MASTER=1"); env_put(t_strconcat("AUTH_NAME=", set->name, NULL)); - env_put(t_strconcat("MECHANISMS=", set->mechanisms, NULL)); - env_put(t_strconcat("REALMS=", set->realms, NULL)); - env_put(t_strconcat("DEFAULT_REALM=", set->default_realm, NULL)); - env_put(t_strconcat("USERNAME_CHARS=", set->username_chars, NULL)); - env_put(t_strconcat("ANONYMOUS_USERNAME=", - set->anonymous_username, NULL)); - env_put(t_strconcat("USERNAME_TRANSLATION=", - set->username_translation, NULL)); - env_put(t_strconcat("USERNAME_FORMAT=", set->username_format, NULL)); - env_put(t_strconcat("MASTER_USER_SEPARATOR=", - set->master_user_separator, NULL)); - env_put(t_strdup_printf("CACHE_SIZE=%u", set->cache_size)); - env_put(t_strdup_printf("CACHE_TTL=%u", set->cache_ttl)); - env_put(t_strdup_printf("CACHE_NEGATIVE_TTL=%u", - set->cache_negative_ttl)); + restrict_process_size(set->process_size, (unsigned int)-1); +} - for (ap = set->passdbs, i = 1; ap != NULL; ap = ap->next, i++) { - env_put(t_strdup_printf("PASSDB_%u_DRIVER=%s", i, ap->driver)); - if (ap->args != NULL) { - env_put(t_strdup_printf("PASSDB_%u_ARGS=%s", - i, ap->args)); - } - if (ap->deny) - env_put(t_strdup_printf("PASSDB_%u_DENY=1", i)); - if (ap->pass) - env_put(t_strdup_printf("PASSDB_%u_PASS=1", i)); - if (ap->master) - env_put(t_strdup_printf("PASSDB_%u_MASTER=1", i)); - } - for (au = set->userdbs, i = 1; au != NULL; au = au->next, i++) { - env_put(t_strdup_printf("USERDB_%u_DRIVER=%s", i, au->driver)); - if (au->args != NULL) { - env_put(t_strdup_printf("USERDB_%u_ARGS=%s", - i, au->args)); - } - } +static const struct master_auth_socket_settings * +get_connect_socket(const struct master_auth_settings *auth_set) +{ + struct master_auth_socket_settings *const *as; + unsigned int count; - for (as = set->sockets, i = 1; as != NULL; as = as->next, i++) { - if (strcmp(as->type, "listen") != 0) - continue; - - str = t_strdup_printf("AUTH_%u", i); - socket_settings_env_put(str, &as->client); - socket_settings_env_put(t_strconcat(str, "_MASTER", NULL), - &as->master); - } + if (!array_is_created(&auth_set->sockets)) + return NULL; - if (set->verbose) - env_put("VERBOSE=1"); - if (set->debug) - env_put("VERBOSE_DEBUG=1"); - if (set->debug_passwords) - env_put("VERBOSE_DEBUG_PASSWORDS=1"); - if (set->ssl_require_client_cert) - env_put("SSL_REQUIRE_CLIENT_CERT=1"); - if (set->ssl_username_from_cert) - env_put("SSL_USERNAME_FROM_CERT=1"); - if (set->use_winbind) - env_put("USE_WINBIND=1"); - if (*set->krb5_keytab != '\0') { - /* Environment may be used by Kerberos 5 library directly, - although we also try to use it directly as well */ - env_put(t_strconcat("KRB5_KTNAME=", set->krb5_keytab, NULL)); - } - if (*set->gssapi_hostname != '\0') { - env_put(t_strconcat("GSSAPI_HOSTNAME=", - set->gssapi_hostname, NULL)); - } - env_put(t_strconcat("WINBIND_HELPER_PATH=", - set->winbind_helper_path, NULL)); - env_put(t_strdup_printf("FAILURE_DELAY=%u", set->failure_delay)); - - restrict_process_size(set->process_size, (unsigned int)-1); + as = array_get(&auth_set->sockets, &count); + if (count > 0 && strcmp(as[0]->type, "connect") == 0) + return as[0]; + else + return NULL; } static int create_auth_process(struct auth_process_group *group) { - struct auth_socket_settings *as; + const struct master_auth_socket_settings *as; + struct master_auth_socket_unix_settings *const *masters; const char *prefix, *executable; struct log_io *log; pid_t pid; + unsigned int count; int fd[2], log_fd, i; /* see if this is a connect socket */ - as = group->set->sockets; - if (as != NULL && strcmp(as->type, "connect") == 0) - return connect_auth_socket(group, as->master.path); + as = get_connect_socket(group->set); + if (as != NULL) { + masters = array_get(&as->masters, &count); + if (count > 0) + return connect_auth_socket(group, masters[0]->path); + } /* create communication to process with a socket pair */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) { @@ -583,7 +516,7 @@ if (dup2(log_fd, 2) < 0) i_fatal("dup2(stderr) failed: %m"); - child_process_init_env(); + child_process_init_env(group->master_set); if (group->listen_fd != 3) { if (dup2(group->listen_fd, 3) < 0) @@ -594,14 +527,12 @@ for (i = 0; i <= 2; i++) fd_close_on_exec(i, FALSE); - auth_set_environment(group->set); + auth_set_environment(group->master_set, group->set); env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s", *group->set->chroot != '\0' ? "" : - group->set->parent->defaults->base_dir, + group->master_set->base_dir, dec2str(getpid()))); - env_put(t_strdup_printf("AUTH_WORKER_MAX_COUNT=%u", - group->set->worker_max_count)); /* make sure we don't leak syslog fd, but do it last so that any errors above will be logged */ @@ -666,8 +597,8 @@ fd_close_on_exec(i, FALSE); fd_close_on_exec(4, FALSE); - child_process_init_env(); - auth_set_environment(process->group->set); + child_process_init_env(process->group->master_set); + auth_set_environment(process->group->master_set, process->group->set); /* make sure we don't leak syslog fd, but do it last so that any errors above will be logged */ @@ -694,25 +625,25 @@ return NULL; } -static void auth_process_group_create(struct auth_settings *auth_set) +static void auth_process_group_create(struct master_settings *set, + struct master_auth_settings *auth_set) { struct auth_process_group *group; const char *path; group = i_new(struct auth_process_group, 1); + group->master_set = set; group->set = auth_set; group->next = process_groups; process_groups = group; - if (auth_set->sockets != NULL && - strcmp(auth_set->sockets->type, "connect") == 0) + if (get_connect_socket(auth_set) != NULL) return; - path = t_strconcat(auth_set->parent->defaults->login_dir, "/", - auth_set->name, NULL); + path = t_strconcat(set->login_dir, "/", auth_set->name, NULL); group->listen_fd = unix_socket_create(path, 0660, master_uid, - auth_set->parent->login_gid, 128); + set->server->login_gid, 128); if (group->listen_fd == -1) i_fatal("Couldn't create auth process listener"); @@ -731,7 +662,7 @@ group->processes = next; } - path = t_strconcat(group->set->parent->defaults->login_dir, "/", + path = t_strconcat(group->master_set->login_dir, "/", group->set->name, NULL); (void)unlink(path); @@ -753,17 +684,14 @@ have_initialized_auth_processes = FALSE; } -static void auth_process_groups_create(struct server_settings *server) +static void auth_process_groups_create(struct master_settings *set) { - struct auth_settings *auth_set; + struct master_auth_settings *const *auth_sets; + unsigned int i, count; - while (server != NULL) { - auth_set = server->auths; - for (; auth_set != NULL; auth_set = auth_set->next) - auth_process_group_create(auth_set); - - server = server->next; - } + auth_sets = array_get(&set->auths, &count); + for (i = 0; i < count; i++) + auth_process_group_create(set, auth_sets[i]); } static void auth_processes_stall(void) @@ -787,7 +715,7 @@ if (process_groups == NULL) { /* first time here, create the groups */ - auth_process_groups_create(settings_root); + auth_process_groups_create(master_set->defaults); } for (group = process_groups; group != NULL; group = group->next) {
--- a/src/master/child-process.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/child-process.c Tue Jan 27 18:21:53 2009 -0500 @@ -41,7 +41,7 @@ hash_table_remove(processes, POINTER_CAST(pid)); } -void child_process_init_env(void) +void child_process_init_env(const struct master_settings *set) { int facility; @@ -53,13 +53,12 @@ if (env_tz != NULL) env_put(t_strconcat("TZ=", env_tz, NULL)); - if (settings_root == NULL || - !syslog_facility_find(settings_root->defaults->syslog_facility, - &facility)) + if (master_set == NULL || + !syslog_facility_find(set->syslog_facility, &facility)) facility = LOG_MAIL; env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility)); - if (settings_root != NULL && !settings_root->defaults->version_ignore) + if (master_set != NULL && !set->version_ignore) env_put("DOVECOT_VERSION="PACKAGE_VERSION); #ifdef DEBUG if (gdb) env_put("GDB=1");
--- a/src/master/child-process.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/child-process.h Tue Jan 27 18:21:53 2009 -0500 @@ -32,7 +32,7 @@ void child_process_add(pid_t pid, struct child_process *process); void child_process_remove(pid_t pid); -void child_process_init_env(void); +void child_process_init_env(const struct master_settings *set); void client_process_exec(const char *cmd, const char *title); void client_process_exec_argv(const char *executable, const char **argv);
--- a/src/master/dict-process.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/dict-process.c Tue Jan 27 18:21:53 2009 -0500 @@ -94,17 +94,17 @@ for (i = 0; i <= 3; i++) fd_close_on_exec(i, FALSE); - child_process_init_env(); + child_process_init_env(master_set->defaults); env_put(t_strconcat("DICT_LISTEN_FROM_FD=", process->listener->path, NULL)); - if (settings_root->defaults->dict_db_config != NULL) { + if (master_set->defaults->dict_db_config != NULL) { env_put(t_strconcat("DB_CONFIG=", - settings_root->defaults->dict_db_config, + master_set->defaults->dict_db_config, NULL)); } - dicts = array_get(&settings_root->dicts, &count); + dicts = array_get(&master_set->defaults->dicts, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) env_put(t_strdup_printf("DICT_%s=%s", dicts[i], dicts[i+1])); @@ -146,14 +146,16 @@ static void dict_listener_input(struct dict_listener *listener) { - unsigned int i; + unsigned int i = 0; int fd; i_assert(listener->processes == NULL); - for (i = 0; i < settings_root->defaults->dict_process_count; i++) { - if (dict_process_create(listener) < 0) - break; + if (array_is_created(&master_set->defaults->dicts)) { + for (i = 0; i < master_set->defaults->dict_process_count; i++) { + if (dict_process_create(listener) < 0) + break; + } } if (i > 0) io_remove(&listener->io); @@ -220,7 +222,7 @@ { const char *path; - path = t_strconcat(settings_root->defaults->base_dir, + path = t_strconcat(master_set->defaults->base_dir, "/"DICT_SERVER_SOCKET_NAME, NULL); dict_listener = dict_listener_init(path);
--- a/src/master/listener.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/listener.c Tue Jan 27 18:21:53 2009 -0500 @@ -79,7 +79,7 @@ } static void -check_conflicts_set(const struct settings *set, const struct ip_addr *ip, +check_conflicts_set(const struct master_settings *set, const struct ip_addr *ip, unsigned int port, const char *name1, const char *name2) { const struct listener *listens = NULL; @@ -115,17 +115,13 @@ static void check_conflicts(const struct ip_addr *ip, unsigned int port, const char *proto) { - struct server_settings *server; - - for (server = settings_root; server != NULL; server = server->next) { - if (server->imap != NULL) { - check_conflicts_set(server->imap, ip, port, - "imap", proto); - } - if (server->pop3 != NULL) { - check_conflicts_set(server->pop3, ip, port, - "pop3", proto); - } + if (master_set->imap != NULL) { + check_conflicts_set(master_set->imap, ip, port, + "imap", proto); + } + if (master_set->pop3 != NULL) { + check_conflicts_set(master_set->pop3, ip, port, + "pop3", proto); } } @@ -200,7 +196,7 @@ array_free(listens_arr); } -static void listen_parse_and_close_unneeded(struct settings *set) +static void listen_parse_and_close_unneeded(struct master_settings *set) { const char *const *proto; unsigned int default_port; @@ -246,7 +242,8 @@ } } -static void listen_copy_old(struct settings *old_set, struct settings *new_set) +static void listen_copy_old(struct master_settings *old_set, + struct master_settings *new_set) { if (old_set == NULL || new_set == NULL) { if (old_set != NULL) { @@ -317,49 +314,37 @@ } static void -listener_listen_missing(struct settings *set, const char *proto, bool retry) +listener_listen_missing(struct master_settings *set, + const char *proto, bool retry) { listener_array_listen_missing(proto, &set->listens, retry); listener_array_listen_missing(t_strconcat(proto, "s", NULL), &set->ssl_listens, retry); } -void listeners_open_fds(struct server_settings *old_set, bool retry) +void listeners_open_fds(struct master_server_settings *old_set, bool retry) { - struct server_settings *server; + if (old_set != NULL) { + listen_copy_old(old_set->imap, master_set->imap); + listen_copy_old(old_set->pop3, master_set->pop3); + } + listen_parse_and_close_unneeded(master_set->imap); + listen_parse_and_close_unneeded(master_set->pop3); - for (server = settings_root; server != NULL; server = server->next) { - if (old_set != NULL) { - listen_copy_old(old_set->imap, server->imap); - listen_copy_old(old_set->pop3, server->pop3); - } - listen_parse_and_close_unneeded(server->imap); - listen_parse_and_close_unneeded(server->pop3); - - if (old_set != NULL) - old_set = old_set->next; - } - - for (server = settings_root; server != NULL; server = server->next) { - if (server->imap != NULL) - listener_listen_missing(server->imap, "imap", retry); - if (server->pop3 != NULL) - listener_listen_missing(server->pop3, "pop3", retry); - } + if (master_set->imap != NULL) + listener_listen_missing(master_set->imap, "imap", retry); + if (master_set->pop3 != NULL) + listener_listen_missing(master_set->pop3, "pop3", retry); } void listeners_close_fds(void) { - struct server_settings *server; - - for (server = settings_root; server != NULL; server = server->next) { - if (server->imap != NULL) { - listener_close_fds(&server->imap->listens); - listener_close_fds(&server->imap->ssl_listens); - } - if (server->pop3 != NULL) { - listener_close_fds(&server->pop3->listens); - listener_close_fds(&server->pop3->ssl_listens); - } + if (master_set->imap != NULL) { + listener_close_fds(&master_set->imap->listens); + listener_close_fds(&master_set->imap->ssl_listens); + } + if (master_set->pop3 != NULL) { + listener_close_fds(&master_set->pop3->listens); + listener_close_fds(&master_set->pop3->ssl_listens); } }
--- a/src/master/listener.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/listener.h Tue Jan 27 18:21:53 2009 -0500 @@ -1,7 +1,7 @@ #ifndef LISTENER_H #define LISTENER_H -void listeners_open_fds(struct server_settings *old_set, bool retry); +void listeners_open_fds(struct master_server_settings *old_set, bool retry); void listeners_close_fds(void); #endif
--- a/src/master/login-process.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/login-process.c Tue Jan 27 18:21:53 2009 -0500 @@ -16,6 +16,7 @@ #include "auth-process.h" #include "mail-process.h" #include "master-login-interface.h" +#include "master-settings.h" #include "log.h" #include "ssl-init.h" @@ -65,7 +66,7 @@ static bool login_process_init_group(struct login_process *p); static void login_processes_start_missing(void *context); -static void login_group_create(struct settings *set) +static void login_group_create(struct master_settings *set) { struct login_group *group; @@ -217,18 +218,14 @@ static void login_process_groups_create(void) { - struct server_settings *server; - - for (server = settings_root; server != NULL; server = server->next) { - if (server->imap != NULL) - login_group_create(server->imap); - if (server->pop3 != NULL) - login_group_create(server->pop3); - } + if (master_set->imap != NULL) + login_group_create(master_set->imap); + if (master_set->pop3 != NULL) + login_group_create(master_set->pop3); } static struct login_group * -login_group_process_find(const char *name, enum mail_protocol protocol) +login_group_process_find(enum mail_protocol protocol) { struct login_group *group; @@ -236,8 +233,7 @@ login_process_groups_create(); for (group = login_groups; group != NULL; group = group->next) { - if (strcmp(group->set->server->name, name) == 0 && - group->set->protocol == protocol) + if (group->set->protocol == protocol) return group; } @@ -247,7 +243,7 @@ static bool login_process_read_group(struct login_process *p) { struct login_group *group; - const char *name, *proto; + const char *proto; unsigned char buf[256]; enum mail_protocol protocol; unsigned int len; @@ -272,15 +268,7 @@ else if (len == 0 || (size_t)ret != len) i_error("login: Server name wasn't sent"); else { - name = t_strndup(buf, len); - proto = strchr(name, '/'); - if (proto == NULL) { - proto = name; - name = "default"; - } else { - name = t_strdup_until(name, proto++); - } - + proto = t_strndup(buf, len); if (strcmp(proto, "imap") == 0) protocol = MAIL_PROTOCOL_IMAP; else if (strcmp(proto, "pop3") == 0) @@ -290,9 +278,9 @@ return FALSE; } - group = login_group_process_find(name, protocol); + group = login_group_process_find(protocol); if (group == NULL) { - i_error("login: Unknown server name '%s'", name); + i_error("login: No group for protocol '%s'", proto); return FALSE; } @@ -534,11 +522,9 @@ static void login_process_init_env(struct login_group *group, pid_t pid) { - struct settings *set = group->set; - const struct auth_settings *auth; - bool require_cert; + struct master_settings *set = group->set; - child_process_init_env(); + child_process_init_env(group->set); /* setup access environment - needs to be done after clean_child_process() since it clears environment. Don't set user @@ -551,79 +537,17 @@ env_put("DOVECOT_MASTER=1"); - if (strcmp(set->ssl, "no") != 0) { - const char *ssl_key_password; - - ssl_key_password = *set->ssl_key_password != '\0' ? - set->ssl_key_password : ssl_manual_key_password; - - if (*set->ssl_ca_file != '\0') { - env_put(t_strconcat("SSL_CA_FILE=", - set->ssl_ca_file, NULL)); - } - if (strcmp(set->ssl, "required") == 0) - env_put("SSL_REQUIRED=1"); - env_put(t_strconcat("SSL_CERT_FILE=", - set->ssl_cert_file, NULL)); - env_put(t_strconcat("SSL_KEY_FILE=", - set->ssl_key_file, NULL)); - env_put(t_strconcat("SSL_KEY_PASSWORD=", - ssl_key_password, NULL)); - env_put("SSL_PARAM_FILE="SSL_PARAMETERS_FILENAME); - if (*set->ssl_cipher_list != '\0') { - env_put(t_strconcat("SSL_CIPHER_LIST=", - set->ssl_cipher_list, NULL)); - } - env_put(t_strconcat("SSL_CERT_USERNAME_FIELD=", - set->ssl_cert_username_field, NULL)); - if (set->ssl_verify_client_cert) - env_put("SSL_VERIFY_CLIENT_CERT=1"); - } - - if (set->disable_plaintext_auth) - env_put("DISABLE_PLAINTEXT_AUTH=1"); - if (set->verbose_proctitle) - env_put("VERBOSE_PROCTITLE=1"); - if (set->verbose_ssl) - env_put("VERBOSE_SSL=1"); - if (set->server->auths->verbose) - env_put("VERBOSE_AUTH=1"); - if (set->server->auths->debug) - env_put("AUTH_DEBUG=1"); - require_cert = TRUE; - for (auth = set->server->auths; auth != NULL; auth = auth->next) { - if (!auth->ssl_require_client_cert) - require_cert = FALSE; - } - if (require_cert) - env_put("SSL_REQUIRE_CLIENT_CERT=1"); - - if (set->login_process_per_connection) { - env_put("PROCESS_PER_CONNECTION=1"); - env_put("MAX_CONNECTIONS=1"); - } else { - env_put(t_strdup_printf("MAX_CONNECTIONS=%u", - set->login_max_connections)); + master_settings_export_to_env(group->set); + if (ssl_manual_key_password != NULL) { + env_put(t_strconcat("SSL_MANUAL_KEY_PASSWORD=", + ssl_manual_key_password, NULL)); } env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL)); - env_put(t_strconcat("GREETING=", set->login_greeting, NULL)); - env_put(t_strconcat("LOG_FORMAT_ELEMENTS=", - set->login_log_format_elements, NULL)); - env_put(t_strconcat("LOG_FORMAT=", set->login_log_format, NULL)); - env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL)); - env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL)); - if (group->mail_process_type == PROCESS_TYPE_IMAP) { - env_put(t_strconcat("CAPABILITY_STRING=", - *set->imap_capability != '\0' ? - set->imap_capability : + env_put(t_strconcat("GENERATED_CAPABILITY=", set->imap_generated_capability, NULL)); } - if (*set->login_trusted_networks != '\0') { - env_put(t_strconcat("TRUSTED_NETWORKS=", - set->login_trusted_networks, NULL)); - } env_put(t_strconcat("LOGIN_DIR=", set->login_dir, NULL)); }
--- a/src/master/login-process.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/login-process.h Tue Jan 27 18:21:53 2009 -0500 @@ -8,7 +8,7 @@ int refcount; enum process_type mail_process_type; - struct settings *set; + struct master_settings *set; unsigned int processes; unsigned int listening_processes;
--- a/src/master/mail-process.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/mail-process.c Tue Jan 27 18:21:53 2009 -0500 @@ -13,6 +13,7 @@ #include "restrict-process-size.h" #include "home-expand.h" #include "var-expand.h" +#include "settings-parser.h" #include "mail-process.h" #include "login-process.h" #include "log.h" @@ -112,7 +113,7 @@ i_free(group); } -static bool validate_uid_gid(struct settings *set, uid_t uid, gid_t gid, +static bool validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid, const char *user) { if (uid == 0) { @@ -146,7 +147,7 @@ return TRUE; } -static bool validate_chroot(struct settings *set, const char *dir) +static bool validate_chroot(struct master_settings *set, const char *dir) { const char *const *chroot_dirs; @@ -214,257 +215,59 @@ var_has_key(str, 'h', "home"); } -static const char * -expand_mail_env(const char *env, const struct var_expand_table *table) +static void +mail_process_set_environment(struct master_settings *set, + const struct var_expand_table *table) { - string_t *str; - const char *p; - - str = t_str_new(256); - - /* it's either type:data or just data */ - p = strchr(env, ':'); - if (p != NULL) { - while (env != p) { - str_append_c(str, *env); - env++; - } - - str_append_c(str, *env++); - } - - if (has_missing_used_home(env, table)) { - i_fatal("userdb didn't return a home directory, " - "but mail location used it (%%h): %s", env); - } - - /* expand %vars */ - var_expand(str, env, table); - return str_c(str); -} - -static void -env_put_namespace(struct namespace_settings *ns, const char *default_location, - const struct var_expand_table *table) -{ - const char *location; - unsigned int i; - string_t *str; - - if (default_location == NULL) - default_location = ""; - for (i = 1; ns != NULL; i++, ns = ns->next) { - location = *ns->location != '\0' ? ns->location : - default_location; - location = expand_mail_env(location, table); - env_put(t_strdup_printf("NAMESPACE_%u=%s", i, location)); - - if (ns->separator != NULL) { - env_put(t_strdup_printf("NAMESPACE_%u_SEP=%s", - i, ns->separator)); - } - if (ns->type != NULL) { - env_put(t_strdup_printf("NAMESPACE_%u_TYPE=%s", - i, ns->type)); - } - if (ns->alias_for != NULL) { - env_put(t_strdup_printf("NAMESPACE_%u_ALIAS=%s", - i, ns->alias_for)); - } - if (ns->prefix != NULL) { - /* expand variables, eg. ~%u/ can be useful */ - str = t_str_new(256); - str_printfa(str, "NAMESPACE_%u_PREFIX=", i); - var_expand(str, ns->prefix, table); - env_put(str_c(str)); - } - if (ns->inbox) - env_put(t_strdup_printf("NAMESPACE_%u_INBOX=1", i)); - if (ns->hidden) - env_put(t_strdup_printf("NAMESPACE_%u_HIDDEN=1", i)); - if (strcmp(ns->list, "no") != 0) { - env_put(t_strdup_printf("NAMESPACE_%u_LIST=%s", - i, ns->list)); - } - if (ns->subscriptions) - env_put(t_strdup_printf("NAMESPACE_%u_SUBSCRIPTIONS=1", - i)); - } -} - -static void -mail_process_set_environment(struct settings *set, const char *mail, - const struct var_expand_table *var_expand_table, - bool exec_mail) -{ - const char *const *envs; + const char **envs; string_t *str; unsigned int i, count; - env_put(t_strconcat("MAIL_CACHE_FIELDS=", - set->mail_cache_fields, NULL)); - env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=", - set->mail_never_cache_fields, NULL)); - env_put(t_strdup_printf("MAIL_CACHE_MIN_MAIL_COUNT=%u", - set->mail_cache_min_mail_count)); - env_put(t_strdup_printf("MAILBOX_IDLE_CHECK_INTERVAL=%u", - set->mailbox_idle_check_interval)); - env_put(t_strdup_printf("MAIL_MAX_KEYWORD_LENGTH=%u", - set->mail_max_keyword_length)); - - if (set->protocol == MAIL_PROTOCOL_IMAP) { - env_put(t_strdup_printf("IMAP_MAX_LINE_LENGTH=%u", - set->imap_max_line_length)); - if (*set->imap_capability != '\0') { - env_put(t_strconcat("IMAP_CAPABILITY=", - set->imap_capability, NULL)); - } - env_put(t_strconcat("IMAP_CLIENT_WORKAROUNDS=", - set->imap_client_workarounds, NULL)); - env_put(t_strconcat("IMAP_LOGOUT_FORMAT=", - set->imap_logout_format, NULL)); - env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL)); - env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL)); - } - if (set->protocol == MAIL_PROTOCOL_POP3) { - env_put(t_strconcat("POP3_CLIENT_WORKAROUNDS=", - set->pop3_client_workarounds, NULL)); - env_put(t_strconcat("POP3_LOGOUT_FORMAT=", - set->pop3_logout_format, NULL)); - if (set->pop3_no_flag_updates) - env_put("POP3_NO_FLAG_UPDATES=1"); - if (set->pop3_reuse_xuidl) - env_put("POP3_REUSE_XUIDL=1"); - if (set->pop3_enable_last) - env_put("POP3_ENABLE_LAST=1"); - if (set->pop3_lock_session) - env_put("POP3_LOCK_SESSION=1"); - } + settings_var_expand(&master_setting_parser_info, set, + system_pool, table); - /* We care about POP3 UIDL format in all process types */ - env_put(t_strconcat("POP3_UIDL_FORMAT=", set->pop3_uidl_format, NULL)); - - if (set->mail_save_crlf) - env_put("MAIL_SAVE_CRLF=1"); - if (set->mmap_disable) - env_put("MMAP_DISABLE=1"); - if (set->dotlock_use_excl) - env_put("DOTLOCK_USE_EXCL=1"); - if (set->fsync_disable) - env_put("FSYNC_DISABLE=1"); - if (set->mail_nfs_storage) - env_put("MAIL_NFS_STORAGE=1"); - if (set->mail_nfs_index) - env_put("MAIL_NFS_INDEX=1"); - if (set->mailbox_list_index_disable) - env_put("MAILBOX_LIST_INDEX_DISABLE=1"); - if (set->maildir_stat_dirs) - env_put("MAILDIR_STAT_DIRS=1"); - if (set->maildir_copy_with_hardlinks) - env_put("MAILDIR_COPY_WITH_HARDLINKS=1"); - if (set->maildir_copy_preserve_filename) - env_put("MAILDIR_COPY_PRESERVE_FILENAME=1"); - if (set->mail_debug) - env_put("DEBUG=1"); - if (set->mail_full_filesystem_access) - env_put("FULL_FILESYSTEM_ACCESS=1"); - if (set->mbox_dirty_syncs) - env_put("MBOX_DIRTY_SYNCS=1"); - if (set->mbox_very_dirty_syncs) - env_put("MBOX_VERY_DIRTY_SYNCS=1"); - if (set->mbox_lazy_writes) - env_put("MBOX_LAZY_WRITES=1"); - /* when we're not certain that the log fd points to the master - process's log pipe (dump-capability, --exec-mail), don't let - the imap process listen for stderr since it might break - (e.g. epoll_ctl() gives EPERM). */ - if (set->shutdown_clients && !exec_mail) - env_put("STDERR_CLOSE_SHUTDOWN=1"); (void)umask(set->umask); - env_put(t_strconcat("LOCK_METHOD=", set->lock_method, NULL)); - env_put(t_strconcat("MBOX_READ_LOCKS=", set->mbox_read_locks, NULL)); - env_put(t_strconcat("MBOX_WRITE_LOCKS=", set->mbox_write_locks, NULL)); - env_put(t_strdup_printf("MBOX_LOCK_TIMEOUT=%u", - set->mbox_lock_timeout)); - env_put(t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u", - set->mbox_dotlock_change_timeout)); - env_put(t_strdup_printf("MBOX_MIN_INDEX_SIZE=%u", - set->mbox_min_index_size)); - - env_put(t_strdup_printf("DBOX_ROTATE_SIZE=%u", - set->dbox_rotate_size)); - env_put(t_strdup_printf("DBOX_ROTATE_MIN_SIZE=%u", - set->dbox_rotate_min_size)); - env_put(t_strdup_printf("DBOX_ROTATE_DAYS=%u", - set->dbox_rotate_days)); - - if (*set->mail_plugins != '\0') { - env_put(t_strconcat("MAIL_PLUGIN_DIR=", - set->mail_plugin_dir, NULL)); - env_put(t_strconcat("MAIL_PLUGINS=", set->mail_plugins, NULL)); + if (array_is_created(&set->plugin_envs)) + envs = array_get_modifiable(&set->plugin_envs, &count); + else { + count = 0; + envs = NULL; } - - /* user given environment - may be malicious. virtual_user comes from - auth process, but don't trust that too much either. Some auth - mechanism might allow leaving extra data there. */ - if ((mail == NULL || *mail == '\0') && *set->mail_location != '\0') - mail = expand_mail_env(set->mail_location, var_expand_table); - env_put(t_strconcat("MAIL=", mail, NULL)); - - if (set->server->namespaces != NULL) { - env_put_namespace(set->server->namespaces, - mail, var_expand_table); - } - str = t_str_new(256); - envs = array_get(&set->plugin_envs, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { - str_truncate(str, 0); - var_expand(str, envs[i+1], var_expand_table); - - if (has_missing_used_home(envs[i+1], var_expand_table)) { + if (has_missing_used_home(envs[i+1], table)) { i_error("userdb didn't return a home directory, " "but it's used in plugin setting %s: %s", envs[i], envs[i+1]); } + str_truncate(str, 0); + var_expand(str, envs[i+1], table); + envs[i+1] = t_strdup(str_c(str)); + } - env_put(t_strconcat(t_str_ucase(envs[i]), "=", - str_c(str), NULL)); - } + master_settings_export_to_env(set); } void mail_process_exec(const char *protocol, const char **args) { - struct server_settings *server = settings_root; const struct var_expand_table *var_expand_table; - struct settings *set; + struct master_settings *set; const char *executable; if (strcmp(protocol, "ext") == 0) { /* external binary. section contains path for it. */ if (*args == NULL) i_fatal("External binary parameter not given"); - set = server->defaults; + set = master_set->defaults; executable = *args; } else { - const char *section = *args; - - if (section != NULL) { - for (; server != NULL; server = server->next) { - if (strcmp(server->name, section) == 0) - break; - } - if (server == NULL) - i_fatal("Section not found: '%s'", section); - } - if (strcmp(protocol, "imap") == 0) - set = server->imap; + set = master_set->imap; else if (strcmp(protocol, "pop3") == 0) - set = server->pop3; + set = master_set->pop3; else i_fatal("Unknown protocol: '%s'", protocol); executable = set->mail_executable; @@ -493,8 +296,7 @@ env_put(str_c(str)); } - mail_process_set_environment(set, getenv("MAIL"), var_expand_table, - TRUE); + mail_process_set_environment(set, var_expand_table); if (args == NULL) client_process_exec(executable, ""); else @@ -542,7 +344,7 @@ } enum master_login_status -create_mail_process(enum process_type process_type, struct settings *set, +create_mail_process(enum process_type process_type, struct master_settings *set, const struct mail_login_request *request, const char *user, const char *const *args, const unsigned char *data, bool dump_capability, @@ -752,7 +554,7 @@ log_set_prefix(log, str_c(str)); } - child_process_init_env(); + child_process_init_env(set); /* move the client socket into stdin and stdout fds, log to stderr */ if (dup2(dump_capability ? null_fd : request->fd, 0) < 0) @@ -825,8 +627,7 @@ i_fatal("chdir(/tmp) failed: %m"); } - mail_process_set_environment(set, mail, var_expand_table, - dump_capability); + mail_process_set_environment(set, var_expand_table); /* extra args. uppercase key value. */ args = array_get(&extra_args, &count); @@ -851,7 +652,7 @@ if (nfs_check) { /* ideally we should check all of the namespaces, but for now don't bother. */ - const char *mail_location = getenv("NAMESPACE_1"); + const char *mail_location = getenv("NAMESPACE_1"); //FIXME if (mail_location == NULL) mail_location = getenv("MAIL");
--- a/src/master/mail-process.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/mail-process.h Tue Jan 27 18:21:53 2009 -0500 @@ -17,7 +17,7 @@ void mail_process_exec(const char *protocol, const char **args) ATTR_NORETURN; enum master_login_status -create_mail_process(enum process_type process_type, struct settings *set, +create_mail_process(enum process_type process_type, struct master_settings *set, const struct mail_login_request *request, const char *user, const char *const *args, const unsigned char *data, bool dump_capability,
--- a/src/master/main.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/main.c Tue Jan 27 18:21:53 2009 -0500 @@ -54,7 +54,7 @@ master_fatal_callback(enum log_type type, int status, const char *format, va_list args) { - const struct settings *set = settings_root->defaults; + const struct master_settings *set = master_set->defaults; const char *path, *str; va_list args2; int fd; @@ -82,7 +82,7 @@ static void fatal_log_check(void) { - const struct settings *set = settings_root->defaults; + const struct master_settings *set = master_set->defaults; const char *path; char buf[1024]; ssize_t ret; @@ -107,12 +107,15 @@ i_error("unlink(%s) failed: %m", path); } -static void auth_warning_print(const struct server_settings *set) +static void auth_warning_print(const struct master_server_settings *set) { + struct master_auth_settings *const *auths; + unsigned int count; struct stat st; auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0; - if (!auth_success_written && !set->auths->debug && + auths = array_get(&set->defaults->auths, &count); + if (!auth_success_written && count > 0 && !auths[0]->debug && strcmp(set->defaults->protocols, "none") != 0) { i_info("If you have trouble with authentication failures,\n" "enable auth_debug setting. " @@ -121,7 +124,7 @@ } } -static void set_logfile(struct settings *set) +static void set_logfile(struct master_settings *set) { int facility; @@ -144,7 +147,9 @@ static void settings_reload(void) { - struct server_settings *old_set = settings_root; + /* this old_set wrapping works because master settings are + alternatingly read using two different pools */ + struct master_server_settings *set, *old_set = master_set; i_warning("SIGHUP received - reloading configuration"); @@ -156,12 +161,13 @@ /* see if hostname changed */ hostpid_init(); - if (!master_settings_read(configfile, FALSE, FALSE)) + if (master_settings_read(configfile, &set) < 0 || + !master_settings_check(set, FALSE, FALSE)) i_warning("Invalid configuration, keeping old one"); else { if (!IS_INETD()) listeners_open_fds(old_set, TRUE); - set_logfile(settings_root->defaults); + set_logfile(set->defaults); } } @@ -183,10 +189,10 @@ static void sig_reopen_logs(int signo ATTR_UNUSED, void *context ATTR_UNUSED) { - set_logfile(settings_root->defaults); + set_logfile(master_set->defaults); } -static bool have_stderr_set(struct settings *set) +static bool have_stderr_set(struct master_settings *set) { if (*set->log_path != '\0' && strcmp(set->log_path, "/dev/stderr") == 0) @@ -199,17 +205,12 @@ return FALSE; } -static bool have_stderr(struct server_settings *server) +static bool have_stderr(struct master_server_settings *server) { - while (server != NULL) { - if (server->imap != NULL && have_stderr_set(server->imap)) - return TRUE; - if (server->pop3 != NULL && have_stderr_set(server->pop3)) - return TRUE; - - server = server->next; - } - + if (server->imap != NULL && have_stderr_set(server->imap)) + return TRUE; + if (server->pop3 != NULL && have_stderr_set(server->pop3)) + return TRUE; return FALSE; } @@ -259,6 +260,44 @@ (void)close(fd); } +static void pid_file_check_running(const struct master_settings *set) +{ + const char *path; + char buf[32]; + int fd; + ssize_t ret; + + path = t_strconcat(set->base_dir, "/master.pid", NULL); + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno == ENOENT) + return; + i_fatal("open(%s) failed: %m", path); + } + + ret = read(fd, buf, sizeof(buf)); + if (ret <= 0) { + if (ret == 0) + i_error("Empty PID file in %s, overriding", path); + else + i_fatal("read(%s) failed: %m", path); + } else { + pid_t pid; + + if (buf[ret-1] == '\n') + ret--; + buf[ret] = '\0'; + pid = atoi(buf); + if (pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)) { + /* doesn't exist */ + } else { + i_fatal("Dovecot is already running with PID %s " + "(read from %s)", buf, path); + } + } + (void)close(fd); +} + static void main_log_startup(void) { #define STARTUP_STRING PACKAGE_NAME" v"VERSION" starting up" @@ -277,9 +316,9 @@ /* deny file access from everyone else except owner */ (void)umask(0077); - set_logfile(settings_root->defaults); + set_logfile(master_set->defaults); /* close stderr unless we're logging into /dev/stderr. */ - if (!have_stderr(settings_root)) { + if (!have_stderr(master_set)) { if (dup2(null_fd, 2) < 0) i_fatal("dup2(2) failed: %m"); } @@ -309,13 +348,13 @@ login_processes_init(); mail_processes_init(); - create_pid_file(t_strconcat(settings_root->defaults->base_dir, + create_pid_file(t_strconcat(master_set->defaults->base_dir, "/master.pid", NULL)); } static void main_deinit(void) { - (void)unlink(t_strconcat(settings_root->defaults->base_dir, + (void)unlink(t_strconcat(master_set->defaults->base_dir, "/master.pid", NULL)); login_processes_destroy_all(); @@ -337,7 +376,7 @@ closelog(); } -static void daemonize(struct settings *set) +static void daemonize(struct master_settings *set) { pid_t pid; @@ -468,6 +507,7 @@ int main(int argc, char *argv[]) { /* parse arguments */ + struct master_server_settings *set; const char *exec_protocol = NULL, **exec_args = NULL, *user, *home; bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE; bool dump_config = FALSE, dump_config_nondefaults = FALSE; @@ -541,28 +581,37 @@ /* read and verify settings before forking */ T_BEGIN { master_settings_init(); - if (!master_settings_read(configfile, exec_protocol != NULL, - dump_config || log_error)) + if (master_settings_read(configfile, &set) < 0) i_fatal("Invalid configuration in %s", configfile); } T_END; + if (!log_error && !dump_config && exec_protocol == NULL) + pid_file_check_running(set->defaults); + + if (!master_settings_check(set, exec_protocol != NULL, + dump_config || log_error)) + i_fatal("Invalid configuration in %s", configfile); + if (dump_config) { const char *info; - info = sysinfo_get(settings_root->defaults->mail_location); + info = sysinfo_get(master_set->defaults->mail_location); if (*info != '\0') printf("# %s\n", info); - master_settings_dump(settings_root, dump_config_nondefaults); - return 0; + if (dump_config_nondefaults) + client_process_exec(DOVECOT_CONFIG_BIN_PATH" -a", ""); + else + client_process_exec(DOVECOT_CONFIG_BIN_PATH" -n", ""); + i_fatal("exec(%s) failed: %m", DOVECOT_CONFIG_BIN_PATH); } - if (ask_key_pass) T_BEGIN { + if (ask_key_pass && !log_error) T_BEGIN { const char *prompt; prompt = t_strdup_printf("Give the password for SSL key file " "%s: ", - settings_root->defaults->ssl_key_file); + master_set->defaults->ssl_key_file); askpass(prompt, ssl_manual_key_password, sizeof(ssl_manual_key_password)); } T_END; @@ -591,9 +640,9 @@ open_fds(); fatal_log_check(); - auth_warning_print(settings_root); + auth_warning_print(master_set); if (!foreground) - daemonize(settings_root->defaults); + daemonize(master_set->defaults); master_original_pid = getpid(); ioloop = io_loop_create();
--- a/src/master/master-settings-defs.c Mon Jan 26 19:17:54 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -/* kludgy: this file is included from master-settings.c and from deliver */ - -#undef DEF_STR -#undef DEF_INT -#undef DEF_BOOL -#define DEF_STR(name) DEF_STRUCT_STR(name, settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, settings) - -static struct setting_def setting_defs[] = { - /* common */ - DEF_STR(base_dir), - DEF_STR(log_path), - DEF_STR(info_log_path), - DEF_STR(log_timestamp), - DEF_STR(syslog_facility), - - /* general */ - DEF_STR(protocols), - DEF_STR(listen), - DEF_STR(ssl_listen), - - DEF_STR(ssl), - DEF_STR(ssl_ca_file), - DEF_STR(ssl_cert_file), - DEF_STR(ssl_key_file), - DEF_STR(ssl_key_password), - DEF_INT(ssl_parameters_regenerate), - DEF_STR(ssl_cipher_list), - DEF_STR(ssl_cert_username_field), - DEF_BOOL(ssl_verify_client_cert), - DEF_BOOL(disable_plaintext_auth), - DEF_BOOL(verbose_ssl), - DEF_BOOL(shutdown_clients), - DEF_BOOL(nfs_check), - DEF_BOOL(version_ignore), - - /* login */ - DEF_STR(login_dir), - DEF_STR(login_executable), - DEF_STR(login_user), - DEF_STR(login_greeting), - DEF_STR(login_log_format_elements), - DEF_STR(login_log_format), - - DEF_BOOL(login_process_per_connection), - DEF_BOOL(login_chroot), - DEF_STR(login_trusted_networks), - - DEF_INT(login_process_size), - DEF_INT(login_processes_count), - DEF_INT(login_max_processes_count), - DEF_INT(login_max_connections), - - /* mail */ - DEF_STR(valid_chroot_dirs), - DEF_STR(mail_chroot), - DEF_INT(max_mail_processes), - DEF_INT(mail_max_userip_connections), - DEF_BOOL(verbose_proctitle), - - DEF_INT(first_valid_uid), - DEF_INT(last_valid_uid), - DEF_INT(first_valid_gid), - DEF_INT(last_valid_gid), - DEF_STR(mail_access_groups), - DEF_STR(mail_privileged_group), - DEF_STR(mail_uid), - DEF_STR(mail_gid), - - DEF_STR(mail_location), - DEF_STR(mail_cache_fields), - DEF_STR(mail_never_cache_fields), - DEF_INT(mail_cache_min_mail_count), - DEF_INT(mailbox_idle_check_interval), - DEF_BOOL(mail_debug), - DEF_BOOL(mail_full_filesystem_access), - DEF_INT(mail_max_keyword_length), - DEF_BOOL(mail_save_crlf), - DEF_BOOL(mmap_disable), - DEF_BOOL(dotlock_use_excl), - DEF_BOOL(fsync_disable), - DEF_BOOL(mail_nfs_storage), - DEF_BOOL(mail_nfs_index), - DEF_BOOL(mailbox_list_index_disable), - DEF_STR(lock_method), - DEF_BOOL(maildir_stat_dirs), - DEF_BOOL(maildir_copy_with_hardlinks), - DEF_BOOL(maildir_copy_preserve_filename), - DEF_STR(mbox_read_locks), - DEF_STR(mbox_write_locks), - DEF_INT(mbox_lock_timeout), - DEF_INT(mbox_dotlock_change_timeout), - DEF_INT(mbox_min_index_size), - DEF_BOOL(mbox_dirty_syncs), - DEF_BOOL(mbox_very_dirty_syncs), - DEF_BOOL(mbox_lazy_writes), - DEF_INT(dbox_rotate_size), - DEF_INT(dbox_rotate_min_size), - DEF_INT(dbox_rotate_days), - DEF_INT(umask), - DEF_BOOL(mail_drop_priv_before_exec), - - DEF_STR(mail_executable), - DEF_INT(mail_process_size), - DEF_STR(mail_plugins), - DEF_STR(mail_plugin_dir), - DEF_STR(mail_log_prefix), - DEF_INT(mail_log_max_lines_per_sec), - - /* imap */ - DEF_INT(imap_max_line_length), - DEF_STR(imap_capability), - DEF_STR(imap_client_workarounds), - DEF_STR(imap_logout_format), - DEF_STR(imap_id_send), - DEF_STR(imap_id_log), - - /* pop3 */ - DEF_BOOL(pop3_no_flag_updates), - DEF_BOOL(pop3_enable_last), - DEF_BOOL(pop3_reuse_xuidl), - DEF_BOOL(pop3_lock_session), - DEF_STR(pop3_uidl_format), - DEF_STR(pop3_client_workarounds), - DEF_STR(pop3_logout_format), - - /* dict */ - DEF_STR(dict_db_config), - DEF_INT(dict_process_count), - - { 0, NULL, 0 } -};
--- a/src/master/master-settings.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/master-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -4,6 +4,7 @@ #include "array.h" #include "str.h" #include "istream.h" +#include "env-util.h" #include "fd-close-on-exec.h" #include "safe-mkdir.h" #include "mkdir-parents.h" @@ -11,7 +12,7 @@ #include "syslog-util.h" #include "mail-process.h" #include "master-login-interface.h" -#include "settings.h" +#include "settings-parser.h" #include <stdio.h> #include <stddef.h> @@ -19,6 +20,7 @@ #include <dirent.h> #include <unistd.h> #include <fcntl.h> +#include <ctype.h> #include <signal.h> #include <sys/stat.h> #include <sys/wait.h> @@ -28,148 +30,187 @@ # include <sys/resource.h> #endif -enum settings_type { - SETTINGS_TYPE_ROOT, - SETTINGS_TYPE_SERVER, - SETTINGS_TYPE_AUTH, - SETTINGS_TYPE_AUTH_SOCKET, - SETTINGS_TYPE_AUTH_PASSDB, - SETTINGS_TYPE_AUTH_USERDB, - SETTINGS_TYPE_NAMESPACE, - SETTINGS_TYPE_SOCKET, - SETTINGS_TYPE_DICT, - SETTINGS_TYPE_PLUGIN +extern struct setting_parser_info master_auth_setting_parser_info; +extern struct setting_parser_info master_setting_parser_info; +extern struct setting_parser_info master_auth_socket_setting_parser_info; + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct master_auth_socket_unix_settings, name), NULL } + +static struct setting_define master_auth_socket_master_setting_defines[] = { + DEF(SET_STR, path), + + SETTING_DEFINE_LIST_END +}; + +static struct master_auth_socket_unix_settings master_auth_socket_master_default_settings = { + MEMBER(path) "auth-master" }; -struct settings_parse_ctx { - enum settings_type type, parent_type; - enum mail_protocol protocol; +struct setting_parser_info master_auth_socket_master_setting_parser_info = { + MEMBER(defines) master_auth_socket_master_setting_defines, + MEMBER(defaults) &master_auth_socket_master_default_settings, + + MEMBER(parent) &master_auth_socket_setting_parser_info, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct master_auth_socket_unix_settings) +}; - struct server_settings *root, *server; - struct auth_settings *auth; - struct socket_settings *socket; - struct auth_socket_settings *auth_socket; - struct auth_passdb_settings *auth_passdb; - struct auth_userdb_settings *auth_userdb; - struct namespace_settings *namespace; +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct master_auth_socket_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct master_auth_socket_settings, field), defines } +static struct setting_define master_auth_socket_setting_defines[] = { + DEF(SET_STR, type), + DEFLIST(masters, "master", &master_auth_socket_master_setting_parser_info), - int level; + SETTING_DEFINE_LIST_END }; -#include "master-settings-defs.c" +struct setting_parser_info master_auth_socket_setting_parser_info = { + MEMBER(defines) master_auth_socket_setting_defines, + MEMBER(defaults) NULL, + + MEMBER(parent) &master_auth_setting_parser_info, + MEMBER(dynamic_parsers) NULL, -#undef DEF_STR -#undef DEF_INT -#undef DEF_BOOL -#define DEF_STR(name) DEF_STRUCT_STR(name, auth_settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, auth_settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_settings) + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) offsetof(struct master_auth_socket_settings, type), + MEMBER(struct_size) sizeof(struct master_auth_socket_settings) +}; + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct master_auth_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct master_auth_settings, field), defines } -static struct setting_def auth_setting_defs[] = { - DEF_STR(mechanisms), - DEF_STR(realms), - DEF_STR(default_realm), - DEF_INT(cache_size), - DEF_INT(cache_ttl), - DEF_INT(cache_negative_ttl), - DEF_STR(executable), - DEF_STR(user), - DEF_STR(chroot), - DEF_STR(username_chars), - DEF_STR(username_translation), - DEF_STR(username_format), - DEF_STR(master_user_separator), - DEF_STR(anonymous_username), - DEF_STR(krb5_keytab), - DEF_STR(gssapi_hostname), - DEF_STR(winbind_helper_path), - DEF_INT(failure_delay), +static struct setting_define master_auth_setting_defines[] = { + DEF(SET_STR, name), + DEF(SET_STR, executable), + DEF(SET_STR, user), + DEF(SET_STR, chroot), + DEF(SET_UINT, count), + DEF(SET_UINT, process_size), + DEF(SET_STR, mechanisms), + DEF(SET_BOOL, debug), + DEFLIST(sockets, "socket", &master_auth_socket_setting_parser_info), - DEF_BOOL(verbose), - DEF_BOOL(debug), - DEF_BOOL(debug_passwords), - DEF_BOOL(ssl_require_client_cert), - DEF_BOOL(ssl_username_from_cert), - DEF_BOOL(use_winbind), + SETTING_DEFINE_LIST_END +}; - DEF_INT(count), - DEF_INT(worker_max_count), - DEF_INT(process_size), +static struct master_auth_settings master_auth_default_settings = { + MEMBER(name) "default", + MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth", + MEMBER(user) "root", + MEMBER(chroot) "", + MEMBER(count) 1, + MEMBER(mechanisms) "plain", + MEMBER(debug) FALSE, + MEMBER(process_size) 256 - { 0, NULL, 0 } + /* .. */ }; -#undef DEF_STR -#undef DEF_INT -#undef DEF_BOOL -#define DEF_STR(name) DEF_STRUCT_STR(name, socket_settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, socket_settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, socket_settings) +struct setting_parser_info master_auth_setting_parser_info = { + MEMBER(defines) master_auth_setting_defines, + MEMBER(defaults) &master_auth_default_settings, -static struct setting_def socket_setting_defs[] = { - DEF_STR(path), - DEF_INT(mode), - DEF_STR(user), - DEF_STR(group), + MEMBER(parent) &master_setting_parser_info, + MEMBER(dynamic_parsers) NULL, - { 0, NULL, 0 } -}; - -static struct setting_def auth_socket_setting_defs[] = { - DEF_STRUCT_STR(type, auth_socket_settings), - - { 0, NULL, 0 } + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) offsetof(struct master_auth_settings, name), + MEMBER(struct_size) sizeof(struct master_auth_settings) }; -#undef DEF_STR -#undef DEF_INT -#undef DEF_BOOL -#define DEF_STR(name) DEF_STRUCT_STR(name, auth_passdb_settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, auth_passdb_settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_passdb_settings) +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct master_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct master_settings, field), defines } + +static struct setting_define master_setting_defines[] = { + /* common */ + DEF(SET_STR, base_dir), + DEF(SET_STR, log_path), + DEF(SET_STR, info_log_path), + DEF(SET_STR, log_timestamp), + DEF(SET_STR, syslog_facility), + + /* general */ + DEF(SET_STR, protocols), + DEF(SET_STR, listen), + DEF(SET_STR, ssl_listen), + + DEF(SET_STR, ssl), + DEF(SET_STR, ssl_key_file), + DEF(SET_UINT, ssl_parameters_regenerate), + DEF(SET_BOOL, nfs_check), + DEF(SET_BOOL, version_ignore), + + /* login */ + DEF(SET_STR, login_dir), + DEF(SET_STR, login_executable), + DEF(SET_STR, login_user), + + DEF(SET_BOOL, login_process_per_connection), + DEF(SET_BOOL, login_chroot), + DEF(SET_BOOL, disable_plaintext_auth), + + DEF(SET_UINT, login_process_size), + DEF(SET_UINT, login_processes_count), + DEF(SET_UINT, login_max_processes_count), -static struct setting_def auth_passdb_setting_defs[] = { - DEF_STR(driver), - DEF_STR(args), - DEF_BOOL(deny), - DEF_BOOL(pass), - DEF_BOOL(master), + /* mail */ + DEF(SET_STR, valid_chroot_dirs), + DEF(SET_STR, mail_chroot), + DEF(SET_UINT, max_mail_processes), + DEF(SET_UINT, mail_max_userip_connections), + DEF(SET_BOOL, verbose_proctitle), + + DEF(SET_UINT, first_valid_uid), + DEF(SET_UINT, last_valid_uid), + DEF(SET_UINT, first_valid_gid), + DEF(SET_UINT, last_valid_gid), + DEF(SET_STR, mail_access_groups), + DEF(SET_STR, mail_privileged_group), + DEF(SET_STR, mail_uid), + DEF(SET_STR, mail_gid), + + DEF(SET_STR, mail_plugins), + DEF(SET_STR, imap_capability), - { 0, NULL, 0 } -}; + DEF(SET_STR_VARS, mail_location), + DEF(SET_BOOL, mail_debug), + DEF(SET_BOOL, mail_nfs_index), + DEF(SET_UINT, umask), + DEF(SET_BOOL, mail_drop_priv_before_exec), -static struct setting_def auth_userdb_setting_defs[] = { - DEF_STRUCT_STR(driver, auth_userdb_settings), - DEF_STRUCT_STR(args, auth_userdb_settings), + DEF(SET_STR, mail_executable), + DEF(SET_UINT, mail_process_size), + DEF(SET_STR_VARS, mail_log_prefix), + DEF(SET_UINT, mail_log_max_lines_per_sec), - { 0, NULL, 0 } + /* dict */ + DEF(SET_STR, dict_db_config), + DEF(SET_UINT, dict_process_count), + DEFLIST(auths, "auth", &master_auth_setting_parser_info), + { SET_STRLIST, "dict", offsetof(struct master_settings, dicts), NULL }, + { SET_STRLIST, "plugin", offsetof(struct master_settings, plugin_envs), NULL }, + + SETTING_DEFINE_LIST_END }; -#undef DEF_STR -#undef DEF_INT -#undef DEF_BOOL -#define DEF_STR(name) DEF_STRUCT_STR(name, namespace_settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, namespace_settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, namespace_settings) - -static struct setting_def namespace_setting_defs[] = { - DEF_STR(type), - DEF_STR(separator), - DEF_STR(prefix), - DEF_STR(location), - DEF_STR(alias_for), - DEF_BOOL(inbox), - DEF_BOOL(hidden), - DEF_STR(list), - DEF_BOOL(subscriptions), - - { 0, NULL, 0 } -}; - -struct settings default_settings = { - MEMBER(server) NULL, - MEMBER(protocol) 0, - +struct master_settings master_default_settings = { /* common */ MEMBER(base_dir) PKG_RUNDIR, MEMBER(log_path) "", @@ -183,17 +224,8 @@ MEMBER(ssl_listen) "", MEMBER(ssl) "yes", - MEMBER(ssl_ca_file) "", - MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem", MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem", - MEMBER(ssl_key_password) "", MEMBER(ssl_parameters_regenerate) 168, - MEMBER(ssl_cipher_list) "", - MEMBER(ssl_cert_username_field) "commonName", - MEMBER(ssl_verify_client_cert) FALSE, - MEMBER(disable_plaintext_auth) TRUE, - MEMBER(verbose_ssl) FALSE, - MEMBER(shutdown_clients) TRUE, MEMBER(nfs_check) TRUE, MEMBER(version_ignore) FALSE, @@ -201,18 +233,14 @@ MEMBER(login_dir) "login", MEMBER(login_executable) NULL, MEMBER(login_user) "dovecot", - MEMBER(login_greeting) PACKAGE_NAME" ready.", - MEMBER(login_log_format_elements) "user=<%u> method=%m rip=%r lip=%l %c", - MEMBER(login_log_format) "%$: %s", MEMBER(login_process_per_connection) TRUE, MEMBER(login_chroot) TRUE, - MEMBER(login_trusted_networks) "", + MEMBER(disable_plaintext_auth) TRUE, MEMBER(login_process_size) 64, MEMBER(login_processes_count) 3, MEMBER(login_max_processes_count) 128, - MEMBER(login_max_connections) 256, /* mail */ MEMBER(valid_chroot_dirs) "", @@ -229,68 +257,20 @@ MEMBER(mail_privileged_group) "", MEMBER(mail_uid) "", MEMBER(mail_gid) "", + MEMBER(mail_plugins) "", + MEMBER(imap_capability) "", MEMBER(mail_location) "", - MEMBER(mail_cache_fields) "", - MEMBER(mail_never_cache_fields) "imap.envelope", - MEMBER(mail_cache_min_mail_count) 0, - MEMBER(mailbox_idle_check_interval) 30, MEMBER(mail_debug) FALSE, - MEMBER(mail_full_filesystem_access) FALSE, - MEMBER(mail_max_keyword_length) 50, - MEMBER(mail_save_crlf) FALSE, -#ifdef MMAP_CONFLICTS_WRITE - MEMBER(mmap_disable) TRUE, -#else - MEMBER(mmap_disable) FALSE, -#endif - MEMBER(dotlock_use_excl) TRUE, - MEMBER(fsync_disable) FALSE, - MEMBER(mail_nfs_storage) FALSE, MEMBER(mail_nfs_index) FALSE, - MEMBER(mailbox_list_index_disable) TRUE, - MEMBER(lock_method) "fcntl", - MEMBER(maildir_stat_dirs) FALSE, - MEMBER(maildir_copy_with_hardlinks) TRUE, - MEMBER(maildir_copy_preserve_filename) FALSE, - MEMBER(mbox_read_locks) "fcntl", - MEMBER(mbox_write_locks) "dotlock fcntl", - MEMBER(mbox_lock_timeout) 300, - MEMBER(mbox_dotlock_change_timeout) 120, - MEMBER(mbox_min_index_size) 0, - MEMBER(mbox_dirty_syncs) TRUE, - MEMBER(mbox_very_dirty_syncs) FALSE, - MEMBER(mbox_lazy_writes) TRUE, - MEMBER(dbox_rotate_size) 2048, - MEMBER(dbox_rotate_min_size) 16, - MEMBER(dbox_rotate_days) 1, MEMBER(umask) 0077, MEMBER(mail_drop_priv_before_exec) FALSE, - MEMBER(mail_executable) PKG_LIBEXECDIR"/imap", + MEMBER(mail_executable) NULL, MEMBER(mail_process_size) 256, - MEMBER(mail_plugins) "", - MEMBER(mail_plugin_dir) MODULEDIR"/imap", MEMBER(mail_log_prefix) "%Us(%u): ", MEMBER(mail_log_max_lines_per_sec) 10, - /* imap */ - MEMBER(imap_max_line_length) 65536, - MEMBER(imap_capability) "", - MEMBER(imap_client_workarounds) "", - MEMBER(imap_logout_format) "bytes=%i/%o", - MEMBER(imap_id_send) "", - MEMBER(imap_id_log) "", - - /* pop3 */ - MEMBER(pop3_no_flag_updates) FALSE, - MEMBER(pop3_enable_last) FALSE, - MEMBER(pop3_reuse_xuidl) FALSE, - MEMBER(pop3_lock_session) FALSE, - MEMBER(pop3_uidl_format) "%08Xu%08Xv", - MEMBER(pop3_client_workarounds) "", - MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s", - /* dict */ MEMBER(dict_db_config) NULL, MEMBER(dict_process_count) 1, @@ -298,365 +278,24 @@ /* .. */ }; -struct auth_settings default_auth_settings = { - MEMBER(parent) NULL, - MEMBER(next) NULL, - - MEMBER(name) NULL, - MEMBER(mechanisms) "plain", - MEMBER(realms) "", - MEMBER(default_realm) "", - MEMBER(cache_size) 0, - MEMBER(cache_ttl) 3600, - MEMBER(cache_negative_ttl) 3600, - MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth", - MEMBER(user) "root", - MEMBER(chroot) "", - MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@", - MEMBER(username_translation) "", - MEMBER(username_format) "", - MEMBER(master_user_separator) "", - MEMBER(anonymous_username) "anonymous", - MEMBER(krb5_keytab) "", - MEMBER(gssapi_hostname) "", - MEMBER(winbind_helper_path) "/usr/bin/ntlm_auth", - MEMBER(failure_delay) 2, - - MEMBER(verbose) FALSE, - MEMBER(debug) FALSE, - MEMBER(debug_passwords) FALSE, - MEMBER(ssl_require_client_cert) FALSE, - MEMBER(ssl_username_from_cert) FALSE, - MEMBER(use_winbind) FALSE, +struct setting_parser_info master_setting_parser_info = { + MEMBER(defines) master_setting_defines, + MEMBER(defaults) &master_default_settings, - MEMBER(count) 1, - MEMBER(worker_max_count) 30, - MEMBER(process_size) 256, - - /* .. */ - MEMBER(uid) 0, - MEMBER(gid) 0, - MEMBER(passdbs) NULL, - MEMBER(userdbs) NULL, - MEMBER(sockets) NULL -}; + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, -struct socket_settings default_socket_settings = { -#define DEFAULT_MASTER_SOCKET_PATH "auth-master" -#define DEFAULT_CLIENT_SOCKET_PATH "auth-client" - MEMBER(path) "", - MEMBER(mode) 0600, - MEMBER(user) "", - MEMBER(group) "" -}; - -struct namespace_settings default_namespace_settings = { - MEMBER(parent) NULL, - MEMBER(next) NULL, - MEMBER(type) NULL, - - MEMBER(separator) "", - MEMBER(prefix) "", - MEMBER(location) "", - MEMBER(alias_for) NULL, - - MEMBER(inbox) FALSE, - MEMBER(hidden) FALSE, - MEMBER(list) "yes", - MEMBER(subscriptions) TRUE + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct master_settings) }; static pool_t settings_pool, settings2_pool; -struct server_settings *settings_root = NULL; - -static void fix_base_path(struct settings *set, const char **str) -{ - if (*str != NULL && **str != '\0' && **str != '/') { - *str = p_strconcat(settings_pool, - set->base_dir, "/", *str, NULL); - } -} - -static bool parse_uid(const char *str, uid_t *uid_r) -{ - struct passwd *pw; - char *p; - - if (*str >= '0' && *str <= '9') { - *uid_r = (uid_t)strtoul(str, &p, 10); - if (*p == '\0') - return TRUE; - } - - pw = getpwnam(str); - if (pw == NULL) - return FALSE; - - *uid_r = pw->pw_uid; - return TRUE; -} - -static bool parse_gid(const char *str, gid_t *gid_r) -{ - struct group *gr; - char *p; - - if (*str >= '0' && *str <= '9') { - *gid_r = (gid_t)strtoul(str, &p, 10); - if (*p == '\0') - return TRUE; - } - - gr = getgrnam(str); - if (gr == NULL) - return FALSE; - - *gid_r = gr->gr_gid; - return TRUE; -} - -static bool get_login_uid(struct settings *set) -{ - struct passwd *pw; - - if ((pw = getpwnam(set->login_user)) == NULL) { - i_error("Login user doesn't exist: %s", set->login_user); - return FALSE; - } - - if (set->server->login_gid == 0) - set->server->login_gid = pw->pw_gid; - else if (set->server->login_gid != pw->pw_gid) { - i_error("All login process users must belong to same group " - "(%s vs %s)", dec2str(set->server->login_gid), - dec2str(pw->pw_gid)); - return FALSE; - } - - set->login_uid = pw->pw_uid; - return TRUE; -} - -static bool auth_settings_verify(struct auth_settings *auth) -{ - struct passwd *pw; - struct auth_socket_settings *s; - - if ((pw = getpwnam(auth->user)) == NULL) { - i_error("Auth user doesn't exist: %s", auth->user); - return FALSE; - } - - if (auth->parent->defaults->login_uid == pw->pw_uid && - master_uid != pw->pw_uid) { - i_error("login_user %s (uid %s) must not be same as auth_user", - auth->user, dec2str(pw->pw_uid)); - return FALSE; - } - auth->uid = pw->pw_uid; - auth->gid = pw->pw_gid; - - if (access(t_strcut(auth->executable, ' '), X_OK) < 0) { - i_error("auth_executable: Can't use %s: %m", - t_strcut(auth->executable, ' ')); - return FALSE; - } - - fix_base_path(auth->parent->defaults, &auth->chroot); - if (*auth->chroot != '\0' && access(auth->chroot, X_OK) < 0) { - i_error("Can't access auth chroot directory %s: %m", - auth->chroot); - return FALSE; - } - - if (auth->ssl_require_client_cert || auth->ssl_username_from_cert) { - /* if we require valid cert, make sure we also ask for it */ - if (auth->parent->pop3 != NULL) - auth->parent->pop3->ssl_verify_client_cert = TRUE; - if (auth->parent->imap != NULL) - auth->parent->imap->ssl_verify_client_cert = TRUE; - } - - for (s = auth->sockets; s != NULL; s = s->next) { - if (auth->count > 1 && strcmp(s->type, "listen") == 0) { - i_error("Currently auth process count must be 1 if " - "you're using auth socket listeners."); - return FALSE; - } - fix_base_path(auth->parent->defaults, &s->master.path); - fix_base_path(auth->parent->defaults, &s->client.path); - } - return TRUE; -} - -static bool namespace_settings_verify(struct server_settings *server, - struct namespace_settings *ns) -{ - struct namespace_settings *n; - const char *name; - - name = ns->prefix != NULL ? ns->prefix : ""; - - if (ns->separator != NULL && - ns->separator[0] != '\0' && ns->separator[1] != '\0') { - i_error("Namespace '%s': " - "Hierarchy separator must be only one character long", - name); - return FALSE; - } - if (strcmp(ns->list, "yes") != 0 && - strcmp(ns->list, "no") != 0 && - strcmp(ns->list, "children") != 0) { - i_error("Namespace '%s': Invalid list value: %s", - name, ns->list); - return FALSE; - } - - if (ns->alias_for != NULL) { - for (n = server->namespaces; n != ns; n = n->next) { - if (strcmp(n->prefix, ns->alias_for) == 0) - break; - } - if (n == ns) { - i_error("Namespace '%s': alias_for points to " - "unknown namespace: %s", name, ns->alias_for); - return FALSE; - } - if (n->alias_for != NULL) { - i_error("Namespace '%s': alias_for chaining isn't " - "allowed: %s -> %s", name, ns->alias_for, - n->alias_for); - return FALSE; - } - } - - return TRUE; -} - -static const char *get_directory(const char *path) -{ - char *str, *p; - - str = t_strdup_noconst(path); - p = strrchr(str, '/'); - if (p == NULL) - return "."; - else { - *p = '\0'; - return str; - } -} - -static bool settings_is_active(struct settings *set) -{ - if (*set->protocols == '\0') { - /* we're probably using this with --exec-mail */ - return TRUE; - } - - if (set->protocol == MAIL_PROTOCOL_IMAP) { - if (strstr(set->protocols, "imap") == NULL) - return FALSE; - } else { - if (strstr(set->protocols, "pop3") == NULL) - return FALSE; - } - - return TRUE; -} - -static bool settings_have_connect_sockets(struct settings *set) -{ - struct auth_settings *auth; - struct server_settings *server; - - for (server = set->server; server != NULL; server = server->next) { - for (auth = server->auths; auth != NULL; auth = auth->next) { - if (auth->sockets != NULL && - strcmp(auth->sockets->type, "connect") == 0) - return TRUE; - } - } - - return FALSE; -} - -static bool settings_have_nonplaintext_auths(struct settings *set) -{ - struct auth_settings *auth; - struct server_settings *server; - const char *const *tmp; - - for (server = set->server; server != NULL; server = server->next) { - for (auth = server->auths; auth != NULL; auth = auth->next) { - tmp = t_strsplit_spaces(auth->mechanisms, " "); - for (; *tmp != NULL; tmp++) { - if (strcasecmp(*tmp, "PLAIN") != 0 && - strcasecmp(*tmp, "LOGIN") != 0) - return TRUE; - } - } - } - - return FALSE; -} - -static void unlink_auth_sockets(const char *path, const char *prefix) -{ - DIR *dirp; - struct dirent *dp; - struct stat st; - string_t *str; - unsigned int prefix_len; - - dirp = opendir(path); - if (dirp == NULL) { - i_error("opendir(%s) failed: %m", path); - return; - } - - prefix_len = strlen(prefix); - str = t_str_new(256); - while ((dp = readdir(dirp)) != NULL) { - if (dp->d_name[0] == '.') - continue; - - if (strncmp(dp->d_name, prefix, prefix_len) != 0) - continue; - - str_truncate(str, 0); - str_printfa(str, "%s/%s", path, dp->d_name); - if (lstat(str_c(str), &st) < 0) { - if (errno != ENOENT) - i_error("lstat(%s) failed: %m", str_c(str)); - continue; - } - if (!S_ISSOCK(st.st_mode)) - continue; - - /* try to avoid unlinking sockets if someone's already - listening in them. do this only at startup, because - when SIGHUPing a child process might catch the new - connection before it notices that it's supposed - to die. null_fd == -1 check is a bit kludgy, but works.. */ - if (null_fd == -1) { - int fd = net_connect_unix(str_c(str)); - if (fd != -1 || errno != ECONNREFUSED) { - i_fatal("Dovecot is already running? " - "Socket already exists: %s", - str_c(str)); - } - } - - if (unlink(str_c(str)) < 0 && errno != ENOENT) - i_error("unlink(%s) failed: %m", str_c(str)); - } - (void)closedir(dirp); -} +struct master_server_settings *master_set = NULL; #ifdef HAVE_MODULES static const char * -get_process_capability(enum process_type ptype, struct settings *set) +get_process_capability(enum process_type ptype, struct master_settings *set) { /* FIXME: pretty ugly code just for getting the capability automatically */ @@ -744,7 +383,7 @@ return i_strdup(buf); } -static bool get_imap_capability(struct settings *set) +static bool get_imap_capability(struct master_settings *set) { static const char *generated_capability = NULL; @@ -768,7 +407,239 @@ } #endif -static bool settings_verify(struct settings *set) +static void fix_base_path(struct master_settings *set, const char **str) +{ + if (*str != NULL && **str != '\0' && **str != '/') { + *str = p_strconcat(settings_pool, + set->base_dir, "/", *str, NULL); + } +} + +static bool parse_uid(const char *str, uid_t *uid_r) +{ + struct passwd *pw; + char *p; + + if (*str >= '0' && *str <= '9') { + *uid_r = (uid_t)strtoul(str, &p, 10); + if (*p == '\0') + return TRUE; + } + + pw = getpwnam(str); + if (pw == NULL) + return FALSE; + + *uid_r = pw->pw_uid; + return TRUE; +} + +static bool parse_gid(const char *str, gid_t *gid_r) +{ + struct group *gr; + char *p; + + if (*str >= '0' && *str <= '9') { + *gid_r = (gid_t)strtoul(str, &p, 10); + if (*p == '\0') + return TRUE; + } + + gr = getgrnam(str); + if (gr == NULL) + return FALSE; + + *gid_r = gr->gr_gid; + return TRUE; +} + +static bool get_login_uid(struct master_settings *set) +{ + struct passwd *pw; + + if ((pw = getpwnam(set->login_user)) == NULL) { + i_error("Login user doesn't exist: %s", set->login_user); + return FALSE; + } + + if (set->server->login_gid == 0) + set->server->login_gid = pw->pw_gid; + else if (set->server->login_gid != pw->pw_gid) { + i_error("All login process users must belong to same group " + "(%s vs %s)", dec2str(set->server->login_gid), + dec2str(pw->pw_gid)); + return FALSE; + } + set->login_uid = pw->pw_uid; + return TRUE; +} + +static bool auth_settings_verify(struct master_settings *set, + struct master_auth_settings *auth) +{ + struct passwd *pw; + struct master_auth_socket_settings *const *sockets; + unsigned int i, count; + + if ((pw = getpwnam(auth->user)) == NULL) { + i_error("Auth user doesn't exist: %s", auth->user); + return FALSE; + } + + if (set->login_uid == pw->pw_uid && master_uid != pw->pw_uid) { + i_error("login_user %s (uid %s) must not be same as auth_user", + auth->user, dec2str(pw->pw_uid)); + return FALSE; + } + auth->uid = pw->pw_uid; + auth->gid = pw->pw_gid; + + if (access(t_strcut(auth->executable, ' '), X_OK) < 0) { + i_error("auth_executable: Can't use %s: %m", + t_strcut(auth->executable, ' ')); + return FALSE; + } + + fix_base_path(set, &auth->chroot); + if (*auth->chroot != '\0' && access(auth->chroot, X_OK) < 0) { + i_error("Can't access auth chroot directory %s: %m", + auth->chroot); + return FALSE; + } + + sockets = array_get(&auth->sockets, &count); + for (i = 0; i < count; i++) { + if (auth->count > 1 && + strcmp(sockets[i]->type, "listen") == 0) { + i_error("Currently auth process count must be 1 if " + "you're using auth socket listeners."); + return FALSE; + } + } + return TRUE; +} + +static const char *get_directory(const char *path) +{ + char *str, *p; + + str = t_strdup_noconst(path); + p = strrchr(str, '/'); + if (p == NULL) + return "."; + else { + *p = '\0'; + return str; + } +} + +static bool settings_is_active(struct master_settings *set) +{ + if (*set->protocols == '\0') { + /* we're probably using this with --exec-mail */ + return TRUE; + } + + if (set->protocol == MAIL_PROTOCOL_IMAP) { + if (strstr(set->protocols, "imap") == NULL) + return FALSE; + } else { + if (strstr(set->protocols, "pop3") == NULL) + return FALSE; + } + + return TRUE; +} + +static bool settings_have_connect_sockets(struct master_settings *set) +{ + struct master_auth_settings *const *auths; + struct master_auth_socket_settings *const *sockets; + unsigned int i, count, count2; + + auths = array_get(&set->auths, &count); + for (i = 0; i < count; i++) { + sockets = array_get(&auths[i]->sockets, &count2); + if (count2 > 0 && strcmp(sockets[0]->type, "connect") == 0) + return TRUE; + } + + return FALSE; +} + +static bool settings_have_nonplaintext_auths(struct master_settings *set) +{ + struct master_auth_settings *const *auths; + const char *const *tmp; + unsigned int i, count; + + auths = array_get(&set->auths, &count); + for (i = 0; i < count; i++) { + tmp = t_strsplit_spaces(auths[i]->mechanisms, " "); + for (; *tmp != NULL; tmp++) { + if (strcasecmp(*tmp, "PLAIN") != 0 && + strcasecmp(*tmp, "LOGIN") != 0) + return TRUE; + } + } + + return FALSE; +} + +static void unlink_auth_sockets(const char *path, const char *prefix) +{ + DIR *dirp; + struct dirent *dp; + struct stat st; + string_t *str; + unsigned int prefix_len; + + dirp = opendir(path); + if (dirp == NULL) { + i_error("opendir(%s) failed: %m", path); + return; + } + + prefix_len = strlen(prefix); + str = t_str_new(256); + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.') + continue; + + if (strncmp(dp->d_name, prefix, prefix_len) != 0) + continue; + + str_truncate(str, 0); + str_printfa(str, "%s/%s", path, dp->d_name); + if (lstat(str_c(str), &st) < 0) { + if (errno != ENOENT) + i_error("lstat(%s) failed: %m", str_c(str)); + continue; + } + if (!S_ISSOCK(st.st_mode)) + continue; + + /* try to avoid unlinking sockets if someone's already + listening in them. do this only at startup, because + when SIGHUPing a child process might catch the new + connection before it notices that it's supposed + to die. null_fd == -1 check is a bit kludgy, but works.. */ + if (null_fd == -1) { + int fd = net_connect_unix(str_c(str)); + if (fd != -1 || errno != ECONNREFUSED) { + i_fatal("Dovecot is already running? " + "Socket already exists: %s", + str_c(str)); + } + } + + if (unlink(str_c(str)) < 0 && errno != ENOENT) + i_error("unlink(%s) failed: %m", str_c(str)); + } + (void)closedir(dirp); +} + +static bool settings_verify(struct master_settings *set) { const char *dir; int facility; @@ -839,28 +710,10 @@ i_error("ssl setting: Invalid value: %s", set->ssl); return FALSE; } -#ifdef HAVE_SSL +#ifndef HAVE_SSL if (strcmp(set->ssl, "no") != 0) { - if (*set->ssl_ca_file != '\0' && - access(set->ssl_ca_file, R_OK) < 0) { - i_fatal("ssl_ca_file: Can't use %s: %m", - set->ssl_ca_file); - } - if (access(set->ssl_cert_file, R_OK) < 0) { - i_error("ssl_cert_file: Can't use %s: %m", - set->ssl_cert_file); - return FALSE; - } - if (access(set->ssl_key_file, R_OK) < 0) { - i_error("ssl_key_file: Can't use %s: %m", - set->ssl_key_file); - return FALSE; - } - } -#else - if (strcmp(set->ssl, "no") != 0) { i_error("SSL support not compiled in but ssl=%s", set->ssl); return FALSE; } @@ -906,11 +759,8 @@ i_error("login_processes_count must be at least 1"); return FALSE; } - if (set->login_max_connections < 1) { - i_error("login_max_connections must be at least 1"); - return FALSE; - } +#if 0 //FIXME if (set->mail_nfs_index && !set->mmap_disable) { i_error("mail_nfs_index=yes requires mmap_disable=yes"); return FALSE; @@ -919,7 +769,6 @@ i_error("mail_nfs_index=yes requires fsync_disable=no"); return FALSE; } - #ifdef HAVE_MODULES if (*set->mail_plugins != '\0' && access(set->mail_plugin_dir, R_OK | X_OK) < 0) { @@ -934,10 +783,11 @@ return FALSE; } #endif +#endif return TRUE; } -static bool settings_do_fixes(struct settings *set) +static bool settings_do_fixes(struct master_settings *set) { struct stat st; @@ -987,7 +837,8 @@ return TRUE; } -static bool settings_fix(struct settings *set, bool nochecks, bool nofixes) +static bool +settings_fix(struct master_settings *set, bool nochecks, bool nofixes) { /* fix relative paths */ fix_base_path(set, &set->login_dir); @@ -999,474 +850,8 @@ return nofixes ? TRUE : settings_do_fixes(set); } -static void pid_file_check_running(const char *path) -{ - char buf[32]; - int fd; - ssize_t ret; - - fd = open(path, O_RDONLY); - if (fd == -1) { - if (errno == ENOENT) - return; - i_fatal("open(%s) failed: %m", path); - } - - ret = read(fd, buf, sizeof(buf)); - if (ret <= 0) { - if (ret == 0) - i_error("Empty PID file in %s, overriding", path); - else - i_fatal("read(%s) failed: %m", path); - } else { - pid_t pid; - - if (buf[ret-1] == '\n') - ret--; - buf[ret] = '\0'; - pid = atoi(buf); - if (pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)) { - /* doesn't exist */ - } else { - i_fatal("Dovecot is already running with PID %s " - "(read from %s)", buf, path); - } - } - (void)close(fd); -} - -static struct auth_settings * -auth_settings_new(struct server_settings *server, const char *name) -{ - struct auth_settings *auth; - - auth = p_new(settings_pool, struct auth_settings, 1); - - /* copy defaults */ - *auth = server->auth_defaults; - auth->parent = server; - auth->name = p_strdup(settings_pool, name); - - auth->next = server->auths; - server->auths = auth; - - return auth; -} - -static struct auth_settings * -parse_new_auth(struct server_settings *server, const char *name, - const char **errormsg) -{ - struct auth_settings *auth; - - if (strchr(name, '/') != NULL) { - *errormsg = "Authentication process name must not contain '/'"; - return NULL; - } - - for (auth = server->auths; auth != NULL; auth = auth->next) { - if (strcmp(auth->name, name) == 0) { - *errormsg = "Authentication process already exists " - "with the same name"; - return NULL; - } - } - - return auth_settings_new(server, name); -} - -static struct auth_passdb_settings * -auth_passdb_settings_new(struct auth_settings *auth, const char *type) -{ - struct auth_passdb_settings *as, **as_p; - - as = p_new(settings_pool, struct auth_passdb_settings, 1); - - as->parent = auth; - as->driver = str_lcase(p_strdup(settings_pool, type)); - - as_p = &auth->passdbs; - while (*as_p != NULL) - as_p = &(*as_p)->next; - *as_p = as; - - return as; -} - -static struct auth_userdb_settings * -auth_userdb_settings_new(struct auth_settings *auth, const char *type) -{ - struct auth_userdb_settings *as, **as_p; - - as = p_new(settings_pool, struct auth_userdb_settings, 1); - - as->parent = auth; - as->driver = str_lcase(p_strdup(settings_pool, type)); - - as_p = &auth->userdbs; - while (*as_p != NULL) - as_p = &(*as_p)->next; - *as_p = as; - - return as; -} - -static struct auth_socket_settings * -auth_socket_settings_new(struct auth_settings *auth, const char *type) -{ - struct auth_socket_settings *as, **as_p; - - as = p_new(settings_pool, struct auth_socket_settings, 1); - - as->parent = auth; - as->type = str_lcase(p_strdup(settings_pool, type)); - as->master = default_socket_settings; - as->client = default_socket_settings; - - as->master.path = DEFAULT_MASTER_SOCKET_PATH; - as->client.path = DEFAULT_CLIENT_SOCKET_PATH; - - as_p = &auth->sockets; - while (*as_p != NULL) - as_p = &(*as_p)->next; - *as_p = as; - - return as; -} - -static struct auth_socket_settings * -parse_new_auth_socket(struct auth_settings *auth, const char *name, - const char **errormsg) -{ - if (strcmp(name, "connect") != 0 && strcmp(name, "listen") != 0) { - *errormsg = "Unknown auth socket type"; - return NULL; - } - - if (auth->sockets != NULL && strcmp(name, "connect") == 0) { - *errormsg = "With connect auth socket no other sockets " - "can be used in same auth section"; - return NULL; - } - - return auth_socket_settings_new(auth, name); -} - -static struct namespace_settings * -namespace_settings_new(struct server_settings *server, const char *type) -{ - struct namespace_settings *ns, **ns_p; - - ns = p_new(settings_pool, struct namespace_settings, 1); - *ns = default_namespace_settings; - - ns->parent = server; - ns->type = str_lcase(p_strdup(settings_pool, type)); - - ns_p = &server->namespaces; - while (*ns_p != NULL) - ns_p = &(*ns_p)->next; - *ns_p = ns; - - return ns; -} - -static struct namespace_settings * -parse_new_namespace(struct server_settings *server, const char *name, - const char **errormsg) -{ - if (strcasecmp(name, "private") != 0 && - strcasecmp(name, "shared") != 0 && - strcasecmp(name, "public") != 0) { - *errormsg = "Unknown namespace type"; - return NULL; - } - - return namespace_settings_new(server, name); -} - -static const char *parse_setting(const char *key, const char *value, - struct settings_parse_ctx *ctx) -{ - const char *error; - - switch (ctx->type) { - case SETTINGS_TYPE_ROOT: - case SETTINGS_TYPE_SERVER: - error = NULL; - if (ctx->protocol == MAIL_PROTOCOL_ANY || - ctx->protocol == MAIL_PROTOCOL_IMAP) { - error = parse_setting_from_defs(settings_pool, - setting_defs, - ctx->server->imap, - key, value); - } - - if (error == NULL && - (ctx->protocol == MAIL_PROTOCOL_ANY || - ctx->protocol == MAIL_PROTOCOL_POP3)) { - error = parse_setting_from_defs(settings_pool, - setting_defs, - ctx->server->pop3, - key, value); - } - - if (error == NULL) - return NULL; - - if (strncmp(key, "auth_", 5) == 0) { - return parse_setting_from_defs(settings_pool, - auth_setting_defs, - ctx->auth, - key + 5, value); - } - return error; - case SETTINGS_TYPE_AUTH: - if (strncmp(key, "auth_", 5) == 0) - key += 5; - return parse_setting_from_defs(settings_pool, auth_setting_defs, - ctx->auth, key, value); - case SETTINGS_TYPE_AUTH_SOCKET: - return parse_setting_from_defs(settings_pool, - auth_socket_setting_defs, - ctx->auth_socket, key, value); - case SETTINGS_TYPE_AUTH_PASSDB: - return parse_setting_from_defs(settings_pool, - auth_passdb_setting_defs, - ctx->auth_passdb, key, value); - case SETTINGS_TYPE_AUTH_USERDB: - return parse_setting_from_defs(settings_pool, - auth_userdb_setting_defs, - ctx->auth_userdb, key, value); - case SETTINGS_TYPE_NAMESPACE: - return parse_setting_from_defs(settings_pool, - namespace_setting_defs, - ctx->namespace, key, value); - case SETTINGS_TYPE_SOCKET: - return parse_setting_from_defs(settings_pool, - socket_setting_defs, - ctx->socket, key, value); - case SETTINGS_TYPE_DICT: - key = p_strdup(settings_pool, key); - value = p_strdup(settings_pool, value); - - array_append(&ctx->server->dicts, &key, 1); - array_append(&ctx->server->dicts, &value, 1); - return NULL; - case SETTINGS_TYPE_PLUGIN: - key = p_strdup(settings_pool, key); - value = p_strdup(settings_pool, value); - - if (ctx->protocol == MAIL_PROTOCOL_ANY || - ctx->protocol == MAIL_PROTOCOL_IMAP) { - array_append(&ctx->server->imap->plugin_envs, &key, 1); - array_append(&ctx->server->imap->plugin_envs, - &value, 1); - } - if (ctx->protocol == MAIL_PROTOCOL_ANY || - ctx->protocol == MAIL_PROTOCOL_POP3) { - array_append(&ctx->server->pop3->plugin_envs, &key, 1); - array_append(&ctx->server->pop3->plugin_envs, - &value, 1); - } - return NULL; - } - - i_unreached(); -} - -static struct server_settings * -create_new_server(const char *name, - struct settings *imap_defaults, - struct settings *pop3_defaults) -{ - struct server_settings *server; - - server = p_new(settings_pool, struct server_settings, 1); - server->name = p_strdup(settings_pool, name); - server->imap = p_new(settings_pool, struct settings, 1); - server->pop3 = p_new(settings_pool, struct settings, 1); - server->auth_defaults = default_auth_settings; - - *server->imap = *imap_defaults; - *server->pop3 = *pop3_defaults; - - p_array_init(&server->dicts, settings_pool, 4); - p_array_init(&server->imap->plugin_envs, settings_pool, 8); - p_array_init(&server->pop3->plugin_envs, settings_pool, 8); - - server->imap->server = server; - server->imap->protocol = MAIL_PROTOCOL_IMAP; - server->imap->login_executable = PKG_LIBEXECDIR"/imap-login"; - server->imap->mail_executable = PKG_LIBEXECDIR"/imap"; - server->imap->mail_plugin_dir = MODULEDIR"/imap"; - - server->pop3->server = server; - server->pop3->protocol = MAIL_PROTOCOL_POP3; - server->pop3->login_executable = PKG_LIBEXECDIR"/pop3-login"; - server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3"; - server->pop3->mail_plugin_dir = MODULEDIR"/pop3"; - - return server; -} - -static bool parse_section(const char *type, const char *name, - struct settings_parse_ctx *ctx, const char **errormsg) -{ - struct server_settings *server; - - if (type == NULL) { - /* section closing */ - if (ctx->level-- > 0) { - ctx->type = ctx->parent_type; - ctx->protocol = MAIL_PROTOCOL_ANY; - - switch (ctx->type) { - case SETTINGS_TYPE_AUTH_SOCKET: - ctx->parent_type = SETTINGS_TYPE_AUTH; - break; - default: - ctx->parent_type = SETTINGS_TYPE_ROOT; - break; - } - } else { - ctx->type = SETTINGS_TYPE_ROOT; - ctx->server = ctx->root; - ctx->auth = &ctx->root->auth_defaults; - ctx->namespace = NULL; - } - return TRUE; - } - - ctx->level++; - ctx->parent_type = ctx->type; - - if (strcmp(type, "server") == 0) { - if (ctx->type != SETTINGS_TYPE_ROOT) { - *errormsg = "Server section not allowed here"; - return FALSE; - } - - ctx->type = SETTINGS_TYPE_SERVER; - ctx->server = create_new_server(name, ctx->server->imap, - ctx->server->pop3); - server = ctx->root; - while (server->next != NULL) - server = server->next; - server->next = ctx->server; - return TRUE; - } - - if (strcmp(type, "protocol") == 0) { - if ((ctx->type != SETTINGS_TYPE_ROOT && - ctx->type != SETTINGS_TYPE_SERVER) || - ctx->level != 1) { - *errormsg = "Protocol section not allowed here"; - return FALSE; - } - - if (strcmp(name, "imap") == 0) - ctx->protocol = MAIL_PROTOCOL_IMAP; - else if (strcmp(name, "pop3") == 0) - ctx->protocol = MAIL_PROTOCOL_POP3; - else if (strcmp(name, "lda") == 0) - ctx->protocol = MAIL_PROTOCOL_LDA; - else { - *errormsg = "Unknown protocol name"; - return FALSE; - } - return TRUE; - } - - if (strcmp(type, "auth") == 0) { - if (ctx->type != SETTINGS_TYPE_ROOT && - ctx->type != SETTINGS_TYPE_SERVER) { - *errormsg = "Auth section not allowed here"; - return FALSE; - } - - ctx->type = SETTINGS_TYPE_AUTH; - ctx->auth = parse_new_auth(ctx->server, name, errormsg); - return ctx->auth != NULL; - } - - if (ctx->type == SETTINGS_TYPE_AUTH && - strcmp(type, "socket") == 0) { - ctx->type = SETTINGS_TYPE_AUTH_SOCKET; - ctx->auth_socket = parse_new_auth_socket(ctx->auth, - name, errormsg); - return ctx->auth_socket != NULL; - } - - if (ctx->type == SETTINGS_TYPE_AUTH && strcmp(type, "passdb") == 0) { - ctx->type = SETTINGS_TYPE_AUTH_PASSDB; - ctx->auth_passdb = auth_passdb_settings_new(ctx->auth, name); - return TRUE; - } - - if (ctx->type == SETTINGS_TYPE_AUTH && strcmp(type, "userdb") == 0) { - ctx->type = SETTINGS_TYPE_AUTH_USERDB; - ctx->auth_userdb = auth_userdb_settings_new(ctx->auth, name); - return TRUE; - } - - if (ctx->type == SETTINGS_TYPE_AUTH_SOCKET) { - ctx->type = SETTINGS_TYPE_SOCKET; - - if (strcmp(type, "master") == 0) { - ctx->socket = &ctx->auth_socket->master; - ctx->socket->used = TRUE; - return TRUE; - } - - if (strcmp(type, "client") == 0) { - ctx->socket = &ctx->auth_socket->client; - ctx->socket->used = TRUE; - return TRUE; - } - } - - if (strcmp(type, "namespace") == 0) { - if (ctx->type != SETTINGS_TYPE_ROOT && - ctx->type != SETTINGS_TYPE_SERVER) { - *errormsg = "Namespace section not allowed here"; - return FALSE; - } - - ctx->type = SETTINGS_TYPE_NAMESPACE; - ctx->namespace = parse_new_namespace(ctx->server, name, - errormsg); - return ctx->namespace != NULL; - } - - if (strcmp(type, "dict") == 0) { - if (ctx->type != SETTINGS_TYPE_ROOT && - ctx->type != SETTINGS_TYPE_SERVER) { - *errormsg = "Plugin section not allowed here"; - return FALSE; - } - - ctx->type = SETTINGS_TYPE_DICT; - return TRUE; - } - - if (strcmp(type, "plugin") == 0) { - if (ctx->type != SETTINGS_TYPE_ROOT && - ctx->type != SETTINGS_TYPE_SERVER) { - *errormsg = "Plugin section not allowed here"; - return FALSE; - } - - ctx->type = SETTINGS_TYPE_PLUGIN; - return TRUE; - } - - *errormsg = "Unknown section type"; - return FALSE; -} - static void -settings_warn_needed_fds(struct server_settings *server ATTR_UNUSED) +settings_warn_needed_fds(struct master_server_settings *server ATTR_UNUSED) { #ifdef HAVE_SETRLIMIT struct rlimit rlim; @@ -1477,341 +862,169 @@ /* count only log pipes needed for login and mail processes. we need more, but they're the ones that can use up most of the fds */ - for (; server != NULL; server = server->next) { - if (server->imap != NULL) - fd_count += server->imap->login_max_processes_count; - if (server->pop3 != NULL) - fd_count += server->pop3->login_max_processes_count; - fd_count += server->defaults->max_mail_processes; - } + if (server->imap != NULL) + fd_count += server->imap->login_max_processes_count; + if (server->pop3 != NULL) + fd_count += server->pop3->login_max_processes_count; + fd_count += server->defaults->max_mail_processes; if (rlim.rlim_cur < fd_count) { i_warning("fd limit %d is lower than what Dovecot can use under " "full load (more than %u). Either grow the limit or " "change login_max_processes_count and " - "max_mail_processes settings", + "max_mail_processes master_settings", (int)rlim.rlim_cur, fd_count); } #endif } -bool master_settings_read(const char *path, bool nochecks, bool nofixes) +static void +config_split_all_settings(struct master_settings *set, const char *input) +{ + const char *p, *line; + string_t *str; + + str = t_str_new(256); + p_array_init(&set->all_settings, settings_pool, 256); + for (p = input; *p != '\n'; p++) { + str_truncate(str, 0); + for (; *p != '='; p++) { + i_assert(*p != '\n' && *p != '\0'); + str_append_c(str, i_toupper(*p)); + } + for (; *p != '\n'; p++) { + i_assert(*p != '\0'); + str_append_c(str, *p); + } + line = p_strdup(settings_pool, str_c(str)); + array_append(&set->all_settings, &line, 1); + } +} + +static int config_exec(const char *path, const char *service, + struct master_settings **set_r) { - struct settings_parse_ctx ctx; - struct server_settings *server, *prev; - struct auth_settings *auth; - struct namespace_settings *ns; + struct setting_parser_context *parser; + string_t *all_settings; + int ret; + + all_settings = str_new(default_pool, 10240); + parser = settings_parser_init(settings_pool, + &master_setting_parser_info, + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); + settings_parse_save_input(parser, all_settings); + if ((ret = settings_parse_exec(parser, DOVECOT_CONFIG_BIN_PATH, + path, service)) == 0) { + *set_r = settings_parser_get(parser); + config_split_all_settings(*set_r, str_c(all_settings)); + } + settings_parser_deinit(&parser); + str_free(&all_settings); + return ret; +} + +int master_settings_read(const char *path, + struct master_server_settings **set_r) +{ + struct master_server_settings *set; + + p_clear(settings_pool); + set = p_new(settings_pool, struct master_server_settings, 1); + + master_default_settings.mail_executable = NULL; + master_default_settings.login_executable = NULL; + if (config_exec(path, "", &set->defaults) < 0) + return -1; + set->defaults->protocol = MAIL_PROTOCOL_ANY; + set->defaults->server = set; + + master_default_settings.mail_executable = PKG_LIBEXECDIR"/imap"; + master_default_settings.login_executable = PKG_LIBEXECDIR"/imap-login"; + if (config_exec(path, "imap", &set->imap) < 0) + return -1; + set->imap->protocol = MAIL_PROTOCOL_IMAP; + set->imap->server = set; + + master_default_settings.mail_executable = PKG_LIBEXECDIR"/pop3"; + master_default_settings.login_executable = PKG_LIBEXECDIR"/pop3-login"; + if (config_exec(path, "pop3", &set->pop3) < 0) + return -1; + set->pop3->protocol = MAIL_PROTOCOL_POP3; + set->pop3->server = set; + + *set_r = set; + return 0; +} + +bool master_settings_check(struct master_server_settings *set, + bool nochecks, bool nofixes) +{ + struct master_auth_settings *const *auths; + unsigned int i, count; pool_t temp; - memset(&ctx, 0, sizeof(ctx)); - - p_clear(settings_pool); + if ((*set->imap->protocols == '\0' || + *set->pop3->protocols == '\0') && !nochecks) { + i_error("protocols: No protocols given in configuration file"); + return FALSE; + } + /* --exec-mail is used if nochecks=TRUE. Allow it regardless + of what's in protocols setting. */ + if (!settings_is_active(set->imap) && !nochecks) { + if (strcmp(set->imap->protocols, "none") == 0) { + set->imap->protocol = MAIL_PROTOCOL_ANY; + if (!settings_fix(set->imap, nochecks, nofixes)) + return FALSE; + } + set->imap = NULL; + } else { + if (!settings_fix(set->imap, nochecks, nofixes)) + return FALSE; + } - ctx.type = SETTINGS_TYPE_ROOT; - ctx.protocol = MAIL_PROTOCOL_ANY; - ctx.server = ctx.root = - create_new_server("default", - &default_settings, &default_settings); - ctx.auth = &ctx.server->auth_defaults; + if (!settings_is_active(set->pop3) && !nochecks) + set->pop3 = NULL; + else { + if (!settings_fix(set->pop3, nochecks, nofixes)) + return FALSE; + } - if (!settings_read(path, NULL, parse_setting, parse_section, &ctx)) + if (!settings_fix(set->defaults, nochecks, nofixes)) return FALSE; - if (ctx.level != 0) { - i_error("Missing '}'"); - return FALSE; - } - - /* If server sections were defined, skip the root */ - if (ctx.root->next != NULL) - ctx.root = ctx.root->next; - - if (!nochecks && !nofixes) { - ctx.root->defaults = settings_is_active(ctx.root->imap) ? - ctx.root->imap : ctx.root->pop3; - - path = t_strconcat(ctx.root->defaults->base_dir, - "/master.pid", NULL); - pid_file_check_running(path); - } - - prev = NULL; - for (server = ctx.root; server != NULL; server = server->next) { - if ((*server->imap->protocols == '\0' || - *server->pop3->protocols == '\0') && !nochecks) { - i_error("protocols: No protocols given " - "in configuration file"); + if (!nochecks) { + auths = array_get(&set->defaults->auths, &count); + if (count == 0) { + i_error("Missing auth section"); return FALSE; } - /* --exec-mail is used if nochecks=TRUE. Allow it regardless - of what's in protocols setting. */ - if (!settings_is_active(server->imap) && !nochecks) { - if (strcmp(server->imap->protocols, "none") == 0) { - server->imap->protocol = MAIL_PROTOCOL_ANY; - if (!settings_fix(server->imap, nochecks, - nofixes)) - return FALSE; - server->defaults = server->imap; - } - server->imap = NULL; - } else { - if (!settings_fix(server->imap, nochecks, nofixes)) - return FALSE; - server->defaults = server->imap; - } - if (!settings_is_active(server->pop3) && !nochecks) - server->pop3 = NULL; - else { - if (!settings_fix(server->pop3, nochecks, nofixes)) + for (i = 0; i < count; i++) { + if (!auth_settings_verify(set->defaults, auths[i])) return FALSE; - if (server->defaults == NULL) - server->defaults = server->pop3; - } - - if (server->defaults == NULL) { - if (prev == NULL) - ctx.root = server->next; - else - prev->next = server->next; - } else { - auth = server->auths; - if (auth == NULL) { - i_error("Missing auth section for server %s", - server->name); - return FALSE; - } - - if (!nochecks) { - for (; auth != NULL; auth = auth->next) { - if (!auth_settings_verify(auth)) - return FALSE; - } - ns = server->namespaces; - for (; ns != NULL; ns = ns->next) { - if (!namespace_settings_verify(server, ns)) - return FALSE; - } - } - prev = server; } } - if (ctx.root == NULL) { - /* We aren't actually checking them separately, but if it - contains only invalid protocols we'll get here.. */ - i_error("Invalid protocols given in configuration file"); - return FALSE; - } - if (!nochecks) - settings_warn_needed_fds(ctx.root); + settings_warn_needed_fds(set); /* settings ok, swap them */ temp = settings_pool; settings_pool = settings2_pool; settings2_pool = temp; - settings_root = ctx.root; + master_set = set; return TRUE; } -static void settings_dump(const struct setting_def *def, const void **sets, - const char **set_names, unsigned int count, - bool nondefaults, unsigned int indent) -{ - const char **str; - unsigned int i; - - str = t_new(const char *, count); - for (; def->name != NULL; def++) { - bool same = TRUE; - - switch (def->type) { - case SET_STR: { - const char *const *strp; - - for (i = 0; i < count; i++) { - strp = CONST_PTR_OFFSET(sets[i], def->offset); - str[i] = *strp != NULL ? *strp : ""; - } - break; - } - case SET_INT: { - const unsigned int *n; - - for (i = 0; i < count; i++) { - n = CONST_PTR_OFFSET(sets[i], def->offset); - str[i] = dec2str(*n); - } - break; - } - case SET_BOOL: { - const bool *b; - - for (i = 0; i < count; i++) { - b = CONST_PTR_OFFSET(sets[i], def->offset); - str[i] = *b ? "yes" : "no"; - } - break; - } - } - - for (i = 2; i < count; i++) { - if (strcmp(str[i], str[i-1]) != 0) - same = FALSE; - } - if (same) { - if (!nondefaults || strcmp(str[0], str[1]) != 0) { - for (i = 0; i < indent; i++) - putc(' ', stdout); - printf("%s: %s\n", def->name, str[1]); - } - } else { - for (i = 0; i < indent; i++) - putc(' ', stdout); - for (i = 1; i < count; i++) { - printf("%s(%s): %s\n", def->name, - set_names[i], str[i]); - } - } - } -} - -static void -namespace_settings_dump(struct namespace_settings *ns, bool nondefaults) +void master_settings_export_to_env(const struct master_settings *set) { - const void *sets[2]; - - sets[0] = t_malloc0(sizeof(struct namespace_settings)); - for (; ns != NULL; ns = ns->next) { - printf("namespace:\n"); - sets[1] = ns; - settings_dump(namespace_setting_defs, sets, NULL, 2, - nondefaults, 2); - } -} - -static void auth_settings_dump(struct auth_settings *auth, bool nondefaults) -{ - const struct auth_passdb_settings *passdb; - const struct auth_userdb_settings *userdb; - const struct auth_socket_settings *socket_set; - const void *sets[2], *sets2[2]; - const void *empty_defaults; - - empty_defaults = t_malloc0(sizeof(struct auth_passdb_settings) + - sizeof(struct auth_userdb_settings) + - sizeof(struct auth_socket_settings)); - - sets[0] = &default_auth_settings; - sets2[0] = empty_defaults; - - for (; auth != NULL; auth = auth->next) { - printf("auth %s:\n", auth->name); - sets[1] = auth; - settings_dump(auth_setting_defs, sets, NULL, 2, nondefaults, 2); - - passdb = auth->passdbs; - for (; passdb != NULL; passdb = passdb->next) { - printf(" passdb:\n"); - sets2[1] = passdb; - settings_dump(auth_passdb_setting_defs, sets2, NULL, 2, - nondefaults, 4); - } - - userdb = auth->userdbs; - for (; userdb != NULL; userdb = userdb->next) { - printf(" userdb:\n"); - sets2[1] = userdb; - settings_dump(auth_userdb_setting_defs, sets2, NULL, 2, - nondefaults, 4); - } - - socket_set = auth->sockets; - for (; socket_set != NULL; socket_set = socket_set->next) { - printf(" socket:\n"); - sets2[1] = socket_set; - settings_dump(auth_socket_setting_defs, sets2, NULL, 2, - nondefaults, 4); - - if (socket_set->client.used) { - printf(" client:\n"); - sets2[1] = &socket_set->client; - settings_dump(socket_setting_defs, sets2, NULL, - 2, nondefaults, 6); - } - - if (socket_set->master.used) { - printf(" master:\n"); - sets2[1] = &socket_set->master; - settings_dump(socket_setting_defs, sets2, NULL, - 2, nondefaults, 6); - } - } - } -} - -static void plugin_settings_dump(const struct settings *set) -{ - const char *const *envs; + const char *const *sets; unsigned int i, count; - envs = array_get(&set->plugin_envs, &count); - i_assert((count % 2) == 0); - - if (count == 0) - return; - - printf("plugin:\n"); - for (i = 0; i < count; i += 2) - printf(" %s: %s\n", envs[i], envs[i+1]); -} - -static void dict_settings_dump(const struct server_settings *set) -{ - const char *const *dicts; - unsigned int i, count; - - dicts = array_get(&set->dicts, &count); - i_assert((count % 2) == 0); - - if (count == 0) - return; - - printf("dict:\n"); - for (i = 0; i < count; i += 2) - printf(" %s: %s\n", dicts[i], dicts[i+1]); -} - -void master_settings_dump(struct server_settings *set, bool nondefaults) -{ - const void *sets[4]; - const char *set_names[4]; - unsigned int count; - - sets[0] = &default_settings; - sets[1] = set->defaults; - - set_names[0] = NULL; - set_names[1] = "default"; - - count = 2; - if (set->imap != NULL) { - sets[count] = set->imap; - set_names[count] = "imap"; - count++; - } - if (set->pop3 != NULL) { - sets[count] = set->pop3; - set_names[count] = "pop3"; - count++; - } - settings_dump(setting_defs, sets, set_names, count, nondefaults, 0); - namespace_settings_dump(set->namespaces, nondefaults); - auth_settings_dump(set->auths, nondefaults); - plugin_settings_dump(set->defaults); - dict_settings_dump(set); + sets = array_get(&set->all_settings, &count); + for (i = 0; i < count; i++) + env_put(sets[i]); } void master_settings_init(void)
--- a/src/master/master-settings.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/master-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -3,6 +3,8 @@ #include "network.h" +#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf" + enum mail_protocol { MAIL_PROTOCOL_ANY, MAIL_PROTOCOL_IMAP, @@ -18,10 +20,36 @@ }; ARRAY_DEFINE_TYPE(listener, struct listener); -struct settings { - struct server_settings *server; - enum mail_protocol protocol; +struct master_auth_socket_unix_settings { + const char *path; +}; + +struct master_auth_socket_settings { + const char *type; + + ARRAY_DEFINE(masters, struct master_auth_socket_unix_settings *); +}; +struct master_auth_settings { + const char *name; + const char *executable; + const char *user; + const char *chroot; + + unsigned int count; + unsigned int process_size; + + const char *mechanisms; + bool debug; + + ARRAY_DEFINE(sockets, struct master_auth_socket_settings *); + + /* .. */ + uid_t uid; + gid_t gid; +}; + +struct master_settings { /* common */ const char *base_dir; const char *log_path; @@ -35,17 +63,8 @@ const char *ssl_listen; const char *ssl; - const char *ssl_ca_file; - const char *ssl_cert_file; const char *ssl_key_file; - const char *ssl_key_password; unsigned int ssl_parameters_regenerate; - const char *ssl_cipher_list; - const char *ssl_cert_username_field; - bool ssl_verify_client_cert; - bool disable_plaintext_auth; - bool verbose_ssl; - bool shutdown_clients; bool nfs_check; bool version_ignore; @@ -53,18 +72,14 @@ const char *login_dir; const char *login_executable; const char *login_user; - const char *login_greeting; - const char *login_log_format_elements; - const char *login_log_format; bool login_process_per_connection; bool login_chroot; - const char *login_trusted_networks; + bool disable_plaintext_auth; unsigned int login_process_size; unsigned int login_processes_count; unsigned int login_max_processes_count; - unsigned int login_max_connections; /* mail */ const char *valid_chroot_dirs; @@ -80,68 +95,34 @@ const char *mail_uid; const char *mail_gid; + const char *mail_plugins; + const char *imap_capability; + const char *mail_location; - const char *mail_cache_fields; - const char *mail_never_cache_fields; - unsigned int mail_cache_min_mail_count; - unsigned int mailbox_idle_check_interval; bool mail_debug; - bool mail_full_filesystem_access; - unsigned int mail_max_keyword_length; - bool mail_save_crlf; - bool mmap_disable; - bool dotlock_use_excl; - bool fsync_disable; - bool mail_nfs_storage; bool mail_nfs_index; - bool mailbox_list_index_disable; - const char *lock_method; - bool maildir_stat_dirs; - bool maildir_copy_with_hardlinks; - bool maildir_copy_preserve_filename; - const char *mbox_read_locks; - const char *mbox_write_locks; - unsigned int mbox_lock_timeout; - unsigned int mbox_dotlock_change_timeout; - unsigned int mbox_min_index_size; - bool mbox_dirty_syncs; - bool mbox_very_dirty_syncs; - bool mbox_lazy_writes; - unsigned int dbox_rotate_size; - unsigned int dbox_rotate_min_size; - unsigned int dbox_rotate_days; unsigned int umask; bool mail_drop_priv_before_exec; const char *mail_executable; unsigned int mail_process_size; - const char *mail_plugins; - const char *mail_plugin_dir; const char *mail_log_prefix; unsigned int mail_log_max_lines_per_sec; - /* imap */ - unsigned int imap_max_line_length; - const char *imap_capability; - const char *imap_client_workarounds; - const char *imap_logout_format; - const char *imap_id_send; - const char *imap_id_log; - - /* pop3 */ - bool pop3_no_flag_updates; - bool pop3_enable_last; - bool pop3_reuse_xuidl; - bool pop3_lock_session; - const char *pop3_uidl_format; - const char *pop3_client_workarounds; - const char *pop3_logout_format; - /* dict */ const char *dict_db_config; unsigned int dict_process_count; + ARRAY_DEFINE(auths, struct master_auth_settings *); + + ARRAY_DEFINE(dicts, const char *); + ARRAY_DEFINE(plugin_envs, const char *); + +#ifndef CONFIG_BINARY /* .. */ + struct master_server_settings *server; + enum mail_protocol protocol; + ARRAY_TYPE(listener) listens; ARRAY_TYPE(listener) ssl_listens; @@ -149,125 +130,26 @@ gid_t mail_gid_t, mail_priv_gid_t; const char *imap_generated_capability; - - ARRAY_DEFINE(plugin_envs, const char *); -}; - -struct socket_settings { - const char *path; - unsigned int mode; - const char *user; - const char *group; - - unsigned int used:1; -}; - -struct auth_socket_settings { - struct auth_settings *parent; - struct auth_socket_settings *next; - - const char *type; - struct socket_settings master; - struct socket_settings client; -}; - -struct auth_passdb_settings { - struct auth_settings *parent; - struct auth_passdb_settings *next; - - const char *driver; - const char *args; - bool deny; - bool pass; - bool master; -}; - -struct auth_userdb_settings { - struct auth_settings *parent; - struct auth_userdb_settings *next; - - const char *driver; - const char *args; + ARRAY_TYPE(const_string) all_settings; +#endif }; -struct auth_settings { - struct server_settings *parent; - struct auth_settings *next; - - const char *name; - const char *mechanisms; - const char *realms; - const char *default_realm; - unsigned int cache_size; - unsigned int cache_ttl; - unsigned int cache_negative_ttl; - const char *executable; - const char *user; - const char *chroot; - const char *username_chars; - const char *username_translation; - const char *username_format; - const char *master_user_separator; - const char *anonymous_username; - const char *krb5_keytab; - const char *gssapi_hostname; - const char *winbind_helper_path; - unsigned int failure_delay; - - bool verbose, debug, debug_passwords; - bool ssl_require_client_cert; - bool ssl_username_from_cert; - bool use_winbind; - - unsigned int count; - unsigned int worker_max_count; - unsigned int process_size; - - /* .. */ - uid_t uid; - gid_t gid; - struct auth_passdb_settings *passdbs; - struct auth_userdb_settings *userdbs; - struct auth_socket_settings *sockets; -}; - -struct namespace_settings { - struct server_settings *parent; - struct namespace_settings *next; - - const char *type; - const char *separator; - const char *prefix; - const char *location; - const char *alias_for; - - bool inbox; - bool hidden; - const char *list; - bool subscriptions; -}; - -struct server_settings { - struct server_settings *next; - - const char *name; - struct settings *defaults; - struct settings *imap; - struct settings *pop3; - struct auth_settings *auths; - struct auth_settings auth_defaults; - struct namespace_settings *namespaces; - - ARRAY_DEFINE(dicts, const char *); +struct master_server_settings { + struct master_settings *defaults; + struct master_settings *imap; + struct master_settings *pop3; gid_t login_gid; }; -extern struct server_settings *settings_root; +extern struct master_server_settings *master_set; +extern struct setting_parser_info master_setting_parser_info; -bool master_settings_read(const char *path, bool nochecks, bool nofixes); - -void master_settings_dump(struct server_settings *set, bool nondefaults); +int master_settings_read(const char *path, + struct master_server_settings **set_r); +bool master_settings_check(struct master_server_settings *set, + bool nochecks, bool nofixes); +void master_settings_export_to_env(const struct master_settings *set); void master_settings_init(void); void master_settings_deinit(void);
--- a/src/master/ssl-init.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/master/ssl-init.c Tue Jan 27 18:21:53 2009 -0500 @@ -60,7 +60,7 @@ if (dup2(log_fd, 2) < 0) i_fatal("dup2(stderr) failed: %m"); - child_process_init_env(); + child_process_init_env(master_set->defaults); client_process_exec(t_strconcat(binpath, " "SSL_PARAMETERS_PERM_PATH, NULL), ""); i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", binpath); @@ -80,7 +80,7 @@ i_free_and_null(generating_path); } -static bool check_parameters_file_set(struct settings *set) +static bool check_parameters_file_set(struct master_settings *set) { const char *path; struct stat st, st2; @@ -141,16 +141,10 @@ void ssl_check_parameters_file(void) { - struct server_settings *server; - if (generating_path != NULL) return; - for (server = settings_root; server != NULL; server = server->next) { - if (server->defaults != NULL && - !check_parameters_file_set(server->defaults)) - break; - } + (void)check_parameters_file_set(master_set->defaults); } static void check_parameters_file_timeout(void *context ATTR_UNUSED)
--- a/src/plugins/acl/acl-mailbox-list.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/acl/acl-mailbox-list.c Tue Jan 27 18:21:53 2009 -0500 @@ -533,7 +533,7 @@ i_fatal("ACL backend initialization failed"); flags = mailbox_list_get_flags(list); - if ((flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0) { + if (list->mail_set->mail_full_filesystem_access) { /* not necessarily, but safer to do this for now. */ i_fatal("mail_full_filesystem_access=yes is " "incompatible with ACLs");
--- a/src/plugins/fts-lucene/fts-backend-lucene.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/fts-lucene/fts-backend-lucene.c Tue Jan 27 18:21:53 2009 -0500 @@ -52,7 +52,7 @@ "INBOX"); if (path == NULL) { /* in-memory indexes */ - if ((box->storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0) + if (box->storage->set->mail_debug) i_info("fts squat: Disabled with in-memory indexes"); return NULL; }
--- a/src/plugins/fts-squat/fts-backend-squat.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/fts-squat/fts-backend-squat.c Tue Jan 27 18:21:53 2009 -0500 @@ -61,18 +61,17 @@ mailbox_get_name(box)); if (*path == '\0') { /* in-memory indexes */ - if ((storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0) + if (storage->set->mail_debug) i_info("fts squat: Disabled with in-memory indexes"); return NULL; } mailbox_get_status(box, STATUS_UIDVALIDITY, &status); - if ((storage->flags & (MAIL_STORAGE_FLAG_MMAP_DISABLE | - MAIL_STORAGE_FLAG_MMAP_NO_WRITE)) != 0) + if (storage->set->mmap_disable || storage->set->mmap_no_write) flags |= SQUAT_INDEX_FLAG_MMAP_DISABLE; - if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX) != 0) + if (storage->set->mail_nfs_index) flags |= SQUAT_INDEX_FLAG_NFS_FLUSH; - if ((storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0) + if (storage->set->dotlock_use_excl) flags |= SQUAT_INDEX_FLAG_DOTLOCK_USE_EXCL; backend = i_new(struct squat_fts_backend, 1);
--- a/src/plugins/fts/fts-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/fts/fts-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -912,7 +912,7 @@ fbox->backend_fast = backend; } } - if ((box->storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0 && + if (box->storage->set->mail_debug && fbox->backend_substr == NULL && fbox->backend_fast == NULL) i_info("fts: No backends enabled by the fts setting"); }
--- a/src/plugins/imap-acl/imap-acl-plugin.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/imap-acl/imap-acl-plugin.c Tue Jan 27 18:21:53 2009 -0500 @@ -48,6 +48,7 @@ const char *imap_acl_plugin_version = PACKAGE_VERSION; +static void (*next_hook_client_created)(struct client **client); static bool acl_anyone_allow = FALSE; static struct mailbox * @@ -465,6 +466,14 @@ return TRUE; } +static void imap_acl_client_created(struct client **client) +{ + str_append((*client)->capability_string, " ACL RIGHTS=texk"); + + if (next_hook_client_created != NULL) + next_hook_client_created(client); +} + void imap_acl_plugin_init(void) { const char *env; @@ -476,13 +485,14 @@ if (env != NULL) acl_anyone_allow = strcmp(env, "allow") == 0; - str_append(capability_string, " ACL RIGHTS=texk"); - command_register("LISTRIGHTS", cmd_listrights, 0); command_register("GETACL", cmd_getacl, 0); command_register("MYRIGHTS", cmd_myrights, 0); command_register("SETACL", cmd_setacl, 0); command_register("DELETEACL", cmd_deleteacl, 0); + + next_hook_client_created = hook_client_created; + hook_client_created = imap_acl_client_created; } void imap_acl_plugin_deinit(void) @@ -495,4 +505,6 @@ command_unregister("SETACL"); command_unregister("DELETEACL"); command_unregister("LISTRIGHTS"); + + hook_client_created = next_hook_client_created; }
--- a/src/plugins/imap-quota/imap-quota-plugin.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/imap-quota/imap-quota-plugin.c Tue Jan 27 18:21:53 2009 -0500 @@ -14,6 +14,7 @@ #define QUOTA_USER_SEPARATOR ':' const char *imap_quota_plugin_version = PACKAGE_VERSION; +static void (*next_hook_client_created)(struct client **client); static const char * imap_quota_root_get_name(struct mail_user *user, struct mail_user *owner, @@ -213,12 +214,22 @@ return TRUE; } +static void imap_quota_client_created(struct client **client) +{ + str_append((*client)->capability_string, " QUOTA"); + + if (next_hook_client_created != NULL) + next_hook_client_created(client); +} + void imap_quota_plugin_init(void) { command_register("GETQUOTAROOT", cmd_getquotaroot, 0); command_register("GETQUOTA", cmd_getquota, 0); command_register("SETQUOTA", cmd_setquota, 0); - str_append(capability_string, " QUOTA"); + + next_hook_client_created = hook_client_created; + hook_client_created = imap_quota_client_created; } void imap_quota_plugin_deinit(void) @@ -226,4 +237,6 @@ command_unregister("GETQUOTAROOT"); command_unregister("GETQUOTA"); command_unregister("SETQUOTA"); + + hook_client_created = next_hook_client_created; }
--- a/src/plugins/mbox-snarf/mbox-snarf-plugin.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/mbox-snarf/mbox-snarf-plugin.c Tue Jan 27 18:21:53 2009 -0500 @@ -147,8 +147,7 @@ /* use ~/mbox as the INBOX */ name = mstorage->snarf_inbox_path; use_snarfing = TRUE; - storage->flags |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS; - list->flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS; + storage->set->mail_full_filesystem_access = TRUE; } else if (errno != ENOENT) { mail_storage_set_critical(storage, "stat(%s) failed: %m",
--- a/src/plugins/virtual/virtual-storage.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/plugins/virtual/virtual-storage.c Tue Jan 27 18:21:53 2009 -0500 @@ -71,7 +71,7 @@ const char *data, struct mail_storage *storage, const char **layout_r, const char **error_r) { - bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + bool debug = storage->set->mail_debug; *layout_r = "fs"; @@ -146,8 +146,7 @@ storage, &storage->list_module_ctx); /* finish list init after we've overridden vfuncs */ - mailbox_list_init(_storage->list, _storage->ns, &list_set, - mail_storage_get_list_flags(_storage->flags)); + mailbox_list_init(_storage->list, _storage->ns, &list_set, 0); return 0; } @@ -619,6 +618,7 @@ MEMBER(mailbox_is_file) FALSE, { + NULL, virtual_class_init, virtual_class_deinit, virtual_alloc,
--- a/src/pop3-login/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3-login/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -10,6 +10,7 @@ pop3_login_LDADD = \ ../login-common/liblogin-common.a \ ../lib-auth/libauth.a \ + ../lib-settings/libsettings.a \ ../lib/liblib.a \ $(SSL_LIBS)
--- a/src/pop3-login/client-authenticate.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3-login/client-authenticate.c Tue Jan 27 18:21:53 2009 -0500 @@ -36,7 +36,7 @@ if (ssl_initialized && !client->common.tls) str_append(str, "STLS\r\n"); - if (!disable_plaintext_auth || client->common.secured) + if (!login_settings->disable_plaintext_auth || client->common.secured) str_append(str, "USER\r\n"); str_append(str, "SASL"); @@ -47,7 +47,8 @@ c) we allow insecure authentication */ if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && - (client->common.secured || !disable_plaintext_auth || + (client->common.secured || + !login_settings->disable_plaintext_auth || (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) { str_append_c(str, ' '); str_append(str, mech[i].name); @@ -155,7 +156,7 @@ master_user = *args + 7; else if (strncmp(*args, "user=", 5) == 0) { /* already handled in login-common */ - } else if (auth_debug) { + } else if (login_settings->auth_debug) { i_info("Ignoring unknown passdb extra field: %s", *args); } @@ -270,8 +271,9 @@ const struct auth_mech_desc *mech; const char *mech_name, *p; - if (!client->common.secured && ssl_required) { - if (verbose_auth) { + if (!client->common.secured && + strcmp(login_settings->ssl, "required") == 0) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "Login failed: " "SSL required for authentication"); } @@ -289,7 +291,8 @@ mech = auth_client_get_available_mechs(auth_client, &count); for (i = 0; i < count; i++) { if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && - (client->common.secured || disable_plaintext_auth || + (client->common.secured || + login_settings->disable_plaintext_auth || (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) client_send_line(client, mech[i].name); } @@ -321,10 +324,11 @@ static bool check_plaintext_auth(struct pop3_client *client) { - if (client->common.secured || !disable_plaintext_auth) + if (client->common.secured || + !login_settings->disable_plaintext_auth) return TRUE; - if (verbose_auth) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "Login failed: " "Plaintext authentication disabled"); } @@ -394,7 +398,7 @@ const char *p; if (client->apop_challenge == NULL) { - if (verbose_auth) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "APOP failed: APOP not enabled"); } @@ -405,7 +409,7 @@ /* <username> <md5 sum in hex> */ p = strchr(args, ' '); if (p == NULL || strlen(p+1) != 32) { - if (verbose_auth) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "APOP failed: Invalid parameters"); } @@ -421,7 +425,7 @@ buffer_append_c(apop_data, '\0'); if (hex_to_binary(p+1, apop_data) < 0) { - if (verbose_auth) { + if (login_settings->verbose_auth) { client_syslog(&client->common, "APOP failed: " "Invalid characters in MD5 response"); }
--- a/src/pop3-login/client.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3-login/client.c Tue Jan 27 18:21:53 2009 -0500 @@ -39,7 +39,8 @@ { const char *addr; - if (!verbose_proctitle || !process_per_connection) + if (!login_settings->verbose_proctitle || + !login_settings->login_process_per_connection) return; addr = net_ip2addr(&client->common.ip); @@ -225,6 +226,7 @@ void client_destroy_oldest(void) { + unsigned int max_connections = login_settings->login_max_connections; struct client *client; struct pop3_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT]; unsigned int i, destroy_count; @@ -286,7 +288,8 @@ client->io = io_add(client->common.fd, IO_READ, client_input, client); client->apop_challenge = get_apop_challenge(client); - client_send_line(client, t_strconcat("+OK ", greeting, + client_send_line(client, t_strconcat("+OK ", + login_settings->login_greeting, client->apop_challenge != NULL ? " " : NULL, client->apop_challenge, NULL));
--- a/src/pop3-login/pop3-proxy.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3-login/pop3-proxy.c Tue Jan 27 18:21:53 2009 -0500 @@ -144,7 +144,7 @@ else client_send_line(client, line); - if (verbose_auth) { + if (login_settings->verbose_auth) { str = t_str_new(128); str_printfa(str, "proxy(%s): Login failed to %s:%u", client->common.virtual_user,
--- a/src/pop3/Makefile.am Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3/Makefile.am Tue Jan 27 18:21:53 2009 -0500 @@ -4,6 +4,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-storage \ @@ -20,6 +21,7 @@ libs = \ $(STORAGE_LIBS) \ ../lib-dict/libdict.a \ + ../lib-settings/libsettings.a \ $(unused_objects) pop3_LDADD = \ @@ -32,13 +34,15 @@ pop3_SOURCES = \ client.c \ commands.c \ - main.c + main.c \ + pop3-settings.c headers = \ capability.h \ client.h \ commands.h \ - common.h + common.h \ + pop3-settings.h if INSTALL_HEADERS pkginc_libdir=$(pkgincludedir)/src/pop3
--- a/src/pop3/client.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3/client.c Tue Jan 27 18:21:53 2009 -0500 @@ -138,7 +138,8 @@ return FALSE; } -struct client *client_create(int fd_in, int fd_out, struct mail_user *user) +struct client *client_create(int fd_in, int fd_out, struct mail_user *user, + const struct pop3_settings *set) { struct mail_storage *storage; const char *inbox; @@ -152,6 +153,7 @@ net_set_nonblock(fd_out, TRUE); client = i_new(struct client, 1); + client->set = set; client->fd_in = fd_in; client->fd_out = fd_out; client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE); @@ -176,9 +178,9 @@ storage = client->inbox_ns->storage; flags = MAILBOX_OPEN_POP3_SESSION; - if (no_flag_updates) + if (set->pop3_no_flag_updates) flags |= MAILBOX_OPEN_KEEP_RECENT; - if (lock_session) + if (set->pop3_lock_session) flags |= MAILBOX_OPEN_KEEP_LOCKED; client->mailbox = mailbox_open(&storage, "INBOX", NULL, flags); if (client->mailbox == NULL) { @@ -236,7 +238,7 @@ tab[8].value = dec2str(client->output->offset); str = t_str_new(128); - var_expand(str, logout_format, tab); + var_expand(str, client->set->pop3_logout_format, tab); return str_c(str); }
--- a/src/pop3/client.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3/client.h Tue Jan 27 18:21:53 2009 -0500 @@ -44,6 +44,11 @@ unsigned char *deleted_bitmask; + /* settings: */ + const struct pop3_settings *set; + enum client_workarounds workarounds; + enum uidl_keys uidl_keymask; + unsigned int disconnected:1; unsigned int deleted:1; unsigned int waiting_input:1; @@ -51,7 +56,8 @@ /* Create new client with specified input/output handles. socket specifies if the handle is a socket. */ -struct client *client_create(int fd_in, int fd_out, struct mail_user *user); +struct client *client_create(int fd_in, int fd_out, struct mail_user *user, + const struct pop3_settings *set); void client_destroy(struct client *client, const char *reason); /* Disconnect client connection */
--- a/src/pop3/commands.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3/commands.c Tue Jan 27 18:21:53 2009 -0500 @@ -8,6 +8,7 @@ #include "var-expand.h" #include "message-size.h" #include "mail-storage.h" +#include "mail-storage-settings.h" #include "mail-search-build.h" #include "capability.h" #include "commands.h" @@ -321,7 +322,7 @@ add = '.'; break; } else if (data[i] == '\0' && - (client_workarounds & + (client->workarounds & WORKAROUND_OUTLOOK_NO_NULS) != 0) { add = 0x80; break; @@ -359,7 +360,8 @@ (void)o_stream_send(client->output, "\r\n", 2); } - if (!ctx->in_body && (client_workarounds & WORKAROUND_OE_NS_EOH) != 0) { + if (!ctx->in_body && + (client->workarounds & WORKAROUND_OE_NS_EOH) != 0) { /* Add the missing end of headers line. */ (void)o_stream_send(client->output, "\r\n", 2); } @@ -408,7 +410,7 @@ return ret; } - if (body_lines == (uoff_t)-1 && !no_flag_updates) { + if (body_lines == (uoff_t)-1 && !client->set->pop3_no_flag_updates) { if ((mail_get_flags(ctx->mail) & MAIL_SEEN) == 0) { /* mark the message seen with RETR command */ (void)mail_update_flags(ctx->mail, @@ -463,7 +465,7 @@ client->deleted_size = 0; } - if (enable_last_command) { + if (client->set->pop3_enable_last) { /* remove all \Seen flags (as specified by RFC 1460) */ search_args = pop3_search_build(client, 0); search_ctx = mailbox_search_init(client->trans, @@ -518,7 +520,7 @@ unsigned int message; }; -static void pop3_get_uid(struct cmd_uidl_context *ctx, +static void pop3_get_uid(struct client *client, struct cmd_uidl_context *ctx, struct var_expand_table *tab, string_t *str) { char uid_str[MAX_INT_STRLEN]; @@ -530,18 +532,18 @@ return; } - if (reuse_xuidl && + if (client->set->pop3_reuse_xuidl && mail_get_first_header(ctx->mail, "X-UIDL", &uidl) > 0) { str_append(str, uidl); return; } - if ((uidl_keymask & UIDL_UID) != 0) { + if ((client->uidl_keymask & UIDL_UID) != 0) { i_snprintf(uid_str, sizeof(uid_str), "%u", ctx->mail->uid); tab[1].value = uid_str; } - if ((uidl_keymask & UIDL_MD5) != 0) { + if ((client->uidl_keymask & UIDL_MD5) != 0) { if (mail_get_special(ctx->mail, MAIL_FETCH_HEADER_MD5, &tab[2].value) < 0 || *tab[2].value == '\0') { @@ -549,7 +551,7 @@ i_fatal("UIDL: Header MD5 not found"); } } - if ((uidl_keymask & UIDL_FILE_NAME) != 0) { + if ((client->uidl_keymask & UIDL_FILE_NAME) != 0) { if (mail_get_special(ctx->mail, MAIL_FETCH_UIDL_FILE_NAME, &tab[3].value) < 0 || @@ -558,7 +560,7 @@ i_fatal("UIDL: File name not found"); } } - var_expand(str, uidl_format, tab); + var_expand(str, client->set->pop3_uidl_format, tab); } static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) @@ -592,7 +594,7 @@ str_truncate(str, 0); str_printfa(str, ctx->message == 0 ? "%u " : "+OK %u ", ctx->mail->seq); - pop3_get_uid(ctx, tab, str); + pop3_get_uid(client, ctx, tab, str); ret = client_send_line(client, "%s", str_c(str)); if (ret < 0) @@ -636,7 +638,7 @@ ctx->message = message; wanted_fields = 0; - if ((uidl_keymask & UIDL_MD5) != 0) + if ((client->uidl_keymask & UIDL_MD5) != 0) wanted_fields |= MAIL_FETCH_HEADER_MD5; ctx->search_ctx = mailbox_search_init(client->trans, search_args, NULL); @@ -692,7 +694,7 @@ case 'L': if (strcmp(name, "LIST") == 0) return cmd_list(client, args); - if (strcmp(name, "LAST") == 0 && enable_last_command) + if (strcmp(name, "LAST") == 0 && client->set->pop3_enable_last) return cmd_last(client, args); break; case 'N':
--- a/src/pop3/common.h Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3/common.h Tue Jan 27 18:21:53 2009 -0500 @@ -1,9 +1,6 @@ #ifndef COMMON_H #define COMMON_H -#include "lib.h" -#include "client.h" - enum client_workarounds { WORKAROUND_OUTLOOK_NO_NULS = 0x01, WORKAROUND_OE_NS_EOH = 0x02 @@ -16,11 +13,11 @@ UIDL_FILE_NAME = 0x08 }; +#include "lib.h" +#include "client.h" +#include "pop3-settings.h" + extern struct ioloop *ioloop; -extern enum client_workarounds client_workarounds; -extern bool enable_last_command, no_flag_updates, reuse_xuidl, lock_session; -extern const char *uidl_format, *logout_format; -extern enum uidl_keys uidl_keymask; extern void (*hook_client_created)(struct client **client);
--- a/src/pop3/main.c Mon Jan 26 19:17:54 2009 -0500 +++ b/src/pop3/main.c Tue Jan 27 18:21:53 2009 -0500 @@ -44,14 +44,6 @@ static char log_prefix[128]; /* syslog() needs this to be permanent */ static struct io *log_io = NULL; -enum client_workarounds client_workarounds = 0; -bool enable_last_command = FALSE; -bool no_flag_updates = FALSE; -bool reuse_xuidl = FALSE; -bool lock_session = FALSE; -const char *uidl_format, *logout_format; -enum uidl_keys uidl_keymask; - static void sig_die(int signo, void *context ATTR_UNUSED) { /* warn about being killed because of some signal, except SIGINT (^C) @@ -66,16 +58,15 @@ io_loop_stop(ioloop); } -static void parse_workarounds(void) +static enum client_workarounds +parse_workarounds(const struct pop3_settings *set) { - struct client_workaround_list *list; - const char *env, *const *str; + enum client_workarounds client_workarounds = 0; + struct client_workaround_list *list; + const char *const *str; - env = getenv("POP3_CLIENT_WORKAROUNDS"); - if (env == NULL) - return; - - for (str = t_strsplit_spaces(env, " ,"); *str != NULL; str++) { + str = t_strsplit_spaces(set->pop3_client_workarounds, " ,"); + for (; *str != NULL; str++) { list = client_workaround_list; for (; list->name != NULL; list++) { if (strcasecmp(*str, list->name) == 0) { @@ -86,6 +77,7 @@ if (list->name == NULL) i_fatal("Unknown client workaround: %s", *str); } + return client_workarounds; } static enum uidl_keys parse_uidl_keymask(const char *format) @@ -150,7 +142,8 @@ i_set_failure_timestamp_format(getenv("LOGSTAMP")); } -static void drop_privileges(void) +static void main_preinit(const struct pop3_settings **set_r, + const struct mail_user_settings **user_set_r) { const char *version; @@ -164,24 +157,28 @@ /* Log file or syslog opening probably requires roots */ open_logfile(); - /* Load the plugins before chrooting. Their init() is called later. */ - if (getenv("MAIL_PLUGINS") != NULL) { - const char *plugin_dir = getenv("MAIL_PLUGIN_DIR"); + mail_storage_init(); + mail_storage_register_all(); + mailbox_list_register_all(); - if (plugin_dir == NULL) - plugin_dir = MODULEDIR"/pop3"; - modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"), - TRUE, version); - } + /* read settings after registering storages so they can have their + own setting definitions too */ + pop3_settings_read(set_r, user_set_r); + + /* Load the plugins before chrooting. Their init() is called later. */ + modules = *(*set_r)->mail_plugins == '\0' ? NULL : + module_dir_load((*set_r)->mail_plugin_dir, + (*set_r)->mail_plugins, TRUE, version); restrict_access_by_env(!IS_STANDALONE()); } -static bool main_init(void) +static bool main_init(const struct pop3_settings *set, + const struct mail_user_settings *user_set) { struct mail_user *user; struct client *client; - const char *str; + const char *str, *error; bool ret = TRUE; lib_signals_init(); @@ -193,12 +190,12 @@ if (getenv("USER") == NULL) i_fatal("USER environment missing"); - if (getenv("DEBUG") != NULL) { + if (set->mail_debug) { i_info("Effective uid=%s, gid=%s", dec2str(geteuid()), dec2str(getegid())); } - if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) { + if (set->shutdown_clients) { /* If master dies, the log fd gets closed and we'll quit */ log_io = io_add(STDERR_FILENO, IO_ERROR, log_error_callback, NULL); @@ -206,38 +203,26 @@ dict_drivers_register_builtin(); mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL); - mail_storage_init(); - mail_storage_register_all(); - mailbox_list_register_all(); clients_init(); module_dir_init(modules); - parse_workarounds(); - enable_last_command = getenv("POP3_ENABLE_LAST") != NULL; - no_flag_updates = getenv("POP3_NO_FLAG_UPDATES") != NULL; - reuse_xuidl = getenv("POP3_REUSE_XUIDL") != NULL; - lock_session = getenv("POP3_LOCK_SESSION") != NULL; + user = mail_user_alloc(getenv("USER"), user_set); + mail_user_set_home(user, getenv("HOME")); + if (mail_user_init(user, &error) < 0) + i_fatal("Mail user initialization failed: %s", error); + if (mail_namespaces_init(user, &error) < 0) + i_fatal("Namespace initialization failed: %s", error); - uidl_format = getenv("POP3_UIDL_FORMAT"); - if (uidl_format == NULL || *uidl_format == '\0') - uidl_format = "%08Xu%08Xv"; - logout_format = getenv("POP3_LOGOUT_FORMAT"); - if (logout_format == NULL) - logout_format = "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s"; - uidl_keymask = parse_uidl_keymask(uidl_format); - if (uidl_keymask == 0) + client = client_create(0, 1, user, set); + if (client == NULL) + return FALSE; + client->workarounds = parse_workarounds(set); + client->uidl_keymask = parse_uidl_keymask(set->pop3_uidl_format); + if (client->uidl_keymask == 0) { i_fatal("pop3_uidl_format setting doesn't contain any " "%% variables."); - - user = mail_user_init(getenv("USER")); - mail_user_set_home(user, getenv("HOME")); - if (mail_namespaces_init(user) < 0) - i_fatal("Namespace initialization failed"); - - client = client_create(0, 1, user); - if (client == NULL) - return FALSE; + } if (!IS_STANDALONE()) client_send_line(client, "+OK Logged in."); @@ -272,8 +257,11 @@ int main(int argc ATTR_UNUSED, char *argv[], char *envp[]) { + const struct pop3_settings *set; + const struct mail_user_settings *user_set; + #ifdef DEBUG - if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL) + if (!IS_STANDALONE() && getenv("GDB") == NULL) fd_debug_verify_leaks(3, 1024); #endif if (IS_STANDALONE() && getuid() == 0 && @@ -286,12 +274,12 @@ /* NOTE: we start rooted, so keep the code minimal until restrict_access_by_env() is called */ lib_init(); - drop_privileges(); + main_preinit(&set, &user_set); process_title_init(argv, envp); ioloop = io_loop_create(); - if (main_init()) + if (main_init(set, user_set)) io_loop_run(ioloop); main_deinit();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pop3/pop3-settings.c Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,101 @@ +/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "mail-storage-settings.h" +#include "pop3-settings.h" + +#include <stddef.h> +#include <stdlib.h> + +#undef DEF +#undef DEFLIST +#define DEF(type, name) \ + { type, #name, offsetof(struct pop3_settings, name), NULL } +#define DEFLIST(field, name, defines) \ + { SET_DEFLIST, name, offsetof(struct pop3_settings, field), defines } + +static struct setting_define pop3_setting_defines[] = { + DEF(SET_BOOL, mail_debug), + DEF(SET_BOOL, shutdown_clients), + DEF(SET_BOOL, verbose_proctitle), + + DEF(SET_STR, mail_plugins), + DEF(SET_STR, mail_plugin_dir), + DEF(SET_STR_VARS, mail_log_prefix), + + DEF(SET_BOOL, pop3_no_flag_updates), + DEF(SET_BOOL, pop3_enable_last), + DEF(SET_BOOL, pop3_reuse_xuidl), + DEF(SET_BOOL, pop3_lock_session), + DEF(SET_STR, pop3_client_workarounds), + DEF(SET_STR, pop3_logout_format), + DEF(SET_STR, pop3_uidl_format), + + SETTING_DEFINE_LIST_END +}; + +static struct pop3_settings pop3_default_settings = { + MEMBER(mail_debug) FALSE, + MEMBER(shutdown_clients) FALSE, + MEMBER(verbose_proctitle) FALSE, + + MEMBER(mail_plugins) "", + MEMBER(mail_plugin_dir) MODULEDIR"/pop3", + MEMBER(mail_log_prefix) "%Us(%u): ", + + MEMBER(pop3_no_flag_updates) FALSE, + MEMBER(pop3_enable_last) FALSE, + MEMBER(pop3_reuse_xuidl) FALSE, + MEMBER(pop3_lock_session) FALSE, + MEMBER(pop3_client_workarounds) NULL, + MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s", + MEMBER(pop3_uidl_format) "%08Xu%08Xv" +}; + +struct setting_parser_info pop3_setting_parser_info = { + MEMBER(defines) pop3_setting_defines, + MEMBER(defaults) &pop3_default_settings, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct pop3_settings) +}; + +static pool_t settings_pool = NULL; + +void pop3_settings_read(const struct pop3_settings **set_r, + const struct mail_user_settings **user_set_r) +{ + static const struct setting_parser_info *roots[] = { + &pop3_setting_parser_info, + &mail_user_setting_parser_info + }; + struct setting_parser_context *parser; + void **sets; + + if (settings_pool == NULL) + settings_pool = pool_alloconly_create("pop3 settings", 1024); + else + p_clear(settings_pool); + + mail_storage_namespace_defines_init(settings_pool); + + parser = settings_parser_init_list(settings_pool, + roots, N_ELEMENTS(roots), + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS); + + settings_parse_set_expanded(parser, TRUE); + if (settings_parse_environ(parser) < 0) { + i_fatal("Error reading configuration: %s", + settings_parser_get_error(parser)); + } + + sets = settings_parser_get_list(parser); + *set_r = sets[0]; + *user_set_r = sets[1]; + settings_parser_deinit(&parser); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pop3/pop3-settings.h Tue Jan 27 18:21:53 2009 -0500 @@ -0,0 +1,28 @@ +#ifndef POP3_SETTINGS_H +#define POP3_SETTINGS_H + +struct mail_user_settings; + +struct pop3_settings { + bool mail_debug; + bool shutdown_clients; + bool verbose_proctitle; + + const char *mail_plugins; + const char *mail_plugin_dir; + const char *mail_log_prefix; + + /* pop3: */ + bool pop3_no_flag_updates; + bool pop3_enable_last; + bool pop3_reuse_xuidl; + bool pop3_lock_session; + const char *pop3_client_workarounds; + const char *pop3_logout_format; + const char *pop3_uidl_format; +}; + +void pop3_settings_read(const struct pop3_settings **set_r, + const struct mail_user_settings **user_set_r); + +#endif