mbox-sync.c revision b8765f6093ab35fc2345293d78132d35794cbff5
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2004 Timo Sirainen */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen Modifying mbox can be slow, so we try to do it all at once minimizing the
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen required disk I/O. We may need to:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - Update message flags in Status, X-Status and X-Keywords headers
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - Write missing X-UID and X-IMAPbase headers
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen - Write missing or broken Content-Length header if there's space
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen - Expunge specified messages
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen Here's how we do it:
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen - Start reading the mails from the beginning
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen - X-Keywords, X-UID and X-IMAPbase headers may contain padding at the end
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen of them, remember how much each message has and offset to beginning of the
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainen - If header needs to be rewritten and there's enough space, do it
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - If we didn't have enough space, remember how much was missing
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - Continue reading and counting the padding in each message. If available
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen padding is enough to rewrite all the previous messages needing it, do it
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - When we encounter expunged message, treat all of it as padding and
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen rewrite previous messages if needed (and there's enough space).
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen Afterwards keep moving messages backwards to fill the expunged space.
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen Moving is done by rewriting each message's headers, with possibly adding
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen missing Content-Length header and padding. Message bodies are moved
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen without modifications.
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen - If we encounter end of file, grow the file and rewrite needed messages
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen - Rewriting is done by moving message body forward, rewriting message's
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen header and doing the same for previous message, until all of them are
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen/* The text below was taken exactly as c-client wrote it to my mailbox,
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen so it's probably copyrighted by University of Washington. */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen"This text is part of the internal format of your mail folder, and is not\n" \
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen"a real message. It is created automatically by the mail system software.\n" \
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen"If deleted, important folder data will be lost, and it will be re-created\n" \
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen"with the data reset to initial values.\n"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mail_storage_set_critical(STORAGE(sync_ctx->mbox->storage),
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen "Unexpectedly lost From-line at offset %"PRIuUOFF_T
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenstatic void mbox_sync_array_delete_to(array_t *syncs_arr, uint32_t last_uid)
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen ARRAY_SET_TYPE(syncs_arr, struct mail_index_sync_rec);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen syncs = array_get_modifyable(syncs_arr, &count);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen /* keep it */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenmbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* get EOF */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset ||
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 && !mail_ctx->pseudo) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* need to add 'O' flag to Status-header */
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainenstatic int mbox_sync_buf_have_expunges(array_t *syncs_arr)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ARRAY_SET_TYPE(syncs_arr, struct mail_index_sync_rec);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen unsigned int i, count;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen for (i = 0; i < count; i++) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (syncs[i].type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
d7e72877b7a5085c3addf9729d0bfbe1b5357853Timo Sirainenstatic int mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
d7e72877b7a5085c3addf9729d0bfbe1b5357853Timo Sirainen struct mail_index_sync_rec *sync_rec = &sync_ctx->sync_rec;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen /* nothing for this or the future ones */
71da447014454c84828d9dface77219875554d7dTimo Sirainen mbox_sync_array_delete_to(&sync_ctx->syncs, uid);
if (ret < 0) {
if (ret == 0) {
if (!*sync_expunge_r)
int *keywords_changed_r)
unsigned int i, count;
for (i = 0; i < count; i++) {
int ret = 0;
if (ret < 0) {
return ret;
unsigned char hdr_md5_sum[],
const void *data;
int ret;
if (ret < 0) {
&data) < 0) {
int nocheck)
const void *data;
if (!nocheck) {
&data) < 0) {
int keywords_changed;
t_push();
t_pop();
MAIL_RECENT) != 0) {
MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
MAIL_INDEX_MAIL_FLAG_DIRTY) != 0;
t_pop();
const unsigned char *data;
if (from_line_size == 0)
const char *str;
int ret;
if (ret < 0) {
if (ret == 0) {
unsigned int i, count;
for (i = 0; i < count; i++) {
int ret;
if (ret < 0)
if (ret > 0) {
move_diff = 0;
if (seq == 0) {
seq++;
if (ret < 0)
if (ret == 0) {
old_offset) < 0) {
uid = 0;
if (seq1 == 0) {
int *skipped_mails)
int ret;
if (ret == 0) {
return ret;
int partial)
if (ret <= 0)
return ret;
uid = 0;
if (uid != 0) {
if (ret < 0)
if (ret == 0) {
uid = 0;
} else if (uid == 0 &&
&rec) < 0)
&expunged) < 0)
if (!expunged) {
if (!expunged) {
if (!expunged) {
} else if (partial) {
&partial,
if (ret <= 0) {
if (ret < 0)
if (!skipped_mails)
unsigned int uid_validity;
trailer_size = 0;
trailer_size) < 0)
if (offset == 0) {
(unsigned int)ioloop_time;
if (ret <= 0) {
if (ret < 0)
if (ret <= 0) {
unsigned int lock_id = 0;
if (!changed)
lock_id = 0;
if (changed) {
if (ret <= 0) {
if (ret < 0)
if (lock_id != 0)
return ret;
if (lock_id != 0)
if (lock_id == 0) {
goto __again;
if (ret < 0)
if (ret < 0)
unsigned int read_lock_id = 0;
return ret;
struct mailbox_sync_context *
int ret = 0;
ioloop_time) {