]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Support for IDLE extension.
authorTimo Sirainen <tss@iki.fi>
Fri, 14 Feb 2003 10:46:44 +0000 (12:46 +0200)
committerTimo Sirainen <tss@iki.fi>
Fri, 14 Feb 2003 10:46:44 +0000 (12:46 +0200)
--HG--
branch : HEAD

20 files changed:
configure.in
src/imap/Makefile.am
src/imap/client.c
src/imap/client.h
src/imap/cmd-idle.c [new file with mode: 0644]
src/imap/cmd-select.c
src/imap/commands.c
src/imap/commands.h
src/imap/common.h
src/imap/main.c
src/lib-storage/index/index-mailbox-check.c
src/lib-storage/index/index-storage.c
src/lib-storage/index/index-storage.h
src/lib-storage/index/index-sync.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/mbox/mbox-storage.c
src/lib-storage/mail-storage.h
src/master/mail-process.c
src/master/master-settings.c
src/master/master-settings.h

index 55e21e64a68df2125f919a85c053ca81db2388dc..560c9f0b1e23be87f278690503ac7f91f306a3b9 100644 (file)
@@ -871,7 +871,7 @@ dnl **
 dnl ** capabilities
 dnl **
 
-capability="IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+"
+capability="IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
 
 CFLAGS="$CFLAGS $EXTRA_CFLAGS"
index eec7f6b1759aa647b2b1d374def625bc21e0a70f..619f0761a7c39f20994e435d15cd5067e9bd58e1 100644 (file)
@@ -35,6 +35,7 @@ cmds = \
        cmd-examine.c \
        cmd-expunge.c \
        cmd-fetch.c \
+       cmd-idle.c \
        cmd-list.c \
        cmd-login.c \
        cmd-logout.c \
index e8693ae991f5218f815af870ed4f19ffd33ca4dd..36395cc8bf917f4442cda7fcb5a6dbace183474e 100644 (file)
@@ -30,8 +30,6 @@ extern struct mail_storage_callbacks mail_storage_callbacks;
 static struct client *my_client; /* we don't need more than one currently */
 static struct timeout *to_idle;
 
-static void client_input(void *context);
-
 static void client_output_timeout(void *context)
 {
        struct client *client = context;
@@ -67,7 +65,7 @@ struct client *client_create(int hin, int hout, struct mail_storage *storage)
        o_stream_set_blocking(client->output, CLIENT_OUTPUT_TIMEOUT,
                              client_output_timeout, client);
 
-       client->io = io_add(hin, IO_READ, client_input, client);
+       client->io = io_add(hin, IO_READ, _client_input, client);
        client->parser = imap_parser_create(client->input, client->output,
                                            MAX_INBUF_SIZE,
                                            MAX_IMAP_ARG_ELEMENTS);
@@ -222,7 +220,7 @@ int client_read_string_args(struct client *client, unsigned int count, ...)
        return i == count;
 }
 
-static void client_reset_command(struct client *client)
+void _client_reset_command(struct client *client)
 {
        client->cmd_tag = NULL;
        client->cmd_name = NULL;
@@ -260,7 +258,7 @@ static int client_handle_input(struct client *client)
                client->input_skip_line = TRUE;
                if (client->cmd_func(client) || client->cmd_error) {
                        /* command execution was finished */
-                       client_reset_command(client);
+                       _client_reset_command(client);
                         client->bad_counter = 0;
                        return TRUE;
                }
@@ -273,7 +271,7 @@ static int client_handle_input(struct client *client)
                        return FALSE;
 
                /* got the newline */
-               client_reset_command(client);
+               _client_reset_command(client);
 
                /* pass through to parse next command */
        }
@@ -302,20 +300,23 @@ static int client_handle_input(struct client *client)
                client_send_command_error(client, t_strconcat(
                        "Unknown command '", client->cmd_name, "'", NULL));
                client->input_skip_line = TRUE;
