changeset 9011:c37f7113b1ee HEAD

doveconf now checks that all settings are ok by calling check functions.
author Timo Sirainen <tss@iki.fi>
date Mon, 09 Feb 2009 18:00:59 -0500
parents 185cc7ad6a30
children 8d4052450e09
files src/auth/auth-settings.c src/config/config-parser.c src/config/settings-get.pl src/imap/imap-settings.c src/lib-settings/settings-parser.c src/lib-settings/settings-parser.h src/lib-storage/mail-storage-settings.c src/lib-storage/mail-storage-settings.h src/login-common/login-settings.c src/master/master-settings.c src/plugins/expire/expire-settings.c src/pop3/pop3-settings.c
diffstat 12 files changed, 341 insertions(+), 123 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/auth/auth-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -12,6 +12,8 @@
 extern struct setting_parser_info auth_setting_parser_info;
 extern struct setting_parser_info auth_root_setting_parser_info;
 
+static bool auth_settings_check(void *_set, const char **error_r);
+
 #undef DEF
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
@@ -239,7 +241,8 @@
 
 	MEMBER(parent_offset) offsetof(struct auth_settings, root),
 	MEMBER(type_offset) offsetof(struct auth_settings, name),
-	MEMBER(struct_size) sizeof(struct auth_settings)
+	MEMBER(struct_size) sizeof(struct auth_settings),
+	MEMBER(check_func) auth_settings_check
 };
 
 #undef DEF
@@ -283,14 +286,18 @@
 	}
 }
 
-static void auth_settings_check(struct auth_settings *set)
+/* <settings checks> */
+static bool auth_settings_check(void *_set ATTR_UNUSED,
+				const char **error_r ATTR_UNUSED)
 {
+#ifndef CONFIG_BINARY
+	struct auth_settings *set = _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;
+		return TRUE;
 
 	sockets = array_get(&set->sockets, &count);
 	for (i = 0; i < count; i++) {
@@ -305,13 +312,17 @@
 				fix_base_path(set, &u[j]->path);
 		}
 	}
+#endif
+	return TRUE;
 }
+/* </settings checks> */
 
 struct auth_settings *auth_settings_read(const char *name)
 {
 	struct setting_parser_context *parser;
 	struct auth_root_settings *set;
 	struct auth_settings *const *auths;
+	const char *error;
 	unsigned int i, count;
 
 	if (settings_pool == NULL)
@@ -330,16 +341,17 @@
 			settings_parser_get_error(parser));
 	}
 
+	if (settings_parser_check(parser, &error) < 0)
+		i_fatal("Invalid settings: %s", error);
+
 	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]);
+			if (strcmp(auths[i]->name, name) == 0)
 				return auths[i];
-			}
 		}
 	}
 	i_fatal("Error reading configuration: No auth section: %s", name);
--- a/src/config/config-parser.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/config/config-parser.c	Mon Feb 09 18:00:59 2009 -0500
@@ -311,6 +311,7 @@
 	ARRAY_DEFINE(pathlen_stack, unsigned int);
 	ARRAY_TYPE(const_string) auth_defaults;
 	const struct setting_parser_info *info;
+	struct config_setting_parser_list *l;
 	unsigned int pathlen = 0;
 	unsigned int counter = 0, auth_counter = 0, cur_counter;
 	const char *errormsg, *name, *type_name;
@@ -573,5 +574,15 @@
 	if (line == NULL && input != NULL)
 		goto prevfile;
 
+	for (l = config_setting_parsers; l->module_name != NULL; l++) {
+		if (l->parser == NULL)
+			continue;
+
+		if (!settings_parser_check(l->parser, &errormsg)) {
+			i_fatal("Error in configuration file %s: %s",
+				path, errormsg);
+		}
+	}
+
 	config_export(dest);
 }
--- a/src/config/settings-get.pl	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/config/settings-get.pl	Mon Feb 09 18:00:59 2009 -0500
@@ -2,9 +2,11 @@
 use strict;
 
 print '#include "lib.h"'."\n";
+print '#include "array.h"'."\n";
 print '#include "settings-parser.h"'."\n";
 print '#include "all-settings.h"'."\n";
 print '#include <stddef.h>'."\n";
+print '#include <unistd.h>'."\n";
 print '#define CONFIG_BINARY'."\n";
 
 my %parsers = {};
