int mdbox_map_view_lookup_rec(struct mdbox_map *map, struct mail_index_view *view, uint32_t seq, struct dbox_mail_lookup_rec *rec_r) { const uint16_t *ref16_p; const void *data; memset(rec_r, 0, sizeof(*rec_r)); mail_index_lookup_uid(view, seq, &rec_r->map_uid); mail_index_lookup_ext(view, seq, map->map_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing map extension"); return -1; } memcpy(&rec_r->rec, data, sizeof(rec_r->rec)); mail_index_lookup_ext(view, seq, map->ref_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing ref extension"); return -1; } ref16_p = data; rec_r->refcount = *ref16_p; return 0; }
static void virtual_mail_set_seq(struct mail *mail, uint32_t seq) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box; struct virtual_backend_box *bbox; const struct virtual_mail_index_record *vrec; const void *data; bool expunged; mail_index_lookup_ext(mail->box->view, seq, mbox->virtual_ext_id, &data, &expunged); vrec = data; bbox = virtual_backend_box_lookup(mbox, vrec->mailbox_id); vmail->backend_mail = backend_mail_find(vmail, bbox->box); if (vmail->backend_mail == NULL) virtual_mail_set_backend_mail(mail, bbox); vmail->lost = !mail_set_uid(vmail->backend_mail, vrec->real_uid); memset(&vmail->imail.data, 0, sizeof(vmail->imail.data)); p_clear(vmail->imail.data_pool); vmail->imail.data.seq = seq; mail->seq = seq; mail_index_lookup_uid(mail->box->view, seq, &mail->uid); if (!vmail->lost) { mail->expunged = vmail->backend_mail->expunged; mail->has_nuls = vmail->backend_mail->has_nuls; mail->has_no_nuls = vmail->backend_mail->has_no_nuls; } else { mail->expunged = TRUE; mail->has_nuls = FALSE; mail->has_no_nuls = FALSE; } }
static int index_list_get_cached_status(struct mailbox *box, struct mailbox_status *status) { struct index_mailbox_list *ilist; struct mail_index_view *view; const void *data; uint32_t seq, *ext_id_p, *counter_p; unsigned int i; bool expunged; int ret; memset(status, 0, sizeof(*status)); ret = index_list_mailbox_open_unchanged_view(box, &view, &seq); if (ret <= 0) return ret; ilist = INDEX_LIST_CONTEXT(box->list); for (i = 0; index_list_map[i].name != NULL; i++) { ext_id_p = PTR_OFFSET(ilist, index_list_map[i].eid_offset); mail_index_lookup_ext(view, seq, *ext_id_p, &data, &expunged); if (expunged || data == NULL) { ret = 0; break; } counter_p = PTR_OFFSET(status, index_list_map[i].status_offset); *counter_p = *(const uint32_t *)data; } mail_index_view_close(&view); return ret; }
void mbox_list_index_update_sync(struct mailbox *box, struct mail_index_transaction *trans, uint32_t seq) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; struct mail_index_view *list_view; const struct mbox_index_header *mhdr = &mbox->mbox_hdr; const struct mbox_list_index_record *old_rec; struct mbox_list_index_record new_rec; const void *data; uint32_t ext_id; bool expunged; index_storage_list_index_update_sync(box, trans, seq); /* get the current record */ list_view = mail_index_transaction_get_view(trans); ext_id = mbox_list_get_ext_id(mbox, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); if (expunged) return; old_rec = data; memset(&new_rec, 0, sizeof(new_rec)); new_rec.mtime = mhdr->sync_mtime; new_rec.size = mhdr->sync_size & 0xffffffffU; if (old_rec == NULL || memcmp(old_rec, &new_rec, sizeof(*old_rec)) != 0) mail_index_update_ext(trans, seq, ext_id, &new_rec, NULL); }
static int index_list_update(struct index_mailbox_list *ilist, struct mailbox *box, struct mail_index_view *view, uint32_t seq, const struct mailbox_status *status) { struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box); struct mail_index_transaction *trans; struct mail_index_transaction_commit_result result; const void *data; const uint32_t *counter_p; uint32_t *ext_id_p; unsigned int i; bool expunged; int ret = 0; trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); /* update counters */ for (i = 0; index_list_map[i].name != NULL; i++) { ext_id_p = PTR_OFFSET(ilist, index_list_map[i].eid_offset); mail_index_lookup_ext(view, seq, *ext_id_p, &data, &expunged); if (expunged) { ret = -1; break; } counter_p = CONST_PTR_OFFSET(status, index_list_map[i].status_offset); if (data == NULL || *(const uint32_t *)data != *counter_p) { mail_index_update_ext(trans, seq, *ext_id_p, counter_p, NULL); } } if (box->v.list_index_update_sync(box, trans, seq) < 0) ret = -1; if (ret < 0) { mail_index_transaction_rollback(&trans); return -1; } if (mail_index_transaction_commit_full(&trans, &result) < 0) return -1; ibox->log_seq = result.log_file_seq; ibox->log_offset = result.log_file_offset; return 0; }
static int mdbox_map_lookup_seq(struct mdbox_map *map, uint32_t seq, const struct mdbox_map_mail_index_record **rec_r) { const struct mdbox_map_mail_index_record *rec; const void *data; uint32_t uid; mail_index_lookup_ext(map->view, seq, map->map_ext_id, &data, NULL); rec = data; if (rec == NULL || rec->file_id == 0) { mail_index_lookup_uid(map->view, seq, &uid); mdbox_map_set_corrupted(map, "file_id=0 for map_uid=%u", uid); return -1; } *rec_r = rec; return 0; }
static int mdbox_mail_get_save_date(struct mail *mail, time_t *date_r) { struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->transaction->box; const struct mdbox_mail_index_record *dbox_rec; const void *data; bool expunged; mail_index_lookup_ext(mail->transaction->view, mail->seq, mbox->ext_id, &data, &expunged); dbox_rec = data; if (dbox_rec == NULL || dbox_rec->map_uid == 0) { /* lost for some reason, use fallback */ return dbox_mail_get_save_date(mail, date_r); } *date_r = dbox_rec->save_date; return TRUE; }
int mdbox_mail_lookup(struct mdbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uint32_t *map_uid_r) { const struct mdbox_mail_index_record *dbox_rec; struct mdbox_index_header hdr; const void *data; uint32_t uid, cur_map_uid_validity; bool expunged; mail_index_lookup_ext(view, seq, mbox->ext_id, &data, &expunged); dbox_rec = data; if (dbox_rec == NULL || dbox_rec->map_uid == 0) { mail_index_lookup_uid(view, seq, &uid); mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: map uid lost for uid %u", mbox->box.path, uid); mdbox_storage_set_corrupted(mbox->storage); return -1; } if (mbox->map_uid_validity == 0) { if (mdbox_read_header(mbox, &hdr) < 0) { mdbox_storage_set_corrupted(mbox->storage); return -1; } mbox->map_uid_validity = hdr.map_uid_validity; } if (mdbox_map_open_or_create(mbox->storage->map) < 0) return -1; cur_map_uid_validity = mdbox_map_get_uid_validity(mbox->storage->map); if (cur_map_uid_validity != mbox->map_uid_validity) { mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: map uidvalidity mismatch (%u vs %u)", mbox->box.path, mbox->map_uid_validity, cur_map_uid_validity); mdbox_storage_set_corrupted(mbox->storage); return -1; } *map_uid_r = dbox_rec->map_uid; return 0; }
int mbox_list_index_has_changed(struct mailbox *box, struct mail_index_view *list_view, uint32_t seq) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; const struct mbox_list_index_record *rec; const void *data; const char *path; struct stat st; uint32_t ext_id; bool expunged; int ret; ret = index_storage_list_index_has_changed(box, list_view, seq); if (ret != 0 || box->storage->set->mailbox_list_index_very_dirty_syncs) return ret; ext_id = mbox_list_get_ext_id(mbox, list_view); mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged); rec = data; if (rec == NULL || expunged || rec->mtime == 0) { /* doesn't exist or not synced */ return 1; } ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); if (ret < 0) return ret; i_assert(ret > 0); if (stat(path, &st) < 0) { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", path); return -1; } if ((time_t)rec->mtime != st.st_mtime || rec->size != (uint32_t)(st.st_size & 0xffffffffU)) return 1; return 0; }
static int dbox_sync_verify_expunge_guid(struct mdbox_sync_context *ctx, uint32_t seq, const guid_128_t guid_128) { const void *data; uint32_t uid; mail_index_lookup_uid(ctx->sync_view, seq, &uid); mail_index_lookup_ext(ctx->sync_view, seq, ctx->mbox->guid_ext_id, &data, NULL); if (guid_128_is_empty(guid_128) || memcmp(data, guid_128, GUID_128_SIZE) == 0) return 0; mail_storage_set_critical(&ctx->mbox->storage->storage.storage, "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s", ctx->mbox->box.vname, uid, guid_128_to_string(data), guid_128_to_string(guid_128)); mdbox_storage_set_corrupted(ctx->mbox->storage); return -1; }
int mdbox_map_lookup_seq_full(struct mdbox_map *map, uint32_t seq, struct mdbox_map_mail_index_record *rec_r, uint16_t *refcount_r) { const struct mdbox_map_mail_index_record *rec; const uint16_t *ref16_p; const void *data; if (mdbox_map_lookup_seq(map, seq, &rec) < 0) return -1; *rec_r = *rec; mail_index_lookup_ext(map->view, seq, map->ref_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing ref extension"); return -1; } ref16_p = data; *refcount_r = *ref16_p; return 1; }
int mbox_file_lookup_offset(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uoff_t *offset_r) { const void *data; bool deleted; mail_index_lookup_ext(view, seq, mbox->mbox_ext_idx, &data, &deleted); if (deleted) return -1; if (data == NULL) { mail_storage_set_critical(&mbox->storage->storage, "Cached message offset lost for seq %u in mbox file %s", seq, mailbox_get_path(&mbox->box)); mbox->mbox_hdr.dirty_flag = 1; mbox->mbox_broken_offsets = TRUE; return 0; } *offset_r = *((const uint64_t *)data); return 1; }
static int dbox_sync_mark_expunges(struct mdbox_sync_context *ctx) { enum mail_index_transaction_flags flags = MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; struct mailbox *box = &ctx->mbox->box; struct mail_index_transaction *trans; struct seq_range_iter iter; unsigned int n; const void *data; uint32_t seq, uid; /* use a separate transaction here so that we can commit the changes during map transaction */ trans = mail_index_transaction_begin(ctx->sync_view, flags); seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); mail_index_lookup_ext(ctx->sync_view, seq, ctx->mbox->guid_ext_id, &data, NULL); mail_index_expunge_guid(trans, seq, data); } if (mail_index_transaction_commit(&trans) < 0) return -1; if (box->v.sync_notify != NULL) { /* do notifications after commit finished successfully */ box->tmp_sync_view = ctx->sync_view; seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } box->tmp_sync_view = NULL; } return 0; }
static int mailbox_list_index_parse_records(struct mailbox_list_index *ilist, struct mail_index_view *view, const char **error_r) { struct mailbox_list_index_node *node; const struct mail_index_record *rec; const struct mailbox_list_index_record *irec; const void *data; bool expunged; uint32_t seq, uid, count; *error_r = NULL; count = mail_index_view_get_messages_count(view); for (seq = 1; seq <= count; seq++) { node = p_new(ilist->mailbox_pool, struct mailbox_list_index_node, 1); rec = mail_index_lookup(view, seq); node->uid = rec->uid; node->flags = rec->flags; mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); if (data == NULL) { *error_r = "Missing list extension data"; return -1; } irec = data; node->name_id = irec->name_id; node->name = hash_table_lookup(ilist->mailbox_names, POINTER_CAST(irec->name_id)); if (node->name == NULL) { *error_r = "name_id not in index header"; if (ilist->has_backing_store) return -1; /* generate a new name and use it */ mailbox_list_index_generate_name(ilist, node); } hash_table_insert(ilist->mailbox_hash, POINTER_CAST(node->uid), node); } /* do a second scan to create the actual mailbox tree hierarchy. this is needed because the parent_uid may be smaller or higher than the current node's uid */ for (seq = 1; seq <= count; seq++) { mail_index_lookup_uid(view, seq, &uid); mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); irec = data; node = mailbox_list_index_lookup_uid(ilist, uid); i_assert(node != NULL); if (irec->parent_uid != 0) { /* node should have a parent */ node->parent = mailbox_list_index_lookup_uid(ilist, irec->parent_uid); if (node->parent != NULL) { node->next = node->parent->children; node->parent->children = node; continue; } *error_r = "parent_uid points to nonexistent record"; if (ilist->has_backing_store) return -1; /* just place it under the root */ } node->next = ilist->mailbox_tree; ilist->mailbox_tree = node; } return *error_r == NULL ? 0 : -1; }
bool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq) { struct mbox_sync_mail_context ctx; struct message_header_parser_ctx *hdr_ctx; struct message_header_line *hdr; struct header_func *func; struct mbox_md5_context *mbox_md5_ctx; const void *data; bool expunged; uint32_t uid; int ret; /* we only wish to be sure that this mail actually is what we expect it to be. If there's X-UID header and it matches our UID, we use it. Otherwise it could mean that the X-UID header is invalid and it's just not yet been rewritten. In that case use MD5 sum, if it exists. */ mail_index_lookup_uid(view, seq, &uid); memset(&ctx, 0, sizeof(ctx)); mbox_md5_ctx = mbox->md5_v.init(); hdr_ctx = message_parse_header_init(mbox->mbox_stream, NULL, 0); while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) { if (hdr->eoh) break; func = bsearch(hdr->name, header_funcs, N_ELEMENTS(header_funcs), sizeof(*header_funcs), mbox_sync_bsearch_header_func_cmp); if (func != NULL) { if (strcasecmp(hdr->name, "X-UID") == 0) { if (hdr->continues) { hdr->use_full_value = TRUE; continue; } (void)parse_x_uid(&ctx, hdr); if (ctx.mail.uid == uid) break; } } else { mbox->md5_v.more(mbox_md5_ctx, hdr); } } i_assert(ret != 0); message_parse_header_deinit(&hdr_ctx); mbox->md5_v.finish(mbox_md5_ctx, ctx.hdr_md5_sum); if (ctx.mail.uid == uid) return TRUE; /* match by MD5 sum */ mbox->mbox_save_md5 = TRUE; mail_index_lookup_ext(view, seq, mbox->md5hdr_ext_idx, &data, &expunged); return data == NULL ? 0 : memcmp(data, ctx.hdr_md5_sum, 16) == 0; }
static int mbox_mail_seek(struct index_mail *mail) { struct mbox_transaction_context *t = (struct mbox_transaction_context *)mail->mail.mail.transaction; struct mail *_mail = &mail->mail.mail; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; enum mbox_sync_flags sync_flags = 0; int ret, try; bool deleted; if (_mail->expunged || mbox->syncing) return -1; if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { mail_set_aborted(_mail); return -1; } if (mbox->mbox_stream != NULL && istream_raw_mbox_is_corrupted(mbox->mbox_stream)) { /* clear the corruption by forcing a full resync */ sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC; } for (try = 0; try < 2; try++) { if ((sync_flags & MBOX_SYNC_FORCE_SYNC) != 0) { /* dirty offsets are broken. make sure we can sync. */ mbox_prepare_resync(_mail); } if (mbox->mbox_lock_type == F_UNLCK) { i_assert(t->read_lock_id == 0); sync_flags |= MBOX_SYNC_LOCK_READING; if (mbox_sync(mbox, sync_flags) < 0) return -1; t->read_lock_id = mbox_get_cur_lock_id(mbox); i_assert(t->read_lock_id != 0); /* refresh index file after mbox has been locked to make sure we get only up-to-date mbox offsets. */ if (mail_index_refresh(mbox->box.index) < 0) { mailbox_set_index_error(&mbox->box); return -1; } i_assert(mbox->mbox_lock_type != F_UNLCK); } else if (t->read_lock_id == 0) { /* file is already locked by another transaction, but we must keep it locked for the entire transaction, so increase the lock counter. */ if (mbox_lock(mbox, mbox->mbox_lock_type, &t->read_lock_id) < 0) i_unreached(); } if (mbox_file_open_stream(mbox) < 0) return -1; ret = mbox_file_seek(mbox, _mail->transaction->view, _mail->seq, &deleted); if (ret > 0) { /* success */ break; } if (ret < 0) { if (deleted) mail_set_expunged(_mail); return -1; } /* we'll need to re-sync it completely */ sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC; } if (ret == 0) { mail_storage_set_critical(&mbox->storage->storage, "Losing sync for mail uid=%u in mbox file %s", _mail->uid, mailbox_get_path(&mbox->box)); } return 0; } static int mbox_mail_get_received_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (mbox_mail_seek(mail) < 0) return -1; data->received_date = istream_raw_mbox_get_received_time(mbox->mbox_stream); if (data->received_date == (time_t)-1) { /* it's broken and conflicts with our "not found" return value. change it. */ data->received_date = 0; } *date_r = data->received_date; return 0; } static int mbox_mail_get_save_date(struct mail *_mail, time_t *date_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; if (index_mail_get_save_date(_mail, date_r) == 0) return 0; /* no way to know this. save the current time into cache and use that from now on. this works only as long as the index files are permanent */ data->save_date = ioloop_time; *date_r = data->save_date; return 0; } static int mbox_mail_get_md5_header(struct index_mail *mail, const char **value_r) { struct mail *_mail = &mail->mail.mail; static uint8_t empty_md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; const void *ext_data; if (mail->data.guid != NULL) { *value_r = mail->data.guid; return 1; } mail_index_lookup_ext(_mail->transaction->view, _mail->seq, mbox->md5hdr_ext_idx, &ext_data, NULL); if (ext_data != NULL && memcmp(ext_data, empty_md5, 16) != 0) { mail->data.guid = p_strdup(mail->mail.data_pool, binary_to_hex(ext_data, 16)); *value_r = mail->data.guid; return 1; } else if (mail_index_is_expunged(_mail->transaction->view, _mail->seq)) { mail_set_expunged(_mail); return -1; } else { return 0; } } static int mbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; uoff_t offset; bool move_offset; int ret; switch (field) { case MAIL_FETCH_FROM_ENVELOPE: if (mbox_mail_seek(mail) < 0) return -1; *value_r = istream_raw_mbox_get_sender(mbox->mbox_stream); return 0; case MAIL_FETCH_GUID: case MAIL_FETCH_HEADER_MD5: if ((ret = mbox_mail_get_md5_header(mail, value_r)) != 0) return ret < 0 ? -1 : 0; /* i guess in theory the empty_md5 is valid and can happen, but it's almost guaranteed that it means the MD5 sum is missing. recalculate it. */ if (mbox->mbox_lock_type == F_UNLCK || mbox->mbox_stream == NULL) { offset = 0; move_offset = FALSE; } else { offset = istream_raw_mbox_get_start_offset(mbox->mbox_stream); move_offset = TRUE; } mbox->mbox_save_md5 = TRUE; if (mbox_sync(mbox, MBOX_SYNC_FORCE_SYNC | MBOX_SYNC_READONLY) < 0) return -1; if (move_offset) { if (istream_raw_mbox_seek(mbox->mbox_stream, offset) < 0) { i_error("mbox %s sync lost during MD5 syncing", _mail->box->name); return -1; } } if ((ret = mbox_mail_get_md5_header(mail, value_r)) == 0) { i_error("mbox %s resyncing didn't save header MD5 values", _mail->box->name); return -1; } return ret < 0 ? -1 : 0; default: break; } return index_mail_get_special(_mail, field, value_r); } static int mbox_mail_get_next_offset(struct index_mail *mail, uoff_t *next_offset_r) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->mail.mail.box; struct mail_index_view *view; const struct mail_index_header *hdr; uint32_t seq; int trailer_size; int ret = 1; *next_offset_r = (uoff_t)-1; hdr = mail_index_get_header(mail->mail.mail.transaction->view); if (mail->mail.mail.seq > hdr->messages_count) { /* we're appending a new message */ return 0; } /* We can't really trust trans_view. The next message may already be expunged from it. Also hdr.messages_count may be incorrect there. So refresh the index to get the latest changes and get the next message's offset using a new view. */ i_assert(mbox->mbox_lock_type != F_UNLCK); if (mbox_sync_header_refresh(mbox) < 0) return -1; view = mail_index_view_open(mail->mail.mail.box->index); hdr = mail_index_get_header(view); if (!mail_index_lookup_seq(view, mail->mail.mail.uid, &seq)) i_panic("Message unexpectedly expunged from index"); if (seq < hdr->messages_count) { if (mbox_file_lookup_offset(mbox, view, seq + 1, next_offset_r) <= 0) ret = -1; } else if (mail->mail.mail.box->input != NULL) { /* opened the mailbox as input stream. we can't trust the sync_size, since it's wrong with compressed mailboxes */ ret = 0; } else { /* last message, use the synced mbox size */ trailer_size = mbox->storage->storage.set->mail_save_crlf ? 2 : 1; *next_offset_r = mbox->mbox_hdr.sync_size - trailer_size; } mail_index_view_close(&view); return ret; } static int mbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box; struct istream *input; struct message_size hdr_size; uoff_t old_offset, body_offset, body_size, next_offset; if (index_mail_get_physical_size(_mail, size_r) == 0) return 0; /* we want to return the header size as seen by mail_get_stream(). */ old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_stream(_mail, &hdr_size, NULL, &input) < 0) return -1; /* our header size varies, so don't do any caching */ if (istream_raw_mbox_get_body_offset(mbox->mbox_stream, &body_offset) < 0) { mail_storage_set_critical(_mail->box->storage, "mbox %s: Couldn't get body offset for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); return -1; } /* use the next message's offset to avoid reading through the entire message body to find out its size */ if (mbox_mail_get_next_offset(mail, &next_offset) > 0) body_size = next_offset - body_offset; else body_size = (uoff_t)-1; /* verify that the calculated body size is correct */ if (istream_raw_mbox_get_body_size(mbox->mbox_stream, body_size, &body_size) < 0) { mail_storage_set_critical(_mail->box->storage, "mbox %s: Couldn't get body size for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); return -1; } data->physical_size = hdr_size.physical_size + body_size; *size_r = data->physical_size; i_stream_seek(input, old_offset); return 0; } static int mbox_mail_init_stream(struct index_mail *mail) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->mail.mail.box; struct istream *raw_stream; uoff_t hdr_offset, next_offset; int ret; if (mbox_mail_seek(mail) < 0) return -1; ret = mbox_mail_get_next_offset(mail, &next_offset); if (ret < 0) { if (mbox_mail_seek(mail) < 0) return -1; ret = mbox_mail_get_next_offset(mail, &next_offset); if (ret < 0) { i_warning("mbox %s: Can't find next message offset " "for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); } } raw_stream = mbox->mbox_stream; if (istream_raw_mbox_get_header_offset(raw_stream, &hdr_offset) < 0) { mail_storage_set_critical(mbox->box.storage, "mbox %s: Couldn't get header offset for uid=%u", mailbox_get_path(&mbox->box), mail->mail.mail.uid); return -1; } i_stream_seek(raw_stream, hdr_offset); if (next_offset != (uoff_t)-1) istream_raw_mbox_set_next_offset(raw_stream, next_offset); raw_stream = i_stream_create_limit(raw_stream, (uoff_t)-1); mail->data.stream = i_stream_create_header_filter(raw_stream, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, mbox_hide_headers, mbox_hide_headers_count, *null_header_filter_callback, (void *)NULL); i_stream_unref(&raw_stream); return 0; } static int mbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; if (mail->data.stream == NULL) { if (mbox_mail_init_stream(mail) < 0) return -1; } return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } static void mbox_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving) { struct index_mail *mail = (struct index_mail *)_mail; index_mail_set_seq(_mail, seq, saving); mail->data.dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; } static bool mbox_mail_set_uid(struct mail *_mail, uint32_t uid) { struct index_mail *mail = (struct index_mail *)_mail; bool ret; ret = index_mail_set_uid(_mail, uid); mail->data.dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; return ret; } struct mail_vfuncs mbox_mail_vfuncs = { index_mail_close, index_mail_free, mbox_mail_set_seq, mbox_mail_set_uid, index_mail_set_uid_cache_updates, index_mail_prefetch, index_mail_precache, index_mail_add_temp_wanted_fields, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_pvt_modseq, index_mail_get_parts, index_mail_get_date, mbox_mail_get_received_date, mbox_mail_get_save_date, index_mail_get_virtual_size, mbox_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, mbox_mail_get_stream, index_mail_get_binary_stream, mbox_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, index_mail_update_pvt_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened };