mbox-sync.c revision aa0647f2debf0d48d504a321186f66c85596aaf4
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (C) 2004 Timo Sirainen */
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen Modifying mbox can be slow, so we try to do it all at once minimizing the
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen required disk I/O. We may need to:
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Update message flags in Status, X-Status and X-Keywords headers
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Write missing X-UID and X-IMAPbase headers
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Write missing or broken Content-Length header if there's space
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Expunge specified messages
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen Here's how we do it:
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Start reading the mails from the beginning
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - X-Keywords, X-UID and X-IMAPbase headers may contain padding at the end
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen of them, remember how much each message has and offset to beginning of the
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - If header needs to be rewritten and there's enough space, do it
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - If we didn't have enough space, remember how much was missing
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Continue reading and counting the padding in each message. If available
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen padding is enough to rewrite all the previous messages needing it, do it
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - When we encounter expunged message, treat all of it as padding and
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen rewrite previous messages if needed (and there's enough space).
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen Afterwards keep moving messages backwards to fill the expunged space.
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen Moving is done by rewriting each message's headers, with possibly adding
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen missing Content-Length header and padding. Message bodies are moved
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen without modifications.
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - If we encounter end of file, grow the file and rewrite needed messages
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen - Rewriting is done by moving message body forward, rewriting message's
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen header and doing the same for previous message, until all of them are
79042f8c2ec1778528584c064b164d1ebcdde16bTimo Sirainen/* The text below was taken exactly as c-client wrote it to my mailbox,
79042f8c2ec1778528584c064b164d1ebcdde16bTimo Sirainen so it's probably copyrighted by University of Washington. */
79042f8c2ec1778528584c064b164d1ebcdde16bTimo Sirainen"This text is part of the internal format of your mail folder, and is not\n" \
79042f8c2ec1778528584c064b164d1ebcdde16bTimo Sirainen"a real message. It is created automatically by the mail system software.\n" \
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen"If deleted, important folder data will be lost, and it will be re-created\n" \
79042f8c2ec1778528584c064b164d1ebcdde16bTimo Sirainen"with the data reset to initial values.\n"
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainenvoid mbox_sync_set_critical(struct mbox_sync_context *sync_ctx,
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen const char *fmt, ...)
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen "mbox file %s was modified while we were syncing, "
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen "check your locking settings", sync_ctx->mbox->path);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainenint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen "Unexpectedly lost From-line at offset %"PRIuUOFF_T
79042f8c2ec1778528584c064b164d1ebcdde16bTimo Sirainenvoid mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx)
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen /* Do this even if ext_modified is already set. Expunging code relies
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen on last_stat being updated. */
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen if (st.st_size != sync_ctx->last_stat.st_size ||
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen /* nanoseconds give better precision to this check if they're
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen supported by the OS */
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen || st.st_mtim.tv_nsec != sync_ctx->last_stat.st_mtim.tv_nsec
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainenvoid mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty)
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen /* just mark the stat as dirty. */
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen if (fstat(sync_ctx->write_fd, &sync_ctx->last_stat) < 0)
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainenstatic void mbox_sync_array_delete_to(ARRAY_TYPE(sync_recs) *syncs_arr,
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen syncs = array_get_modifiable(syncs_arr, &count);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen /* keep it */
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainenmbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen /* get EOF */
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset ||
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 &&
2028d80c2704bbf62b29b2c624b0ee3c3a03c462Timo Sirainen /* need to add 'O' flag to Status-header */
unsigned int i, count;
for (i = 0; i < count; i++) {
return TRUE;
return FALSE;
int ret;
if (uid == 0) {
if (ret < 0) {
if (ret == 0) {
if (seq1 == 0)
(enum mail_flags)
if (!*sync_expunge_r)
unsigned int i, count;
for (i = 0; i < count; i++) {
int ret = 0;
if (ret < 0) {
return ret;
unsigned char hdr_md5_sum[],
const void *data;
int ret;
if (ret < 0) {
&data) < 0) {
bool nocheck)
const void *data;
if (!nocheck) {
&data) < 0) {
const void *ext_data;
&ext_data) < 0) {
t_push();
t_pop();
&sync_type);
MAIL_RECENT) != 0) {
MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
MAIL_INDEX_MAIL_FLAG_DIRTY) != 0;
(enum mail_flags)
t_pop();
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 (ret == 0) {
old_offset) < 0) {
uid = 0;
if (seq1 == 0) {
bool *skipped_mails)
int ret;
if (ret == 0) {
return ret;
bool partial)
int ret;
if (ret <= 0)
return ret;
uid = 0;
if (uid != 0) {
if (ret < 0)
if (ret == 0) {
uid = 0;
} else if (uid == 0 &&
&rec) < 0)
&expunged) < 0)
if (!expunged) {
if (!expunged) {
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) {
(unsigned int)ioloop_time;
if (ret > 0)
if (ret < 0)
unsigned int lock_id = 0;
bool delay_writes;
if (!changed)
lock_id = 0;
if (changed) {
if (ret <= 0) {
if (ret < 0)
if (lock_id != 0)
return ret;
if (lock_id != 0)
bool expunged;
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;
ioloop_time) {