@@ -16,6 +18,8 @@
   my $state = 0;
   my $file_contents = "";
   my $externs = "";
+  my $code = "";
+  my %funcs;
   
   while (<$f>) {
     my $write = 0;
@@ -29,6 +33,9 @@
 	$parsers{$2} = 1;
       } elsif (/^extern struct setting_parser_info (.*);/) {
 	$externs .= "extern struct setting_parser_info $1;\n";
+      } elsif (/\/\* <settings checks> \*\//) {
+	$state = 4;
+	$code .= $_;
       }
 
       if (/#define.*DEF/ || /^#undef.*DEF/) {
@@ -38,6 +45,9 @@
     } elsif ($state == 2) {
       $write = 1;
       $state = 0 if (!/\\$/);
+    } elsif ($state == 4) {
+      $code .= $_;
+      $state = 0 if (/\/\* <\/settings checks> \*\//);
     }
     
     if ($state == 1 || $state == 3) {
@@ -68,6 +78,7 @@
   
   print "/* $file */\n";
   print $externs;
+  print $code;
   print $file_contents;
 
   close $f;
--- a/src/imap/imap-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/imap/imap-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -7,6 +7,9 @@
 
 #include <stddef.h>
 #include <stdlib.h>
+#include <unistd.h>
+
+static bool imap_settings_check(void *_set, const char **error_r);
 
 #undef DEF
 #undef DEFLIST
@@ -67,7 +70,8 @@
 
 	MEMBER(parent_offset) (size_t)-1,
 	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct imap_settings)
+	MEMBER(struct_size) sizeof(struct imap_settings),
+	MEMBER(check_func) imap_settings_check
 };
 
 static pool_t settings_pool = NULL;
@@ -80,6 +84,48 @@
 	}
 }
 
+/* <settings checks> */
+static bool imap_settings_check(void *_set, const char **error_r)
+{
+	struct imap_settings *set = _set;
+
+#ifndef CONFIG_BINARY
+	fix_base_path(set, &set->auth_socket_path);
+#endif
+
+	if (*set->mail_plugins != '\0' &&
+	    access(set->mail_plugin_dir, R_OK | X_OK) < 0) {
+		*error_r = t_strdup_printf(
+			"mail_plugin_dir: access(%s) failed: %m",
+			set->mail_plugin_dir);
+		return FALSE;
+	}
+	return TRUE;
+}
+/* </settings checks> */
+
+static void
+parse_expand_vars(struct setting_parser_context *parser, const char *value)
+{
+	const char *const *expanded;
+
+	expanded = t_strsplit(value, " ");
+	settings_parse_set_keys_expandeded(parser, settings_pool, expanded);
+	/* settings from userdb are in the VARS_EXPANDED list. for each
+	   unknown setting in the list assume it's a plugin setting. */
+	for (; *expanded != NULL; expanded++) {
+		if (settings_parse_is_valid_key(parser, *expanded))
+			continue;
+
+		value = getenv(t_str_ucase(*expanded));
+		if (value == NULL)
+			continue;
+
+		settings_parse_line(parser, t_strconcat("plugin/", *expanded,
+							"=", value, NULL));
+	}
+}
+
 void imap_settings_read(const struct imap_settings **set_r,
 			const struct mail_user_settings **user_set_r)
 {
@@ -88,8 +134,7 @@
                 &mail_user_setting_parser_info
 	};
 	struct setting_parser_context *parser;
-	struct imap_settings *set;
-	const char *const *expanded, *value;
+	const char *value, *error;
 	void **sets;
 
 	if (settings_pool == NULL)
@@ -108,27 +153,15 @@
 			settings_parser_get_error(parser));
 	}
 
-	expanded = t_strsplit(getenv("VARS_EXPANDED"), " ");
-	settings_parse_set_keys_expandeded(parser, settings_pool, expanded);
-	/* settings from userdb are in the VARS_EXPANDED list. for each
-	   unknown setting in the list assume it's a plugin setting. */
-	for (; *expanded != NULL; expanded++) {
-		if (settings_parse_is_valid_key(parser, *expanded))
-			continue;
+	value = getenv("VARS_EXPANDED");
+	if (value != NULL)
+		parse_expand_vars(parser, value);
 
-		value = getenv(t_str_ucase(*expanded));
-		if (value == NULL)
-			continue;
-
-		settings_parse_line(parser, t_strconcat("plugin/", *expanded,
-							"=", value, NULL));
-	}
+	if (settings_parser_check(parser, &error) < 0)
+		i_fatal("Invalid settings: %s", error);
 
 	sets = settings_parser_get_list(parser);