-               client_reset_command(client);
+               _client_reset_command(client);
        } else {
                client->input_skip_line = TRUE;
                if (client->cmd_func(client) || client->cmd_error) {
                        /* command execution was finished */
-                       client_reset_command(client);
+                       _client_reset_command(client);
                         client->bad_counter = 0;
+               } else {
+                       /* unfinished */
+                       return FALSE;
                }
        }
 
        return TRUE;
 }
 
-static void client_input(void *context)
+void _client_input(void *context)
 {
        struct client *client = context;
 
@@ -333,7 +334,7 @@ static void client_input(void *context)
                client->input_skip_line = TRUE;
 
                client_send_command_error(client, "Too long argument.");
-               client_reset_command(client);
+               _client_reset_command(client);
                break;
        }
 
index 499d8e84b81fc42787fdeb4ddcdfc30a0112f5d9..30004b919a9827f447ded8fca0fc93bd8fc9709a 100644 (file)
@@ -70,4 +70,7 @@ int client_read_string_args(struct client *client, unsigned int count, ...);
 void clients_init(void);
 void clients_deinit(void);
 
+void _client_input(void *context);
+void _client_reset_command(struct client *client);
+
 #endif
diff --git a/src/imap/cmd-idle.c b/src/imap/cmd-idle.c
new file mode 100644 (file)
index 0000000..470c26e
--- /dev/null
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "commands.h"
+
+#include <stdlib.h>
+
+#define DEFAULT_IDLE_CHECK_INTERVAL 30
+
+static void idle_finish(struct client *client)
+{
+       io_remove(client->io);
+       client->io = io_add(i_stream_get_fd(client->input),
+                           IO_READ, _client_input, client);
+
+       _client_reset_command(client);
+       client->bad_counter = 0;
+
+       client->mailbox->auto_sync(client->mailbox,
+                                  mailbox_check_interval != 0 ?
+                                  MAILBOX_SYNC_NO_EXPUNGES : MAILBOX_SYNC_NONE,
+                                  mailbox_check_interval);
+
+       client_sync_full(client);
+       client_send_tagline(client, "OK Idle completed.");
+}
+
+static void idle_client_input(void *context)
+{
+       struct client *client = context;
+       char *line;
+
+       client->last_input = ioloop_time;
+
+       switch (i_stream_read(client->input)) {
+       case -1:
+               /* disconnected */
+               client_destroy(client);
+               return;
+       case -2:
+               client->input_skip_line = TRUE;
+               client_send_line(client, "* BAD Expected DONE.");
+               idle_finish(client);
+               break;
+       }
+
+       while ((line = i_stream_next_line(client->input)) != NULL) {
+               if (client->input_skip_line)
+                       client->input_skip_line = FALSE;
+               else {
+                       if (strcmp(line, "DONE") != 0) {
+                               client_send_line(client,
+                                                "* BAD Expected DONE.");
+                       }
+                       idle_finish(client);
+                       break;
+               }
+       }
+}
+
+int cmd_idle(struct client *client)
+{
+       const char *str;
+       unsigned int interval;
+
+       if (!client_verify_open_mailbox(client))
+               return TRUE;
+
+       str = getenv("MAILBOX_IDLE_CHECK_INTERVAL");
+       interval = str == NULL ? 0 : (unsigned int)strtoul(str, NULL, 10);
+       if (interval == 0)
+               interval = DEFAULT_IDLE_CHECK_INTERVAL;
+
+       client->mailbox->auto_sync(client->mailbox, MAILBOX_SYNC_ALL, interval);
+
+       client_send_line(client, "+ idling");
+
+       io_remove(client->io);
+       client->io = io_add(i_stream_get_fd(client->input),
+                           IO_READ, idle_client_input, client);
+       return FALSE;
+}
index 5e02521609026faf86f70e24b35f9e1c3bfac806..8170cfcc2fdfe2a3b1ab16e60a20a00468a114fc 100644 (file)
@@ -73,6 +73,11 @@ int _cmd_select_full(struct client *client, int readonly)
                            "OK [READ-ONLY] Select completed." :
                            "OK [READ-WRITE] Select completed.");
 
