mbox-sync.c revision 1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2004 Timo Sirainen */
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo 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
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
e54512a5189192fe72d1e2c53927c98c5ac920b4Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen Here's how we do it:
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
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
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen padding
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
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen rewritten.
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen*/
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "lib.h"
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen#include "ioloop.h"
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen#include "array.h"
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen#include "buffer.h"
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen#include "hostpid.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "istream.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "file-set-size.h"
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen#include "str.h"
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen#include "read-full.h"
8fb1e3e2349c9940732b5bb77a2a4053b8f72a4fTimo Sirainen#include "write-full.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "message-date.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "istream-raw-mbox.h"
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen#include "mbox-storage.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "index-sync-changes.h"
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen#include "mbox-from.h"
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen#include "mbox-file.h"
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen#include "mbox-lock.h"
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen#include "mbox-sync-private.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen#include <stddef.h>
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen#include <stdlib.h>
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen#include <utime.h>
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen#include <sys/stat.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
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#define PSEUDO_MESSAGE_BODY \
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"
5cda0bfea032000c4a51134c748d9efe6614870bTimo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid mbox_sync_set_critical(struct mbox_sync_context *sync_ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *fmt, ...)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen va_list va;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen if (sync_ctx->ext_modified) {
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 }
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen va_start(va, fmt);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen "%s", t_strdup_vprintf(fmt, va));
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen va_end(va);
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen}
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainenint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen{
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
8b12e7b44abca3bd51a1c46e19ca504f3b55e723Timo Sirainen mbox_sync_set_critical(sync_ctx,
872b8fd8a8db97dc54067b7ab25bda96ec0aac0dTimo Sirainen "Unexpectedly lost From-line at offset %"PRIuUOFF_T
872b8fd8a8db97dc54067b7ab25bda96ec0aac0dTimo Sirainen " from mbox file %s", from_offset,
872b8fd8a8db97dc54067b7ab25bda96ec0aac0dTimo Sirainen sync_ctx->mbox->path);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen return -1;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainenvoid mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen struct stat st;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* Do this even if ext_modified is already set. Expunging code relies
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen on last_stat being updated. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (fstat(sync_ctx->write_fd, &st) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen if (st.st_size != sync_ctx->last_stat.st_size ||
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen (sync_ctx->last_stat.st_mtime != 0 &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen !CMP_ST_MTIME(&st, &sync_ctx->last_stat)))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->ext_modified = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->last_stat = st;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenvoid mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (dirty) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* just mark the stat as dirty. */
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen sync_ctx->last_stat.st_mtime = 0;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen return;
5cda0bfea032000c4a51134c748d9efe6614870bTimo Sirainen }
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen if (fstat(sync_ctx->write_fd, &sync_ctx->last_stat) < 0)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen i_stream_sync(sync_ctx->input);
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen}
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainenstatic int
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainenmbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen struct mbox_sync_mail_context *mail_ctx)
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen{
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen /* get EOF */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen if (istream_raw_mbox_is_eof(sync_ctx->input))
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen return 0;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen p_clear(sync_ctx->mail_keyword_pool);
70612e07102b75a8511aa7f9de60771176b18de0Timo Sirainen memset(mail_ctx, 0, sizeof(*mail_ctx));
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen mail_ctx->sync_ctx = sync_ctx;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen mail_ctx->seq = ++sync_ctx->seq;
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen mail_ctx->header = sync_ctx->header;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen mail_ctx->mail.from_offset =
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
5df33e9ee65eec194105b338c55dedbf8422f695Timo Sirainen mail_ctx->mail.offset =
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch
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 sync_ctx->input->eof);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen mail_ctx->mail.body_size =
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen mail_ctx->content_length);
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen
77af0bd168cf3e3ddc3ae68abc82bfad7e9b5ff4Timo Sirainen if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 &&
77af0bd168cf3e3ddc3ae68abc82bfad7e9b5ff4Timo Sirainen !mail_ctx->mail.pseudo) {
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen if (!sync_ctx->mbox->ibox.keep_recent) {
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* need to add 'O' flag to Status-header */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen mail_ctx->need_rewrite = TRUE;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen mail_ctx->recent = TRUE;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen return 1;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen}
77af0bd168cf3e3ddc3ae68abc82bfad7e9b5ff4Timo Sirainen
77af0bd168cf3e3ddc3ae68abc82bfad7e9b5ff4Timo Sirainenstatic int mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen uint32_t uid, bool *sync_expunge_r)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen{
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen if (uid == 0 || sync_ctx->index_reset) {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* nothing for this or the future ones */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen uid = (uint32_t)-1;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen if (index_sync_changes_read(sync_ctx->sync_changes, uid,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen sync_expunge_r) < 0)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return -1;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (sync_ctx->mbox->mbox_readonly) {
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen /* we can't expunge anything from read-only mboxes */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen *sync_expunge_r = FALSE;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return 0;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen}
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenstatic int
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenmbox_sync_read_index_rec(struct mbox_sync_context *sync_ctx,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen uint32_t uid, const struct mail_index_record **rec_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen const struct mail_index_record *rec = NULL;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen uint32_t messages_count;
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen int ret = 0;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen if (sync_ctx->index_reset) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *rec_r = NULL;
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen return 1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen messages_count =
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen mail_index_view_get_messages_count(sync_ctx->sync_view);
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen while (sync_ctx->idx_seq <= messages_count) {
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen ret = mail_index_lookup(sync_ctx->sync_view,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->idx_seq, &rec);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret < 0) {
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen return -1;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen }
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen i_assert(ret != 0); /* we should be looking at head index */
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen if (uid <= rec->uid)
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen break;
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen
1952eb389b8aba39195380970f905dcebea38dfcTimo Sirainen /* externally expunged message, remove from index */
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen sync_ctx->idx_seq++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen rec = NULL;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (rec == NULL && uid < sync_ctx->idx_next_uid) {
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen /* this UID was already in index and it was expunged */
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen mbox_sync_set_critical(sync_ctx,
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,
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen sync_ctx->seq, messages_count);
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen ret = 0; rec = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else if (rec != NULL && rec->uid != uid) {
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen /* new UID in the middle of the mailbox - shouldn't happen */
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen mbox_sync_set_critical(sync_ctx,
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);
45b2a27617d8475f71fdfc870690e46cd63849f2Timo Sirainen ret = 0; rec = NULL;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } else {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = 1;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen }
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen *rec_r = rec;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen return ret;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen}
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainenstatic int mbox_sync_find_index_md5(struct mbox_sync_context *sync_ctx,
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen unsigned char hdr_md5_sum[],
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen const struct mail_index_record **rec_r)
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen{
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen const struct mail_index_record *rec = NULL;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen uint32_t messages_count;
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen const void *data;
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen int ret;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen if (sync_ctx->index_reset) {
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen *rec_r = NULL;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen return 0;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen }
70612e07102b75a8511aa7f9de60771176b18de0Timo Sirainen
70612e07102b75a8511aa7f9de60771176b18de0Timo Sirainen messages_count =
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch mail_index_view_get_messages_count(sync_ctx->sync_view);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen while (sync_ctx->idx_seq <= messages_count) {
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen ret = mail_index_lookup(sync_ctx->sync_view,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->idx_seq, &rec);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen if (ret < 0) {
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
469a6cf705835ade57dcb59979b6e054207ae5d7Timo Sirainen return -1;
469a6cf705835ade57dcb59979b6e054207ae5d7Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view,
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen sync_ctx->idx_seq,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->mbox->ibox.md5hdr_ext_idx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &data) < 0) {
644268f7848a7c4221146d0b11feb8ed5bbed233Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen return -1;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen }
469a6cf705835ade57dcb59979b6e054207ae5d7Timo Sirainen
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (data != NULL && memcmp(data, hdr_md5_sum, 16) == 0)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen break;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* externally expunged message, remove from index */
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen sync_ctx->idx_seq++;
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen rec = NULL;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen *rec_r = rec;
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen return 0;
38d7db318188c4ac9cdc8c6cdb936b36a5258e19Timo Sirainen}
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
70612e07102b75a8511aa7f9de60771176b18de0Timo Sirainenstatic int
70612e07102b75a8511aa7f9de60771176b18de0Timo Sirainenmbox_sync_update_from_offset(struct mbox_sync_context *sync_ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mbox_sync_mail *mail,
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen bool nocheck)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const void *data;
df459621b9124dfd88d56619ac84611f30fec854Stephan Bosch uint64_t offset;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen if (!nocheck) {
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* see if from_offset needs updating */
946f22af116d5af80d5bbe1710ac121aa5acef71Stephan Bosch if (mail_index_lookup_ext(sync_ctx->sync_view,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen sync_ctx->idx_seq,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen sync_ctx->mbox->mbox_ext_idx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &data) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen return -1;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen }
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen if (data != NULL &&
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen *((const uint64_t *)data) == mail->from_offset)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen return 0;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen offset = mail->from_offset;
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen sync_ctx->mbox->mbox_ext_idx, &offset, NULL);
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen return 0;
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen}
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainenstatic void
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainenmbox_sync_update_index_keywords(struct mbox_sync_mail_context *mail_ctx)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen struct mail_keywords *keywords;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
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,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen &mail_ctx->mail.keywords);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen mail_index_update_keywords(sync_ctx->t, sync_ctx->idx_seq,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen MODIFY_REPLACE, keywords);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_keywords_free(&keywords);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen}
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainenstatic int
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainenmbox_sync_update_md5_if_changed(struct mbox_sync_mail_context *mail_ctx)
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen{
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen const void *ext_data;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->mbox->ibox.md5hdr_ext_idx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &ext_data) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
60576cd64e6a537413cd90104f7e862f71d48c81Timo Sirainen return -1;
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen }
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen if (ext_data == NULL ||
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,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->mbox->ibox.md5hdr_ext_idx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_ctx->hdr_md5_sum, NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen
644268f7848a7c4221146d0b11feb8ed5bbed233Timo Sirainenstatic int mbox_sync_update_index(struct mbox_sync_mail_context *mail_ctx,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen const struct mail_index_record *rec)
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen{
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;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen uint8_t mbox_flags;
80cdfe47daf6129410bafcecbe5c9faf09f2721bTimo Sirainen
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen mbox_flags = mail->flags & MAIL_FLAGS_NONRECENT;
80cdfe47daf6129410bafcecbe5c9faf09f2721bTimo Sirainen
80cdfe47daf6129410bafcecbe5c9faf09f2721bTimo Sirainen if (mail_ctx->dirty)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mbox_flags |= MAIL_INDEX_MAIL_FLAG_DIRTY;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen else if (!sync_ctx->delay_writes)
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen mbox_flags &= ~MAIL_INDEX_MAIL_FLAG_DIRTY;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (rec == NULL) {
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,
7a54d58280aad8a64f266c61273ea1e8dff511a3Timo Sirainen MODIFY_REPLACE, mbox_flags);
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen mbox_sync_update_index_keywords(mail_ctx);
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen
e54512a5189192fe72d1e2c53927c98c5ac920b4Timo Sirainen if (sync_ctx->mbox->mbox_save_md5 != 0) {
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
6f6f3dc5b33b09097192124bce17b7017d6bbfcfTimo Sirainen sync_ctx->mbox->ibox.md5hdr_ext_idx,
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen mail_ctx->hdr_md5_sum, NULL);
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen }
4d0d535efdfc4aad3bd48b74adfafecf58094e0aTimo Sirainen } else {
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 */
4d0d535efdfc4aad3bd48b74adfafecf58094e0aTimo Sirainen struct mbox_sync_mail idx_mail;
4d0d535efdfc4aad3bd48b74adfafecf58094e0aTimo Sirainen enum mailbox_sync_type sync_type;
6f6f3dc5b33b09097192124bce17b7017d6bbfcfTimo Sirainen
6f6f3dc5b33b09097192124bce17b7017d6bbfcfTimo Sirainen memset(&idx_mail, 0, sizeof(idx_mail));
6f6f3dc5b33b09097192124bce17b7017d6bbfcfTimo Sirainen idx_mail.flags = rec->flags & ~MAIL_RECENT;
e54512a5189192fe72d1e2c53927c98c5ac920b4Timo Sirainen
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen /* get old keywords */
49be238e250e99af8c69321264a461d8f6ceef62Timo Sirainen t_push();
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen t_array_init(&idx_mail.keywords, 32);
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen if (mail_index_lookup_keywords(sync_ctx->sync_view,
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen sync_ctx->idx_seq,
e54512a5189192fe72d1e2c53927c98c5ac920b4Timo Sirainen &idx_mail.keywords) < 0) {
e54512a5189192fe72d1e2c53927c98c5ac920b4Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen t_pop();
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen return -1;
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen }
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen index_sync_changes_apply(sync_ctx->sync_changes,
992118a50af940482b6cf884a89be56d7015580aTimo Sirainen sync_ctx->mail_keyword_pool,
8b5b1f6cb19253dfd7821fcef8e9b7e95e6caf3aTimo Sirainen &idx_mail.flags, &idx_mail.keywords,
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen &sync_type);
49be238e250e99af8c69321264a461d8f6ceef62Timo Sirainen if (sync_type != 0 && box->v.sync_notify != NULL)
49be238e250e99af8c69321264a461d8f6ceef62Timo Sirainen box->v.sync_notify(box, rec->uid, sync_type);
8b5b1f6cb19253dfd7821fcef8e9b7e95e6caf3aTimo Sirainen
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. */
49be238e250e99af8c69321264a461d8f6ceef62Timo Sirainen mbox_flags &= ~MAIL_INDEX_MAIL_FLAG_DIRTY;
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen mbox_flags |=
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen idx_mail.flags & ~MAIL_INDEX_MAIL_FLAG_DIRTY;
49be238e250e99af8c69321264a461d8f6ceef62Timo Sirainen if (sync_ctx->delay_writes)
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen mbox_flags |= MAIL_INDEX_MAIL_FLAG_DIRTY;
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen }
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen if ((idx_mail.flags & ~MAIL_INDEX_MAIL_FLAG_DIRTY) !=
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen (mbox_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,
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen MODIFY_REPLACE, mbox_flags);
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen } else {
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen if (((idx_mail.flags ^ mbox_flags) &
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen /* dirty flag state changed */
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen bool dirty = (mbox_flags &
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen MAIL_INDEX_MAIL_FLAG_DIRTY) != 0;
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen mail_index_update_flags(sync_ctx->t,
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen sync_ctx->idx_seq,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen dirty ? MODIFY_ADD : MODIFY_REMOVE,
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen (enum mail_flags)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen MAIL_INDEX_MAIL_FLAG_DIRTY);
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen }
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen }
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen if ((idx_mail.flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0 &&
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen !index_keyword_array_cmp(&idx_mail.keywords,
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen &mail_ctx->mail.keywords))
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen mbox_sync_update_index_keywords(mail_ctx);
2fa3e62e491621ca7a25211ecc6746c5823cacdeAki Tuomi t_pop();
2fa3e62e491621ca7a25211ecc6746c5823cacdeAki Tuomi
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen /* see if we need to update md5 sum. */
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen if (sync_ctx->mbox->mbox_save_md5 != 0) {
1045a1d4c191a14867cde0d5cea9e4ac5e36f85fTimo Sirainen if (mbox_sync_update_md5_if_changed(mail_ctx) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
dd0dea1fdd913a04bae16e82dd66d67571a5f6c2Timo Sirainen }
96541d31299bb40b5a6efdbf9b4cb3d4f4b4a069Timo Sirainen
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (!mail_ctx->recent) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen sync_ctx->last_nonrecent_idx_seq = sync_ctx->idx_seq;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sync_ctx->last_nonrecent_uid = mail->uid;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen
eed20b28dd9039d21f5c2770beef2e8b19f7c2f9Timo Sirainen /* update from_offsets, but not if we're going to rewrite this message.
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen rewriting would just move it anyway. */
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen if (sync_ctx->need_space_seq == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bool nocheck = rec == NULL || sync_ctx->expunged_space > 0;
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen if (mbox_sync_update_from_offset(sync_ctx, mail, nocheck) < 0)
50de46721446795c42943c572625f2f1a9abfe01Timo Sirainen return -1;
50de46721446795c42943c572625f2f1a9abfe01Timo Sirainen }
96541d31299bb40b5a6efdbf9b4cb3d4f4b4a069Timo Sirainen return 0;
55773f17bccf6361d6599ffcbe072d7c9fe205bfTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainenstatic int mbox_read_from_line(struct mbox_sync_mail_context *ctx)
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct istream *input = ctx->sync_ctx->file_input;
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen const unsigned char *data;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen size_t size, from_line_size;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen buffer_set_used_size(ctx->sync_ctx->from_line, 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen from_line_size = ctx->hdr_offset - ctx->mail.from_offset;
946f22af116d5af80d5bbe1710ac121aa5acef71Stephan Bosch
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_stream_seek(input, ctx->mail.from_offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (;;) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen data = i_stream_get_data(input, &size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (size >= from_line_size)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen size = from_line_size;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen buffer_append(ctx->sync_ctx->from_line, data, size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_stream_skip(input, size);
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen from_line_size -= size;
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (from_line_size == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen break;
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen if (i_stream_read(input) < 0)
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen return -1;
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen }
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen return 0;
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen}
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainenstatic int mbox_rewrite_base_uid_last(struct mbox_sync_context *sync_ctx)
3889d05019a072a602f7a8c1eeb8a6f1c1362720Timo Sirainen{
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen unsigned char buf[10];
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen const char *str;
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen uint32_t uid_last;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int i;
36977c4a74e164f7d81eb4785f0a5d3ff436fd19Timo Sirainen int ret;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(sync_ctx->base_uid_last_offset != 0);
00db1d630a723113609598e28acbae4d416e0cb4Timo Sirainen
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),
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen sync_ctx->base_uid_last_offset);
9db263f2b9ab771fbf9a2bff44a245c45eaef218Timo Sirainen if (ret < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "pread_full()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret == 0) {
045e41df5a72f7726aca03f6687cd79e7281fb4bTimo Sirainen mbox_sync_set_critical(sync_ctx,
d052dcfff0c96a0af17a3158e51f709edf4b93a1Timo Sirainen "X-IMAPbase uid-last unexpectedly points outside "
50de46721446795c42943c572625f2f1a9abfe01Timo Sirainen "mbox file %s", sync_ctx->mbox->path);
045e41df5a72f7726aca03f6687cd79e7281fb4bTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen for (i = 0, uid_last = 0; i < sizeof(buf); i++) {
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen if (buf[i] < '0' || buf[i] > '9') {
20195ef995a4eb63a282283db63f1dc0605323e0Timo Sirainen uid_last = (uint32_t)-1;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen break;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
645f258ea29afaf09b673fc65d1bd788dfec8db8Timo Sirainen uid_last = uid_last * 10 + (buf[i] - '0');
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen }
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen if (uid_last != sync_ctx->base_uid_last) {
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen mbox_sync_set_critical(sync_ctx,
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen "X-IMAPbase uid-last unexpectedly lost in mbox file %s",
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->mbox->path);
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen return -1;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen }
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen /* and write it */
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen str = t_strdup_printf("%010u", sync_ctx->next_uid - 1);
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen if (pwrite_full(sync_ctx->write_fd, str, 10,
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->base_uid_last_offset) < 0) {
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()");
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen return -1;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen }
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen mbox_sync_file_updated(sync_ctx, FALSE);
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->base_uid_last = sync_ctx->next_uid - 1;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen return 0;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen}
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainen
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainenstatic int
cf52b37d807553e91a2d6fb7cb2c8b4c34589e1dTimo Sirainenmbox_write_from_line(struct mbox_sync_mail_context *ctx)
86ad841251a38aa9ffcf4db4ee2c9fd449121bcbTimo Sirainen{
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen string_t *str = ctx->sync_ctx->from_line;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen if (pwrite_full(ctx->sync_ctx->write_fd, str_data(str), str_len(str),
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen ctx->mail.from_offset) < 0) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen mbox_set_syscall_error(ctx->sync_ctx->mbox, "pwrite_full()");
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen return -1;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen }
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mbox_sync_file_updated(ctx->sync_ctx, FALSE);
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen return 0;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen}
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainenstatic void update_from_offsets(struct mbox_sync_context *sync_ctx)
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen{
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen const struct mbox_sync_mail *mails;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen unsigned int i, count;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen uint32_t ext_idx;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen uint64_t offset;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen ext_idx = sync_ctx->mbox->mbox_ext_idx;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mails = array_get(&sync_ctx->mails, &count);
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen for (i = 0; i < count; i++) {
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen if (mails[i].idx_seq == 0 || mails[i].expunged)
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen continue;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->moved_offsets = TRUE;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen offset = mails[i].from_offset;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen mail_index_update_ext(sync_ctx->t, mails[i].idx_seq,
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen ext_idx, &offset, NULL);
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen }
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen}
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainenstatic void mbox_sync_handle_expunge(struct mbox_sync_mail_context *mail_ctx)
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen{
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mailbox *box = &sync_ctx->mbox->ibox.box;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen if (box->v.sync_notify != NULL) {
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen box->v.sync_notify(box, mail_ctx->mail.uid,
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen MAILBOX_SYNC_TYPE_EXPUNGE);
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen }
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_index_expunge(sync_ctx->t, mail_ctx->mail.idx_seq);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_ctx->mail.expunged = TRUE;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_ctx->mail.offset = mail_ctx->mail.from_offset;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_ctx->mail.space =
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mail_ctx->body_offset - mail_ctx->mail.from_offset +
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mail_ctx->mail.body_size;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mail_ctx->mail.body_size = 0;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mail_ctx->mail.uid = 0;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen if (sync_ctx->seq == 1) {
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen /* expunging first message, fix space to contain next
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen message's \n header too since it will be removed. */
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen mail_ctx->mail.space++;
1e242794e7a4f653f18fbb8edfe9ccec489a3a08Timo Sirainen if (istream_raw_mbox_has_crlf_ending(sync_ctx->input)) {
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen mail_ctx->mail.space++;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->first_mail_crlf_expunged = TRUE;
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen }
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen /* uid-last offset is invalid now */
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen sync_ctx->base_uid_last_offset = 0;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen sync_ctx->expunged_space += mail_ctx->mail.space;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen}
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainenstatic int mbox_sync_handle_header(struct mbox_sync_mail_context *mail_ctx)
2aecf7be5834e7f6520f8deaad683a6fa1de4d61Timo Sirainen{
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen uoff_t orig_from_offset;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen off_t move_diff;
d8a7046624a082938501e8268ed0cdcba4826e96Timo Sirainen int ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen if (sync_ctx->expunged_space > 0 && sync_ctx->need_space_seq == 0) {
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen /* move the header backwards to fill expunged space */
042e2b8447b34051f0380baafcf0073704430239Timo Sirainen move_diff = -sync_ctx->expunged_space;
042e2b8447b34051f0380baafcf0073704430239Timo Sirainen
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen orig_from_offset = mail_ctx->mail.from_offset;
e217d6fce33746e198ecc21bff0bc658664c9ef4Timo Sirainen if (sync_ctx->dest_first_mail) {
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) */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen mail_ctx->mail.from_offset++;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen if (sync_ctx->first_mail_crlf_expunged)
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen mail_ctx->mail.from_offset++;
6b44fc75c0039d1006ce4d543544552449b8e229Aki Tuomi }
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen /* read the From-line before rewriting overwrites it */
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen if (mbox_read_from_line(mail_ctx) < 0)
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen return -1;
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen
ea5a14af8ae816feda08937084954e3912748181Timo Sirainen mbox_sync_update_header(mail_ctx);
6b44fc75c0039d1006ce4d543544552449b8e229Aki Tuomi ret = mbox_sync_try_rewrite(mail_ctx, move_diff);
5dabb8e733a8af2337eb543f782eb6c43ea462dcTimo Sirainen if (ret < 0)
5dabb8e733a8af2337eb543f782eb6c43ea462dcTimo Sirainen return -1;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen if (ret > 0) {
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* rewrite successful, write From-line to
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen new location */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen i_assert(move_diff > 0 ||
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen (off_t)mail_ctx->mail.from_offset >=
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen -move_diff);
f95b3d29bc56f139c18c880aa574a0ca72b0cffbTimo Sirainen mail_ctx->mail.from_offset += move_diff;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mail_ctx->mail.offset += move_diff;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (mbox_write_from_line(mail_ctx) < 0)
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen return -1;
98dd8e6e81f11f1e6040ca72f4916242d246c863Timo Sirainen } else {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (sync_ctx->dest_first_mail) {
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 }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen } else if (mail_ctx->need_rewrite ||
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen index_sync_changes_have(sync_ctx->sync_changes)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mbox_sync_update_header(mail_ctx);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen if (sync_ctx->delay_writes) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* mark it dirty and do it later */
8f61542ca70f3f0dda15630447a00877b132efa8Timo Sirainen mail_ctx->dirty = TRUE;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen return 0;
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen }
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if ((ret = mbox_sync_try_rewrite(mail_ctx, 0)) < 0)
38d7db318188c4ac9cdc8c6cdb936b36a5258e19Timo Sirainen return -1;
38d7db318188c4ac9cdc8c6cdb936b36a5258e19Timo Sirainen } else {
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen /* nothing to do */
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen return 0;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret == 0 && sync_ctx->need_space_seq == 0) {
e3411c496000d3e2797b43a33584dfba954e815eTimo Sirainen /* first mail with no space to write it */
e3411c496000d3e2797b43a33584dfba954e815eTimo Sirainen sync_ctx->need_space_seq = sync_ctx->seq;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen sync_ctx->space_diff = 0;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen
d8a7046624a082938501e8268ed0cdcba4826e96Timo Sirainen if (sync_ctx->expunged_space > 0) {
d8a7046624a082938501e8268ed0cdcba4826e96Timo Sirainen /* create dummy message to describe the expunged data */
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct mbox_sync_mail mail;
d8a7046624a082938501e8268ed0cdcba4826e96Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen memset(&mail, 0, sizeof(mail));
48ac48572e6a196ac6c415180f0c8dcfad35d788Timo Sirainen mail.expunged = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail.offset = mail.from_offset =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (sync_ctx->dest_first_mail ? 1 : 0) +
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_ctx->mail.from_offset -
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen sync_ctx->expunged_space;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mail.space = sync_ctx->expunged_space;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen sync_ctx->space_diff = sync_ctx->expunged_space;
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen sync_ctx->expunged_space = 0;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen i_assert(sync_ctx->space_diff < -mail_ctx->mail.space);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen sync_ctx->need_space_seq--;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen array_append(&sync_ctx->mails, &mail, 1);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen }
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen }
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen return 0;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen}
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainenstatic int
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainenmbox_sync_handle_missing_space(struct mbox_sync_mail_context *mail_ctx)
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen{
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 uint32_t last_seq;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen ARRAY_TYPE(keyword_indexes) keywords_copy;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen
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
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_count(&mail_ctx->mail.keywords));
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen array_append_array(&keywords_copy, &mail_ctx->mail.keywords);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen mail_ctx->mail.keywords = keywords_copy;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen }
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen array_append(&sync_ctx->mails, &mail_ctx->mail, 1);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen sync_ctx->space_diff += mail_ctx->mail.space;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen if (sync_ctx->space_diff < 0) {
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen if (sync_ctx->expunged_space > 0) {
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen i_assert(sync_ctx->expunged_space ==
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen mail_ctx->mail.space);
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen sync_ctx->expunged_space = 0;
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen }
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen return 0;
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen }
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen /* we have enough space now */
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen if (mail_ctx->mail.uid == 0) {
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);
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen extra_space = MBOX_HEADER_PADDING *
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 */
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen move_diff = needed_space + extra_space;
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen sync_ctx->expunged_space =
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen mail_ctx->mail.space - move_diff;
d3837c0362588792db3e3148d217f31ff3172922Timo Sirainen } else {
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen move_diff = mail_ctx->mail.space;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen extra_space = sync_ctx->space_diff;
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen sync_ctx->expunged_space = 0;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen }
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen last_seq = sync_ctx->seq - 1;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen array_delete(&sync_ctx->mails,
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen array_count(&sync_ctx->mails) - 1, 1);
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen end_offset = mail_ctx->mail.from_offset;
b9a664472557182cff0825a2285b8fca39f72947Timo Sirainen } else {
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen /* this message gave enough space from headers. rewriting stops
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen at the end of this message's headers. */
b9a664472557182cff0825a2285b8fca39f72947Timo Sirainen sync_ctx->expunged_space = 0;
b9a664472557182cff0825a2285b8fca39f72947Timo Sirainen last_seq = sync_ctx->seq;
82d3a1d1594ed93d04d7bf999027b3e5104de6e4Timo Sirainen end_offset = mail_ctx->body_offset;
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen move_diff = 0;
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen extra_space = sync_ctx->space_diff;
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen }
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen mbox_sync_file_update_ext_modified(sync_ctx);
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen if (mbox_sync_rewrite(sync_ctx,
66f9709e0c7604e2282b930b6a48fe9f0dd20ab8Timo Sirainen last_seq == sync_ctx->seq ? mail_ctx : NULL,
f38854c96aef76e0c859df4e8f7303325b7ae8a1Timo Sirainen end_offset, move_diff, extra_space,
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen sync_ctx->need_space_seq, last_seq) < 0)
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen return -1;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen update_from_offsets(sync_ctx);
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* mail_ctx may contain wrong data after rewrite, so make sure we
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen don't try to access it */
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen memset(mail_ctx, 0, sizeof(*mail_ctx));
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen
073f965351846b8c97347b882c441dc336965e26Timo Sirainen sync_ctx->need_space_seq = 0;
073f965351846b8c97347b882c441dc336965e26Timo Sirainen sync_ctx->space_diff = 0;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen array_clear(&sync_ctx->mails);
073f965351846b8c97347b882c441dc336965e26Timo Sirainen p_clear(sync_ctx->saved_keywords_pool);
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen return 0;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen}
990d55ce3fc461eeacce3ef830b1c5dde5c3f150Josef 'Jeff' Sipek
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainenstatic int
990d55ce3fc461eeacce3ef830b1c5dde5c3f150Josef 'Jeff' Sipekmbox_sync_seek_to_seq(struct mbox_sync_context *sync_ctx, uint32_t seq)
990d55ce3fc461eeacce3ef830b1c5dde5c3f150Josef 'Jeff' Sipek{
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen struct mbox_mailbox *mbox = sync_ctx->mbox;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen uoff_t old_offset;
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen uint32_t uid;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen int ret;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen bool deleted;
073f965351846b8c97347b882c441dc336965e26Timo Sirainen
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen if (seq == 0) {
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen if (istream_raw_mbox_seek(mbox->mbox_stream, 0) < 0) {
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
d48ab236010e588c7b52e54db47fe9842a2e27e8Timo Sirainen MAIL_ERROR_NOTPOSSIBLE,
d48ab236010e588c7b52e54db47fe9842a2e27e8Timo Sirainen "Mailbox isn't a valid mbox file");
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen return -1;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen }
073f965351846b8c97347b882c441dc336965e26Timo Sirainen seq++;
073f965351846b8c97347b882c441dc336965e26Timo Sirainen } else {
073f965351846b8c97347b882c441dc336965e26Timo Sirainen old_offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
073f965351846b8c97347b882c441dc336965e26Timo Sirainen
073f965351846b8c97347b882c441dc336965e26Timo Sirainen ret = mbox_file_seek(mbox, sync_ctx->sync_view, seq, &deleted);
073f965351846b8c97347b882c441dc336965e26Timo Sirainen if (ret < 0) {
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen if (deleted) {
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen mbox_sync_set_critical(sync_ctx,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen "Message was expunged unexpectedly "
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen "in mbox file %s", mbox->path);
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen }
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen return -1;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen }
073f965351846b8c97347b882c441dc336965e26Timo Sirainen if (ret == 0) {
073f965351846b8c97347b882c441dc336965e26Timo Sirainen if (istream_raw_mbox_seek(mbox->mbox_stream,
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen old_offset) < 0) {
073f965351846b8c97347b882c441dc336965e26Timo Sirainen mbox_sync_set_critical(sync_ctx,
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen "Error seeking back to original "
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen "offset %s in mbox file %s",
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen dec2str(old_offset), mbox->path);
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen return -1;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen }
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen return 0;
dce5a2719df4fc64a8762d2aa94ba98dcf9cd6feTimo Sirainen }
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen }
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen if (seq <= 1)
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen uid = 0;
073f965351846b8c97347b882c441dc336965e26Timo Sirainen else if (mail_index_lookup_uid(sync_ctx->sync_view, seq-1, &uid) < 0) {
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen mail_storage_set_index_error(&mbox->ibox);
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen return -1;
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen }
d48ab236010e588c7b52e54db47fe9842a2e27e8Timo Sirainen
d48ab236010e588c7b52e54db47fe9842a2e27e8Timo Sirainen sync_ctx->prev_msg_uid = uid;
d48ab236010e588c7b52e54db47fe9842a2e27e8Timo Sirainen
96a464e3e417557153272c964fc8a0e9bb6d6b86Timo Sirainen /* set to -1, since it's always increased later */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen sync_ctx->seq = seq-1;
073f965351846b8c97347b882c441dc336965e26Timo Sirainen if (sync_ctx->seq == 0 &&
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 */
073f965351846b8c97347b882c441dc336965e26Timo Sirainen sync_ctx->seq++;
073f965351846b8c97347b882c441dc336965e26Timo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
073f965351846b8c97347b882c441dc336965e26Timo Sirainen sync_ctx->idx_seq = seq;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen sync_ctx->dest_first_mail = sync_ctx->seq == 0;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen (void)istream_raw_mbox_get_body_offset(sync_ctx->input);
f7ec15aefabeb0a17d1f262bc5e9a15e43dfc5adTimo Sirainen return 1;
f7ec15aefabeb0a17d1f262bc5e9a15e43dfc5adTimo Sirainen}
f7ec15aefabeb0a17d1f262bc5e9a15e43dfc5adTimo Sirainen
6202c9d2492d33ff28ba61a5c57c6768df9577d0Timo Sirainenstatic int
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainenmbox_sync_seek_to_uid(struct mbox_sync_context *sync_ctx, uint32_t uid)
d2b94d25f842cd1b7acaf4dd7de858f7c6a821c9Timo Sirainen{
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen struct mail_index_view *sync_view = sync_ctx->sync_view;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen uint32_t seq1, seq2;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen const struct stat *st;
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_assert(!sync_ctx->index_reset);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mail_index_lookup_uid_range(sync_view, uid, (uint32_t)-1,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen &seq1, &seq2) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen }
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen if (seq1 == 0) {
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* doesn't exist anymore, seek to end of file */
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen st = i_stream_stat(sync_ctx->file_input, TRUE);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (st == NULL) {
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox,
a24b0595f0f7d3925d4c9ac26fa503ff87c43e43Timo Sirainen "i_stream_stat()");
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen return -1;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen }
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (istream_raw_mbox_seek(sync_ctx->mbox->mbox_stream,
6202c9d2492d33ff28ba61a5c57c6768df9577d0Timo Sirainen st->st_size) < 0) {
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen mbox_sync_set_critical(sync_ctx,
19a1cfc537d979c532fac71264dba0b9dabc65d9Timo Sirainen "Error seeking to end of mbox file %s",
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen sync_ctx->mbox->path);
19a1cfc537d979c532fac71264dba0b9dabc65d9Timo Sirainen return -1;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen }
19a1cfc537d979c532fac71264dba0b9dabc65d9Timo Sirainen sync_ctx->idx_seq =
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mail_index_view_get_messages_count(sync_view) + 1;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return 1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen return mbox_sync_seek_to_seq(sync_ctx, seq1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainenstatic int mbox_sync_partial_seek_next(struct mbox_sync_context *sync_ctx,
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen uint32_t next_uid, bool *partial,
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen bool *skipped_mails)
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen{
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen uint32_t messages_count, uid;
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen int ret;
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen i_assert(!sync_ctx->index_reset);
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen
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 return 1;
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen if (sync_ctx->hdr->first_recent_uid <= next_uid &&
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen !sync_ctx->mbox->ibox.keep_recent) {
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen /* we'll need to rewrite Status: O headers */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return 1;
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen uid = index_sync_changes_get_next_uid(sync_ctx->sync_changes);
1bdda5c0c30463160c47151537e6bb2c6c994841Timo Sirainen
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 !sync_ctx->mbox->ibox.keep_recent) {
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen /* we'll need to rewrite Status: O headers */
6202c9d2492d33ff28ba61a5c57c6768df9577d0Timo Sirainen uid = sync_ctx->hdr->first_recent_uid;
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (uid != 0) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* we can skip forward to next record which needs updating. */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (uid != next_uid) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *skipped_mails = TRUE;
next_uid = uid;
}
ret = mbox_sync_seek_to_uid(sync_ctx, next_uid);
} else {
/* if there's no sync records left, we can stop. except if
this is a dirty sync, check if there are new messages. */
if (!sync_ctx->mbox->mbox_sync_dirty)
return 0;
messages_count =
mail_index_view_get_messages_count(sync_ctx->sync_view);
if (sync_ctx->seq + 1 != messages_count) {
ret = mbox_sync_seek_to_seq(sync_ctx, messages_count);
*skipped_mails = TRUE;
} else {
ret = 1;
}
*partial = FALSE;
}
if (ret == 0) {
/* seek failed because the offset is dirty. just ignore and
continue from where we are now. */
*partial = FALSE;
ret = 1;
}
return ret;
}
static bool mbox_sync_uidvalidity_changed(struct mbox_sync_context *sync_ctx)
{
if (sync_ctx->base_uid_validity != 0 &&
sync_ctx->hdr->uid_validity != 0 &&
sync_ctx->base_uid_validity != sync_ctx->hdr->uid_validity) {
i_warning("UIDVALIDITY changed (%u -> %u) in mbox file %s",
sync_ctx->hdr->uid_validity,
sync_ctx->base_uid_validity,
sync_ctx->mbox->path);
sync_ctx->index_reset = TRUE;
return TRUE;
}
return FALSE;
}
static int mbox_sync_loop(struct mbox_sync_context *sync_ctx,
struct mbox_sync_mail_context *mail_ctx,
bool partial)
{
const struct mail_index_record *rec;
uint32_t uid, messages_count;
uoff_t offset;
int ret;
bool expunged, skipped_mails, uids_broken;
messages_count =
mail_index_view_get_messages_count(sync_ctx->sync_view);
/* always start from first message so we can read X-IMAP or
X-IMAPbase header */
ret = mbox_sync_seek_to_seq(sync_ctx, 0);
if (ret <= 0)
return ret;
if (sync_ctx->renumber_uids) {
/* expunge everything */
while (sync_ctx->idx_seq <= messages_count) {
mail_index_expunge(sync_ctx->t,
sync_ctx->idx_seq++);
}
}
skipped_mails = uids_broken = FALSE;
while ((ret = mbox_sync_read_next_mail(sync_ctx, mail_ctx)) > 0) {
uid = mail_ctx->mail.uid;
if (mail_ctx->seq == 1) {
if (mbox_sync_uidvalidity_changed(sync_ctx)) {
sync_ctx->mbox->mbox_sync_dirty = TRUE;
return 0;
}
}
if (mail_ctx->mail.uid_broken && partial) {
/* UID ordering problems, resync everything to make
sure we get everything right */
if (sync_ctx->mbox->mbox_sync_dirty)
return 0;
mbox_sync_set_critical(sync_ctx,
"UIDs broken with partial sync in mbox file %s",
sync_ctx->mbox->path);
sync_ctx->mbox->mbox_sync_dirty = TRUE;
return 0;
}
if (mail_ctx->mail.uid_broken)
uids_broken = TRUE;
if (mail_ctx->mail.pseudo)
uid = 0;
rec = NULL; ret = 1;
if (uid != 0) {
ret = mbox_sync_read_index_rec(sync_ctx, uid, &rec);
if (ret < 0)
return -1;
}
if (ret == 0) {
/* UID found but it's broken */
uid = 0;
} else if (uid == 0 &&
!mail_ctx->mail.pseudo &&
(sync_ctx->delay_writes ||
sync_ctx->idx_seq <= messages_count)) {
/* If we can't use/store X-UID header, use MD5 sum.
Also check for existing MD5 sums when we're actually
able to write X-UIDs. */
sync_ctx->mbox->mbox_save_md5 = TRUE;
if (mbox_sync_find_index_md5(sync_ctx,
mail_ctx->hdr_md5_sum,
&rec) < 0)
return -1;
if (rec != NULL)
uid = mail_ctx->mail.uid = rec->uid;
}
/* get all sync records related to this message. with pseudo
message just get the first sync record so we can jump to
it with partial seeking. */
if (mbox_sync_read_index_syncs(sync_ctx,
mail_ctx->mail.pseudo ? 1 : uid,
&expunged) < 0)
return -1;
if (mail_ctx->mail.pseudo) {
/* if it was set, it was for the next message */
expunged = FALSE;
} else {
if (rec == NULL) {
/* message wasn't found from index. we have to
read everything from now on, no skipping */
partial = FALSE;
}
}
if (uid == 0 && !mail_ctx->mail.pseudo) {
/* missing/broken X-UID. all the rest of the mails
need new UIDs. */
while (sync_ctx->idx_seq <= messages_count) {
mail_index_expunge(sync_ctx->t,
sync_ctx->idx_seq++);
}
if (sync_ctx->next_uid == (uint32_t)-1) {
/* oh no, we're out of UIDs. this shouldn't
happen normally, so just try to get it fixed
without crashing. */
mail_storage_set_critical(
&sync_ctx->mbox->storage->storage,
"Out of UIDs, renumbering them in mbox "
"file %s", sync_ctx->mbox->path);
sync_ctx->renumber_uids = TRUE;
return 0;
}
mail_ctx->need_rewrite = TRUE;
mail_ctx->mail.uid = sync_ctx->next_uid++;
}
sync_ctx->prev_msg_uid = mail_ctx->mail.uid;
if (!mail_ctx->mail.pseudo)
mail_ctx->mail.idx_seq = sync_ctx->idx_seq;
if (!expunged) {
if (mbox_sync_handle_header(mail_ctx) < 0)
return -1;
sync_ctx->dest_first_mail = FALSE;
} else {
mbox_sync_handle_expunge(mail_ctx);
}
if (!mail_ctx->mail.pseudo) {
if (!expunged) {
if (mbox_sync_update_index(mail_ctx, rec) < 0)
return -1;
}
sync_ctx->idx_seq++;
}
istream_raw_mbox_next(sync_ctx->input,
mail_ctx->mail.body_size);
offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
if (sync_ctx->need_space_seq != 0) {
if (mbox_sync_handle_missing_space(mail_ctx) < 0)
return -1;
if (mbox_sync_seek(sync_ctx, offset) < 0)
return -1;
} else if (sync_ctx->expunged_space > 0) {
if (!expunged) {
/* move the body */
mbox_sync_file_update_ext_modified(sync_ctx);
if (mbox_move(sync_ctx,
mail_ctx->body_offset -
sync_ctx->expunged_space,
mail_ctx->body_offset,
mail_ctx->mail.body_size) < 0)
return -1;
if (mbox_sync_seek(sync_ctx, offset) < 0)
return -1;
}
} else if (partial) {
ret = mbox_sync_partial_seek_next(sync_ctx, uid + 1,
&partial,
&skipped_mails);
if (ret <= 0) {
if (ret < 0)
return -1;
break;
}
}
}
if (istream_raw_mbox_is_eof(sync_ctx->input)) {
/* rest of the messages in index don't exist -> expunge them */
while (sync_ctx->idx_seq <= messages_count)
mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq++);
}
if (!skipped_mails)
sync_ctx->mbox->mbox_sync_dirty = FALSE;
if (uids_broken && sync_ctx->delay_writes) {
/* once we get around to writing the changes, we'll need to do
a full sync to avoid the "UIDs broken in partial sync"
error */
sync_ctx->mbox->mbox_sync_dirty = TRUE;
}
return 1;
}
static int mbox_write_pseudo(struct mbox_sync_context *sync_ctx)
{
string_t *str;
unsigned int uid_validity;
i_assert(sync_ctx->write_fd != -1);
uid_validity = sync_ctx->base_uid_validity != 0 ?
sync_ctx->base_uid_validity : sync_ctx->hdr->uid_validity;
i_assert(uid_validity != 0);
str = t_str_new(1024);
str_printfa(str, "%sDate: %s\n"
"From: Mail System Internal Data <MAILER-DAEMON@%s>\n"
"Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA"
"\nMessage-ID: <%s@%s>\n"
"X-IMAP: %u %010u\n"
"Status: RO\n"
"\n"
PSEUDO_MESSAGE_BODY
"\n",
mbox_from_create("MAILER_DAEMON", ioloop_time),
message_date_create(ioloop_time),
my_hostname, dec2str(ioloop_time), my_hostname,
uid_validity, sync_ctx->next_uid-1);
if (pwrite_full(sync_ctx->write_fd,
str_data(str), str_len(str), 0) < 0) {
if (!ENOSPACE(errno)) {
mbox_set_syscall_error(sync_ctx->mbox,
"pwrite_full()");
return -1;
}
/* out of disk space, truncate to empty */
if (ftruncate(sync_ctx->write_fd, 0) < 0)
mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()");
}
sync_ctx->base_uid_last_offset = 0; /* don't bother calculating */
sync_ctx->base_uid_last = sync_ctx->next_uid-1;
return 0;
}
static int mbox_sync_handle_eof_updates(struct mbox_sync_context *sync_ctx,
struct mbox_sync_mail_context *mail_ctx)
{
const struct stat *st;
uoff_t file_size, offset, padding, trailer_size;
if (!istream_raw_mbox_is_eof(sync_ctx->input)) {
i_assert(sync_ctx->need_space_seq == 0);
i_assert(sync_ctx->expunged_space == 0);
return 0;
}
st = i_stream_stat(sync_ctx->file_input, TRUE);
if (st == NULL) {
mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()");
return -1;
}
file_size = st->st_size;
if (file_size < sync_ctx->file_input->v_offset) {
mbox_sync_set_critical(sync_ctx,
"file size unexpectedly shrinked in mbox file %s "
"(%"PRIuUOFF_T" vs %"PRIuUOFF_T")",
sync_ctx->mbox->path, file_size,
sync_ctx->file_input->v_offset);
return -1;
}
trailer_size = file_size - sync_ctx->file_input->v_offset;
i_assert(trailer_size <= 2);
if (sync_ctx->need_space_seq != 0) {
i_assert(sync_ctx->write_fd != -1);
i_assert(sync_ctx->space_diff < 0);
padding = MBOX_HEADER_PADDING *
(sync_ctx->seq - sync_ctx->need_space_seq + 1);
sync_ctx->space_diff -= padding;
i_assert(sync_ctx->expunged_space <= -sync_ctx->space_diff);
sync_ctx->space_diff += sync_ctx->expunged_space;
sync_ctx->expunged_space = 0;
if (mail_ctx->have_eoh && !mail_ctx->updated)
str_append_c(mail_ctx->header, '\n');
i_assert(sync_ctx->space_diff < 0);
if (file_set_size(sync_ctx->write_fd,
file_size + -sync_ctx->space_diff) < 0) {
mbox_set_syscall_error(sync_ctx->mbox,
"file_set_size()");
if (ftruncate(sync_ctx->write_fd, file_size) < 0) {
mbox_set_syscall_error(sync_ctx->mbox,
"ftruncate()");
}
return -1;
}
mbox_sync_file_updated(sync_ctx, FALSE);
if (mbox_sync_rewrite(sync_ctx, mail_ctx, file_size,
-sync_ctx->space_diff, padding,
sync_ctx->need_space_seq,
sync_ctx->seq) < 0)
return -1;
update_from_offsets(sync_ctx);
sync_ctx->need_space_seq = 0;
array_clear(&sync_ctx->mails);
p_clear(sync_ctx->saved_keywords_pool);
}
if (sync_ctx->expunged_space > 0) {
i_assert(sync_ctx->write_fd != -1);
mbox_sync_file_update_ext_modified(sync_ctx);
/* copy trailer, then truncate the file */
file_size = sync_ctx->last_stat.st_size;
if (file_size == (uoff_t)sync_ctx->expunged_space) {
/* everything deleted, the trailer_size still contains
the \n trailer though */
trailer_size = 0;
}
i_assert(file_size >= sync_ctx->expunged_space + trailer_size);
offset = file_size - sync_ctx->expunged_space - trailer_size;
i_assert(offset == 0 || offset > 31);
if (mbox_move(sync_ctx, offset,
offset + sync_ctx->expunged_space,
trailer_size) < 0)
return -1;
if (ftruncate(sync_ctx->write_fd,
offset + trailer_size) < 0) {
mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()");
return -1;
}
if (offset == 0) {
if (mbox_write_pseudo(sync_ctx) < 0)
return -1;
}
sync_ctx->expunged_space = 0;
mbox_sync_file_updated(sync_ctx, FALSE);
}
return 0;
}
static int mbox_sync_update_index_header(struct mbox_sync_context *sync_ctx)
{
const struct stat *st;
uint32_t first_recent_uid;
st = i_stream_stat(sync_ctx->file_input, FALSE);
if (st == NULL) {
mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()");
return -1;
}
if (sync_ctx->moved_offsets &&
((uint64_t)st->st_size == sync_ctx->hdr->sync_size ||
(uint64_t)st->st_size == sync_ctx->orig_size)) {
/* We moved messages inside the mbox file without changing
the file's size. If mtime doesn't change, another process
not using the same index file as us can't know that the file
was changed. So make sure the mtime changes. This should
happen rarely enough that the sleeping doesn't become a
performance problem.
Note that to do this perfectly safe we should do this wait
whenever mails are moved or expunged, regardless of whether
the file's size changed. That however could become a
performance problem and the consequences of being wrong are
quite minimal (an extra logged error message). */
while (sync_ctx->orig_mtime == st->st_mtime) {
usleep(500000);
if (utime(sync_ctx->mbox->path, NULL) < 0) {
mbox_set_syscall_error(sync_ctx->mbox,
"utime()");
return -1;
}
st = i_stream_stat(sync_ctx->file_input, FALSE);
if (st == NULL) {
mbox_set_syscall_error(sync_ctx->mbox,
"i_stream_stat()");
return -1;
}
}
}
/* only reason not to have UID validity at this point is if the file
is entirely empty. In that case just make up a new one if needed. */
i_assert(sync_ctx->base_uid_validity != 0 || st->st_size == 0);
if (sync_ctx->base_uid_validity != sync_ctx->hdr->uid_validity ||
sync_ctx->base_uid_validity == 0) {
if (sync_ctx->base_uid_validity == 0) {
sync_ctx->base_uid_validity =
sync_ctx->hdr->uid_validity != 0 ?
sync_ctx->hdr->uid_validity :
(unsigned int)ioloop_time;
}
mail_index_update_header(sync_ctx->t,
offsetof(struct mail_index_header, uid_validity),
&sync_ctx->base_uid_validity,
sizeof(sync_ctx->base_uid_validity), TRUE);
}
if (istream_raw_mbox_is_eof(sync_ctx->input) &&
sync_ctx->next_uid != sync_ctx->hdr->next_uid) {
i_assert(sync_ctx->next_uid != 0);
mail_index_update_header(sync_ctx->t,
offsetof(struct mail_index_header, next_uid),
&sync_ctx->next_uid, sizeof(sync_ctx->next_uid), FALSE);
}
if ((uint32_t)st->st_mtime != sync_ctx->hdr->sync_stamp &&
!sync_ctx->mbox->mbox_sync_dirty) {
uint32_t sync_stamp = st->st_mtime;
mail_index_update_header(sync_ctx->t,
offsetof(struct mail_index_header, sync_stamp),
&sync_stamp, sizeof(sync_stamp), TRUE);
}
if ((uint64_t)st->st_size != sync_ctx->hdr->sync_size &&
!sync_ctx->mbox->mbox_sync_dirty) {
uint64_t sync_size = st->st_size;
mail_index_update_header(sync_ctx->t,
offsetof(struct mail_index_header, sync_size),
&sync_size, sizeof(sync_size), TRUE);
}
first_recent_uid = !sync_ctx->mbox->ibox.keep_recent ? 0 :
sync_ctx->last_nonrecent_uid + 1;
if (sync_ctx->hdr->first_recent_uid < first_recent_uid) {
mail_index_update_header(sync_ctx->t,
offsetof(struct mail_index_header, first_recent_uid),
&first_recent_uid, sizeof(first_recent_uid), FALSE);
}
sync_ctx->mbox->mbox_dirty_stamp = st->st_mtime;
sync_ctx->mbox->mbox_dirty_size = st->st_size;
return 0;
}
static int mbox_sync_recent_state(struct mbox_sync_context *sync_ctx)
{
uint32_t seq1, seq2;
/* see what index thinks is the lowest recent sequence */
if (mail_index_lookup_uid_range(sync_ctx->sync_view,
sync_ctx->hdr->first_recent_uid,
sync_ctx->hdr->next_uid,
&seq1, &seq2) < 0) {
mail_storage_set_index_error(&sync_ctx->mbox->ibox);
return -1;
}
if (seq1 == 0 && sync_ctx->idx_seq <= 1) {
/* nothing new */
return 0;
}
if (sync_ctx->last_nonrecent_idx_seq >= seq1) {
/* we've seen newer non-recent messages. */
seq1 = sync_ctx->last_nonrecent_idx_seq + 1;
}
if (seq2 < sync_ctx->idx_seq)
seq2 = sync_ctx->idx_seq - 1;
if (seq1 <= seq2) {
index_mailbox_set_recent_seq(&sync_ctx->mbox->ibox,
sync_ctx->sync_view, seq1, seq2);
}
return 0;
}
static void mbox_sync_restart(struct mbox_sync_context *sync_ctx)
{
sync_ctx->base_uid_validity = 0;
sync_ctx->base_uid_last = 0;
sync_ctx->base_uid_last_offset = 0;
array_clear(&sync_ctx->mails);
p_clear(sync_ctx->saved_keywords_pool);
index_sync_changes_reset(sync_ctx->sync_changes);
mail_index_sync_reset(sync_ctx->index_sync_ctx);
mail_index_transaction_reset(sync_ctx->t);
if (sync_ctx->index_reset) {
mail_index_reset(sync_ctx->t);
sync_ctx->reset_hdr.next_uid = 1;
sync_ctx->hdr = &sync_ctx->reset_hdr;
}
sync_ctx->prev_msg_uid = 0;
sync_ctx->next_uid = sync_ctx->hdr->next_uid;
sync_ctx->idx_next_uid = sync_ctx->hdr->next_uid;
sync_ctx->seq = 0;
sync_ctx->idx_seq = 1;
sync_ctx->need_space_seq = 0;
sync_ctx->expunged_space = 0;
sync_ctx->space_diff = 0;
sync_ctx->dest_first_mail = TRUE;
sync_ctx->ext_modified = FALSE;
}
static int mbox_sync_do(struct mbox_sync_context *sync_ctx,
enum mbox_sync_flags flags)
{
struct mbox_sync_mail_context mail_ctx;
const struct stat *st;
unsigned int i;
int ret, partial;
st = i_stream_stat(sync_ctx->file_input, FALSE);
if (st == NULL) {
mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()");
return -1;
}
sync_ctx->last_stat = *st;
sync_ctx->orig_size = st->st_size;
sync_ctx->orig_atime = st->st_atime;
sync_ctx->orig_mtime = st->st_mtime;
if ((flags & MBOX_SYNC_FORCE_SYNC) != 0) {
/* forcing a full sync. assume file has changed. */
partial = FALSE;
sync_ctx->mbox->mbox_sync_dirty = TRUE;
} else if ((uint32_t)st->st_mtime == sync_ctx->hdr->sync_stamp &&
(uint64_t)st->st_size == sync_ctx->hdr->sync_size) {
/* file is fully synced */
partial = TRUE;
sync_ctx->mbox->mbox_sync_dirty = FALSE;
} else if ((flags & MBOX_SYNC_UNDIRTY) != 0 ||
(uint64_t)st->st_size == sync_ctx->hdr->sync_size) {
/* we want to do full syncing. always do this if
file size hasn't changed but timestamp has. it most
likely means that someone had modified some header
and we probably want to know about it */
partial = FALSE;
sync_ctx->mbox->mbox_sync_dirty = TRUE;
} else {
/* see if we can delay syncing the whole file.
normally we only notice expunges and appends
in partial syncing. */
partial = TRUE;
sync_ctx->mbox->mbox_sync_dirty = TRUE;
}
mbox_sync_restart(sync_ctx);
for (i = 0; i < 3; i++) {
ret = mbox_sync_loop(sync_ctx, &mail_ctx, partial);
if (ret > 0)
break;
if (ret < 0)
return -1;
/* partial syncing didn't work, do it again. we get here
also if we ran out of UIDs. */
i_assert(sync_ctx->mbox->mbox_sync_dirty);
mbox_sync_restart(sync_ctx);
partial = FALSE;
}
if (mbox_sync_recent_state(sync_ctx) < 0)
return -1;
if (mbox_sync_handle_eof_updates(sync_ctx, &mail_ctx) < 0)
return -1;
/* only syncs left should be just appends (and their updates)
which weren't synced yet for some reason (crash). we'll just
ignore them, as we've overwritten them above. */
index_sync_changes_reset(sync_ctx->sync_changes);
if (mbox_sync_update_index_header(sync_ctx) < 0)
return -1;
return 0;
}
int mbox_sync_has_changed(struct mbox_mailbox *mbox, bool leave_dirty)
{
const struct mail_index_header *hdr;
const struct stat *st;
struct stat statbuf;
if (mbox->mbox_file_stream != NULL && mbox->mbox_fd == -1) {
/* read-only stream */
st = i_stream_stat(mbox->mbox_file_stream, FALSE);
if (st == NULL) {
mbox_set_syscall_error(mbox, "i_stream_stat()");
return -1;
}
} else {
if (stat(mbox->path, &statbuf) < 0) {
mbox_set_syscall_error(mbox, "stat()");
return -1;
}
st = &statbuf;
}
hdr = mail_index_get_header(mbox->ibox.view);
if ((uint32_t)st->st_mtime == hdr->sync_stamp &&
(uint64_t)st->st_size == hdr->sync_size) {
/* fully synced */
mbox->mbox_sync_dirty = FALSE;
return 0;
}
if (!mbox->mbox_sync_dirty || !leave_dirty) {
mbox->mbox_sync_dirty = TRUE;
return 1;
}
return st->st_mtime != mbox->mbox_dirty_stamp ||
st->st_size != mbox->mbox_dirty_size;
}
static void mbox_sync_context_free(struct mbox_sync_context *sync_ctx)
{
index_sync_changes_deinit(&sync_ctx->sync_changes);
if (sync_ctx->index_sync_ctx != NULL)
mail_index_sync_rollback(&sync_ctx->index_sync_ctx);
pool_unref(sync_ctx->mail_keyword_pool);
pool_unref(sync_ctx->saved_keywords_pool);
str_free(&sync_ctx->header);
str_free(&sync_ctx->from_line);
array_free(&sync_ctx->mails);
}
static int mbox_sync_int(struct mbox_mailbox *mbox, enum mbox_sync_flags flags)
{
struct mail_index_sync_ctx *index_sync_ctx;
struct mail_index_view *sync_view;
struct mail_index_transaction *trans;
struct mbox_sync_context sync_ctx;
enum mail_index_sync_flags sync_flags;
uint32_t seq;
uoff_t offset;
unsigned int lock_id = 0;
int ret, changed;
bool delay_writes;
delay_writes = mbox->mbox_readonly ||
((flags & MBOX_SYNC_REWRITE) == 0 &&
getenv("MBOX_LAZY_WRITES") != NULL);
mbox->ibox.sync_last_check = ioloop_time;
if (!mbox->mbox_do_dirty_syncs)
flags |= MBOX_SYNC_UNDIRTY;
if ((flags & MBOX_SYNC_LOCK_READING) != 0) {
if (mbox_lock(mbox, F_RDLCK, &lock_id) <= 0)
return -1;
}
if ((flags & MBOX_SYNC_HEADER) != 0 ||
(flags & MBOX_SYNC_FORCE_SYNC) != 0)
changed = 1;
else {
bool leave_dirty = (flags & MBOX_SYNC_UNDIRTY) == 0;
if ((changed = mbox_sync_has_changed(mbox, leave_dirty)) < 0) {
if ((flags & MBOX_SYNC_LOCK_READING) != 0)
(void)mbox_unlock(mbox, lock_id);
return -1;
}
}
if ((flags & MBOX_SYNC_LOCK_READING) != 0) {
/* we just want to lock it for reading. if mbox hasn't been
modified don't do any syncing. */
if (!changed)
return 0;
/* have to sync to make sure offsets have stayed the same */
(void)mbox_unlock(mbox, lock_id);
lock_id = 0;
}
/* reopen input stream to make sure it has nothing buffered */
mbox_file_close_stream(mbox);
__again:
if (changed) {
/* we're most likely modifying the mbox while syncing, just
lock it for writing immediately. the mbox must be locked
before index syncing is started to avoid deadlocks, so we
don't have much choice either (well, easy ones anyway). */
int lock_type = mbox->mbox_readonly ? F_RDLCK : F_WRLCK;
if (mbox_lock(mbox, lock_type, &lock_id) <= 0)
return -1;
}
if ((flags & MBOX_SYNC_LAST_COMMIT) != 0) {
seq = mbox->ibox.commit_log_file_seq;
offset = mbox->ibox.commit_log_file_offset;
} else {
seq = (uint32_t)-1;
offset = (uoff_t)-1;
}
sync_flags = 0;
if (!mbox->ibox.keep_recent)
sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
if ((flags & MBOX_SYNC_REWRITE) != 0)
sync_flags |= MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY;
ret = mail_index_sync_begin(mbox->ibox.index, &index_sync_ctx,
&sync_view, &trans, seq, offset,
sync_flags);
if (ret <= 0) {
if (ret < 0)
mail_storage_set_index_error(&mbox->ibox);
if (lock_id != 0)
(void)mbox_unlock(mbox, lock_id);
return ret;
}
if (!mbox->ibox.keep_recent) {
/* see if we need to drop recent flags */
sync_ctx.hdr = mail_index_get_header(sync_view);
if (sync_ctx.hdr->first_recent_uid < sync_ctx.hdr->next_uid)
changed = 1;
}
if (!changed && !mail_index_sync_have_more(index_sync_ctx)) {
/* nothing to do */
__nothing_to_do:
if (lock_id != 0)
(void)mbox_unlock(mbox, lock_id);
/* index may need to do internal syncing though, so commit
instead of rollbacking. */
if (mail_index_sync_commit(&index_sync_ctx) < 0) {
mail_storage_set_index_error(&mbox->ibox);
return -1;
}
return 0;
}
memset(&sync_ctx, 0, sizeof(sync_ctx));
sync_ctx.mbox = mbox;
sync_ctx.hdr = mail_index_get_header(sync_view);
sync_ctx.from_line = str_new(default_pool, 256);
sync_ctx.header = str_new(default_pool, 4096);
sync_ctx.index_sync_ctx = index_sync_ctx;
sync_ctx.sync_view = sync_view;
sync_ctx.t = trans;
sync_ctx.mail_keyword_pool =
pool_alloconly_create("mbox keywords", 256);
sync_ctx.saved_keywords_pool =
pool_alloconly_create("mbox saved keywords", 4096);
/* make sure we've read the latest keywords in index */
(void)mail_index_get_keywords(mbox->ibox.index);
i_array_init(&sync_ctx.mails, 64);
sync_ctx.flags = flags;
sync_ctx.delay_writes = delay_writes || sync_ctx.mbox->mbox_readonly;
sync_ctx.sync_changes =
index_sync_changes_init(&mbox->ibox, index_sync_ctx,
sync_view, trans,
sync_ctx.delay_writes);
if (!changed && delay_writes) {
/* if we have only flag changes, we don't need to open the
mbox file */
bool expunged;
uint32_t uid;
if (mbox_sync_read_index_syncs(&sync_ctx, 1, &expunged) < 0)
return -1;
uid = index_sync_changes_get_next_uid(sync_ctx.sync_changes);
if (uid == 0) {
sync_ctx.index_sync_ctx = NULL;
mbox_sync_context_free(&sync_ctx);
goto __nothing_to_do;
}
}
if (lock_id == 0) {
/* ok, we have something to do but no locks. we'll have to
restart syncing to avoid deadlocking. */
mbox_sync_context_free(&sync_ctx);
changed = 1;
goto __again;
}
if (mbox_file_open_stream(mbox) < 0) {
mbox_sync_context_free(&sync_ctx);
(void)mbox_unlock(mbox, lock_id);
return -1;
}
sync_ctx.file_input = sync_ctx.mbox->mbox_file_stream;
sync_ctx.input = sync_ctx.mbox->mbox_stream;
sync_ctx.write_fd = sync_ctx.mbox->mbox_lock_type != F_WRLCK ? -1 :
sync_ctx.mbox->mbox_fd;
ret = mbox_sync_do(&sync_ctx, flags);
if (ret < 0)
mail_index_sync_rollback(&index_sync_ctx);
else if (mail_index_sync_commit(&index_sync_ctx) < 0) {
mail_storage_set_index_error(&mbox->ibox);
ret = -1;
} else {
mbox->ibox.commit_log_file_seq = 0;
mbox->ibox.commit_log_file_offset = 0;
}
sync_ctx.t = NULL;
sync_ctx.index_sync_ctx = NULL;
if (sync_ctx.base_uid_last != sync_ctx.next_uid-1 &&
ret == 0 && !sync_ctx.delay_writes &&
sync_ctx.base_uid_last_offset != 0) {
/* Rewrite uid_last in X-IMAPbase header if we've seen it
(ie. the file isn't empty) */
ret = mbox_rewrite_base_uid_last(&sync_ctx);
}
if (ret == 0 && mbox->mbox_fd != -1 && mbox->ibox.keep_recent) {
/* try to set atime back to its original value */
struct utimbuf buf;
struct stat st;
if (fstat(mbox->mbox_fd, &st) < 0)
mbox_set_syscall_error(mbox, "fstat()");
else {
buf.modtime = st.st_mtime;
buf.actime = sync_ctx.orig_atime;
if (utime(mbox->path, &buf) < 0)
mbox_set_syscall_error(mbox, "utime()");
}
}
i_assert(lock_id != 0);
if ((mbox->storage->storage.flags &
MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0 && mbox->mbox_fd != -1) {
if (fdatasync(mbox->mbox_fd) < 0) {
mbox_set_syscall_error(mbox, "fdatasync()");
ret = -1;
}
}
if (mbox->mbox_lock_type != F_RDLCK) {
/* drop to read lock */
unsigned int read_lock_id = 0;
if (mbox_lock(mbox, F_RDLCK, &read_lock_id) <= 0)
ret = -1;
else {
if (mbox_unlock(mbox, lock_id) < 0)
ret = -1;
lock_id = read_lock_id;
}
}
if ((flags & MBOX_SYNC_LOCK_READING) == 0) {
if (mbox_unlock(mbox, lock_id) < 0)
ret = -1;
}
mbox_sync_context_free(&sync_ctx);
return ret;
}
int mbox_sync(struct mbox_mailbox *mbox, enum mbox_sync_flags flags)
{
int ret;
mbox->syncing = TRUE;
ret = mbox_sync_int(mbox, flags);
mbox->syncing = FALSE;
if (mbox->ibox.box.v.sync_notify != NULL)
mbox->ibox.box.v.sync_notify(&mbox->ibox.box, 0, 0);
return ret;
}
struct mailbox_sync_context *
mbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
{
struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
enum mbox_sync_flags mbox_sync_flags = 0;
int ret = 0;
if (!box->opened)
index_storage_mailbox_open(&mbox->ibox);
if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 ||
mbox->ibox.sync_last_check + MAILBOX_FULL_SYNC_INTERVAL <=
ioloop_time) {
if ((flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0 &&
!mbox->mbox_very_dirty_syncs)
mbox_sync_flags |= MBOX_SYNC_UNDIRTY;
if ((flags & MAILBOX_SYNC_FLAG_FULL_WRITE) != 0)
mbox_sync_flags |= MBOX_SYNC_REWRITE;
ret = mbox_sync(mbox, mbox_sync_flags);
}
return index_mailbox_sync_init(box, flags, ret < 0);
}