mbox-sync.c revision 145ecefd208f5d7f6a52cb326a85f28d2154e217
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Modifying mbox can be slow, so we try to do it all at once minimizing the
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa required disk I/O. We may need to:
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa - Update message flags in Status, X-Status and X-Keywords headers
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa - Write missing X-UID and X-IMAPbase headers
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa - Write missing or broken Content-Length header if there's space
b1fe9054ad7c7192fe4c474363247dad15963e99Eugen Kuksa - Expunge specified messages
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa Here's how we do it:
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa - Start reading the mails from the beginning
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa - X-Keywords, X-UID and X-IMAPbase headers may contain padding at the end
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa of them, remember how much each message has and offset to beginning of the
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa - If header needs to be rewritten and there's enough space, do it
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa - If we didn't have enough space, remember how much was missing
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa - Continue reading and counting the padding in each message. If available
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa padding is enough to rewrite all the previous messages needing it, do it
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa - When we encounter expunged message, treat all of it as padding and
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa rewrite previous messages if needed (and there's enough space).
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Afterwards keep moving messages backwards to fill the expunged space.
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa Moving is done by rewriting each message's headers, with possibly adding
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa missing Content-Length header and padding. Message bodies are moved
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa without modifications.
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa - If we encounter end of file, grow the file and rewrite needed messages
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa - Rewriting is done by moving message body forward, rewriting message's
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa header and doing the same for previous message, until all of them are
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa/* The text below was taken exactly as c-client wrote it to my mailbox,
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa so it's probably copyrighted by University of Washington. */
923d69139038e74c0936e826bbfdc8717fbbc7b3Eugen Kuksa"This text is part of the internal format of your mail folder, and is not\n" \
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa"a real message. It is created automatically by the mail system software.\n" \
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa"If deleted, important folder data will be lost, and it will be re-created\n" \
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa"with the data reset to initial values.\n"
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksavoid mbox_sync_set_critical(struct mbox_sync_context *sync_ctx,
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa const char *fmt, ...)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa "mbox file %s was modified while we were syncing, "
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa "check your locking settings", sync_ctx->mbox->path);
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksaint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa "Unexpectedly lost From-line at offset %"PRIuUOFF_T
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksavoid mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa /* Do this even if ext_modified is already set. Expunging code relies
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa on last_stat being updated. */
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa if (st.st_size != sync_ctx->last_stat.st_size ||
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksavoid mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa /* just mark the stat as dirty. */
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa if (fstat(sync_ctx->write_fd, &sync_ctx->last_stat) < 0)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksambox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa /* get EOF */
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa istream_raw_mbox_get_start_offset(sync_ctx->input);
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa istream_raw_mbox_get_header_offset(sync_ctx->input);
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset ||
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 &&
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa /* need to add 'O' flag to Status-header */
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksastatic void mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa /* nothing for this or the future ones */
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa index_sync_changes_read(sync_ctx->sync_changes, uid, sync_expunge_r);
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa /* we can't expunge anything from read-only mboxes */
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksambox_sync_read_index_rec(struct mbox_sync_context *sync_ctx,
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa uint32_t uid, const struct mail_index_record **rec_r)
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa mail_index_view_get_messages_count(sync_ctx->sync_view);
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa rec = mail_index_lookup(sync_ctx->sync_view, sync_ctx->idx_seq);
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa /* externally expunged message, remove from index */
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa if (rec == NULL && uid < sync_ctx->idx_next_uid) {
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa /* this UID was already in index and it was expunged */
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa "mbox sync: Expunged message reappeared in mailbox %s "
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa "(UID %u < %u, seq=%u, idx_msgs=%u)",
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa sync_ctx->mbox->path, uid, sync_ctx->idx_next_uid,
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa /* new UID in the middle of the mailbox - shouldn't happen */
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa "mbox sync: UID inserted in the middle of mailbox %s "
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa "(%u > %u, seq=%u, idx_msgs=%u)", sync_ctx->mbox->path,
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksastatic void mbox_sync_find_index_md5(struct mbox_sync_context *sync_ctx,
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa unsigned char hdr_md5_sum[],
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa const void *data;
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa mail_index_view_get_messages_count(sync_ctx->sync_view);
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa rec = mail_index_lookup(sync_ctx->sync_view, sync_ctx->idx_seq);
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa if (data != NULL && memcmp(data, hdr_md5_sum, 16) == 0)
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa /* externally expunged message, remove from index */
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksambox_sync_update_from_offset(struct mbox_sync_context *sync_ctx,
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa const void *data;
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa /* see if from_offset needs updating */
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq,
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
const void *ext_data;
&idx_keywords);
MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
bool dirty;
const unsigned char *data;
if (from_line_size == 0)
const char *str;
int ret;
if (ret < 0) {
if (ret == 0) {
unsigned int i, count;
for (i = 0; i < count; i++) {
int ret;
if (ret < 0)
if (ret > 0) {
-move_diff);
move_diff = 0;
int ret;
bool deleted;
if (seq == 0) {
seq++;
if (ret < 0) {
if (deleted) {
if (ret == 0) {
old_offset) < 0) {
uid = 0;
bool *skipped_mails)
int ret;
if (uid != 0) {
if (ret == 0) {
return ret;
return TRUE;
return FALSE;
bool partial)
int ret;
if (ret <= 0)
return ret;
uid = 0;
if (uid != 0) {
ret = 0;
if (ret == 0) {
uid = 0;
} else if (uid == 0 &&
&expunged);
if (!expunged) {
} T_END;
} T_END;
if (!expunged) {
} else if (partial) {
&partial,
if (ret <= 0) {
if (ret < 0)
if (!skipped_mails)
unsigned int uid_validity;
trailer_size = 0;
trailer_size) < 0)
if (offset == 0) {
if (ret < 0)
bool empty;
bool *empty_r)
unsigned int lock_id = 0;
bool delay_writes;
if (!changed)
lock_id = 0;
if (changed) {
sync_flags = 0;
if (ret <= 0) {
if (ret < 0)
if (lock_id != 0)
return ret;
if (lock_id != 0)
bool expunged;
if (uid == 0) {
goto nothing_to_do;
if (lock_id == 0) {
goto again;
if (ret < 0)
unsigned int read_lock_id = 0;
return ret;
int ret;
return ret;
struct mailbox_sync_context *
int ret = 0;