changeset 4197:c3ded5b815aa HEAD

If we have plugins set and imap_capability unset, figure out the IMAP capabilities automatically by running imap binary at startup. The generated capability list isn't updated until Dovecot is restarted completely, so if you add or remove IMAP plugins you should restart.
author Timo Sirainen <tss@iki.fi>
date Fri, 14 Apr 2006 21:20:54 +0300
parents 6ac0d63b297f
children def18c75ced2
files src/imap-login/client.c src/imap/main.c src/login-common/common.h src/login-common/main.c src/master/login-process.c src/master/mail-process.c src/master/mail-process.h src/master/main.c src/master/master-settings.c src/master/master-settings.h src/pop3-login/client-authenticate.c
diffstat 11 files changed, 167 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/imap-login/client.c	Fri Apr 14 21:20:54 2006 +0300
@@ -44,6 +44,7 @@
 #endif
 
 const char *login_protocol = "IMAP";
+const char *capability_string = CAPABILITY_STRING;
 
 static struct hash_table *clients;
 static struct timeout *to_idle;
@@ -97,7 +98,7 @@
 	const char *auths;
 
 	auths = client_authenticate_get_capabilities(client->common.secured);
-	return t_strconcat(CAPABILITY_STRING,
+	return t_strconcat(capability_string,
 			   (ssl_initialized && !client->common.tls) ?
 			   " STARTTLS" : "",
 			   disable_plaintext_auth && !client->common.secured ?
--- a/src/imap/main.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/imap/main.c	Fri Apr 14 21:20:54 2006 +0300
@@ -186,6 +186,18 @@
 					  getenv("MODULE_LIST"), TRUE);
 	}
 
+	if (getenv("DUMP_CAPABILITY") != NULL) {
+		printf("%s\n", str_c(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) :
--- a/src/login-common/common.h	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/login-common/common.h	Fri Apr 14 21:20:54 2006 +0300
@@ -15,6 +15,7 @@
 extern bool verbose_proctitle, verbose_ssl, verbose_auth;
 extern const char *greeting, *log_format;
 extern const char *const *log_format_elements;
+extern const char *capability_string;
 extern unsigned int max_logging_users;
 extern unsigned int login_process_uid;
 extern struct auth_client *auth_client;
--- a/src/login-common/main.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/login-common/main.c	Fri Apr 14 21:20:54 2006 +0300
@@ -207,6 +207,11 @@
 	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/master/login-process.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/master/login-process.c	Fri Apr 14 21:20:54 2006 +0300
@@ -88,9 +88,10 @@
 
 		t_push();
 		master_reply.success =
-			create_mail_process(group, request->fd,
-					    &request->local_ip,
-					    &request->remote_ip, user, args);
+			create_mail_process(group->process_type, group->set,
+					    request->fd, &request->local_ip,
+					    &request->remote_ip, user, args,
+					    FALSE);
 		t_pop();
 	}
 
@@ -455,6 +456,13 @@
 	env_put(t_strconcat("LOG_FORMAT=", set->login_log_format, NULL));
 	if (set->login_greeting_capability)
 		env_put("GREETING_CAPABILITY=1");
+
+	if (group->process_type == PROCESS_TYPE_IMAP) {
+		env_put(t_strconcat("CAPABILITY_STRING=",
+				    *set->imap_capability != '\0' ?
+				    set->imap_capability :
+				    set->imap_generated_capability, NULL));
+	}
 }
 
 static pid_t create_login_process(struct login_group *group)
--- a/src/master/mail-process.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/master/mail-process.c	Fri Apr 14 21:20:54 2006 +0300
@@ -209,8 +209,10 @@
 				set->mail_max_keyword_length));
 	env_put(t_strdup_printf("IMAP_MAX_LINE_LENGTH=%u",
 				set->imap_max_line_length));
-	env_put(t_strconcat("IMAP_CAPABILITY=",
-			    set->imap_capability, NULL));
+	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("POP3_UIDL_FORMAT=",
@@ -372,12 +374,12 @@
 		"If you're sure this check was wrong, set nfs_check=no.", path);
 }
 
-bool create_mail_process(struct login_group *group, int socket,
-			 const struct ip_addr *local_ip,
+bool create_mail_process(enum process_type process_type, struct settings *set,
+			 int socket, const struct ip_addr *local_ip,
 			 const struct ip_addr *remote_ip,
-			 const char *user, const char *const *args)
+			 const char *user, const char *const *args,
+			 bool dump_capability)
 {
-	struct settings *set = group->set;
 	const struct var_expand_table *var_expand_table;
 	const char *p, *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
 	const char *system_user;
@@ -448,7 +450,13 @@
 		return FALSE;
 	}
 
