changeset 7917:ca2ff54ee9b4 HEAD

Added support for IMAP ID extension.
author Timo Sirainen <tss@iki.fi>
date Sat, 21 Jun 2008 09:22:44 +0300
parents 8f3115354d14
children b1784b48821d
files README configure.in dovecot-example.conf src/imap-login/client.c src/imap-login/client.h src/imap/Makefile.am src/imap/client.h src/imap/cmd-id.c src/imap/commands.c src/imap/commands.h src/imap/common.h src/imap/main.c src/lib-imap/Makefile.am src/lib-imap/imap-id.c src/lib-imap/imap-id.h src/master/login-process.c src/master/mail-process.c src/master/master-settings-defs.c src/master/master-settings.c src/master/master-settings.h
diffstat 20 files changed, 273 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/README	Sat Jun 21 09:21:51 2008 +0300
+++ b/README	Sat Jun 21 09:22:44 2008 +0300
@@ -31,6 +31,7 @@
  2177       - IMAP4 IDLE command
  2221       - IMAP4 Login Referrals
  2342       - IMAP4 Namespace
+ 2971       - IMAP4 ID extension
  3348       - IMAP4 Child Mailbox Extension
  3502       - IMAP4 MULTIAPPEND Extension
  3691       - IMAP4 UNSELECT command
--- a/configure.in	Sat Jun 21 09:21:51 2008 +0300
+++ b/configure.in	Sat Jun 21 09:22:44 2008 +0300
@@ -19,7 +19,7 @@
   sys/uio.h sys/sysmacros.h sys/resource.h sys/select.h libgen.h \
   sys/quota.h sys/fs/ufs_quota.h ufs/ufs/quota.h jfs/quota.h sys/fs/quota_common.h \
   mntent.h sys/mnttab.h sys/event.h sys/time.h sys/mkdev.h linux/dqblk_xfs.h \
-  xfs/xqm.h sasl.h sasl/sasl.h execinfo.h ucontext.h malloc_np.h)
+  xfs/xqm.h sasl.h sasl/sasl.h execinfo.h ucontext.h malloc_np.h sys/utsname.h)
 
 AC_ARG_ENABLE(ipv6,
 [  --enable-ipv6           Enable IPv6 support (auto)],
@@ -425,9 +425,11 @@
 ])
 AC_SUBST(LIBCAP)
 
+AC_DEFINE(PACKAGE_WEBPAGE, "http://www.dovecot.org/", Support URL)
+
 dnl * after -lsocket and -lnsl tests, inet_aton() may be in them
 AC_CHECK_FUNCS(fcntl flock lockf inet_aton sigaction getpagesize madvise \
-               strcasecmp stricmp vsyslog writev pread \
+               strcasecmp stricmp vsyslog writev pread uname \
 	       setrlimit setproctitle seteuid setreuid setegid setresgid \
 	       strtoull strtoll strtouq strtoq \
 	       setpriority quotactl getmntent kqueue kevent backtrace_symbols \
@@ -2183,7 +2185,7 @@
 dnl ** capabilities
 dnl **
 
-capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS UIDPLUS LIST-EXTENDED I18NLEVEL=1 ENABLE CONDSTORE QRESYNC ESEARCH SEARCHRES WITHIN CONTEXT=SEARCH"
+capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS UIDPLUS LIST-EXTENDED I18NLEVEL=1 ENABLE CONDSTORE QRESYNC ESEARCH SEARCHRES WITHIN CONTEXT=SEARCH ID"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
 
 CFLAGS="$CFLAGS $EXTRA_CFLAGS"
--- a/dovecot-example.conf	Sat Jun 21 09:21:51 2008 +0300
+++ b/dovecot-example.conf	Sat Jun 21 09:22:44 2008 +0300
@@ -544,6 +544,14 @@
   # Override the IMAP CAPABILITY response.
   #imap_capability = 
 
+  # ID field names and values to send to clients. Using * as the value makes
+  # Dovecot use the default value. The following fields have default values
+  # currently: name, version, os, os-version, support-url, support-email.
+  #imap_id_send = 
+
+  # ID fields sent by client to log. * means everything.
+  #imap_id_log =
+
   # Workarounds for various client bugs:
   #   delay-newmail:
   #     Send EXISTS/RECENT new mail notifications only when replying to NOOP
--- a/src/imap-login/client.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap-login/client.c	Sat Jun 21 09:22:44 2008 +0300
@@ -10,12 +10,15 @@
 #include "str.h"
 #include "strescape.h"
 #include "imap-parser.h"
+#include "imap-id.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
 #include "imap-proxy.h"
 
+#include <stdlib.h>
+
 /* max. size of one parameter in line, or max reply length in SASL
    authentication */
 #define MAX_INBUF_SIZE 4096
@@ -197,6 +200,27 @@
 	return 1;
 }
 
