Mercurial > dovecot > core-2.2
view src/lib-storage/index/imapc/imapc-mail.c @ 13564:5726f89486a0
imapc: Keep the last fetched message body cached until mailbox is closed.
This primarily helps partial IMAP fetches so each partial fetch doesn't
redownload the message body.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 30 Sep 2011 18:53:09 +0300 |
parents | b6633cb57814 |
children | 4cfe3f14bb46 |
line wrap: on
line source
/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "imap-envelope.h" #include "imapc-msgmap.h" #include "imapc-mail.h" #include "imapc-client.h" #include "imapc-storage.h" struct mail * imapc_mail_alloc(struct mailbox_transaction_context *t, enum mail_fetch_field wanted_fields, struct mailbox_header_lookup_ctx *wanted_headers) { struct imapc_mail *mail; pool_t pool; pool = pool_alloconly_create("mail", 2048); mail = p_new(pool, struct imapc_mail, 1); mail->imail.mail.pool = pool; mail->fd = -1; index_mail_init(&mail->imail, t, wanted_fields, wanted_headers); return &mail->imail.mail.mail; } static void imapc_mail_free(struct mail *_mail) { struct imapc_mail *mail = (struct imapc_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct imapc_mail_cache *cache = &mbox->prev_mail_cache; if (mail->body_fetched) { imapc_mail_cache_free(cache); cache->uid = _mail->uid; if (cache->fd != -1) { cache->fd = mail->fd; mail->fd = -1; } else { cache->buf = mail->body; mail->body = NULL; } } if (mail->fd != -1) { if (close(mail->fd) < 0) i_error("close(imapc mail) failed: %m"); } if (mail->body != NULL) buffer_free(&mail->body); index_mail_free(_mail); } static bool imapc_mail_is_expunged(struct mail *_mail) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct imapc_msgmap *msgmap; struct imapc_simple_context sctx; uint32_t lseq, rseq; if (mbox->sync_view != NULL) { /* check if another session has already expunged it */ if (!mail_index_lookup_seq(mbox->sync_view, _mail->uid, &lseq)) return TRUE; } /* check if we've received EXPUNGE for it */ msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); if (!imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq)) return TRUE; /* we may be running against a server that hasn't bothered sending us an EXPUNGE. see if NOOP sends it. */ imapc_simple_context_init(&sctx, mbox->storage); imapc_client_cmdf(mbox->storage->client, imapc_simple_callback, &sctx, "NOOP"); imapc_simple_run(&sctx); return !imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq); } static void imapc_mail_failed(struct mail *mail, const char *field) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->box; if (mail->expunged || imapc_mail_is_expunged(mail)) mail_set_expunged(mail); else if (!imapc_client_mailbox_is_connected(mbox->client_box)) { /* we've already logged a disconnection error */ mail_storage_set_internal_error(mail->box->storage); } else { mail_storage_set_critical(mail->box->storage, "imapc: Remote server didn't send %s for UID %u in %s", field, mail->uid, mail->box->vname); } } static int imapc_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; if (index_mail_get_received_date(_mail, date_r) == 0) return 0; if (data->received_date == (time_t)-1) { if (imapc_mail_fetch(_mail, MAIL_FETCH_RECEIVED_DATE) < 0) return -1; if (data->received_date == (time_t)-1) { imapc_mail_failed(_mail, "INTERNALDATE"); return -1; } } *date_r = data->received_date; return 0; } static int imapc_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 (data->save_date == (time_t)-1) { /* FIXME */ return -1; } *date_r = data->save_date; return 0; } static int imapc_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 istream *input; uoff_t old_offset; int ret; if (data->physical_size == (uoff_t)-1) (void)index_mail_get_physical_size(_mail, size_r); if (data->physical_size == (uoff_t)-1) { old_offset = data->stream == NULL ? 0 : data->stream->v_offset; if (mail_get_stream(_mail, NULL, NULL, &input) < 0) return -1; i_stream_seek(data->stream, old_offset); ret = i_stream_get_size(data->stream, TRUE, &data->physical_size); if (ret <= 0) { i_assert(ret != 0); mail_storage_set_critical(_mail->box->storage, "imapc: stat(%s) failed: %m", i_stream_get_name(data->stream)); return -1; } } *size_r = data->physical_size; return 0; } static int imapc_mail_get_stream(struct mail *_mail, struct message_size *hdr_size, struct message_size *body_size, struct istream **stream_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; enum mail_fetch_field fetch_field; if (data->stream == NULL) { if (!mail->data.initialized) { /* coming here from mail_set_seq() */ return mail_set_aborted(_mail); } fetch_field = body_size != NULL || (mail->wanted_fields & MAIL_FETCH_STREAM_BODY) != 0 ? MAIL_FETCH_STREAM_BODY : MAIL_FETCH_STREAM_HEADER; if (imapc_mail_fetch(_mail, fetch_field) < 0) return -1; if (data->stream == NULL) { imapc_mail_failed(_mail, "BODY[]"); return -1; } } return index_mail_init_stream(mail, hdr_size, body_size, stream_r); } static bool imapc_mail_has_headers_in_cache(struct index_mail *mail, struct mailbox_header_lookup_ctx *headers) { struct mail *_mail = &mail->mail.mail; unsigned int i; for (i = 0; i < headers->count; i++) { if (mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, headers->idx[i]) <= 0) return FALSE; } return TRUE; } static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving) { struct imapc_mail *imail = (struct imapc_mail *)_mail; struct index_mail *mail = &imail->imail; struct mailbox_header_lookup_ctx *header_ctx; time_t date; uoff_t size; index_mail_set_seq(_mail, seq, saving); if ((mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0) (void)index_mail_get_received_date(_mail, &date); if ((mail->wanted_fields & MAIL_FETCH_PHYSICAL_SIZE) != 0) { if (index_mail_get_physical_size(_mail, &size) < 0) mail->data.access_part |= READ_HDR | READ_BODY; } if (mail->data.access_part == 0 && mail->wanted_headers != NULL) { /* see if all wanted headers exist in cache */ if (!imapc_mail_has_headers_in_cache(mail, mail->wanted_headers)) mail->data.access_part |= PARSE_HDR; } if (mail->data.access_part == 0 && (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0) { /* the common code already checked this partially, but we need a guaranteed correct answer */ header_ctx = mailbox_header_lookup_init(_mail->box, imap_envelope_headers); if (!imapc_mail_has_headers_in_cache(mail, header_ctx)) mail->data.access_part |= PARSE_HDR; mailbox_header_lookup_unref(&header_ctx); } /* searching code handles prefetching internally, elsewhere we want to do it immediately */ if (!mail->search_mail && !_mail->saving) (void)imapc_mail_prefetch(_mail); } static void imapc_mail_close(struct mail *_mail) { struct imapc_mail *imail = (struct imapc_mail *)_mail; struct imapc_storage *storage = (struct imapc_storage *)_mail->box->storage; while (imail->fetch_count > 0) imapc_storage_run(storage); index_mail_close(_mail); } struct mail_vfuncs imapc_mail_vfuncs = { imapc_mail_close, imapc_mail_free, imapc_mail_set_seq, index_mail_set_uid, index_mail_set_uid_cache_updates, imapc_mail_prefetch, index_mail_precache, index_mail_get_flags, index_mail_get_keywords, index_mail_get_keyword_indexes, index_mail_get_modseq, index_mail_get_parts, index_mail_get_date, imapc_mail_get_received_date, imapc_mail_get_save_date, index_mail_get_virtual_size, imapc_mail_get_physical_size, index_mail_get_first_header, index_mail_get_headers, index_mail_get_header_stream, imapc_mail_get_stream, index_mail_get_special, index_mail_get_real_mail, index_mail_update_flags, index_mail_update_keywords, index_mail_update_modseq, NULL, index_mail_expunge, index_mail_set_cache_corrupted, index_mail_opened };