-	log_fd = log_create_pipe(&log, 10);
+	if (!dump_capability)
+		log_fd = log_create_pipe(&log, 10);
+	else {
+		log = NULL;
+		log_fd = dup(STDERR_FILENO);
+		fd_close_on_exec(log_fd, TRUE);
+	}
 
 	pid = fork();
 	if (pid < 0) {
@@ -458,7 +466,7 @@
 	}
 
 	var_expand_table =
-		get_var_expand_table(process_names[group->process_type],
+		get_var_expand_table(process_names[process_type],
 				     user, home_given ? home_dir : NULL,
 				     net_ip2addr(local_ip),
 				     net_ip2addr(remote_ip),
@@ -468,10 +476,12 @@
 	if (pid != 0) {
 		/* master */
 		var_expand(str, set->mail_log_prefix, var_expand_table);
-		log_set_prefix(log, str_c(str));
 
 		mail_process_count++;
-		PID_ADD_PROCESS_TYPE(pid, group->process_type);
+		if (!dump_capability) {
+			log_set_prefix(log, str_c(str));
+			PID_ADD_PROCESS_TYPE(pid, process_type);
+		}
 		(void)close(log_fd);
 		return TRUE;
 	}
@@ -483,14 +493,16 @@
 	}
 #endif
 
-	str_append(str, "master-");
-	var_expand(str, set->mail_log_prefix, var_expand_table);
-	log_set_prefix(log, str_c(str));
+	if (!dump_capability) {
+		str_append(str, "master-");
+		var_expand(str, set->mail_log_prefix, var_expand_table);
+		log_set_prefix(log, str_c(str));
+	}
 
 	child_process_init_env();
 
 	/* move the client socket into stdin and stdout fds, log to stderr */
-	if (dup2(socket, 0) < 0)
+	if (dup2(dump_capability ? null_fd : socket, 0) < 0)
 		i_fatal("dup2(stdin) failed: %m");
 	if (dup2(socket, 1) < 0)
 		i_fatal("dup2(stdout) failed: %m");
@@ -508,6 +520,9 @@
 
 	restrict_process_size(set->mail_process_size, (unsigned int)-1);
 
+	if (dump_capability)
+		env_put("DUMP_CAPABILITY=1");
+
 	if (*home_dir == '\0')
 		ret = -1;
 	else {
@@ -568,7 +583,7 @@
 		}
 	}
 