+static int cmd_id(struct imap_client *client, const struct imap_arg *args)
+{
+	const char *env, *value;
+
+	if (!client->id_logged) {
+		client->id_logged = TRUE;
+		env = getenv("IMAP_ID_LOG");
+		value = imap_id_args_get_log_reply(args, env);
+		if (value != NULL) {
+			client_syslog(&client->common,
+				      t_strdup_printf("ID sent: %s", value));
+		}
+	}
+
+	env = getenv("IMAP_ID_SEND");
+	client_send_line(client, t_strdup_printf("* ID %s",
+						 imap_id_reply_generate(env)));
+	client_send_tagline(client, "OK ID completed.");
+	return 1;
+}
+
 static int cmd_noop(struct imap_client *client)
 {
 	client_send_tagline(client, "OK NOOP completed.");
@@ -228,6 +252,8 @@
 		return cmd_capability(client);
 	if (strcmp(cmd, "STARTTLS") == 0)
 		return cmd_starttls(client);
+	if (strcmp(cmd, "ID") == 0)
+		return cmd_id(client, args);
 	if (strcmp(cmd, "NOOP") == 0)
 		return cmd_noop(client);
 	if (strcmp(cmd, "LOGOUT") == 0)
--- a/src/imap-login/client.h	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap-login/client.h	Sat Jun 21 09:22:44 2008 +0300
@@ -31,6 +31,7 @@
 	unsigned int input_blocked:1;
 	unsigned int destroyed:1;
 	unsigned int greeting_sent:1;
+	unsigned int id_logged:1;
 };
 
 void client_destroy(struct imap_client *client, const char *reason);
--- a/src/imap/Makefile.am	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap/Makefile.am	Sat Jun 21 09:22:44 2008 +0300
@@ -49,6 +49,7 @@
 	cmd-examine.c \
 	cmd-expunge.c \
 	cmd-fetch.c \
+	cmd-id.c \
 	cmd-idle.c \
 	cmd-list.c \
 	cmd-logout.c \
--- a/src/imap/client.h	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap/client.h	Sat Jun 21 09:22:44 2008 +0300
@@ -115,6 +115,7 @@
 	unsigned int destroyed:1;
 	unsigned int handling_input:1;
 	unsigned int syncing:1;
+	unsigned int id_logged:1;
 	unsigned int input_skip_line:1; /* skip all the data until we've
 					   found a new line */
 	unsigned int modseqs_sent_since_sync:1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/cmd-id.c	Sat Jun 21 09:22:44 2008 +0300
@@ -0,0 +1,26 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "imap-id.h"
+
+bool cmd_id(struct client_command_context *cmd)
+{
+	const struct imap_arg *args;
+	const char *value;
+
+	if (!client_read_args(cmd, 0, 0, &args))
+		return FALSE;
+
+	if (!cmd->client->id_logged) {
+		cmd->client->id_logged = TRUE;
+		value = imap_id_args_get_log_reply(args, 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)));
+	client_send_tagline(cmd, "OK ID completed.");
+	return TRUE;
+}
+
--- a/src/imap/commands.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap/commands.c	Sat Jun 21 09:22:44 2008 +0300
@@ -43,6 +43,7 @@
 const struct command imap_ext_commands[] = {
 	{ "CANCELUPDATE",	cmd_cancelupdate,0 },
 	{ "ENABLE",		cmd_enable,      0 },
+	{ "ID",			cmd_id,          0 },
 	{ "IDLE",		cmd_idle,        COMMAND_FLAG_BREAKS_SEQS },
 	{ "NAMESPACE",		cmd_namespace,   0 },
 	{ "SORT",		cmd_sort,        COMMAND_FLAG_USES_SEQS },
--- a/src/imap/commands.h	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap/commands.h	Sat Jun 21 09:22:44 2008 +0300
@@ -83,6 +83,7 @@
 /* IMAP extensions: */
 bool cmd_cancelupdate(struct client_command_context *cmd);
 bool cmd_enable(struct client_command_context *cmd);
+bool cmd_id(struct client_command_context *cmd);
 bool cmd_idle(struct client_command_context *cmd);
 bool cmd_namespace(struct client_command_context *cmd);
 bool cmd_sort(struct client_command_context *cmd);
--- a/src/imap/common.h	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap/common.h	Sat Jun 21 09:22:44 2008 +0300
@@ -31,6 +31,7 @@
 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;
 
--- a/src/imap/main.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/imap/main.c	Sat Jun 21 09:22:44 2008 +0300
@@ -41,6 +41,7 @@
 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;
@@ -227,6 +228,9 @@
 	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();
 
 	namespace_pool = pool_alloconly_create("namespaces", 1024);
--- a/src/lib-imap/Makefile.am	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/lib-imap/Makefile.am	Sat Jun 21 09:22:44 2008 +0300
@@ -10,6 +10,7 @@
 	imap-bodystructure.c \
 	imap-date.c \
 	imap-envelope.c \
+	imap-id.c \
 	imap-match.c \
 	imap-parser.c \
 	imap-quote.c \
@@ -21,6 +22,7 @@
 	imap-bodystructure.h \
 	imap-date.h \
 	imap-envelope.h \
+	imap-id.h \
 	imap-match.h \
 	imap-parser.h \
 	imap-quote.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap/imap-id.c	Sat Jun 21 09:22:44 2008 +0300
@@ -0,0 +1,174 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "istream.h"
+#include "imap-parser.h"
+#include "imap-quote.h"
+#include "imap-id.h"
+
+#ifdef HAVE_SYS_UTSNAME_H
+#  include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_UNAME
+static struct utsname utsname_result;
+static bool utsname_set = FALSE;
+
+static const char *imap_id_get_uname(const char *key)
+{
+	if (!utsname_set) {
+		utsname_set = TRUE;
+		if (uname(&utsname_result) < 0) {
+			i_error("uname() failed: %m");
+			memset(&utsname_result, 0, sizeof(utsname_result));
+		}
+	}
+
+	if (strcmp(key, "os") == 0)
+		return utsname_result.sysname;
+	if (strcmp(key, "os-version") == 0)
+		return utsname_result.release;
+	return NULL;
+}
+#endif
+
+static const char *imap_id_get_default(const char *key)
+{
+	if (strcasecmp(key, "name") == 0)
+		return PACKAGE_NAME;
+	if (strcasecmp(key, "version") == 0)
+		return PACKAGE_VERSION;
+	if (strcasecmp(key, "support-url") == 0)
+		return PACKAGE_WEBPAGE;
+	if (strcasecmp(key, "support-email") == 0)
+		return PACKAGE_BUGREPORT;
+#ifdef HAVE_UNAME
+	return imap_id_get_uname(key);
+#endif
+}
+
+static const char *
+imap_id_reply_generate_from_imap_args(const struct imap_arg *args)
+{
+	string_t *str;
+	const char *key, *value;
+
+	if (args->type == IMAP_ARG_EOL)
+		return "NIL";
+
+	str = t_str_new(256);
+	str_append_c(str, '(');
+	for (; args->type != IMAP_ARG_EOL; args++) {
+		if (!IMAP_ARG_TYPE_IS_STRING(args->type)) {
+			/* broken input */
+			if (args[1].type == IMAP_ARG_EOL)
+				break;
+			args++;
+		} else {
+			/* key */
+			if (str_len(str) > 1)
+				str_append_c(str, ' ');
+			key = IMAP_ARG_STR_NONULL(args);
+			imap_dquote_append(str, key);
+			str_append_c(str, ' ');
+			/* value */
+			if (args[1].type == IMAP_ARG_EOL) {
+				str_append(str, "NIL");
+				break;
+			}
+			args++;
+			if (!IMAP_ARG_TYPE_IS_STRING(args->type))
+				value = NULL;
+			else {
+				value = IMAP_ARG_STR_NONULL(args);
+				if (strcmp(value, "*") == 0)
+					value = imap_id_get_default(key);
+			}
+
+			if (value == NULL)
+				str_append(str, "NIL");
+			else
+				imap_quote_append_string(str, value, FALSE);
+		}
+	}
+	if (str_len(str) == 1) {
+		/* broken */
+		return "NIL";
+	}
+	str_append_c(str, ')');
+	return str_c(str);
+}
+
+const char *imap_id_reply_generate(const char *settings)
+{
+	struct istream *input;
+	struct imap_parser *parser;
+	const struct imap_arg *args;
+	const char *ret;
+
+	if (settings == NULL)
+		return "NIL";
+
+	input = i_stream_create_from_data(settings, strlen(settings));
+	(void)i_stream_read(input);
+
+	parser = imap_parser_create(input, NULL, (size_t)-1);
+	if (imap_parser_finish_line(parser, 0, 0, &args) <= 0)
+		ret = "NIL";
+	else
+		ret = imap_id_reply_generate_from_imap_args(args);
+
+	imap_parser_destroy(&parser);
+	i_stream_destroy(&input);
+	return ret;
+}
+
+const char *imap_id_args_get_log_reply(const struct imap_arg *args,
+				       const char *settings)
+{
+	const char *const *keys, *key, *value;
+	string_t *reply;
+	bool log_all;
+
+	if (settings == NULL || *settings == '\0' ||
+	    args->type != IMAP_ARG_LIST)
+		return NULL;
+
+	args = IMAP_ARG_LIST_ARGS(args);
+
+	log_all = strcmp(settings, "*") == 0;
+	reply = t_str_new(256);
+	keys = t_strsplit_spaces(settings, " ");
+	while (args->type != IMAP_ARG_EOL && args[1].type != IMAP_ARG_EOL) {
+		if (args->type != IMAP_ARG_STRING) {
+			/* broken input */
+			args += 2;
+			continue;
+		}
+		key = IMAP_ARG_STR_NONULL(args);
+		args++;
+		if (strlen(key) > 30) {
+			/* broken: ID spec requires fields to be max. 30
+			   octets */
+			args++;
+			continue;
+		}
+
+		if (log_all || str_array_icase_find(keys, key)) {
+			if (IMAP_ARG_TYPE_IS_STRING(args->type))
+				value = IMAP_ARG_STR_NONULL(args);
+			else if (args->type == IMAP_ARG_NIL)
+				value = "NIL";
+			else
+				value = "";
+			if (str_len(reply) > 0)
+				str_append(reply, ", ");
+			str_append(reply, str_sanitize(key, 30));
+			str_append_c(reply, '=');
+			str_append(reply, str_sanitize(value, 80));
+		}
+	}
+	return str_len(reply) == 0 ? NULL : str_c(reply);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap/imap-id.h	Sat Jun 21 09:22:44 2008 +0300
@@ -0,0 +1,11 @@
+#ifndef IMAP_ID_H
+#define IMAP_ID_H
+
+/* Return ID reply based on given settings. */
+const char *imap_id_reply_generate(const char *settings);
+/* Return a line that should be logged based on given args and settings.
+   Returns NULL if nothing should be logged. */
+const char *imap_id_args_get_log_reply(const struct imap_arg *args,
+				       const char *settings);
+
+#endif
--- a/src/master/login-process.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/master/login-process.c	Sat Jun 21 09:22:44 2008 +0300
@@ -571,6 +571,8 @@
 	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 (set->login_greeting_capability)
 		env_put("GREETING_CAPABILITY=1");
 
--- a/src/master/mail-process.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/master/mail-process.c	Sat Jun 21 09:22:44 2008 +0300
@@ -310,6 +310,8 @@
 				    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=",
--- a/src/master/master-settings-defs.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/master/master-settings-defs.c	Sat Jun 21 09:22:44 2008 +0300
@@ -114,6 +114,8 @@
 	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),
--- a/src/master/master-settings.c	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/master/master-settings.c	Sat Jun 21 09:22:44 2008 +0300
@@ -280,6 +280,8 @@
 	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,
--- a/src/master/master-settings.h	Sat Jun 21 09:21:51 2008 +0300
+++ b/src/master/master-settings.h	Sat Jun 21 09:22:44 2008 +0300
@@ -126,6 +126,8 @@
 	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;