mbox-sync.c revision 1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2004 Timo Sirainen */
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen Modifying mbox can be slow, so we try to do it all at once minimizing the
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen required disk I/O. We may need to:
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen - Update message flags in Status, X-Status and X-Keywords headers
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen - Write missing X-UID and X-IMAPbase headers
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Write missing or broken Content-Length header if there's space
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen - Expunge specified messages
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen Here's how we do it:
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen - Start reading the mails from the beginning
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen - X-Keywords, X-UID and X-IMAPbase headers may contain padding at the end
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen of them, remember how much each message has and offset to beginning of the
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen - If header needs to be rewritten and there's enough space, do it
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen - If we didn't have enough space, remember how much was missing
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen - Continue reading and counting the padding in each message. If available
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen padding is enough to rewrite all the previous messages needing it, do it
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen - When we encounter expunged message, treat all of it as padding and
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen rewrite previous messages if needed (and there's enough space).
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen Afterwards keep moving messages backwards to fill the expunged space.
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen Moving is done by rewriting each message's headers, with possibly adding
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen missing Content-Length header and padding. Message bodies are moved
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen without modifications.
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen - If we encounter end of file, grow the file and rewrite needed messages
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen - Rewriting is done by moving message body forward, rewriting message's
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen header and doing the same for previous message, until all of them are
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen/* The text below was taken exactly as c-client wrote it to my mailbox,
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen so it's probably copyrighted by University of Washington. */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen"This text is part of the internal format of your mail folder, and is not\n" \
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen"a real message. It is created automatically by the mail system software.\n" \
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen"If deleted, important folder data will be lost, and it will be re-created\n" \
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen"with the data reset to initial values.\n"
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid mbox_sync_set_critical(struct mbox_sync_context *sync_ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *fmt, ...)
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen "mbox file %s was modified while we were syncing, "
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen "check your locking settings", sync_ctx->mbox->path);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainenint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
872b8fd8a8db97dc54067b7ab25bda96ec0aac0dTimo Sirainen "Unexpectedly lost From-line at offset %"PRIuUOFF_T
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainenvoid mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* Do this even if ext_modified is already set. Expunging code relies
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen on last_stat being updated. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen if (st.st_size != sync_ctx->last_stat.st_size ||
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenvoid mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* just mark the stat as dirty. */
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen if (fstat(sync_ctx->write_fd, &sync_ctx->last_stat) < 0)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainenmbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen /* get EOF */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
5df33e9ee65eec194105b338c55dedbf8422f695Timo Sirainen mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset ||
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
77af0bd168cf3e3ddc3ae68abc82bfad7e9b5ff4Timo Sirainen if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 &&
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* need to add 'O' flag to Status-header */
77af0bd168cf3e3ddc3ae68abc82bfad7e9b5ff4Timo Sirainenstatic int mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* nothing for this or the future ones */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen if (index_sync_changes_read(sync_ctx->sync_changes, uid,
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen /* we can't expunge anything from read-only mboxes */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenmbox_sync_read_index_rec(struct mbox_sync_context *sync_ctx,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen uint32_t uid, const struct mail_index_record **rec_r)
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen mail_index_view_get_messages_count(sync_ctx->sync_view);
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen i_assert(ret != 0); /* we should be looking at head index */
1952eb389b8aba39195380970f905dcebea38dfcTimo Sirainen /* externally expunged message, remove from index */
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (rec == NULL && uid < sync_ctx->idx_next_uid) {
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen /* this UID was already in index and it was expunged */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "mbox sync: Expunged message reappeared in mailbox %s "
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "(UID %u < %u, seq=%u, idx_msgs=%u)",
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen sync_ctx->mbox->path, uid, sync_ctx->idx_next_uid,
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen /* new UID in the middle of the mailbox - shouldn't happen */
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen "mbox sync: UID inserted in the middle of mailbox %s "
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen "(%u > %u, seq=%u, idx_msgs=%u)", sync_ctx->mbox->path,
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen rec->uid, uid, sync_ctx->seq, messages_count);
d22301419109ed4a38351715e6760011421dadecTimo Sirainenstatic int mbox_sync_find_index_md5(struct mbox_sync_context *sync_ctx,
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen unsigned char hdr_md5_sum[],
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch mail_index_view_get_messages_count(sync_ctx->sync_view);
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view,
644268f7848a7c4221146d0b11feb8ed5bbed233Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (data != NULL && memcmp(data, hdr_md5_sum, 16) == 0)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* externally expunged message, remove from index */
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
70612e07102b75a8511aa7f9de60771176b18de0Timo Sirainenmbox_sync_update_from_offset(struct mbox_sync_context *sync_ctx,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* see if from_offset needs updating */
946f22af116d5af80d5bbe1710ac121aa5acef71Stephan Bosch if (mail_index_lookup_ext(sync_ctx->sync_view,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen *((const uint64_t *)data) == mail->from_offset)
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainenmbox_sync_update_index_keywords(struct mbox_sync_mail_context *mail_ctx)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen keywords = !array_is_created(&mail_ctx->mail.keywords) ?
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen mail_index_keywords_create(sync_ctx->t, NULL) :
d22301419109ed4a38351715e6760011421dadecTimo Sirainen mail_index_keywords_create_from_indexes(sync_ctx->t,
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen mail_index_update_keywords(sync_ctx->t, sync_ctx->idx_seq,
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainenmbox_sync_update_md5_if_changed(struct mbox_sync_mail_context *mail_ctx)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen memcmp(mail_ctx->hdr_md5_sum, ext_data, 16) != 0) {
203560029e3ad8687c2c759e6a81ecdb8b37ebe6Timo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
644268f7848a7c4221146d0b11feb8ed5bbed233Timo Sirainenstatic int mbox_sync_update_index(struct mbox_sync_mail_context *mail_ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen struct mbox_sync_mail *mail = &mail_ctx->mail;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen struct mailbox *box = &sync_ctx->mbox->ibox.box;
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen mbox_flags = mail->flags & MAIL_FLAGS_NONRECENT;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* new message */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mail_index_append(sync_ctx->t, mail->uid, &sync_ctx->idx_seq);
7a54d58280aad8a64f266c61273ea1e8dff511a3Timo Sirainen mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
4d0d535efdfc4aad3bd48b74adfafecf58094e0aTimo Sirainen /* see if we need to update flags in index file. the flags in
4d0d535efdfc4aad3bd48b74adfafecf58094e0aTimo Sirainen sync records are automatically applied to rec->flags at the
4d0d535efdfc4aad3bd48b74adfafecf58094e0aTimo Sirainen end of index syncing, so calculate those new flags first */
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen /* get old keywords */
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen if (mail_index_lookup_keywords(sync_ctx->sync_view,
e54512a5189192fe72d1e2c53927c98c5ac920b4Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen index_sync_changes_apply(sync_ctx->sync_changes,
49be238e250e99af8c69321264a461d8f6ceef62Timo Sirainen if (sync_type != 0 && box->v.sync_notify != NULL)
489301ee88b2174e3171875e979e667de2c4a174Timo Sirainen if ((idx_mail.flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
8b5b1f6cb19253dfd7821fcef8e9b7e95e6caf3aTimo Sirainen /* flags are dirty. ignore whatever was in the mbox,
8b5b1f6cb19253dfd7821fcef8e9b7e95e6caf3aTimo Sirainen but update dirty flag state if needed. */
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen if ((idx_mail.flags & ~MAIL_INDEX_MAIL_FLAG_DIRTY) !=
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen /* flags other than recent/dirty have changed */
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen /* dirty flag state changed */
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen if ((idx_mail.flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0 &&
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen /* see if we need to update md5 sum. */
1045a1d4c191a14867cde0d5cea9e4ac5e36f85fTimo Sirainen if (mbox_sync_update_md5_if_changed(mail_ctx) < 0)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen sync_ctx->last_nonrecent_idx_seq = sync_ctx->idx_seq;
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen /* update from_offsets, but not if we're going to rewrite this message.
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen rewriting would just move it anyway. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bool nocheck = rec == NULL || sync_ctx->expunged_space > 0;
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen if (mbox_sync_update_from_offset(sync_ctx, mail, nocheck) < 0)
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainenstatic int mbox_read_from_line(struct mbox_sync_mail_context *ctx)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct istream *input = ctx->sync_ctx->file_input;
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen const unsigned char *data;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen buffer_set_used_size(ctx->sync_ctx->from_line, 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen from_line_size = ctx->hdr_offset - ctx->mail.from_offset;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen buffer_append(ctx->sync_ctx->from_line, data, size);
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainenstatic int mbox_rewrite_base_uid_last(struct mbox_sync_context *sync_ctx)
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen const char *str;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int i;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(sync_ctx->base_uid_last_offset != 0);
f95b3d29bc56f139c18c880aa574a0ca72b0cffbTimo Sirainen /* first check that the 10 bytes are there and they're exactly as
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen expected. just an extra safety check to make sure we never write
00db1d630a723113609598e28acbae4d416e0cb4Timo Sirainen to wrong location in the mbox file. */
50de46721446795c42943c572625f2f1a9abfe01Timo Sirainen ret = pread_full(sync_ctx->write_fd, buf, sizeof(buf),
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "pread_full()");
d052dcfff0c96a0af17a3158e51f709edf4b93a1Timo Sirainen "X-IMAPbase uid-last unexpectedly points outside "
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen for (i = 0, uid_last = 0; i < sizeof(buf); i++) {
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen "X-IMAPbase uid-last unexpectedly lost in mbox file %s",
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen /* and write it */
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen str = t_strdup_printf("%010u", sync_ctx->next_uid - 1);
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()");
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->base_uid_last = sync_ctx->next_uid - 1;
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainenmbox_write_from_line(struct mbox_sync_mail_context *ctx)
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen if (pwrite_full(ctx->sync_ctx->write_fd, str_data(str), str_len(str),
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen mbox_set_syscall_error(ctx->sync_ctx->mbox, "pwrite_full()");
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainenstatic void update_from_offsets(struct mbox_sync_context *sync_ctx)
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen unsigned int i, count;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen for (i = 0; i < count; i++) {
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen if (mails[i].idx_seq == 0 || mails[i].expunged)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen mail_index_update_ext(sync_ctx->t, mails[i].idx_seq,
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainenstatic void mbox_sync_handle_expunge(struct mbox_sync_mail_context *mail_ctx)
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mailbox *box = &sync_ctx->mbox->ibox.box;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_index_expunge(sync_ctx->t, mail_ctx->mail.idx_seq);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_ctx->mail.offset = mail_ctx->mail.from_offset;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mail_ctx->body_offset - mail_ctx->mail.from_offset +
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen /* expunging first message, fix space to contain next
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen message's \n header too since it will be removed. */
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen if (istream_raw_mbox_has_crlf_ending(sync_ctx->input)) {
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen /* uid-last offset is invalid now */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen sync_ctx->expunged_space += mail_ctx->mail.space;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainenstatic int mbox_sync_handle_header(struct mbox_sync_mail_context *mail_ctx)
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen if (sync_ctx->expunged_space > 0 && sync_ctx->need_space_seq == 0) {
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* move the header backwards to fill expunged space */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen orig_from_offset = mail_ctx->mail.from_offset;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* we're moving this mail to beginning of file.
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen skip the initial \n (it's already counted in
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen expunged_space) */
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen /* read the From-line before rewriting overwrites it */
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* rewrite successful, write From-line to
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen new location */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* didn't have enough space, move the offset
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen back so seeking into it doesn't fail */
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen mail_ctx->mail.from_offset = orig_from_offset;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen index_sync_changes_have(sync_ctx->sync_changes)) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* mark it dirty and do it later */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if ((ret = mbox_sync_try_rewrite(mail_ctx, 0)) < 0)
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen /* nothing to do */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret == 0 && sync_ctx->need_space_seq == 0) {
e3411c496000d3e2797b43a33584dfba954e815eTimo Sirainen /* first mail with no space to write it */
d8a7046624a082938501e8268ed0cdcba4826e96Timo Sirainen /* create dummy message to describe the expunged data */
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen sync_ctx->space_diff = sync_ctx->expunged_space;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen i_assert(sync_ctx->space_diff < -mail_ctx->mail.space);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainenmbox_sync_handle_missing_space(struct mbox_sync_mail_context *mail_ctx)
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen uoff_t end_offset, move_diff, extra_space, needed_space;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen i_assert(mail_ctx->mail.uid == 0 || mail_ctx->mail.space > 0 ||
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen mail_ctx->mail.offset == mail_ctx->hdr_offset);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen if (array_is_created(&mail_ctx->mail.keywords)) {
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen /* mail's keywords are allocated from a pool that's cleared
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen for each mail. we'll need to copy it to something more
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen permanent. */
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen p_array_init(&keywords_copy, sync_ctx->saved_keywords_pool,
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen array_append_array(&keywords_copy, &mail_ctx->mail.keywords);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen array_append(&sync_ctx->mails, &mail_ctx->mail, 1);
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen /* we have enough space now */
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen /* this message was expunged. fill more or less of the space.
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen space_diff now consists of a negative "bytes needed" sum,
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen plus the expunged space of this message. so it contains how
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen many bytes of _extra_ space we have. */
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen i_assert(mail_ctx->mail.space >= sync_ctx->space_diff);
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen (sync_ctx->seq - sync_ctx->need_space_seq + 1);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen needed_space = mail_ctx->mail.space - sync_ctx->space_diff;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen if ((uoff_t)sync_ctx->space_diff > needed_space + extra_space) {
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen /* don't waste too much on padding */
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen /* this message gave enough space from headers. rewriting stops
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen at the end of this message's headers. */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* mail_ctx may contain wrong data after rewrite, so make sure we
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen don't try to access it */
990d55ce3fc461eeacce3ef830b1c5dde5c3f150Josef 'Jeff' Sipekmbox_sync_seek_to_seq(struct mbox_sync_context *sync_ctx, uint32_t seq)
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen if (istream_raw_mbox_seek(mbox->mbox_stream, 0) < 0) {
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
d48ab236010e588c7b52e54db47fe9842a2e27e8Timo Sirainen "Mailbox isn't a valid mbox file");
073f965351846b8c97347b882c441dc336965e26Timo Sirainen old_offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
073f965351846b8c97347b882c441dc336965e26Timo Sirainen ret = mbox_file_seek(mbox, sync_ctx->sync_view, seq, &deleted);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen "Message was expunged unexpectedly "
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen "Error seeking back to original "
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen "offset %s in mbox file %s",
073f965351846b8c97347b882c441dc336965e26Timo Sirainen else if (mail_index_lookup_uid(sync_ctx->sync_view, seq-1, &uid) < 0) {
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen /* set to -1, since it's always increased later */
073f965351846b8c97347b882c441dc336965e26Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input) != 0) {
073f965351846b8c97347b882c441dc336965e26Timo Sirainen /* this mbox has pseudo mail which contains the X-IMAP header */
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen sync_ctx->dest_first_mail = sync_ctx->seq == 0;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen (void)istream_raw_mbox_get_body_offset(sync_ctx->input);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainenmbox_sync_seek_to_uid(struct mbox_sync_context *sync_ctx, uint32_t uid)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen struct mail_index_view *sync_view = sync_ctx->sync_view;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mail_index_lookup_uid_range(sync_view, uid, (uint32_t)-1,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* doesn't exist anymore, seek to end of file */
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen st = i_stream_stat(sync_ctx->file_input, TRUE);
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen "i_stream_stat()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (istream_raw_mbox_seek(sync_ctx->mbox->mbox_stream,
19a1cfc537d979c532fac71264dba0b9dabc65d9Timo Sirainen "Error seeking to end of mbox file %s",
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mail_index_view_get_messages_count(sync_view) + 1;
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainenstatic int mbox_sync_partial_seek_next(struct mbox_sync_context *sync_ctx,
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* delete sync records up to next message. so if there's still
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen something left in array, it means the next message needs modifying */
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen index_sync_changes_delete_to(sync_ctx->sync_changes, next_uid);
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen if (index_sync_changes_have(sync_ctx->sync_changes))
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen if (sync_ctx->hdr->first_recent_uid <= next_uid &&
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* we'll need to rewrite Status: O headers */
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen uid = index_sync_changes_get_next_uid(sync_ctx->sync_changes);
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen if (sync_ctx->hdr->first_recent_uid < sync_ctx->hdr->next_uid &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (uid > sync_ctx->hdr->first_recent_uid || uid == 0) &&
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen /* we'll need to rewrite Status: O headers */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* we can skip forward to next record which needs updating. */
if (ret == 0) {
return ret;
return TRUE;
return FALSE;
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) {
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;
ioloop_time) {