-	if (set->nfs_check && !set->mmap_disable) {
+	if (set->nfs_check && !set->mmap_disable && !dump_capability) {
 		/* do this only once */
 		nfs_warn_if_found(getenv("MAIL"), home_dir);
 		set->nfs_check = FALSE;
--- a/src/master/mail-process.h	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/master/mail-process.h	Fri Apr 14 21:20:54 2006 +0300
@@ -6,10 +6,11 @@
 
 void mail_process_exec(const char *protocol, const char *section);
 
-bool create_mail_process(struct login_group *group, int socket,
-			 const struct ip_addr *local_ip,
+bool create_mail_process(enum process_type process_type, struct settings *set,
+			 int socket, const struct ip_addr *local_ip,
 			 const struct ip_addr *remote_ip,
-			 const char *user, const char *const *args);
+			 const char *user, const char *const *args,
+			 bool dump_capability);
 
 void mail_process_destroyed(pid_t pid);
 
--- a/src/master/main.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/master/main.c	Fri Apr 14 21:20:54 2006 +0300
@@ -77,7 +77,8 @@
 	if (env_tz != NULL)
 		env_put(t_strconcat("TZ=", env_tz, NULL));
 
-	if (!syslog_facility_find(settings_root->defaults->syslog_facility,
+	if (settings_root == NULL ||
+	    !syslog_facility_find(settings_root->defaults->syslog_facility,
 				  &facility))
 		facility = LOG_MAIL;
 	env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility));
@@ -545,6 +546,7 @@
         lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
         lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
         lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL);
+        lib_signals_set_handler(SIGALRM, FALSE, NULL, NULL);
         lib_signals_set_handler(SIGHUP, TRUE, sig_reload_settings, NULL);
         lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
 
--- a/src/master/master-settings.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/master/master-settings.c	Fri Apr 14 21:20:54 2006 +0300
@@ -4,10 +4,12 @@
 #include "array.h"
 #include "str.h"
 #include "istream.h"
+#include "fd-close-on-exec.h"
 #include "safe-mkdir.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
 #include "syslog-util.h"
+#include "mail-process.h"
 #include "settings.h"
 
 #include <stdio.h>
@@ -16,6 +18,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 #include <pwd.h>
 
 enum settings_type {
@@ -350,7 +353,7 @@
 
 	/* imap */
 	MEMBER(imap_max_line_length) 65536,
-	MEMBER(imap_capability) NULL,
+	MEMBER(imap_capability) "",
 	MEMBER(imap_client_workarounds) "outlook-idle",
 
 	/* pop3 */
@@ -572,6 +575,90 @@
 	(void)closedir(dirp);
 }
 
+#ifdef HAVE_MODULES
+static bool get_imap_capability(struct settings *set)
+{
+	/* FIXME: pretty ugly code just for getting the capability
+	   automatically */
+	static const char *generated_capability = NULL;
+	static const char *args[] = {
+		"uid=65534",
+		"gid=65534",
+		NULL
+	};
+	struct ip_addr ip;
+	char buf[4096];
+	int fd[2], status;
+	ssize_t ret;
+	unsigned int pos;
+
+	if (generated_capability != NULL) {
+		/* Reloading configuration. Don't try to execute the imap
+		   process again. Too risky and the wait() call below will
+		   break it anyway. Just use the previous capability list we
+		   already had generated. */
+		set->imap_generated_capability =
+			p_strdup(settings_pool, generated_capability);
+		return TRUE;
+	}
+
+	memset(&ip, 0, sizeof(ip));
+	if (pipe(fd) < 0) {
+		i_error("pipe() failed: %m");
+		return FALSE;
+	}
+	fd_close_on_exec(fd[0], TRUE);
+	fd_close_on_exec(fd[1], TRUE);
+	if (!create_mail_process(MAIL_PROTOCOL_IMAP, set, fd[1],
+				 &ip, &ip, "dump-capability", args, TRUE)) {
+		(void)close(fd[0]);
+		(void)close(fd[1]);
+		return FALSE;
+	}
+	(void)close(fd[1]);
+
+	alarm(5);
+	if (wait(&status) == -1)
+		i_fatal("imap dump-capability process got stuck");
+	alarm(0);
+
+	if (status != 0) {
+		(void)close(fd[0]);
+		if (WIFSIGNALED(status)) {
+			i_error("imap dump-capability process "
+				"killed with signal %d", WTERMSIG(status));
+		} else {
+			i_error("imap dump-capability process returned %d",
+				status);
+		}
+		return FALSE;
+	}
+
+	pos = 0;
+	while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0)
+		pos += ret;
+
+	if (ret < 0) {
+		i_error("read(imap dump-capability process) failed: %m");
+		(void)close(fd[0]);
+		return FALSE;
+	}
+	(void)close(fd[0]);
+
+	if (pos == 0 || buf[pos-1] != '\n') {
+		i_error("imap dump-capability: Couldn't read capability "
+			"(got %u bytes)", pos);
+		return FALSE;
+	}
+	buf[pos-1] = '\0';
+
+	generated_capability = i_strdup(buf);
+	set->imap_generated_capability =
+		p_strdup(settings_pool, generated_capability);
+	return TRUE;
+}
+#endif
+
 static bool settings_verify(struct settings *set)
 {
 	const char *dir;
@@ -594,6 +681,11 @@
 			set->mail_plugin_dir);
 		return FALSE;
 	}
+	if (*set->mail_plugins != '\0' && set->protocol == MAIL_PROTOCOL_IMAP &&
+	    *set->imap_capability == '\0') {
+		if (!get_imap_capability(set))
+			return FALSE;
+	}
 #else
 	if (*set->mail_plugins != '\0') {
 		i_error("Module support wasn't built into Dovecot, "
--- a/src/master/master-settings.h	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/master/master-settings.h	Fri Apr 14 21:20:54 2006 +0300
@@ -118,6 +118,8 @@
 	/* .. */
 	uid_t login_uid;
 
+	const char *imap_generated_capability;
+
 	int listen_fd, ssl_listen_fd;
 	struct ip_addr listen_ip, ssl_listen_ip;
 	unsigned int listen_port, ssl_listen_port;
--- a/src/pop3-login/client-authenticate.c	Fri Apr 14 15:30:21 2006 +0300
+++ b/src/pop3-login/client-authenticate.c	Fri Apr 14 21:20:54 2006 +0300
@@ -19,6 +19,8 @@
 
 #include <stdlib.h>
 
+const char *capability_string = POP3_CAPABILITY_REPLY;
+
 bool cmd_capa(struct pop3_client *client, const char *args __attr_unused__)
 {
 	const struct auth_mech_desc *mech;
@@ -26,7 +28,8 @@
 	string_t *str;
 
 	str = t_str_new(128);
-	str_append(str, "+OK\r\n" POP3_CAPABILITY_REPLY);
+	str_append(str, "+OK\r\n");
+	str_append(str, capability_string);
 
 	if (ssl_initialized && !client->common.tls)
 		str_append(str, "STLS\r\n");