-	set = sets[0];
-	fix_base_path(set, &set->auth_socket_path);
-
-	*set_r = set;
+	*set_r = sets[0];
 	*user_set_r = sets[1];
 	settings_parser_deinit(&parser);
 }
--- a/src/lib-settings/settings-parser.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/lib-settings/settings-parser.c	Mon Feb 09 18:00:59 2009 -0500
@@ -642,6 +642,51 @@
 	return ret;
 }
 
+static bool settings_parser_check_info(const struct setting_parser_info *info,
+				       void *set, const char **error_r)
+{
+	const struct setting_define *def;
+	const ARRAY_TYPE(void_array) *val;
+	void *const *children;
+	unsigned int i, count;
+
+	if (info->check_func != NULL) {
+		if (!info->check_func(set, error_r))
+			return FALSE;
+	}
+
+	for (def = info->defines; def->key != NULL; def++) {
+		if (def->type != SET_DEFLIST)
+			continue;
+
+		val = CONST_PTR_OFFSET(set, def->offset);;
+		if (!array_is_created(val))
+			continue;
+
+		children = array_get(val, &count);
+		for (i = 0; i < count; i++) {
+			if (!settings_parser_check_info(def->list_info,
+							children[i], error_r))
+				return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+bool settings_parser_check(struct setting_parser_context *ctx,
+			   const char **error_r)
+{
+	unsigned int i;
+
+	for (i = 0; i < ctx->root_count; i++) {
+		if (!settings_parser_check_info(ctx->roots[i].info,
+						ctx->roots[i].set_struct,
+						error_r))
+		    return FALSE;
+	}
+	return TRUE;
+}
+
 void settings_parse_set_expanded(struct setting_parser_context *ctx,
 				 bool is_expanded)
 {
--- a/src/lib-settings/settings-parser.h	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/lib-settings/settings-parser.h	Mon Feb 09 18:00:59 2009 -0500
@@ -57,6 +57,7 @@
 	size_t parent_offset;
 	size_t type_offset;
 	size_t struct_size;
+	bool (*check_func)(void *set, const char **error_r);
 };
 ARRAY_DEFINE_TYPE(setting_parser_info, struct setting_parser_info);
 
@@ -118,6 +119,9 @@
 int settings_parse_exec(struct setting_parser_context *ctx,
 			const char *bin_path, const char *config_path,
 			const char *service);
+/* Call all check_func()s to see if currently parsed settings are valid. */
+bool settings_parser_check(struct setting_parser_context *ctx,
+			   const char **error_r);
 
 /* While parsing values, specifies if STR_VARS strings are already expanded. */
 void settings_parse_set_expanded(struct setting_parser_context *ctx,
--- a/src/lib-storage/mail-storage-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/lib-storage/mail-storage-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -11,6 +11,9 @@
 
 #include <stddef.h>
 
+static bool mail_storage_settings_check(void *_set, const char **error_r);
+static bool namespace_settings_check(void *_set, const char **error_r);
+
 #undef DEF
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct mail_storage_settings, name), NULL }
@@ -66,7 +69,8 @@
 
 	MEMBER(parent_offset) (size_t)-1,
 	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct mail_storage_settings)
+	MEMBER(struct_size) sizeof(struct mail_storage_settings),
+	MEMBER(check_func) mail_storage_settings_check
 };
 
 #undef DEF
@@ -108,9 +112,10 @@
 	MEMBER(parent) &mail_user_setting_parser_info,
 	MEMBER(dynamic_parsers) NULL,
 
-	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(parent_offset) offsetof(struct mail_namespace_settings, user_set),
 	MEMBER(type_offset) offsetof(struct mail_namespace_settings, type),
-	MEMBER(struct_size) sizeof(struct mail_namespace_settings)
+	MEMBER(struct_size) sizeof(struct mail_namespace_settings),
+	MEMBER(check_func) namespace_settings_check
 };
 
 #undef DEF
@@ -208,3 +213,60 @@
 
 	settings_parser_info_update(pool, parsers[j-1].info->parent, parsers);
 }
+
+/* <settings checks> */
+static bool mail_storage_settings_check(void *_set, const char **error_r)
+{
+	const struct mail_storage_settings *set = _set;
+
+	if (set->mail_nfs_index && !set->mmap_disable) {
+		*error_r = "mail_nfs_index=yes requires mmap_disable=yes";
+		return FALSE;
+	}
+	if (set->mail_nfs_index && set->fsync_disable) {
+		*error_r = "mail_nfs_index=yes requires fsync_disable=no";
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static bool namespace_settings_check(void *_set, const char **error_r)
+{
+	struct mail_namespace_settings *ns = _set;
+	struct mail_namespace_settings *const *namespaces;
+	const char *name;
+	unsigned int i, count;
+
+	name = ns->prefix != NULL ? ns->prefix : "";
+
+	if (ns->separator != NULL &&
+	    ns->separator[0] != '\0' && ns->separator[1] != '\0') {
+		*error_r = t_strdup_printf("Namespace '%s': "
+			"Hierarchy separator must be only one character long",
+			name);
+		return FALSE;
+	}
+
+	if (ns->alias_for != NULL) {
+		namespaces = array_get(&ns->user_set->namespaces, &count);
+		for (i = 0; i < count; i++) {
+			if (strcmp(namespaces[i]->prefix, ns->alias_for) == 0)
+				break;
+		}
+		if (i == count) {
+			*error_r = t_strdup_printf(
+				"Namespace '%s': alias_for points to "
+				"unknown namespace: %s", name, ns->alias_for);
+			return FALSE;
+		}
+		if (namespaces[i]->alias_for != NULL) {
+			*error_r = t_strdup_printf(
+				"Namespace '%s': alias_for chaining isn't "
+				"allowed: %s -> %s", name, ns->alias_for,
+				namespaces[i]->alias_for);
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+/* </settings checks> */
--- a/src/lib-storage/mail-storage-settings.h	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/lib-storage/mail-storage-settings.h	Mon Feb 09 18:00:59 2009 -0500
@@ -35,6 +35,8 @@
 	bool hidden;
 	const char *list;
 	bool subscriptions;
+
+	struct mail_user_settings *user_set;
 };
 
 struct mail_user_settings {
--- a/src/login-common/login-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/login-common/login-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -7,6 +7,8 @@
 #include <stddef.h>
 #include <unistd.h>
 
+static bool login_settings_check(void *_set, const char **error_r);
+
 #undef DEF
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct login_settings, name), NULL }
@@ -86,66 +88,58 @@
 
 	MEMBER(parent_offset) (size_t)-1,
 	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct login_settings)
+	MEMBER(struct_size) sizeof(struct login_settings),
+	MEMBER(check_func) login_settings_check
 };
 
 static pool_t settings_pool = NULL;
 
-static int ssl_settings_check(struct login_settings *set ATTR_UNUSED)
+/* <settings checks> */
+static int ssl_settings_check(void *_set ATTR_UNUSED, const char **error_r)
 {
+	struct login_settings *set = _set;
+
 #ifndef HAVE_SSL
-        i_error("SSL support not compiled in but ssl_disable=no");
+        *error_r = "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");
+		*error_r = "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);
+		*error_r = t_strdup_printf("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");
+		*error_r = "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);
+		*error_r = t_strdup_printf("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);
+	if (*set->ssl_ca_file != '\0' && access(set->ssl_ca_file, R_OK) < 0) {
+		*error_r = t_strdup_printf("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");
+		*error_r = "ssl_verify_client_cert set, but ssl_ca_file not";
 		return FALSE;
 	}
 	return TRUE;
 #endif
 }
 
-static void login_settings_check(struct login_settings *set)
+static bool login_settings_check(void *_set, const char **error_r)
 {
-	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);
-	}
+	struct login_settings *set = _set;
 
 	set->log_format_elements_split =
 		t_strsplit(set->login_log_format_elements, " ");
@@ -154,14 +148,34 @@
 		/* 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");
+	if (set->login_max_connections < 1) {
+		*error_r = "login_max_connections must be at least 1";
+		return FALSE;
+	}
+
+	if (strcmp(set->ssl, "no") == 0) {
+		/* disabled */
+	} else if (strcmp(set->ssl, "yes") == 0) {
+		if (!ssl_settings_check(set, error_r))
+			return FALSE;
+	} else if (strcmp(set->ssl, "required") == 0) {
+		if (!ssl_settings_check(set, error_r))
+			return FALSE;
+		set->disable_plaintext_auth = TRUE;
+	} else {
+		*error_r = t_strdup_printf("Unknown ssl setting value: %s",
+					   set->ssl);
+		return FALSE;
+	}
+	return TRUE;
 }
