return list->sep;
}
+static struct mailbox_list *imapc_list_get_fs(struct imapc_mailbox_list *list)
+{
+ struct mailbox_list_settings list_set;
+ const char *error, *dir;
+
+ dir = list->list.set.index_dir;
+ if (dir == NULL)
+ dir = list->list.set.root_dir;
+
+ if (dir == NULL) {
+ /* indexes disabled */
+ } else if (list->index_list == NULL && !list->index_list_failed) {
+ memset(&list_set, 0, sizeof(list_set));
+ list_set.layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS;
+ /* the root dir shouldn't actually ever be used. we just need
+ it to be different from index_dir so the index directories
+ get autocreated */
+ list_set.root_dir = t_strconcat(dir, "/", NULL);
+ list_set.index_dir = dir;
+ list_set.escape_char = '%';
+
+ if (mailbox_list_create(list_set.layout, list->list.ns,
+ &list_set, MAILBOX_LIST_FLAG_SECONDARY,
+ &list->index_list, &error) < 0) {
+ i_error("imapc: Couldn't create %s mailbox list: %s",
+ list_set.layout, error);
+ list->index_list_failed = TRUE;
+ }
+ }
+ return list->index_list;
+}
+
static const char *
-imapc_list_get_path(struct mailbox_list *list ATTR_UNUSED,
- const char *name ATTR_UNUSED,
+imapc_list_get_fs_name(struct imapc_mailbox_list *list, const char *name)
+{
+ struct mailbox_list *fs_list = imapc_list_get_fs(list);
+ const char *vname;
+
+ if (name == NULL)
+ return name;
+
+ vname = mailbox_list_get_vname(&list->list, name);
+ return mailbox_list_get_storage_name(fs_list, vname);
+}
+
+static const char *
+imapc_list_get_path(struct mailbox_list *_list, const char *name,
enum mailbox_list_path_type type)
{
- if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
- return "";
- return NULL;
+ struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+ struct mailbox_list *fs_list = imapc_list_get_fs(list);
+ const char *fs_name;
+
+ if (fs_list != NULL) {
+ fs_name = imapc_list_get_fs_name(list, name);
+ return mailbox_list_get_path(fs_list, fs_name, type);
+ } else {
+ if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
+ return "";
+ return NULL;
+ }
}
static const char *
-imapc_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED)
+imapc_list_get_temp_prefix(struct mailbox_list *_list, bool global)
{
- i_panic("imapc: Can't return a temp prefix for '%s'",
- list->ns->prefix);
- return NULL;
+ struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+ struct mailbox_list *fs_list = imapc_list_get_fs(list);
+
+ if (fs_list != NULL) {
+ return global ?
+ mailbox_list_get_global_temp_prefix(fs_list) :
+ mailbox_list_get_temp_prefix(fs_list);
+ } else {
+ i_panic("imapc: Can't return a temp prefix for '%s'",
+ _list->ns->prefix);
+ return NULL;
+ }
}
static const char *
}
static int
-imapc_list_delete_dir(struct mailbox_list *list ATTR_UNUSED,
- const char *name ATTR_UNUSED)
+imapc_list_delete_dir(struct mailbox_list *_list, const char *name)
{
+ struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+ struct mailbox_list *fs_list = imapc_list_get_fs(list);
+
+ if (fs_list != NULL) {
+ name = imapc_list_get_fs_name(list, name);
+ (void)mailbox_list_delete_dir(fs_list, name);
+ }
return 0;
}
bool rename_children)
{
struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)oldlist;
+ struct mailbox_list *fs_list = imapc_list_get_fs(list);
struct imapc_simple_context ctx;
if (!rename_children) {
imapc_list_simple_callback, &ctx,
"RENAME %s %s", oldname, newname);
imapc_simple_run(&ctx);
+ if (ctx.ret == 0 && fs_list != NULL && oldlist == newlist) {
+ oldname = imapc_list_get_fs_name(list, oldname);
+ newname = imapc_list_get_fs_name(list, newname);
+ (void)fs_list->v.rename_mailbox(fs_list, oldname,
+ fs_list, newname,
+ rename_children);
+ }
return ctx.ret;
}
#include "imap-util.h"
#include "imapc-client.h"
#include "imapc-seqmap.h"
+#include "imapc-sync.h"
#include "imapc-storage.h"
#define NOTIFY_DELAY_MSECS 500
mail_index_transaction_open_updated_view(mbox->delayed_sync_trans);
}
+int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox,
+ bool *changes_r)
+{
+ int ret = 0;
+
+ *changes_r = FALSE;
+
+ if (mbox->delayed_sync_view != NULL)
+ mail_index_view_close(&mbox->delayed_sync_view);
+ if (mbox->delayed_sync_trans != NULL) {
+ if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) {
+ mail_storage_set_index_error(&mbox->box);
+ ret = -1;
+ }
+ *changes_r = TRUE;
+ }
+ if (mbox->sync_view != NULL)
+ mail_index_view_close(&mbox->sync_view);
+ return ret;
+}
+
static void
imapc_newmsgs_callback(const struct imapc_command_reply *reply,
void *context)
uint32_t rcount = reply->num;
const struct mail_index_header *hdr;
struct imapc_seqmap *seqmap;
- uint32_t next_lseq, next_rseq;
+ uint32_t first_uid, next_lseq, next_rseq;
if (mbox == NULL)
return;
seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
next_lseq = mail_index_view_get_messages_count(view) + 1;
next_rseq = imapc_seqmap_lseq_to_rseq(seqmap, next_lseq);
- if (next_rseq > rcount)
- return;
-
- hdr = mail_index_get_header(view);
+ if (next_rseq > rcount) {
+ if (rcount == 0 || !mbox->opening)
+ return;
+ /* initial SELECT. we don't know what the flags are. */
+ first_uid = 1;
+ } else {
+ hdr = mail_index_get_header(view);
+ first_uid = hdr->next_uid;
+ }
mbox->new_msgs = TRUE;
imapc_client_mailbox_cmdf(mbox->client_box, imapc_newmsgs_callback,
- mbox, "UID FETCH %u:* FLAGS", hdr->next_uid);
+ mbox, "UID FETCH %u:* FLAGS", first_uid);
}
static void imapc_mailbox_idle_timeout(struct imapc_mailbox *mbox)
}
}
+static bool keywords_are_equal(struct mail_keywords *kw,
+ const ARRAY_TYPE(keyword_indexes) *kw_arr)
+{
+ const unsigned int *kw_idx;
+ unsigned int i, j, count;
+
+ kw_idx = array_get(kw_arr, &count);
+ if (count != kw->count)
+ return FALSE;
+
+ /* there are normally only a few keywords, so O(n^2) is fine */
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < count; j++) {
+ if (kw->idx[i] == kw_idx[j])
+ break;
+ }
+ if (j == count)
+ return FALSE;
+ }
+ return TRUE;
+}
+
static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply,
struct imapc_mailbox *mbox)
{
imapc_mailbox_init_delayed_trans(mbox);
old_count = mail_index_view_get_messages_count(mbox->delayed_sync_view);
+ while (lseq <= old_count) {
+ rec = mail_index_lookup(mbox->delayed_sync_view, lseq);
+ if (rec->uid == uid || uid == 0)
+ break;
+
+ if (!mbox->opening || uid < rec->uid) {
+ imapc_mailbox_set_corrupted(mbox,
+ "Message UID changed %u -> %u", rec->uid, uid);
+ return;
+ }
+ /* we're opening the mailbox. this message was expunged
+ externally, so expunge it ourself too. */
+ imapc_seqmap_expunge(seqmap, rseq);
+ mail_index_expunge(mbox->delayed_sync_trans, lseq);
+ lseq++;
+ }
if (lseq > old_count) {
if (uid == 0 || lseq != old_count + 1)
return;
i_assert(lseq == old_count + 1);
mail_index_append(mbox->delayed_sync_trans, uid, &lseq);
}
- rec = mail_index_lookup(mbox->delayed_sync_view, lseq);
- if (rec->uid != uid && uid != 0) {
- imapc_mailbox_set_corrupted(mbox, "Message UID changed %u -> %u",
- rec->uid, uid);
- return;
- }
if (seen_flags && rec->flags != flags) {
mail_index_update_flags(mbox->delayed_sync_trans, lseq,
MODIFY_REPLACE, flags);
}
- if (seen_flags) {
+ if (seen_flags) T_BEGIN {
+ ARRAY_TYPE(keyword_indexes) old_kws;
struct mail_keywords *kw;
+ t_array_init(&old_kws, 8);
+ mail_index_lookup_keywords(mbox->delayed_sync_view, lseq,
+ &old_kws);
+
(void)array_append_space(&keywords);
kw = mail_index_keywords_create(mbox->box.index,
array_idx(&keywords, 0));
- mail_index_update_keywords(mbox->delayed_sync_trans, lseq,
- MODIFY_REPLACE, kw);
+ if (!keywords_are_equal(kw, &old_kws)) {
+ mail_index_update_keywords(mbox->delayed_sync_trans,
+ lseq, MODIFY_REPLACE, kw);
+ }
mail_index_keywords_unref(&kw);
- }
+ } T_END;
imapc_mailbox_idle_notify(mbox);
}
imapc_resp_text_uidvalidity(const struct imapc_untagged_reply *reply,
struct imapc_mailbox *mbox)
{
+ const struct mail_index_header *hdr;
uint32_t uid_validity;
+ bool changes;
if (mbox == NULL || reply->resp_text_value == NULL ||
str_to_uint32(reply->resp_text_value, &uid_validity) < 0)
return;
- imapc_mailbox_init_delayed_trans(mbox);
- mail_index_update_header(mbox->delayed_sync_trans,
- offsetof(struct mail_index_header, uid_validity),
- &uid_validity, sizeof(uid_validity), TRUE);
+ hdr = mail_index_get_header(mbox->box.view);
+ if (hdr->uid_validity != uid_validity) {
+ imapc_mailbox_init_delayed_trans(mbox);
+ if (hdr->uid_validity != 0) {
+ /* uidvalidity changed, reset the entire mailbox */
+ mail_index_reset(mbox->delayed_sync_trans);
+ (void)imapc_mailbox_commit_delayed_trans(mbox, &changes);
+ imapc_mailbox_init_delayed_trans(mbox);
+ }
+ mail_index_update_header(mbox->delayed_sync_trans,
+ offsetof(struct mail_index_header, uid_validity),
+ &uid_validity, sizeof(uid_validity), TRUE);
+ }
}
static void
imapc_resp_text_uidnext(const struct imapc_untagged_reply *reply,
struct imapc_mailbox *mbox)
{
+ const struct mail_index_header *hdr;
uint32_t uid_next;
if (mbox == NULL || reply->resp_text_value == NULL ||
str_to_uint32(reply->resp_text_value, &uid_next) < 0)
return;
- imapc_mailbox_init_delayed_trans(mbox);
- mail_index_update_header(mbox->delayed_sync_trans,
- offsetof(struct mail_index_header, next_uid),
- &uid_next, sizeof(uid_next), FALSE);
+ hdr = mail_index_get_header(mbox->box.view);
+ if (hdr->next_uid != uid_next) {
+ imapc_mailbox_init_delayed_trans(mbox);
+ mail_index_update_header(mbox->delayed_sync_trans,
+ offsetof(struct mail_index_header, next_uid),
+ &uid_next, sizeof(uid_next), FALSE);
+ }
}
void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox,