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"
cmd-examine.c \
cmd-expunge.c \
cmd-fetch.c \
+ cmd-idle.c \
cmd-list.c \
cmd-login.c \
cmd-logout.c \
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;
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);
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;
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;
}
return FALSE;
/* got the newline */
- client_reset_command(client);
+ _client_reset_command(client);
/* pass through to parse next command */
}
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;
client->input_skip_line = TRUE;
client_send_command_error(client, "Too long argument.");
- client_reset_command(client);
+ _client_reset_command(client);
break;
}
void clients_init(void);
void clients_deinit(void);
+void _client_input(void *context);
+void _client_reset_command(struct client *client);
+
#endif
--- /dev/null
+/* 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;
+}
"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;
}
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;
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);
#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
(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 */
(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()) {
#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;
+ }
}
{
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);
#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;
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;
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,
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;
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;
}
}
}
+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 */
index_storage_close,
index_storage_get_status,
index_storage_sync,
+ maildir_storage_auto_sync,
index_storage_expunge,
index_storage_update_flags,
maildir_storage_copy,
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;
}
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 */
mbox_storage_close,
index_storage_get_status,
index_storage_sync,
+ mbox_storage_auto_sync,
index_storage_expunge,
index_storage_update_flags,
index_storage_copy,
MAILBOX_NAME_NOINFERIORS
};
+enum mailbox_sync_type {
+ MAILBOX_SYNC_NONE,
+ MAILBOX_SYNC_ALL,
+ MAILBOX_SYNC_NO_EXPUNGES
+};
+
enum modify_type {
MODIFY_ADD,
MODIFY_REMOVE,
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);
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",
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),
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,
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;