+/* </settings checks> */
 
 struct login_settings *login_settings_read(void)
 {
 	struct setting_parser_context *parser;
         struct login_settings *set;
+	const char *error;
 
 	if (settings_pool == NULL)
 		settings_pool = pool_alloconly_create("settings pool", 512);
@@ -177,8 +191,10 @@
 			settings_parser_get_error(parser));
 	}
 
+	if (settings_parser_check(parser, &error) < 0)
+		i_fatal("Invalid settings: %s", error);
+
 	set = settings_parser_get(parser);
 	settings_parser_deinit(&parser);
-	login_settings_check(set);
 	return set;
 }
--- a/src/master/master-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/master/master-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -699,16 +699,8 @@
 		return FALSE;
 	}
 
-	if (strcmp(set->ssl, "no") != 0 &&
-	    strcmp(set->ssl, "yes") != 0 &&
-	    strcmp(set->ssl, "required") != 0) {
-		i_error("ssl setting: Invalid value: %s", set->ssl);
-		return FALSE;
-	}
 #ifndef HAVE_SSL
 	if (strcmp(set->ssl, "no") != 0) {
-
-
 		i_error("SSL support not compiled in but ssl=%s", set->ssl);
 		return FALSE;
 	}
@@ -726,6 +718,10 @@
 		i_error("max_mail_processes must be at least 1");
 		return FALSE;
 	}
+	if (strcmp(set->login_dir, set->base_dir) == 0) {
+		i_error("login_dir can't be the same as base_dir");
+		return FALSE;
+	}
 
 	if (set->last_valid_uid != 0 &&
 	    set->first_valid_uid > set->last_valid_uid) {
@@ -755,30 +751,13 @@
 		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;
-	}
-	if (set->mail_nfs_index && set->fsync_disable) {
-		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) {
-		i_error("mail_plugin_dir: Can't access directory: %s: %m",
-			set->mail_plugin_dir);
-		return FALSE;
-	}
-#else
+#ifndef HAVE_MODULES
 	if (*set->mail_plugins != '\0') {
 		i_error("mail_plugins: Plugin support wasn't built into Dovecot, "
 			"can't load plugins: %s", set->mail_plugins);
 		return FALSE;
 	}
 #endif
-#endif
 	return TRUE;
 }
 
--- a/src/plugins/expire/expire-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/plugins/expire/expire-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -46,6 +46,28 @@
 	}
 }
 
+static void
+parse_expand_vars(struct setting_parser_context *parser, const char *value)
+{
+	const char *const *expanded;
+
+	expanded = t_strsplit(value, " ");
+	settings_parse_set_keys_expandeded(parser, settings_pool, expanded);
+	/* settings from userdb are in the VARS_EXPANDED list. for each
+	   unknown setting in the list assume it's a plugin setting. */
+	for (; *expanded != NULL; expanded++) {
+		if (settings_parse_is_valid_key(parser, *expanded))
+			continue;
+
+		value = getenv(t_str_ucase(*expanded));
+		if (value == NULL)
+			continue;
+
+		settings_parse_line(parser, t_strconcat("plugin/", *expanded,
+							"=", value, NULL));
+	}
+}
+
 void expire_settings_read(const struct expire_settings **set_r,
 			  const struct mail_user_settings **user_set_r)
 {
@@ -55,7 +77,7 @@
 	};
 	struct setting_parser_context *parser;
 	struct expire_settings *set;
-	const char *const *expanded, *value;
+	const char *value;
 	void **sets;
 
 	if (settings_pool == NULL)
@@ -74,21 +96,9 @@
 			settings_parser_get_error(parser));
 	}
 
