"%s: Message UID=%u unexpectedly inserted to mailbox", ctx->box->index_pvt->filepath, uid_shared); return -1; } } return 0; } static void sync_pvt_copy_self_flags(struct index_mailbox_sync_pvt_context *ctx, ARRAY_TYPE(keyword_indexes) *keywords, uint32_t seq_old, uint32_t seq_new) { const struct mail_index_record *old_rec; old_rec = mail_index_lookup(ctx->view_pvt, seq_old); mail_index_lookup_keywords(ctx->view_pvt, seq_old, keywords); if (old_rec->flags != 0) { mail_index_update_flags(ctx->trans_pvt, seq_new, MODIFY_ADD, old_rec->flags); } if (array_count(keywords) > 0) { struct mail_keywords *kw; kw = mail_index_keywords_create_from_indexes(ctx->box->index_pvt, keywords); mail_index_update_keywords(ctx->trans_pvt, seq_new, MODIFY_ADD, kw); mail_index_keywords_unref(&kw); } }
static int mailbox_list_index_parse_records(struct mailbox_list_index *ilist, struct mail_index_view *view, const char **error_r) { struct mailbox_list_index_node *node; const struct mail_index_record *rec; const struct mailbox_list_index_record *irec; const void *data; bool expunged; uint32_t seq, uid, count; *error_r = NULL; count = mail_index_view_get_messages_count(view); for (seq = 1; seq <= count; seq++) { node = p_new(ilist->mailbox_pool, struct mailbox_list_index_node, 1); rec = mail_index_lookup(view, seq); node->uid = rec->uid; node->flags = rec->flags; mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); if (data == NULL) { *error_r = "Missing list extension data"; return -1; } irec = data; node->name_id = irec->name_id; node->name = hash_table_lookup(ilist->mailbox_names, POINTER_CAST(irec->name_id)); if (node->name == NULL) { *error_r = "name_id not in index header"; if (ilist->has_backing_store) return -1; /* generate a new name and use it */ mailbox_list_index_generate_name(ilist, node); } hash_table_insert(ilist->mailbox_hash, POINTER_CAST(node->uid), node); } /* do a second scan to create the actual mailbox tree hierarchy. this is needed because the parent_uid may be smaller or higher than the current node's uid */ for (seq = 1; seq <= count; seq++) { mail_index_lookup_uid(view, seq, &uid); mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); irec = data; node = mailbox_list_index_lookup_uid(ilist, uid); i_assert(node != NULL); if (irec->parent_uid != 0) { /* node should have a parent */ node->parent = mailbox_list_index_lookup_uid(ilist, irec->parent_uid); if (node->parent != NULL) { node->next = node->parent->children; node->parent->children = node; continue; } *error_r = "parent_uid points to nonexistent record"; if (ilist->has_backing_store) return -1; /* just place it under the root */ } node->next = ilist->mailbox_tree; ilist->mailbox_tree = node; } return *error_r == NULL ? 0 : -1; }
int maildir_sync_index(struct maildir_index_sync_context *ctx, bool partial) { struct maildir_mailbox *mbox = ctx->mbox; struct mail_index_view *view = ctx->view; struct mail_index_view *view2; struct maildir_uidlist_iter_ctx *iter; struct mail_index_transaction *trans = ctx->trans; const struct mail_index_header *hdr; struct mail_index_header empty_hdr; const struct mail_index_record *rec; uint32_t seq, seq2, uid, prev_uid; enum maildir_uidlist_rec_flag uflags; const char *filename; uint32_t uid_validity, next_uid, hdr_next_uid, first_recent_uid; uint32_t first_uid; unsigned int changes = 0; int ret = 0; time_t time_before_sync; guid_128_t expunged_guid_128; enum mail_flags private_flags_mask; bool expunged, full_rescan = FALSE; i_assert(!mbox->syncing_commit); first_uid = 1; hdr = mail_index_get_header(view); uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist); if (uid_validity != hdr->uid_validity && uid_validity != 0 && hdr->uid_validity != 0) { /* uidvalidity changed and index isn't being synced for the first time, reset the index so we can add all messages as new */ i_warning("Maildir %s: UIDVALIDITY changed (%u -> %u)", mailbox_get_path(&ctx->mbox->box), hdr->uid_validity, uid_validity); mail_index_reset(trans); mailbox_recent_flags_reset(&mbox->box); first_uid = hdr->messages_count + 1; memset(&empty_hdr, 0, sizeof(empty_hdr)); empty_hdr.next_uid = 1; hdr = &empty_hdr; } hdr_next_uid = hdr->next_uid; ctx->mbox->box.tmp_sync_view = view; private_flags_mask = mailbox_get_private_flags_mask(&mbox->box); time_before_sync = time(NULL); mbox->syncing_commit = TRUE; seq = prev_uid = 0; first_recent_uid = I_MAX(hdr->first_recent_uid, 1); i_array_init(&ctx->keywords, MAILDIR_MAX_KEYWORDS); i_array_init(&ctx->idx_keywords, MAILDIR_MAX_KEYWORDS); iter = maildir_uidlist_iter_init(mbox->uidlist); while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) { maildir_filename_flags_get(ctx->keywords_sync_ctx, filename, &ctx->flags, &ctx->keywords); i_assert(uid > prev_uid); prev_uid = uid; /* the private flags are kept only in indexes. don't use them at all even for newly seen mails */ ctx->flags &= ~private_flags_mask; again: seq++; ctx->uid = uid; if (seq > hdr->messages_count) { if (uid < hdr_next_uid) { if (maildir_handle_uid_insertion(ctx, uflags, filename, uid) < 0) ret = -1; seq--; continue; } /* Trust uidlist recent flags only for newly added messages. When saving/copying messages with flags they're stored to cur/ and uidlist treats them as non-recent. */ if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) == 0) { if (uid >= first_recent_uid) first_recent_uid = uid + 1; } hdr_next_uid = uid + 1; mail_index_append(trans, uid, &seq); mail_index_update_flags(trans, seq, MODIFY_REPLACE, ctx->flags); if (array_count(&ctx->keywords) > 0) { struct mail_keywords *kw; kw = mail_index_keywords_create_from_indexes( mbox->box.index, &ctx->keywords); mail_index_update_keywords(trans, seq, MODIFY_REPLACE, kw); mail_index_keywords_unref(&kw); } continue; } rec = mail_index_lookup(view, seq); if (uid > rec->uid) { /* already expunged (no point in showing guid in the expunge record anymore) */ mail_index_expunge(ctx->trans, seq); goto again; } if (uid < rec->uid) { if (maildir_handle_uid_insertion(ctx, uflags, filename, uid) < 0) ret = -1; seq--; continue; } index_sync_changes_read(ctx->sync_changes, ctx->uid, &expunged, expunged_guid_128); if (expunged) { if (!maildir_expunge_is_valid_guid(ctx, ctx->uid, filename, expunged_guid_128)) continue; if (maildir_file_do(mbox, ctx->uid, maildir_expunge, ctx) >= 0) { /* successful expunge */ mail_index_expunge(ctx->trans, seq); } if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0) maildir_sync_notify(ctx->maildir_sync_ctx); continue; } /* the private flags are stored only in indexes, keep them */ ctx->flags |= rec->flags & private_flags_mask; if (index_sync_changes_have(ctx->sync_changes)) { /* apply flag changes to maildir */ if (maildir_file_do(mbox, ctx->uid, maildir_sync_flags, ctx) < 0) ctx->flags |= MAIL_INDEX_MAIL_FLAG_DIRTY; if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0) maildir_sync_notify(ctx->maildir_sync_ctx); } if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) { /* partial syncing */ if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) { /* we last saw this mail in new/, but it's not there anymore. possibly expunged, make sure. */ full_rescan = TRUE; } continue; } if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) { /* we haven't been able to update maildir with this record's flag changes. don't sync them. */ continue; } if (ctx->flags != (rec->flags & MAIL_FLAGS_NONRECENT)) { mail_index_update_flags(trans, seq, MODIFY_REPLACE, ctx->flags); } maildir_sync_mail_keywords(ctx, seq); } maildir_uidlist_iter_deinit(&iter); if (!partial) { /* expunge the rest */ for (seq++; seq <= hdr->messages_count; seq++) mail_index_expunge(ctx->trans, seq); } /* add \Recent flags. use updated view so it contains newly appended messages. */ view2 = mail_index_transaction_open_updated_view(trans); if (mail_index_lookup_seq_range(view2, first_recent_uid, (uint32_t)-1, &seq, &seq2) && seq2 >= first_uid) { if (seq < first_uid) { /* UIDVALIDITY changed, skip over the old messages */ seq = first_uid; } mailbox_recent_flags_set_seqs(&mbox->box, view2, seq, seq2); } mail_index_view_close(&view2); if (ctx->uidlist_sync_ctx != NULL) { if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, TRUE) < 0) ret = -1; } if (mbox->box.v.sync_notify != NULL) mbox->box.v.sync_notify(&mbox->box, 0, 0); ctx->mbox->box.tmp_sync_view = NULL; /* check cur/ mtime later. if we came here from saving messages they could still be moved to cur/ directory. */ ctx->update_maildir_hdr_cur = TRUE; mbox->maildir_hdr.cur_check_time = time_before_sync; if (uid_validity == 0) { uid_validity = hdr->uid_validity != 0 ? hdr->uid_validity : maildir_get_uidvalidity_next(mbox->box.list); maildir_uidlist_set_uid_validity(mbox->uidlist, uid_validity); } maildir_uidlist_set_next_uid(mbox->uidlist, hdr_next_uid, FALSE); if (uid_validity != hdr->uid_validity) { mail_index_update_header(trans, offsetof(struct mail_index_header, uid_validity), &uid_validity, sizeof(uid_validity), TRUE); }