]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added support for IMAP ID extension.
authorTimo Sirainen <tss@iki.fi>
Sat, 21 Jun 2008 06:22:44 +0000 (09:22 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 21 Jun 2008 06:22:44 +0000 (09:22 +0300)
--HG--
branch : HEAD

20 files changed:
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 [new file with mode: 0644]
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 [new file with mode: 0644]
src/lib-imap/imap-id.h [new file with mode: 0644]
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

diff --git a/README b/README
index b35213c37dae575b58929cc6e5519d44b7cf1895..f1ac2fb1e6ca04cafeff309c712965d15cd04a3e 100644 (file)
--- a/README
+++ b/README
@@ -31,6 +31,7 @@ RFCs conformed
  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
index 46ab89c9a9bbc18a40ccb01154327509e09ba891..6fb14ba19456baef46965260f76a9b8622493a8e 100644 (file)
@@ -19,7 +19,7 @@ AC_CHECK_HEADERS(strings.h stdint.h unistd.h dirent.h malloc.h inttypes.h \
   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_CHECK_LIB(cap, cap_init, [
 ])
 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 **
 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"
index e9a2e40bb96160bccc52ab9fe0dd4b72fb37a704..ebad3364c2d263495110ae340e67e2e7aab3b4a3 100644 (file)
@@ -544,6 +544,14 @@ protocol imap {
   # 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
index 3bf830c5851c9175ca26047517c26f972fc68052..8dfad62195b028bad3e23c28f3452fc44f91adc2 100644 (file)
 #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 @@ static int cmd_starttls(struct imap_client *client)
        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 @@ static int client_command_execute(struct imap_client *client, const char *cmd,
                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)
index 26e3c33ed834b4022e488325c1108d8522c065da..fd36aab0ead2f21ed4435a62daad4fd0f19c80e1 100644 (file)
@@ -31,6 +31,7 @@ struct imap_client {
        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);
index 4ff5218431c6d7a6c8b084cec04b0512a19764a1..3028683b32ff12204fc89ee14e0951f9f45183d1 100644 (file)
@@ -49,6 +49,7 @@ cmds = \
        cmd-examine.c \
        cmd-expunge.c \
        cmd-fetch.c \
+       cmd-id.c \
        cmd-idle.c \
        cmd-list.c \
        cmd-logout.c \
index 7c00ea65c6bc2282d61931e0379896b3c2f0a07e..82b1d80d030e8b07d2e682c5b8a8e28f36c0a8a8 100644 (file)
@@ -115,6 +115,7 @@ struct client {
        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;
diff --git a/src/imap/cmd-id.c b/src/imap/cmd-id.c
new file mode 100644 (file)
index 0000000..fe20564
--- /dev/null
@@ -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;
+}
+
index 1328c49353e0be44435e7b2ca60291e997f3dc73..3856f8f4158777893ec678c9eeb202164d3fc308 100644 (file)
@@ -43,6 +43,7 @@ const struct command imap4rev1_commands[] = {
 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 },
index e7bdddfbc3d5c2528e256c427d901e591ce70d1b..b8f416ae22db0e39cccc19733246ff52ad2267ec 100644 (file)
@@ -83,6 +83,7 @@ bool cmd_uid(struct client_command_context *cmd);
 /* 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);
index 7ab28b4ea1a0836d79a2b52a6d5ff7cc01035331..afea42f9a6494dd0d36eebf71bd6320e201ff11d 100644 (file)
@@ -31,6 +31,7 @@ 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;
 
index 82f2585739c97b10f1dfc1659c51f80f45baa865..be3b456fa3b0fa4aef7848bad23f163c30e84fbf 100644 (file)
@@ -41,6 +41,7 @@ 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;
@@ -227,6 +228,9 @@ static void main_init(void)
        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);
index 0f2e0dc6166342cfee52b33ee5e7bec64fc8e9e0..c13aa7a57d01987195798c23e2c181685975f44a 100644 (file)
@@ -10,6 +10,7 @@ libimap_a_SOURCES = \
        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 @@ headers = \
        imap-bodystructure.h \
        imap-date.h \
        imap-envelope.h \
+       imap-id.h \
        imap-match.h \
        imap-parser.h \
        imap-quote.h \
diff --git a/src/lib-imap/imap-id.c b/src/lib-imap/imap-id.c
new file mode 100644 (file)
index 0000000..fd21125
--- /dev/null
@@ -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);
+}
diff --git a/src/lib-imap/imap-id.h b/src/lib-imap/imap-id.h
new file mode 100644 (file)
index 0000000..0af57aa
--- /dev/null
@@ -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
index 7e5c422af3833dc61c67f3e6e0eb052cdfdf29d7..6cd7d63e48803ff04c4ec148a3ddba54c4474285 100644 (file)
@@ -571,6 +571,8 @@ static void login_process_init_env(struct login_group *group, pid_t pid)
        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");
 
index c9b4878401fa0c2444824ae44bb47eef5e80718b..8cb9109e37a063189e7fce766f047bfccfa68cb3 100644 (file)
@@ -310,6 +310,8 @@ mail_process_set_environment(struct settings *set, const char *mail,
                                    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=",
index 8cb56718bbe589814a0273d5fbe5d30400604414..41b21b72f80dd2673eb78f32fdc9a218cbf1c4d0 100644 (file)
@@ -114,6 +114,8 @@ static struct setting_def setting_defs[] = {
        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),
index 0e5a11222042aa11fb072e09ae95e5eb26759056..ea87462f5ac7f85bc4abc90e8774c3affaa9853c 100644 (file)
@@ -280,6 +280,8 @@ struct settings default_settings = {
        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,
index 29da4e36fc8d3e1d08cfea999cf7993ef990b626..58fe37430fc07e868b477abb8066d1c2d4973fb5 100644 (file)
@@ -126,6 +126,8 @@ struct settings {
        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;