diff options
Diffstat (limited to 'src/lib-storage/index/maildir/maildir-mail.c')
-rw-r--r-- | src/lib-storage/index/maildir/maildir-mail.c | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/src/lib-storage/index/maildir/maildir-mail.c b/src/lib-storage/index/maildir/maildir-mail.c new file mode 100644 index 0000000..c3abbd3 --- /dev/null +++ b/src/lib-storage/index/maildir/maildir-mail.c @@ -0,0 +1,809 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream.h" +#include "nfs-workarounds.h" +#include "index-mail.h" +#include "maildir-storage.h" +#include "maildir-filename.h" +#include "maildir-uidlist.h" +#include "maildir-sync.h" + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +struct maildir_open_context { + int fd; + char *path; +}; + +static int +do_open(struct maildir_mailbox *mbox, const char *path, + struct maildir_open_context *ctx) +{ + ctx->fd = nfs_safe_open(path, O_RDONLY); + if (ctx->fd != -1) { + ctx->path = i_strdup(path); + return 1; + } + if (errno == ENOENT) + return 0; + + if (errno == EACCES) { + mailbox_set_critical(&mbox->box, "%s", + mail_error_eacces_msg("open", path)); + } else { + mailbox_set_critical(&mbox->box, + "open(%s) failed: %m", path); + } + return -1; +} + +static int +do_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st) +{ + if (stat(path, st) == 0) + return 1; + if (errno == ENOENT) + return 0; + + if (errno == EACCES) { + mailbox_set_critical(&mbox->box, "%s", + mail_error_eacces_msg("stat", path)); + } else { + mailbox_set_critical(&mbox->box, "stat(%s) failed: %m", path); + } + return -1; +} + +static struct istream * +maildir_open_mail(struct maildir_mailbox *mbox, struct mail *mail, + bool *deleted_r) +{ + struct istream *input; + const char *path; + struct maildir_open_context ctx; + + *deleted_r = FALSE; + + if (!mail_stream_access_start(mail)) + return NULL; + + ctx.fd = -1; + ctx.path = NULL; + + mail->transaction->stats.open_lookup_count++; + if (!mail->saving) { + if (maildir_file_do(mbox, mail->uid, do_open, &ctx) < 0) + return NULL; + } else { + path = maildir_save_file_get_path(mail->transaction, mail->seq); + if (do_open(mbox, path, &ctx) <= 0) + return NULL; + } + + if (ctx.fd == -1) { + *deleted_r = TRUE; + return NULL; + } + + input = i_stream_create_fd_autoclose(&ctx.fd, 0); + if (input->stream_errno == EISDIR) { + i_stream_destroy(&input); + if (maildir_lose_unexpected_dir(&mbox->storage->storage, + ctx.path) >= 0) + *deleted_r = TRUE; + } else { + i_stream_set_name(input, ctx.path); + index_mail_set_read_buffer_size(mail, input); + } + i_free(ctx.path); + return input; +} + +static int maildir_mail_stat(struct mail *mail, struct stat *st_r) +{ + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(mail->box); + struct index_mail *imail = INDEX_MAIL(mail); + const char *path; + int fd, ret; + + if (!mail_metadata_access_start(mail)) + return -1; + + if (imail->data.access_part != 0 && + imail->data.stream == NULL) { + /* we're going to open the mail anyway */ + struct istream *input; + + (void)mail_get_stream(mail, NULL, NULL, &input); + } + + if (imail->data.stream != NULL && + (fd = i_stream_get_fd(imail->data.stream)) != -1) { + mail->transaction->stats.fstat_lookup_count++; + if (fstat(fd, st_r) < 0) { + mail_set_critical(mail, "fstat(%s) failed: %m", + i_stream_get_name(imail->data.stream)); + return -1; + } + } else if (!mail->saving) { + mail->transaction->stats.stat_lookup_count++; + ret = maildir_file_do(mbox, mail->uid, do_stat, st_r); + if (ret <= 0) { + if (ret == 0) + mail_set_expunged(mail); + return -1; + } + } else { + mail->transaction->stats.stat_lookup_count++; + path = maildir_save_file_get_path(mail->transaction, mail->seq); + if (stat(path, st_r) < 0) { + mail_set_critical(mail, "stat(%s) failed: %m", path); + return -1; + } + } + return 0; +} + +static int maildir_mail_get_received_date(struct mail *_mail, time_t *date_r) +{ + struct index_mail *mail = INDEX_MAIL(_mail); + struct index_mail_data *data = &mail->data; + struct stat st; + + if (index_mail_get_received_date(_mail, date_r) == 0) + return 0; + + if (maildir_mail_stat(_mail, &st) < 0) + return -1; + + *date_r = data->received_date = st.st_mtime; + return 0; +} + +static int maildir_mail_get_save_date(struct mail *_mail, time_t *date_r) +{ + struct index_mail *mail = INDEX_MAIL(_mail); + struct index_mail_data *data = &mail->data; + struct stat st; + + if (index_mail_get_save_date(_mail, date_r) > 0) + return 1; + + if (maildir_mail_stat(_mail, &st) < 0) + return -1; + + *date_r = data->save_date = st.st_ctime; + return 1; +} + +static int +maildir_mail_get_fname(struct maildir_mailbox *mbox, struct mail *mail, + const char **fname_r) +{ + enum maildir_uidlist_rec_flag flags; + struct mail_index_view *view; + uint32_t seq; + bool exists; + int ret; + + ret = maildir_sync_lookup(mbox, mail->uid, &flags, fname_r); + if (ret != 0) + return ret; + + /* file exists in index file, but not in dovecot-uidlist anymore. */ + mail_set_expunged(mail); + + /* one reason this could happen is if we delayed opening + dovecot-uidlist and we're trying to open a mail that got recently + expunged. Let's test this theory first: */ + mail_index_refresh(mbox->box.index); + view = mail_index_view_open(mbox->box.index); + exists = mail_index_lookup_seq(view, mail->uid, &seq); + mail_index_view_close(&view); + + if (exists) { + /* the message still exists in index. this means there's some + kind of a desync, which doesn't get fixed if cur/ mtime is + the same as in index. fix this by forcing a resync. */ + (void)maildir_storage_sync_force(mbox, mail->uid); + } + return 0; +} + +static int maildir_get_pop3_state(struct index_mail *mail) +{ + struct mailbox *box = mail->mail.mail.box; + struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); + const struct mail_cache_field *fields; + unsigned int i, count, psize_idx, vsize_idx; + enum mail_cache_decision_type dec, vsize_dec; + enum mail_fetch_field allowed_pop3_fields; + bool not_pop3_only = FALSE; + + if (mail->pop3_state_set) + return mail->pop3_state; + + /* if this mail itself has non-pop3 fields we know we're not + pop3-only */ + allowed_pop3_fields = MAIL_FETCH_FLAGS | MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY | MAIL_FETCH_STORAGE_ID | + MAIL_FETCH_VIRTUAL_SIZE; + + if (mail->data.wanted_headers != NULL || + (mail->data.wanted_fields & ENUM_NEGATE(allowed_pop3_fields)) != 0) + not_pop3_only = TRUE; + + /* get vsize decisions */ + psize_idx = ibox->cache_fields[MAIL_CACHE_PHYSICAL_FULL_SIZE].idx; + vsize_idx = ibox->cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx; + if (not_pop3_only) { + vsize_dec = mail_cache_field_get_decision(box->cache, + vsize_idx); + vsize_dec &= ENUM_NEGATE(MAIL_CACHE_DECISION_FORCED); + } else { + /* also check if there are any non-[pv]size cached fields */ + vsize_dec = MAIL_CACHE_DECISION_NO; + fields = mail_cache_register_get_list(box->cache, + pool_datastack_create(), + &count); + for (i = 0; i < count; i++) { + dec = fields[i].decision & ENUM_NEGATE(MAIL_CACHE_DECISION_FORCED); + if (fields[i].idx == vsize_idx) + vsize_dec = dec; + else if (dec != MAIL_CACHE_DECISION_NO && + fields[i].idx != psize_idx) + not_pop3_only = TRUE; + } + } + + if (index_mail_get_vsize_extension(&mail->mail.mail) != NULL) { + /* having a vsize extension in index is the same as having + vsize's caching decision YES */ + vsize_dec = MAIL_CACHE_DECISION_YES; + } + + if (!not_pop3_only) { + /* either nothing is cached, or only vsize is cached. */ + mail->pop3_state = 1; + } else if (vsize_dec != MAIL_CACHE_DECISION_YES && + (box->flags & MAILBOX_FLAG_POP3_SESSION) == 0) { + /* if virtual size isn't cached permanently, + POP3 isn't being used */ + mail->pop3_state = -1; + } else { + /* possibly a mixed pop3/imap */ + mail->pop3_state = 0; + } + mail->pop3_state_set = TRUE; + return mail->pop3_state; +} + +static int maildir_quick_size_lookup(struct index_mail *mail, bool vsize, + uoff_t *size_r) +{ + struct mail *_mail = &mail->mail.mail; + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_mail->box); + enum maildir_uidlist_rec_ext_key key; + const char *path, *fname, *value; + + if (!_mail->saving) { + if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) + return -1; + } else { + if (maildir_save_file_get_size(_mail->transaction, _mail->seq, + vsize, size_r) == 0) + return 1; + + path = maildir_save_file_get_path(_mail->transaction, + _mail->seq); + fname = strrchr(path, '/'); + fname = fname != NULL ? fname + 1 : path; + } + + /* size can be included in filename */ + if (vsize || !mbox->storage->set->maildir_broken_filename_sizes) { + if (maildir_filename_get_size(fname, + vsize ? MAILDIR_EXTRA_VIRTUAL_SIZE : + MAILDIR_EXTRA_FILE_SIZE, size_r)) + return 1; + } + + /* size can be included in uidlist entry */ + if (!_mail->saving) { + key = vsize ? MAILDIR_UIDLIST_REC_EXT_VSIZE : + MAILDIR_UIDLIST_REC_EXT_PSIZE; + value = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, + key); + if (value != NULL && str_to_uoff(value, size_r) == 0) + return 1; + } + return 0; +} + +static void +maildir_handle_size_caching(struct index_mail *mail, bool quick_check, + bool vsize) +{ + struct mailbox *box = mail->mail.mail.box; + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box); + enum mail_fetch_field field; + uoff_t size; + int pop3_state; + + field = vsize ? MAIL_FETCH_VIRTUAL_SIZE : MAIL_FETCH_PHYSICAL_SIZE; + if ((mail->data.dont_cache_fetch_fields & field) != 0) + return; + + if (quick_check && maildir_quick_size_lookup(mail, vsize, &size) > 0) { + /* already in filename / uidlist. don't add it anywhere, + including to the uidlist if it's already in filename. + do some extra checks here to catch potential cache bugs. */ + if (vsize && mail->data.virtual_size != size) { + mail_set_mail_cache_corrupted(&mail->mail.mail, + "Corrupted virtual size: " + "%"PRIuUOFF_T" != %"PRIuUOFF_T, + mail->data.virtual_size, size); + mail->data.virtual_size = size; + } else if (!vsize && mail->data.physical_size != size) { + mail_set_mail_cache_corrupted(&mail->mail.mail, + "Corrupted physical size: " + "%"PRIuUOFF_T" != %"PRIuUOFF_T, + mail->data.physical_size, size); + mail->data.physical_size = size; + } + mail->data.dont_cache_fetch_fields |= field; + return; + } + + /* 1 = pop3-only, 0 = mixed, -1 = no pop3 */ + pop3_state = maildir_get_pop3_state(mail); + if (pop3_state >= 0 && mail->mail.mail.uid != 0) { + /* if size is wanted permanently, store it to uidlist + so that in case cache file gets lost we can get it quickly */ + mail->data.dont_cache_fetch_fields |= field; + size = vsize ? mail->data.virtual_size : + mail->data.physical_size; + maildir_uidlist_set_ext(mbox->uidlist, mail->mail.mail.uid, + vsize ? MAILDIR_UIDLIST_REC_EXT_VSIZE : + MAILDIR_UIDLIST_REC_EXT_PSIZE, + dec2str(size)); + } +} + +static int maildir_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) +{ + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_mail->box); + struct index_mail *mail = INDEX_MAIL(_mail); + struct index_mail_data *data = &mail->data; + struct message_size hdr_size, body_size; + struct istream *input; + uoff_t old_offset; + + if (maildir_uidlist_is_read(mbox->uidlist) || + (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) { + /* try to get the size from uidlist. this is especially useful + with pop3 to avoid unnecessarily opening the cache file. */ + if (maildir_quick_size_lookup(mail, TRUE, + &data->virtual_size) < 0) + return -1; + } + + if (data->virtual_size == UOFF_T_MAX) { + if (index_mail_get_cached_virtual_size(mail, size_r)) { + i_assert(mail->data.virtual_size != UOFF_T_MAX); + maildir_handle_size_caching(mail, TRUE, TRUE); + return 0; + } + if (maildir_quick_size_lookup(mail, TRUE, + &data->virtual_size) < 0) + return -1; + } + if (data->virtual_size != UOFF_T_MAX) { + data->dont_cache_fetch_fields |= MAIL_FETCH_VIRTUAL_SIZE; + *size_r = data->virtual_size; + return 0; + } + + /* fallback to reading the file */ + old_offset = data->stream == NULL ? 0 : data->stream->v_offset; + if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0) + return -1; + i_stream_seek(data->stream, old_offset); + + maildir_handle_size_caching(mail, FALSE, TRUE); + *size_r = data->virtual_size; + return 0; +} + +static int maildir_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) +{ + struct index_mail *mail = INDEX_MAIL(_mail); + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_mail->box); + struct index_mail_data *data = &mail->data; + struct stat st; + struct message_size hdr_size, body_size; + struct istream *input; + const char *path; + int ret; + + if (maildir_uidlist_is_read(mbox->uidlist) || + (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) { + /* try to get the size from uidlist (see virtual size above) */ + if (maildir_quick_size_lookup(mail, FALSE, + &data->physical_size) < 0) + return -1; + } + + if (data->physical_size == UOFF_T_MAX) { + if (index_mail_get_physical_size(_mail, size_r) == 0) { + i_assert(mail->data.physical_size != UOFF_T_MAX); + maildir_handle_size_caching(mail, TRUE, FALSE); + return 0; + } + if (maildir_quick_size_lookup(mail, FALSE, + &data->physical_size) < 0) + return -1; + } + if (data->physical_size != UOFF_T_MAX) { + data->dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE; + *size_r = data->physical_size; + return 0; + } + + if (mail->mail.v.istream_opened != NULL) { + /* we can't use stat(), because this may be a mail that some + plugin has changed (e.g. zlib). need to do it the slow + way. */ + if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0) + return -1; + st.st_size = hdr_size.physical_size + body_size.physical_size; + } else if (!_mail->saving) { + ret = maildir_file_do(mbox, _mail->uid, do_stat, &st); + if (ret <= 0) { + if (ret == 0) + mail_set_expunged(_mail); + return -1; + } + } else { + /* saved mail which hasn't been committed yet */ + path = maildir_save_file_get_path(_mail->transaction, + _mail->seq); + if (stat(path, &st) < 0) { + mail_set_critical(_mail, "stat(%s) failed: %m", path); + return -1; + } + } + + data->physical_size = st.st_size; + maildir_handle_size_caching(mail, FALSE, FALSE); + *size_r = st.st_size; + return 0; +} + +static int +maildir_mail_get_special(struct mail *_mail, enum mail_fetch_field field, + const char **value_r) +{ + struct index_mail *mail = INDEX_MAIL(_mail); + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_mail->box); + const char *path, *fname = NULL, *end, *guid, *uidl, *order; + struct stat st; + + switch (field) { + case MAIL_FETCH_GUID: + /* use GUID from uidlist if it exists */ + i_assert(!_mail->saving); + + if (mail->data.guid != NULL) { + *value_r = mail->data.guid; + return 0; + } + + /* first make sure that we have a refreshed uidlist */ + if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) + return -1; + + guid = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, + MAILDIR_UIDLIST_REC_EXT_GUID); + if (guid != NULL) { + if (*guid != '\0') { + *value_r = mail->data.guid = + p_strdup(mail->mail.data_pool, guid); + return 0; + } + + mail_set_critical(_mail, + "Maildir: Corrupted dovecot-uidlist: " + "UID had empty GUID, clearing it"); + maildir_uidlist_unset_ext(mbox->uidlist, _mail->uid, + MAILDIR_UIDLIST_REC_EXT_GUID); + } + + /* default to base filename: */ + if (maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, + value_r) < 0) + return -1; + mail->data.guid = mail->data.filename; + return 0; + case MAIL_FETCH_STORAGE_ID: + if (mail->data.filename != NULL) { + *value_r = mail->data.filename; + return 0; + } + if (fname != NULL) { + /* we came here from MAIL_FETCH_GUID, + avoid a second lookup */ + } else if (!_mail->saving) { + if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) + return -1; + } else { + path = maildir_save_file_get_path(_mail->transaction, + _mail->seq); + fname = strrchr(path, '/'); + fname = fname != NULL ? fname + 1 : path; + } + end = strchr(fname, MAILDIR_INFO_SEP); + mail->data.filename = end == NULL ? + p_strdup(mail->mail.data_pool, fname) : + p_strdup_until(mail->mail.data_pool, fname, end); + *value_r = mail->data.filename; + return 0; + case MAIL_FETCH_UIDL_BACKEND: + uidl = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, + MAILDIR_UIDLIST_REC_EXT_POP3_UIDL); + if (uidl == NULL) { + /* use the default */ + *value_r = ""; + } else if (*uidl == '\0') { + /* special optimization case: use the base file name */ + return maildir_mail_get_special(_mail, + MAIL_FETCH_STORAGE_ID, value_r); + } else { + *value_r = p_strdup(mail->mail.data_pool, uidl); + } + return 0; + case MAIL_FETCH_POP3_ORDER: + order = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, + MAILDIR_UIDLIST_REC_EXT_POP3_ORDER); + if (order == NULL) { + *value_r = ""; + } else { + *value_r = p_strdup(mail->mail.data_pool, order); + } + return 0; + case MAIL_FETCH_REFCOUNT: + if (maildir_mail_stat(_mail, &st) < 0) + return -1; + *value_r = p_strdup_printf(mail->mail.data_pool, "%lu", + (unsigned long)st.st_nlink); + return 0; + case MAIL_FETCH_REFCOUNT_ID: + if (maildir_mail_stat(_mail, &st) < 0) + return -1; + *value_r = p_strdup_printf(mail->mail.data_pool, "%llu", + (unsigned long long)st.st_ino); + return 0; + default: + return index_mail_get_special(_mail, field, value_r); + } +} + +static int +maildir_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 = INDEX_MAIL(_mail); + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_mail->box); + struct index_mail_data *data = &mail->data; + bool deleted; + + if (data->stream == NULL) { + data->stream = maildir_open_mail(mbox, _mail, &deleted); + if (data->stream == NULL) { + if (deleted) + mail_set_expunged(_mail); + return -1; + } + if (mail->mail.v.istream_opened != NULL) { + if (mail->mail.v.istream_opened(_mail, + &data->stream) < 0) { + i_stream_unref(&data->stream); + return -1; + } + } + } + + return index_mail_init_stream(mail, hdr_size, body_size, stream_r); +} + +static void maildir_update_pop3_uidl(struct mail *_mail, const char *uidl) +{ + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_mail->box); + const char *fname; + + if (maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, + &fname) == 0 && + strcmp(uidl, fname) == 0) { + /* special case optimization: empty UIDL means the same + as base filename */ + uidl = ""; + } + + maildir_uidlist_set_ext(mbox->uidlist, _mail->uid, + MAILDIR_UIDLIST_REC_EXT_POP3_UIDL, uidl); +} + +static void maildir_mail_remove_sizes_from_uidlist(struct mail *mail) +{ + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(mail->box); + + if (maildir_uidlist_lookup_ext(mbox->uidlist, mail->uid, + MAILDIR_UIDLIST_REC_EXT_VSIZE) != NULL) { + maildir_uidlist_unset_ext(mbox->uidlist, mail->uid, + MAILDIR_UIDLIST_REC_EXT_VSIZE); + } + if (maildir_uidlist_lookup_ext(mbox->uidlist, mail->uid, + MAILDIR_UIDLIST_REC_EXT_PSIZE) != NULL) { + maildir_uidlist_unset_ext(mbox->uidlist, mail->uid, + MAILDIR_UIDLIST_REC_EXT_PSIZE); + } +} + +struct maildir_size_fix_ctx { + uoff_t physical_size; + char wrong_key; +}; + +static int +do_fix_size(struct maildir_mailbox *mbox, const char *path, + struct maildir_size_fix_ctx *ctx) +{ + const char *fname, *newpath, *extra, *info, *dir; + struct stat st; + + fname = strrchr(path, '/'); + i_assert(fname != NULL); + dir = t_strdup_until(path, fname++); + + extra = strchr(fname, MAILDIR_EXTRA_SEP); + i_assert(extra != NULL); + info = strchr(fname, MAILDIR_INFO_SEP); + if (info == NULL) info = ""; + + if (ctx->physical_size == UOFF_T_MAX) { + if (stat(path, &st) < 0) { + if (errno == ENOENT) + return 0; + mailbox_set_critical(&mbox->box, + "stat(%s) failed: %m", path); + return -1; + } + ctx->physical_size = st.st_size; + } + + newpath = t_strdup_printf("%s/%s,S=%"PRIuUOFF_T"%s", dir, + t_strdup_until(fname, extra), + ctx->physical_size, info); + + if (rename(path, newpath) == 0) { + mailbox_set_critical(&mbox->box, + "Maildir filename has wrong %c value, " + "renamed the file from %s to %s", + ctx->wrong_key, path, newpath); + return 1; + } + if (errno == ENOENT) + return 0; + + mailbox_set_critical(&mbox->box, "rename(%s, %s) failed: %m", + path, newpath); + return -1; +} + +static void +maildir_mail_remove_sizes_from_filename(struct mail *mail, + enum mail_fetch_field field) +{ + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(mail->box); + struct mail_private *pmail = (struct mail_private *)mail; + enum maildir_uidlist_rec_flag flags; + const char *fname; + uoff_t size; + struct maildir_size_fix_ctx ctx; + + if (mbox->storage->set->maildir_broken_filename_sizes) { + /* never try to fix sizes in maildir filenames */ + return; + } + + if (maildir_sync_lookup(mbox, mail->uid, &flags, &fname) <= 0) + return; + if (strchr(fname, MAILDIR_EXTRA_SEP) == NULL) + return; + + i_zero(&ctx); + ctx.physical_size = UOFF_T_MAX; + if (field == MAIL_FETCH_VIRTUAL_SIZE && + maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE, + &size)) { + ctx.wrong_key = 'W'; + } else if (field == MAIL_FETCH_PHYSICAL_SIZE && + maildir_filename_get_size(fname, MAILDIR_EXTRA_FILE_SIZE, + &size)) { + ctx.wrong_key = 'S'; + } else { + /* the broken size isn't in filename */ + return; + } + + if (pmail->v.istream_opened != NULL) { + /* the mail could be e.g. compressed. get the physical size + the slow way by actually reading the mail. */ + struct istream *input; + const struct stat *stp; + + if (mail_get_stream(mail, NULL, NULL, &input) < 0) + return; + if (i_stream_stat(input, TRUE, &stp) < 0) + return; + ctx.physical_size = stp->st_size; + } + + (void)maildir_file_do(mbox, mail->uid, do_fix_size, &ctx); +} + +static void maildir_mail_set_cache_corrupted(struct mail *_mail, + enum mail_fetch_field field, + const char *reason) +{ + if (field == MAIL_FETCH_PHYSICAL_SIZE || + field == MAIL_FETCH_VIRTUAL_SIZE) { + maildir_mail_remove_sizes_from_uidlist(_mail); + maildir_mail_remove_sizes_from_filename(_mail, field); + } + index_mail_set_cache_corrupted(_mail, field, reason); +} + +struct mail_vfuncs maildir_mail_vfuncs = { + index_mail_close, + index_mail_free, + index_mail_set_seq, + index_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, + maildir_mail_get_received_date, + maildir_mail_get_save_date, + maildir_mail_get_virtual_size, + maildir_mail_get_physical_size, + index_mail_get_first_header, + index_mail_get_headers, + index_mail_get_header_stream, + maildir_mail_get_stream, + index_mail_get_binary_stream, + maildir_mail_get_special, + index_mail_get_backend_mail, + index_mail_update_flags, + index_mail_update_keywords, + index_mail_update_modseq, + index_mail_update_pvt_modseq, + maildir_update_pop3_uidl, + index_mail_expunge, + maildir_mail_set_cache_corrupted, + index_mail_opened, +}; |