-	expanded = t_strsplit(getenv("VARS_EXPANDED"), " ");
-	settings_parse_set_keys_expandeded(parser, settings_pool, expanded);
-	/* settings from userdb are in the VARS_EXPANDED list. for each
-	   unknown setting in the list assume it's a plugin setting. */
-	for (; *expanded != NULL; expanded++) {
-		if (settings_parse_is_valid_key(parser, *expanded))
-			continue;
-
-		value = getenv(t_str_ucase(*expanded));
-		if (value == NULL)
-			continue;
-
-		settings_parse_line(parser, t_strconcat("plugin/", *expanded,
-							"=", value, NULL));
-	}
+	value = getenv("VARS_EXPANDED");
+	if (value != NULL)
+		parse_expand_vars(parser, value);
 
 	sets = settings_parser_get_list(parser);
 	set = sets[0];
--- a/src/pop3/pop3-settings.c	Tue Feb 03 12:05:35 2009 -0500
+++ b/src/pop3/pop3-settings.c	Mon Feb 09 18:00:59 2009 -0500
@@ -7,6 +7,9 @@
 
 #include <stddef.h>
 #include <stdlib.h>
+#include <unistd.h>
+
+static bool pop3_settings_check(void *_set, const char **error_r);
 
 #undef DEF
 #undef DEFLIST
@@ -66,7 +69,8 @@
 
 	MEMBER(parent_offset) (size_t)-1,
 	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct pop3_settings)
+	MEMBER(struct_size) sizeof(struct pop3_settings),
+	MEMBER(check_func) pop3_settings_check
 };
 
 static pool_t settings_pool = NULL;
@@ -79,6 +83,48 @@
 	}
 }
 
+/* <settings checks> */
+static bool pop3_settings_check(void *_set, const char **error_r)
+{
+	struct pop3_settings *set = _set;
+
+#ifndef CONFIG_BINARY
+	fix_base_path(set, &set->auth_socket_path);
+#endif
+
+	if (*set->mail_plugins != '\0' &&
+	    access(set->mail_plugin_dir, R_OK | X_OK) < 0) {
+		*error_r = t_strdup_printf(
+			"mail_plugin_dir: access(%s) failed: %m",
+			set->mail_plugin_dir);
+		return FALSE;
+	}
+	return TRUE;
+}
+/* </settings checks> */
+
+static void
+parse_expand_vars(struct setting_parser_context *parser, const char *value)
+{
+	const char *const *expanded;
+
+	expanded = t_strsplit(value, " ");
+	settings_parse_set_keys_expandeded(parser, settings_pool, expanded);
+	/* settings from userdb are in the VARS_EXPANDED list. for each
+	   unknown setting in the list assume it's a plugin setting. */
+	for (; *expanded != NULL; expanded++) {
+		if (settings_parse_is_valid_key(parser, *expanded))
+			continue;
+
+		value = getenv(t_str_ucase(*expanded));
+		if (value == NULL)
+			continue;
+
+		settings_parse_line(parser, t_strconcat("plugin/", *expanded,
+							"=", value, NULL));
+	}
+}
+
 void pop3_settings_read(const struct pop3_settings **set_r,
 			const struct mail_user_settings **user_set_r)
 {
@@ -87,8 +133,7 @@
                 &mail_user_setting_parser_info
 	};
 	struct setting_parser_context *parser;
-	struct pop3_settings *set;
-	const char *const *expanded, *value;
+	const char *value, *error;
 	void **sets;
 
 	if (settings_pool == NULL)
@@ -107,27 +152,15 @@
 			settings_parser_get_error(parser));
 	}
 
-	expanded = t_strsplit(getenv("VARS_EXPANDED"), " ");
-	settings_parse_set_keys_expandeded(parser, settings_pool, expanded);
-	/* settings from userdb are in the VARS_EXPANDED list. for each
-	   unknown setting in the list assume it's a plugin setting. */
-	for (; *expanded != NULL; expanded++) {
-		if (settings_parse_is_valid_key(parser, *expanded))
-			continue;
+	value = getenv("VARS_EXPANDED");
+	if (value != NULL)
+		parse_expand_vars(parser, value);
 
-		value = getenv(t_str_ucase(*expanded));
-		if (value == NULL)
-			continue;
-
-		settings_parse_line(parser, t_strconcat("plugin/", *expanded,
-							"=", value, NULL));
-	}
+	if (settings_parser_check(parser, &error) < 0)
+		i_fatal("Invalid settings: %s", error);
 
 	sets = settings_parser_get_list(parser);
-	set = sets[0];
-	fix_base_path(set, &set->auth_socket_path);
-
-	*set_r = set;
+	*set_r = sets[0];
 	*user_set_r = sets[1];
 	settings_parser_deinit(&parser);
 }