diff options
Diffstat (limited to 'src/lib-storage/index/dbox-common/dbox-mail.c')
-rw-r--r-- | src/lib-storage/index/dbox-common/dbox-mail.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/src/lib-storage/index/dbox-common/dbox-mail.c b/src/lib-storage/index/dbox-common/dbox-mail.c new file mode 100644 index 0000000..49279f9 --- /dev/null +++ b/src/lib-storage/index/dbox-common/dbox-mail.c @@ -0,0 +1,318 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream.h" +#include "str.h" +#include "index-storage.h" +#include "index-mail.h" +#include "index-pop3-uidl.h" +#include "dbox-attachment.h" +#include "dbox-storage.h" +#include "dbox-file.h" +#include "dbox-mail.h" + + +struct mail * +dbox_mail_alloc(struct mailbox_transaction_context *t, + enum mail_fetch_field wanted_fields, + struct mailbox_header_lookup_ctx *wanted_headers) +{ + struct dbox_mail *mail; + pool_t pool; + + pool = pool_alloconly_create("mail", 2048); + mail = p_new(pool, struct dbox_mail, 1); + + index_mail_init(&mail->imail, t, wanted_fields, wanted_headers, pool, NULL); + return &mail->imail.mail.mail; +} + +void dbox_mail_close(struct mail *_mail) +{ + struct dbox_mail *mail = DBOX_MAIL(_mail); + + index_mail_close(_mail); + /* close the dbox file only after index is closed, since it may still + try to read from it. */ + if (mail->open_file != NULL) + dbox_file_unref(&mail->open_file); +} + +int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r) +{ + struct dbox_storage *storage = + DBOX_STORAGE(mail->imail.mail.mail.box->storage); + uoff_t offset; + + if (storage->v.mail_open(mail, &offset, file_r) < 0) + return -1; + + if (dbox_file_seek(*file_r, offset) <= 0) + return -1; + if (dbox_file_metadata_read(*file_r) <= 0) + return -1; + + if (mail->imail.data.stream != NULL) { + /* we just messed up mail's input stream by reading metadata */ + i_stream_seek((*file_r)->input, offset); + i_stream_sync(mail->imail.data.stream); + } + return 0; +} + +static int +dbox_mail_metadata_get(struct dbox_mail *mail, enum dbox_metadata_key key, + const char **value_r) +{ + struct dbox_file *file; + + if (dbox_mail_metadata_read(mail, &file) < 0) + return -1; + + *value_r = dbox_file_metadata_get(file, key); + return 0; +} + +int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) +{ + struct dbox_mail *mail = DBOX_MAIL(_mail); + struct index_mail_data *data = &mail->imail.data; + struct dbox_file *file; + + if (index_mail_get_physical_size(_mail, size_r) == 0) + return 0; + + if (dbox_mail_metadata_read(mail, &file) < 0) + return -1; + + data->physical_size = dbox_file_get_plaintext_size(file); + *size_r = data->physical_size; + return 0; +} + +int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) +{ + struct dbox_mail *mail = DBOX_MAIL(_mail); + struct index_mail_data *data = &mail->imail.data; + const char *value; + uintmax_t size; + + if (index_mail_get_cached_virtual_size(&mail->imail, size_r)) + return 0; + + if (dbox_mail_metadata_get(mail, DBOX_METADATA_VIRTUAL_SIZE, + &value) < 0) + return -1; + if (value == NULL) + return index_mail_get_virtual_size(_mail, size_r); + + if (str_to_uintmax_hex(value, &size) < 0 || size > UOFF_T_MAX) + return -1; + data->virtual_size = (uoff_t)size; + *size_r = data->virtual_size; + return 0; +} + +int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r) +{ + struct dbox_mail *mail = DBOX_MAIL(_mail); + struct index_mail_data *data = &mail->imail.data; + const char *value; + uintmax_t time; + + if (index_mail_get_received_date(_mail, date_r) == 0) + return 0; + + if (dbox_mail_metadata_get(mail, DBOX_METADATA_RECEIVED_TIME, + &value) < 0) + return -1; + + time = 0; + if (value != NULL && str_to_uintmax_hex(value, &time) < 0) + return -1; + + data->received_date = (time_t)time; + *date_r = data->received_date; + return 0; +} + +int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r) +{ + struct dbox_storage *storage = DBOX_STORAGE(_mail->box->storage); + struct dbox_mail *mail = DBOX_MAIL(_mail); + struct index_mail_data *data = &mail->imail.data; + struct dbox_file *file; + struct stat st; + uoff_t offset; + + if (index_mail_get_save_date(_mail, date_r) > 0) + return 1; + + if (storage->v.mail_open(mail, &offset, &file) < 0) + return -1; + + _mail->transaction->stats.fstat_lookup_count++; + if (dbox_file_stat(file, &st) < 0) { + if (errno == ENOENT) + mail_set_expunged(_mail); + return -1; + } + *date_r = data->save_date = st.st_ctime; + return 1; +} + +static int +dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key, + enum index_cache_field cache_field, + const char **value_r) +{ + struct index_mail *imail = &mail->imail; + struct index_mailbox_context *ibox = + INDEX_STORAGE_CONTEXT(imail->mail.mail.box); + const char *value; + string_t *str; + uint32_t order; + + str = str_new(imail->mail.data_pool, 64); + if (mail_cache_lookup_field(imail->mail.mail.transaction->cache_view, + str, imail->mail.mail.seq, + ibox->cache_fields[cache_field].idx) > 0) { + if (cache_field == MAIL_CACHE_POP3_ORDER) { + i_assert(str_len(str) == sizeof(order)); + memcpy(&order, str_data(str), sizeof(order)); + str_truncate(str, 0); + if (order != 0) + str_printfa(str, "%u", order); + else { + /* order=0 means it doesn't exist. we don't + want to return "0" though, because then the + mails get ordered to beginning, while + nonexistent are supposed to be ordered at + the end. */ + } + } + *value_r = str_c(str); + return 0; + } + + if (dbox_mail_metadata_get(mail, key, &value) < 0) + return -1; + + if (value == NULL) + value = ""; + if (cache_field != MAIL_CACHE_POP3_ORDER) { + index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, + value, strlen(value)); + } else { + if (str_to_uint(value, &order) < 0) + order = 0; + index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, + &order, sizeof(order)); + } + + /* don't return pointer to dbox metadata directly, since it may + change unexpectedly */ + str_truncate(str, 0); + str_append(str, value); + *value_r = str_c(str); + return 0; +} + +int dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, + const char **value_r) +{ + struct dbox_mail *mail = DBOX_MAIL(_mail); + int ret; + + /* keep the UIDL in cache file, otherwise POP3 would open all + mail files and read the metadata. same for GUIDs if they're + used. */ + switch (field) { + case MAIL_FETCH_UIDL_BACKEND: + if (!index_pop3_uidl_can_exist(_mail)) { + *value_r = ""; + return 0; + } + ret = dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL, + MAIL_CACHE_POP3_UIDL, value_r); + if (ret == 0) { + index_pop3_uidl_update_exists(&mail->imail.mail.mail, + (*value_r)[0] != '\0'); + } + return ret; + case MAIL_FETCH_POP3_ORDER: + if (!index_pop3_uidl_can_exist(_mail)) { + /* we're assuming that if there's a POP3 order, there's + also a UIDL */ + *value_r = ""; + return 0; + } + return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_ORDER, + MAIL_CACHE_POP3_ORDER, value_r); + case MAIL_FETCH_GUID: + return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID, + MAIL_CACHE_GUID, value_r); + default: + break; + } + + return index_mail_get_special(_mail, field, value_r); +} + +static int +get_mail_stream(struct dbox_mail *mail, uoff_t offset, + struct istream **stream_r) +{ + struct mail_private *pmail = &mail->imail.mail; + struct dbox_file *file = mail->open_file; + int ret; + + if ((ret = dbox_file_seek(file, offset)) <= 0) { + *stream_r = NULL; + return ret; + } + + *stream_r = i_stream_create_limit(file->input, file->cur_physical_size); + if (pmail->v.istream_opened != NULL) { + if (pmail->v.istream_opened(&pmail->mail, stream_r) < 0) + return -1; + } + if (file->storage->attachment_dir == NULL) + return 1; + else + return dbox_attachment_file_get_stream(file, stream_r); +} + +int dbox_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 dbox_storage *storage = DBOX_STORAGE(_mail->box->storage); + struct dbox_mail *mail = DBOX_MAIL(_mail); + struct index_mail_data *data = &mail->imail.data; + struct istream *input; + uoff_t offset; + int ret; + + if (data->stream == NULL) { + if (storage->v.mail_open(mail, &offset, &mail->open_file) < 0) + return -1; + + ret = get_mail_stream(mail, offset, &input); + if (ret <= 0) { + if (ret < 0) + return -1; + dbox_file_set_corrupted(mail->open_file, + "uid=%u points to broken data at offset=" + "%"PRIuUOFF_T, _mail->uid, offset); + i_stream_unref(&input); + return -1; + } + data->stream = input; + index_mail_set_read_buffer_size(_mail, input); + } + + return index_mail_init_stream(&mail->imail, hdr_size, body_size, + stream_r); +} |