summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/dbox-common/dbox-mail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/index/dbox-common/dbox-mail.c')
-rw-r--r--src/lib-storage/index/dbox-common/dbox-mail.c318
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);
+}