+       if (mailbox_check_interval != 0) {
+               box->auto_sync(box, MAILBOX_SYNC_NO_EXPUNGES,
+                              mailbox_check_interval);
+       }
+
        return TRUE;
 }
 
index 7660cfbd6be9d5ed56eba025a12ad2fe8c88e205..1d6bf6209c4aa53d4906a2474580095209011b0a 100644 (file)
@@ -41,6 +41,10 @@ client_command_func_t *client_command_find(const char *name)
                if (strcmp(name, "FETCH") == 0)
                        return cmd_fetch;
                break;
+       case 'I':
+               if (strcmp(name, "IDLE") == 0)
+                       return cmd_idle;
+               break;
        case 'L':
                if (strcmp(name, "LIST") == 0)
                        return cmd_list;
index d80524e717fa37d6bd4d49106efa28d2c53e6ae9..2c178941e030d30083ac3b2605c7583ed469fd4b 100644 (file)
@@ -44,6 +44,7 @@ int cmd_store(struct client *client);
 int cmd_copy(struct client *client);
 int cmd_uid(struct client *client);
 int cmd_unselect(struct client *client);
+int cmd_idle(struct client *client);
 
 /* private: */
 int _cmd_list_full(struct client *client, int subscribed);
index 35ef692ac22698dd6305bd3ee7bcaf0215ef4436..b87df823584481ae7e8e35372028b96c83d80c6b 100644 (file)
@@ -11,6 +11,6 @@
 #define DEFAULT_MAX_CUSTOM_FLAG_LENGTH 50
 
 extern struct ioloop *ioloop;
-extern unsigned int max_custom_flag_length;
+extern unsigned int max_custom_flag_length, mailbox_check_interval;
 
 #endif
index b141edd30eca00fcc260d9c4ae8b38bbf57231bf..cd267442c0bbf14e627614fa96d26521ce0b8969 100644 (file)
@@ -16,7 +16,7 @@
         (getenv("LOGGED_IN") == NULL)
 
 struct ioloop *ioloop;
-unsigned int max_custom_flag_length;
+unsigned int max_custom_flag_length, mailbox_check_interval;
 
 static char log_prefix[128]; /* syslog() needs this to be permanent */
 
@@ -107,6 +107,10 @@ static void main_init(void)
                (unsigned int)strtoul(str, NULL, 10) :
                DEFAULT_MAX_CUSTOM_FLAG_LENGTH;
 
+       str = getenv("MAILBOX_CHECK_INTERVAL");
+       mailbox_check_interval = str == NULL ? 0 :
+               (unsigned int)strtoul(str, NULL, 10);
+
        client = client_create(hin, hout, storage);
 
        if (IS_STANDALONE()) {
index b74d436505776eb459e4f9df926323543fad04f6..45070dd78a5b62678872afbcf522bbf2cbe5d55d 100644 (file)
@@ -7,47 +7,65 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 
-static int check_interval = -1;
-
 static void check_timeout(void *context)
 {
        struct index_mailbox *ibox = context;
+       struct index_autosync_file *file;
        struct stat st;
+       int synced, sync_expunges;
 
-       if (ioloop_time - ibox->last_check < check_interval)
+       /* check changes only when we can also notify of new mail */
+       if ((unsigned int) (ioloop_time - ibox->sync_last_check) <
+           ibox->min_newmail_notify_interval)
                return;
 
-       ibox->last_check = ioloop_time;
-       if (stat(ibox->check_path, &st) == 0 &&
-           ibox->check_file_stamp != st.st_mtime) {
-               ibox->check_file_stamp = st.st_mtime;
-               ibox->box.sync(&ibox->box, FALSE);
+       ibox->sync_last_check = ioloop_time;
+
+       synced = FALSE;
+       sync_expunges = ibox->autosync_type != MAILBOX_SYNC_NO_EXPUNGES;
+
+       for (file = ibox->autosync_files; file != NULL; file = file->next) {
+               if (stat(file->path, &st) == 0 &&
+                   file->last_stamp != st.st_mtime) {
+                       file->last_stamp = st.st_mtime;
+                       if (!synced) {
+                               ibox->box.sync(&ibox->box, sync_expunges);
+                               synced = TRUE;
+                       }
+               }
        }
 }
 
 void index_mailbox_check_add(struct index_mailbox *ibox, const char *path)
 {
-       const char *str;
+       struct index_autosync_file *file;
        struct stat st;
 
-       if (check_interval < 0) {
-               str = getenv("MAILBOX_CHECK_INTERVAL");
-               check_interval = str == NULL ? 0 : atoi(str);
-               if (check_interval < 0)
-                       check_interval = 0;
-       }
+       file = i_new(struct index_autosync_file, 1);
+       file->path = i_strdup(path);
+       file->last_stamp = stat(path, &st) < 0 ? 0 : st.st_mtime;
 
-       if (check_interval == 0)
-               return;
+       file->next = ibox->autosync_files;
+        ibox->autosync_files = file;
 
-       ibox->check_path = i_strdup(path);
-       ibox->check_file_stamp = stat(path, &st) < 0 ? 0 : st.st_mtime;
-       ibox->check_to = timeout_add(1000, check_timeout, ibox);
+       if (ibox->autosync_to == NULL)
+               ibox->autosync_to = timeout_add(1000, check_timeout, ibox);
 }
 
-void index_mailbox_check_remove(struct index_mailbox *ibox)
+void index_mailbox_check_remove_all(struct index_mailbox *ibox)
 {
-       if (ibox->check_to != NULL)
-               timeout_remove(ibox->check_to);
-       i_free(ibox->check_path);
+       struct index_autosync_file *file;
+
+       while (ibox->autosync_files != NULL) {
+               file = ibox->autosync_files;
+               ibox->autosync_files = file->next;
+
+                i_free(file->path);
+               i_free(file);
+       }
+
+       if (ibox->autosync_to != NULL) {
+               timeout_remove(ibox->autosync_to);
+               ibox->autosync_to = NULL;
+       }
 }
index 00d07ece432f66d7265a297f9d451f5bcd462734..7b5250f08ec57e80db5ed07b3aba3cdddea92e07 100644 (file)
@@ -276,7 +276,7 @@ int index_storage_close(struct mailbox *box)
 {
        struct index_mailbox *ibox = (struct index_mailbox *) box;
 
-       index_mailbox_check_remove(ibox);
+       index_mailbox_check_remove_all(ibox);
        if (ibox->index != NULL)
                index_storage_unref(ibox->index);
 
index 8b8a0f94933bffba2923a08824206db1f7d21c17..0b8b349af1dd8b9bd0e0b6c25ae117bb319549a5 100644 (file)
@@ -5,6 +5,13 @@
 #include "mail-index.h"
 #include "index-mail.h"
 
+struct index_autosync_file {
+       struct index_autosync_file *next;
+
+       char *path;
+       time_t last_stamp;
+};
+
 struct index_mailbox {
        struct mailbox box;
 
@@ -14,10 +21,11 @@ struct index_mailbox {
 
        struct mail_index *index;
 
-       char *check_path;
-       struct timeout *check_to;
-       time_t check_file_stamp;
-       time_t last_check;
+       struct timeout *autosync_to;
+        struct index_autosync_file *autosync_files;
+       enum mailbox_sync_type autosync_type;
+       time_t sync_last_check;
+       unsigned int min_newmail_notify_interval;
 
        struct index_mail fetch_mail; /* fetch_uid() or fetch_seq() */
        unsigned int synced_messages_count;
@@ -64,7 +72,7 @@ int index_storage_save(struct mail_storage *storage, const char *path,
                       struct istream *input, struct ostream *output);
 
 void index_mailbox_check_add(struct index_mailbox *ibox, const char *path);
-void index_mailbox_check_remove(struct index_mailbox *ibox);
+void index_mailbox_check_remove_all(struct index_mailbox *ibox);
 
 /* mailbox methods: */
 void index_storage_set_callbacks(struct mail_storage *storage,
index 923b2b0784a4290c9ccf7fc2837a0d1572f6fe20..2d2d3eb33d7587595506f456ee31b291eaab3f63 100644 (file)
@@ -214,7 +214,7 @@ int index_storage_sync(struct mailbox *box, int sync_expunges)
        struct index_mailbox *ibox = (struct index_mailbox *) box;
        int ret;
 
-       ibox->last_check = ioloop_time;
+       ibox->sync_last_check = ioloop_time;
 
        if (!index_storage_sync_and_lock(ibox, FALSE, MAIL_LOCK_UNLOCK))
                return FALSE;
index 6bd95465e452706b6ef8ebc0ad2120631708b0d3..b2645ff74c8ba567c8d86b127b4e34bffbd20fc7 100644 (file)
@@ -224,10 +224,8 @@ maildir_open(struct mail_storage *storage, const char *name,
 
        ibox = index_storage_init(storage, &maildir_mailbox, index, name,
                                  readonly, fast);
-       if (ibox != NULL) {
+       if (ibox != NULL)
                ibox->expunge_locked = maildir_expunge_locked;
-               index_mailbox_check_add(ibox, t_strconcat(path, "/new", NULL));
-       }
        return (struct mailbox *) ibox;
 }
 
@@ -553,6 +551,23 @@ static int maildir_get_mailbox_name_status(struct mail_storage *storage,
        }
 }
 
+static void maildir_storage_auto_sync(struct mailbox *box,
+                                     enum mailbox_sync_type sync_type,
+                                     unsigned int min_newmail_notify_interval)
+{
+       struct index_mailbox *ibox = (struct index_mailbox *) box;
+
+       ibox->autosync_type = sync_type;
+       ibox->min_newmail_notify_interval = min_newmail_notify_interval;
+
+        index_mailbox_check_remove_all(ibox);
+       index_mailbox_check_add(ibox, t_strconcat(ibox->index->mailbox_path,
+                                                 "/new", NULL));
+       index_mailbox_check_add(ibox, t_strconcat(ibox->index->mailbox_path,
+                                                 "/cur", NULL));
+}
+
+
 struct mail_storage maildir_storage = {
        "maildir", /* name */
 
@@ -589,6 +604,7 @@ struct mailbox maildir_mailbox = {
        index_storage_close,
        index_storage_get_status,
        index_storage_sync,
+       maildir_storage_auto_sync,
        index_storage_expunge,
        index_storage_update_flags,
        maildir_storage_copy,
index 25411420ea493f44c90fc3dd2d80446651d23b99..b4491b4b7a2ad5137d04f6b7efc53f8ad98aa0e5 100644 (file)
@@ -338,10 +338,8 @@ static struct mailbox *mbox_open(struct mail_storage *storage, const char *name,
 
        ibox = index_storage_init(storage, &mbox_mailbox, index,
                                  name, readonly, fast);
-       if (ibox != NULL) {
+       if (ibox != NULL)
                ibox->expunge_locked = mbox_expunge_locked;
-               index_mailbox_check_add(ibox, index->mailbox_path);
-       }
        return (struct mailbox *) ibox;
 }
 
@@ -621,6 +619,19 @@ static int mbox_storage_close(struct mailbox *box)
        return index_storage_close(box) && !failed;
 }
 
+static void mbox_storage_auto_sync(struct mailbox *box,
+                                  enum mailbox_sync_type sync_type,
+                                  unsigned int min_newmail_notify_interval)
+{
+       struct index_mailbox *ibox = (struct index_mailbox *) box;
+
+       ibox->autosync_type = sync_type;
+       ibox->min_newmail_notify_interval = min_newmail_notify_interval;
+
+        index_mailbox_check_remove_all(ibox);
+       index_mailbox_check_add(ibox, ibox->index->mailbox_path);
+}
+
 struct mail_storage mbox_storage = {
        "mbox", /* name */
 
@@ -657,6 +668,7 @@ struct mailbox mbox_mailbox = {
        mbox_storage_close,
        index_storage_get_status,
        index_storage_sync,
+       mbox_storage_auto_sync,
        index_storage_expunge,
        index_storage_update_flags,
        index_storage_copy,
index e286fa79ce6a258787c13762ff5e46ed5ffc8082..d96fe594b5b8218a5654ea2bb2ad7487ffc1a1d4 100644 (file)
@@ -33,6 +33,12 @@ enum mailbox_name_status {
        MAILBOX_NAME_NOINFERIORS
 };
 
+enum mailbox_sync_type {
+       MAILBOX_SYNC_NONE,
+       MAILBOX_SYNC_ALL,
+       MAILBOX_SYNC_NO_EXPUNGES
+};
+
 enum modify_type {
        MODIFY_ADD,
        MODIFY_REMOVE,
@@ -206,6 +212,11 @@ struct mailbox {
           but expunges are synced. */
        int (*sync)(struct mailbox *box, int sync_expunges);
 
+       /* Synchronize mailbox in background. It's done until this function is
+          called with sync_type = MAILBOX_SYNC_NONE */
+       void (*auto_sync)(struct mailbox *box, enum mailbox_sync_type sync_type,
+                         unsigned int min_newmail_notify_interval);
+
        /* Expunge all mails with \Deleted flag. If notify is TRUE, call
           expunge callbacks. Also always does full syncing. */
        int (*expunge)(struct mailbox *box, int notify);
index 405dc10cb2a0f4d0214ee6227d124b2f70c61151..f7fb69b7ef43c1f2ad22b1b85f76de422332026f 100644 (file)
@@ -160,6 +160,8 @@ int create_mail_process(int socket, struct ip_addr *ip,
                            set->mail_never_cache_fields, NULL));
        env_put(t_strdup_printf("MAILBOX_CHECK_INTERVAL=%u",
                                set->mailbox_check_interval));
+       env_put(t_strdup_printf("MAILBOX_IDLE_CHECK_INTERVAL=%u",
+                               set->mailbox_idle_check_interval));
        env_put(t_strconcat("CLIENT_WORKAROUNDS=",
                            set->client_workarounds, NULL));
        env_put(t_strdup_printf("MAIL_MAX_FLAG_LENGTH=%u",
index 5f13b6687006135f57627903dc58d2976fe6299c..f1b317629c5c5216927bda27db859322af1b0da3 100644 (file)
@@ -55,6 +55,7 @@ static struct setting_def setting_defs[] = {
        DEF(SET_STR, mail_never_cache_fields),
        DEF(SET_STR, client_workarounds),
        DEF(SET_INT, mailbox_check_interval),
+       DEF(SET_INT, mailbox_idle_check_interval),
        DEF(SET_BOOL, mail_full_filesystem_access),
        DEF(SET_INT, mail_max_flag_length),
        DEF(SET_BOOL, mail_save_crlf),
@@ -159,6 +160,7 @@ struct settings default_settings = {
        MEMBER(mail_never_cache_fields) NULL,
        MEMBER(client_workarounds) NULL,
        MEMBER(mailbox_check_interval) 0,
+       MEMBER(mailbox_idle_check_interval) 30,
        MEMBER(mail_full_filesystem_access) FALSE,
        MEMBER(mail_max_flag_length) 50,
        MEMBER(mail_save_crlf) FALSE,
index c3b98f7992d73f0a9e282504825d7e166df8ab34..555230025027c184d8e38542715ea3018fbce0db 100644 (file)
@@ -39,6 +39,7 @@ struct settings {
        const char *mail_never_cache_fields;
        const char *client_workarounds;
        unsigned int mailbox_check_interval;
+       unsigned int mailbox_idle_check_interval;
        int mail_full_filesystem_access;
        int mail_max_flag_length;
        int mail_save_crlf;