index-mail.c revision 6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "lib.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "array.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "buffer.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "ioloop.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "istream.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "str.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "message-date.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "message-part-serialize.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "message-parser.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "imap-bodystructure.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "imap-envelope.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "mail-cache.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "mail-index-modseq.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "index-storage.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "istream-mail.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include "index-mail.h"
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen#include <fcntl.h>
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstruct mail_cache_field global_cache_fields[MAIL_INDEX_CACHE_FIELD_COUNT] = {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "flags",
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen .type = MAIL_CACHE_FIELD_BITMASK,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .field_size = sizeof(uint32_t) },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "date.sent",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_FIXED_SIZE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .field_size = sizeof(struct mail_sent_date) },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "date.received",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_FIXED_SIZE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .field_size = sizeof(uint32_t) },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "date.save",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_FIXED_SIZE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .field_size = sizeof(uint32_t) },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "size.virtual",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_FIXED_SIZE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .field_size = sizeof(uoff_t) },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "size.physical",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_FIXED_SIZE,
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen .field_size = sizeof(uoff_t) },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "imap.body",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_STRING },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "imap.bodystructure",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_STRING },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "imap.envelope",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_STRING },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "pop3.uidl",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_STRING },
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen { .name = "guid",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_STRING },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "mime.parts",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_VARIABLE_SIZE },
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen { .name = "binary.parts",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen .type = MAIL_CACHE_FIELD_VARIABLE_SIZE }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen};
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic int index_mail_parse_body(struct index_mail *mail,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen enum index_cache_field field);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint index_mail_cache_lookup_field(struct index_mail *mail, buffer_t *buf,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen unsigned int field_idx)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen int ret;
87b5c1fc03945708726c175d540fd307f7f78480Phil Carmody
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = mail_cache_lookup_field(mail->mail.mail.transaction->cache_view,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen buf, mail->data.seq, field_idx);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (ret > 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->mail.mail.transaction->stats.cache_hit_count++;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return ret;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic struct message_part *get_unserialized_parts(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const unsigned int field_idx =
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct message_part *parts;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen buffer_t *part_buf;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const char *error;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen int ret;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen part_buf = buffer_create_dynamic(pool_datastack_create(), 128);
d06e111700769927a64b46dc157ac1c122dff1a0Timo Sirainen ret = index_mail_cache_lookup_field(mail, part_buf, field_idx);
d06e111700769927a64b46dc157ac1c122dff1a0Timo Sirainen if (ret <= 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return NULL;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen parts = message_part_deserialize(mail->data_pool, part_buf->data,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen part_buf->used, &error);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (parts == NULL) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_cache_set_corrupted(mail->mail.mail.box->cache,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "Corrupted cached mime.parts data (%s)", error);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return parts;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic bool get_cached_parts(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct message_part *part;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen T_BEGIN {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen part = get_unserialized_parts(mail);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen } T_END;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (part == NULL)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen /* we know the NULs now, update them */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->mail.mail.has_nuls = TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->mail.mail.has_no_nuls = FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen } else {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->mail.mail.has_nuls = FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->mail.mail.has_no_nuls = TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->data.parts = part;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic bool index_mail_get_fixed_field(struct index_mail *mail,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen enum index_cache_field field,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen void *data, size_t data_size)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const unsigned int field_idx = mail->ibox->cache_fields[field].idx;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen buffer_t buf;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen int ret;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen buffer_create_data(&buf, data, data_size);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mail_cache_lookup_field(mail, &buf, field_idx) <= 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen else {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen i_assert(buf.used == data_size);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return ret;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenbool index_mail_get_cached_uoff_t(struct index_mail *mail,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen enum index_cache_field field, uoff_t *size_r)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return index_mail_get_fixed_field(mail, field,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen size_r, sizeof(*size_r));
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic bool index_mail_get_pvt(struct mail *_mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail->seq_pvt != 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (_mail->box->view_pvt == NULL) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* no private view (set by view syncing) -> no private flags */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen index_transaction_init_pvt(_mail->transaction);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!mail_index_lookup_seq(_mail->transaction->view_pvt, _mail->uid,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen &mail->seq_pvt))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->seq_pvt = 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return mail->seq_pvt != 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenenum mail_flags index_mail_get_flags(struct mail *_mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const struct mail_index_record *rec;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen enum mail_flags flags, pvt_flags_mask;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen rec = mail_index_lookup(_mail->transaction->view, _mail->seq);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen flags = rec->flags & (MAIL_FLAGS_NONRECENT |
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen MAIL_INDEX_MAIL_FLAG_BACKEND);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mailbox_is_recent(_mail->box, _mail->uid))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen flags |= MAIL_RECENT;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mail_get_pvt(_mail)) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* mailbox has private flags */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen pvt_flags_mask = mailbox_get_private_flags_mask(_mail->box);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen flags &= ~pvt_flags_mask;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen rec = mail_index_lookup(_mail->transaction->view_pvt,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->seq_pvt);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen flags |= rec->flags & pvt_flags_mask;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return flags;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmodyuint64_t index_mail_get_modseq(struct mail *_mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail->data.modseq != 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return mail->data.modseq;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen mail_index_modseq_enable(_mail->box->index);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->data.modseq =
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen mail_index_modseq_lookup(_mail->transaction->view, _mail->seq);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return mail->data.modseq;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
6b2f82c7ed4e646217a1ee29e99c83d95762a8d6Phil Carmodyconst char *const *index_mail_get_keywords(struct mail *_mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen struct index_mail_data *data = &mail->data;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen const char *const *names;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const unsigned int *keyword_indexes;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen unsigned int i, count, names_count;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
2282944443f78bd186809df235da1b7e801f0430Phil Carmody if (array_is_created(&data->keywords))
2282944443f78bd186809df235da1b7e801f0430Phil Carmody return array_idx(&data->keywords, 0);
2282944443f78bd186809df235da1b7e801f0430Phil Carmody
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (void)index_mail_get_keyword_indexes(_mail);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen keyword_indexes = array_get(&data->keyword_indexes, &count);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen names = array_get(mail->ibox->keyword_names, &names_count);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen p_array_init(&data->keywords, mail->data_pool, count + 1);
6b2f82c7ed4e646217a1ee29e99c83d95762a8d6Phil Carmody for (i = 0; i < count; i++) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const char *name;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen i_assert(keyword_indexes[i] < names_count);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen name = names[keyword_indexes[i]];
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen array_append(&data->keywords, &name, 1);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
2282944443f78bd186809df235da1b7e801f0430Phil Carmody /* end with NULL */
2282944443f78bd186809df235da1b7e801f0430Phil Carmody array_append_zero(&data->keywords);
2282944443f78bd186809df235da1b7e801f0430Phil Carmody return array_idx(&data->keywords, 0);
2282944443f78bd186809df235da1b7e801f0430Phil Carmody}
2282944443f78bd186809df235da1b7e801f0430Phil Carmody
2282944443f78bd186809df235da1b7e801f0430Phil Carmodyconst ARRAY_TYPE(keyword_indexes) *
2282944443f78bd186809df235da1b7e801f0430Phil Carmodyindex_mail_get_keyword_indexes(struct mail *_mail)
2282944443f78bd186809df235da1b7e801f0430Phil Carmody{
2282944443f78bd186809df235da1b7e801f0430Phil Carmody struct index_mail *mail = (struct index_mail *)_mail;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!array_is_created(&data->keyword_indexes)) {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody p_array_init(&data->keyword_indexes, mail->data_pool, 32);
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody mail_index_lookup_keywords(_mail->transaction->view,
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody mail->data.seq,
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody &data->keyword_indexes);
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody }
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody return &data->keyword_indexes;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody}
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmodyint index_mail_get_parts(struct mail *_mail, struct message_part **parts_r)
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody{
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody struct index_mail *mail = (struct index_mail *)_mail;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody struct index_mail_data *data = &mail->data;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody data->cache_fetch_fields |= MAIL_FETCH_MESSAGE_PARTS;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (data->parts != NULL || get_cached_parts(mail)) {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody *parts_r = data->parts;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody return 0;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody }
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (data->parser_ctx == NULL) {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (index_mail_parse_headers(mail, NULL) < 0)
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody return -1;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody }
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody data->save_message_parts = TRUE;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody if (index_mail_parse_body(mail, 0) < 0)
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody return -1;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody *parts_r = data->parts;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint index_mail_get_received_date(struct mail *_mail, time_t *date_r)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen data->cache_fetch_fields |= MAIL_FETCH_RECEIVED_DATE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (data->received_date == (time_t)-1) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen uint32_t t;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mail_get_fixed_field(mail, MAIL_CACHE_RECEIVED_DATE,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen &t, sizeof(t)))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->received_date = t;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *date_r = data->received_date;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return *date_r == (time_t)-1 ? -1 : 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint index_mail_get_save_date(struct mail *_mail, time_t *date_r)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->cache_fetch_fields |= MAIL_FETCH_SAVE_DATE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (data->save_date == (time_t)-1) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen uint32_t t;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mail_get_fixed_field(mail, MAIL_CACHE_SAVE_DATE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen &t, sizeof(t)))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->save_date = t;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *date_r = data->save_date;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return *date_r == (time_t)-1 ? -1 : 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic int index_mail_cache_sent_date(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const char *str;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen time_t t;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen int ret, tz;
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody if (data->sent_date.time != (uint32_t)-1)
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if ((ret = mail_get_first_header(&mail->mail.mail, "Date", &str)) < 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return ret;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (ret == 0 ||
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen !message_date_parse((const unsigned char *)str,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen strlen(str), &t, &tz)) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* 0 = not found / invalid */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen t = 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen tz = 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->sent_date.time = t;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->sent_date.timezone = tz;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen &data->sent_date, sizeof(data->sent_date));
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint index_mail_get_date(struct mail *_mail, time_t *date_r, int *timezone_r)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->cache_fetch_fields |= MAIL_FETCH_DATE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (data->sent_date.time != (uint32_t)-1) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *timezone_r = data->sent_date.timezone;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *date_r = data->sent_date.time;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (void)index_mail_get_fixed_field(mail, MAIL_CACHE_SENT_DATE,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen &data->sent_date,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen sizeof(data->sent_date));
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mail_cache_sent_date(mail) < 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return -1;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *timezone_r = data->sent_date.timezone;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *date_r = data->sent_date.time;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic bool get_cached_msgpart_sizes(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody if (data->parts == NULL)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (void)get_cached_parts(mail);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (data->parts != NULL) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->hdr_size_set = TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->hdr_size = data->parts->header_size;
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody data->body_size = data->parts->body_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->body_size_set = TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->virtual_size = data->parts->header_size.virtual_size +
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->body_size.virtual_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->physical_size = data->parts->header_size.physical_size +
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->body_size.physical_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen return data->parts != NULL;
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen}
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainenbool index_mail_get_cached_virtual_size(struct index_mail *mail, uoff_t *size_r)
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen{
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen struct index_mail_data *data = &mail->data;
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen data->cache_fetch_fields |= MAIL_FETCH_VIRTUAL_SIZE;
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen if (data->virtual_size == (uoff_t)-1) {
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen if (!index_mail_get_cached_uoff_t(mail,
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen MAIL_CACHE_VIRTUAL_FULL_SIZE,
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen &data->virtual_size)) {
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen if (!get_cached_msgpart_sizes(mail))
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody return FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody }
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody if (data->hdr_size_set && data->physical_size != (uoff_t)-1) {
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen data->body_size.physical_size = data->physical_size -
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody data->hdr_size.physical_size;
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody data->body_size.virtual_size = data->virtual_size -
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody data->hdr_size.virtual_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->body_size_set = TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *size_r = data->virtual_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic void index_mail_get_cached_body_size(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen uoff_t tmp;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!data->hdr_size_set)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return;
87b5c1fc03945708726c175d540fd307f7f78480Phil Carmody
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* we've already called get_cached_msgpart_sizes() and it didn't work.
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen try to do this by using cached virtual size and a quick physical
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen size lookup. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!index_mail_get_cached_virtual_size(mail, &tmp))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!data->body_size_set) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail_get_physical_size(&mail->mail.mail, &tmp) < 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* we should have everything now. try again. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (void)index_mail_get_cached_virtual_size(mail, &tmp);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint index_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct message_size hdr_size, body_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct istream *input;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen uoff_t old_offset;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (index_mail_get_cached_virtual_size(mail, size_r))
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return -1;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen i_stream_seek(data->stream, old_offset);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen i_assert(data->virtual_size != (uoff_t)-1);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen *size_r = data->virtual_size;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint index_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data->cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (data->physical_size == (uoff_t)-1) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!index_mail_get_cached_uoff_t(mail,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen MAIL_CACHE_PHYSICAL_FULL_SIZE,
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen &data->physical_size))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (void)get_cached_msgpart_sizes(mail);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen *size_r = data->physical_size;
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen return *size_r == (uoff_t)-1 ? -1 : 0;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenvoid index_mail_cache_add(struct index_mail *mail, enum index_cache_field field,
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen const void *data, size_t data_size)
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen index_mail_cache_add_idx(mail, mail->ibox->cache_fields[field].idx,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data, data_size);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenvoid index_mail_cache_add_idx(struct index_mail *mail, unsigned int field_idx,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const void *data, size_t data_size)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct mail *_mail = &mail->mail.mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const struct mail_storage_settings *set = _mail->box->storage->set;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const struct mail_index_header *hdr;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (set->mail_cache_min_mail_count > 0) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* First check if we've configured caching not to be used with
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen low enough message count. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen hdr = mail_index_get_header(_mail->transaction->view);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (hdr->messages_count < set->mail_cache_min_mail_count)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!mail->data.no_caching &&
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->data.dont_cache_field_idx != field_idx) {
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody mail_cache_add(_mail->transaction->cache_trans, _mail->seq,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen field_idx, data, data_size);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen }
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic void parse_bodystructure_part_header(struct message_part *part,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct message_header_line *hdr,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen pool_t pool)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen imap_bodystructure_parse_header(pool, part, hdr);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic bool want_plain_bodystructure_cached(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const unsigned int cache_field_body =
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const unsigned int cache_field_bodystructure =
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct mail *_mail = &mail->mail.mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if ((mail->data.wanted_fields & (MAIL_FETCH_IMAP_BODY |
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0)
3281669db44d09a087a203201248abbc81b3cc1aTimo Sirainen return TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail_cache_field_want_add(_mail->transaction->cache_trans,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen _mail->seq, cache_field_body))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail_cache_field_want_add(_mail->transaction->cache_trans,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen _mail->seq, cache_field_bodystructure))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return TRUE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return FALSE;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen}
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic void index_mail_body_parsed_cache_flags(struct index_mail *mail)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen{
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct mail *_mail = &mail->mail.mail;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct index_mail_data *data = &mail->data;
9afa8aff5bda7c24cce6cc19b8232d2a228b4109Timo Sirainen unsigned int cache_flags_idx;
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen uint32_t cache_flags = data->cache_flags;
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen bool want_cached;
9afa8aff5bda7c24cce6cc19b8232d2a228b4109Timo Sirainen
9afa8aff5bda7c24cce6cc19b8232d2a228b4109Timo Sirainen cache_flags_idx = mail->ibox->cache_fields[MAIL_CACHE_FLAGS].idx;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen want_cached = mail_cache_field_want_add(_mail->transaction->cache_trans,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen _mail->seq, cache_flags_idx);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (data->parsed_bodystructure &&
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen imap_bodystructure_is_plain_7bit(data->parts) &&
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody (want_cached || want_plain_bodystructure_cached(mail))) {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody cache_flags |= MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody /* we need message_parts cached to be able to
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody actually use it in BODY/BODYSTRUCTURE reply */
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody want_cached = TRUE;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody data->save_message_parts = TRUE;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody }
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody /* cache flags should never get unset as long as the message doesn't
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody change, but try to handle it anyway */
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody cache_flags &= ~(MAIL_CACHE_FLAG_BINARY_HEADER |
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody MAIL_CACHE_FLAG_BINARY_BODY |
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody MAIL_CACHE_FLAG_HAS_NULS |
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody MAIL_CACHE_FLAG_HAS_NO_NULS);
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody _mail->has_nuls = TRUE;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody _mail->has_no_nuls = FALSE;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody cache_flags |= MAIL_CACHE_FLAG_HAS_NULS;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody } else {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody _mail->has_nuls = FALSE;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody _mail->has_no_nuls = TRUE;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody cache_flags |= MAIL_CACHE_FLAG_HAS_NO_NULS;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody }
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (data->hdr_size.virtual_size == data->hdr_size.physical_size)
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody cache_flags |= MAIL_CACHE_FLAG_BINARY_HEADER;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (data->body_size.virtual_size == data->body_size.physical_size)
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody cache_flags |= MAIL_CACHE_FLAG_BINARY_BODY;
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody if (cache_flags != data->cache_flags && want_cached) {
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody index_mail_cache_add_idx(mail, cache_flags_idx,
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody &cache_flags, sizeof(cache_flags));
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody }
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody data->cache_flags = cache_flags;
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody}
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmodystatic void index_mail_body_parsed_cache_message_parts(struct index_mail *mail)
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody{
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody struct mail *_mail = &mail->mail.mail;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody struct index_mail_data *data = &mail->data;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody const unsigned int cache_field =
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody enum mail_cache_decision_type decision;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody buffer_t *buffer;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody if (data->messageparts_saved_to_cache ||
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody cache_field) != 0) {
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody /* already cached */
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody return;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody }
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody decision = mail_cache_field_get_decision(_mail->box->cache,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody cache_field);
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody if (decision == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody /* we never want it cached */
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody return;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody }
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody if (decision == MAIL_CACHE_DECISION_NO &&
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody !data->save_message_parts &&
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody (data->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) == 0) {
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody /* we didn't really care about the message parts themselves,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody just wanted to use something that depended on it */
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody return;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody }
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody T_BEGIN {
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody buffer = buffer_create_dynamic(pool_datastack_create(), 1024);
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody message_part_serialize(mail->data.parts, buffer);
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody index_mail_cache_add_idx(mail, cache_field,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody buffer->data, buffer->used);
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody } T_END;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody data->messageparts_saved_to_cache = TRUE;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody}
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmodystatic void
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmodyindex_mail_body_parsed_cache_bodystructure(struct index_mail *mail,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody enum index_cache_field field)
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody{
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody struct mail *_mail = &mail->mail.mail;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody struct index_mail_data *data = &mail->data;
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody const unsigned int cache_field_parts =
mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
const unsigned int cache_field_body =
mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx;
const unsigned int cache_field_bodystructure =
mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
enum mail_cache_decision_type dec;
string_t *str;
bool bodystructure_cached = FALSE;
bool plain_bodystructure = FALSE;
bool cache_bodystructure, cache_body;
if ((data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0) {
if (data->messageparts_saved_to_cache ||
mail_cache_field_exists(_mail->transaction->cache_view,
_mail->seq, cache_field_parts) > 0) {
/* cached it as flag + message_parts */
plain_bodystructure = TRUE;
}
}
if (!data->parsed_bodystructure)
return;
/* If BODY is fetched first but BODYSTRUCTURE is also wanted, we don't
normally want to first cache BODY and then BODYSTRUCTURE. So check
the wanted_fields also in here. */
if (plain_bodystructure)
cache_bodystructure = FALSE;
else if (field == MAIL_CACHE_IMAP_BODYSTRUCTURE ||
(data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) {
cache_bodystructure =
mail_cache_field_can_add(_mail->transaction->cache_trans,
_mail->seq, cache_field_bodystructure);
} else {
cache_bodystructure =
mail_cache_field_want_add(_mail->transaction->cache_trans,
_mail->seq, cache_field_bodystructure);
}
if (cache_bodystructure) {
str = str_new(mail->data_pool, 128);
imap_bodystructure_write(data->parts, str, TRUE);
data->bodystructure = str_c(str);
index_mail_cache_add(mail, MAIL_CACHE_IMAP_BODYSTRUCTURE,
str_c(str), str_len(str)+1);
bodystructure_cached = TRUE;
} else {
bodystructure_cached =
mail_cache_field_exists(_mail->transaction->cache_view,
_mail->seq, cache_field_bodystructure) > 0;
}
/* normally don't cache both BODY and BODYSTRUCTURE, but do it
if BODY is forced to be cached */
dec = mail_cache_field_get_decision(_mail->box->cache,
cache_field_body);
if (plain_bodystructure ||
(bodystructure_cached &&
(dec != (MAIL_CACHE_DECISION_FORCED | MAIL_CACHE_DECISION_YES))))
cache_body = FALSE;
else if (field == MAIL_CACHE_IMAP_BODY) {
cache_body =
mail_cache_field_can_add(_mail->transaction->cache_trans,
_mail->seq, cache_field_body);
} else {
cache_body =
mail_cache_field_want_add(_mail->transaction->cache_trans,
_mail->seq, cache_field_body);
}
if (cache_body) {
str = str_new(mail->data_pool, 128);
imap_bodystructure_write(data->parts, str, FALSE);
data->body = str_c(str);
index_mail_cache_add(mail, MAIL_CACHE_IMAP_BODY,
str_c(str), str_len(str)+1);
}
}
static bool
index_mail_want_cache(struct index_mail *mail, enum index_cache_field field)
{
struct mail *_mail = &mail->mail.mail;
enum mail_fetch_field fetch_field;
unsigned int cache_field;
switch (field) {
case MAIL_CACHE_SENT_DATE:
fetch_field = MAIL_FETCH_DATE;
break;
case MAIL_CACHE_RECEIVED_DATE:
fetch_field = MAIL_FETCH_RECEIVED_DATE;
break;
case MAIL_CACHE_SAVE_DATE:
fetch_field = MAIL_FETCH_SAVE_DATE;
break;
case MAIL_CACHE_VIRTUAL_FULL_SIZE:
fetch_field = MAIL_FETCH_VIRTUAL_SIZE;
break;
case MAIL_CACHE_PHYSICAL_FULL_SIZE:
fetch_field = MAIL_FETCH_PHYSICAL_SIZE;
break;
default:
i_unreached();
}
if ((mail->data.dont_cache_fetch_fields & fetch_field) != 0)
return FALSE;
cache_field = mail->ibox->cache_fields[field].idx;
if ((mail->data.cache_fetch_fields & fetch_field) != 0) {
return mail_cache_field_can_add(_mail->transaction->cache_trans,
_mail->seq, cache_field);
} else {
return mail_cache_field_want_add(_mail->transaction->cache_trans,
_mail->seq, cache_field);
}
}
static void index_mail_cache_sizes(struct index_mail *mail)
{
static enum index_cache_field size_fields[] = {
MAIL_CACHE_VIRTUAL_FULL_SIZE,
MAIL_CACHE_PHYSICAL_FULL_SIZE
};
uoff_t sizes[N_ELEMENTS(size_fields)];
unsigned int i;
sizes[0] = mail->data.virtual_size;
sizes[1] = mail->data.physical_size;
for (i = 0; i < N_ELEMENTS(size_fields); i++) {
if (sizes[i] != (uoff_t)-1 &&
index_mail_want_cache(mail, size_fields[i])) {
index_mail_cache_add(mail, size_fields[i],
&sizes[i], sizeof(sizes[i]));
}
}
}
static void index_mail_cache_dates(struct index_mail *mail)
{
static enum index_cache_field date_fields[] = {
MAIL_CACHE_RECEIVED_DATE,
MAIL_CACHE_SAVE_DATE
};
time_t dates[N_ELEMENTS(date_fields)];
unsigned int i;
uint32_t t;
dates[0] = mail->data.received_date;
dates[1] = mail->mail.mail.saving ? ioloop_time :
mail->data.save_date;
for (i = 0; i < N_ELEMENTS(date_fields); i++) {
if (dates[i] != (time_t)-1 &&
index_mail_want_cache(mail, date_fields[i])) {
t = dates[i];
index_mail_cache_add(mail, date_fields[i],
&t, sizeof(t));
}
}
if (mail->data.sent_date_parsed &&
index_mail_want_cache(mail, MAIL_CACHE_SENT_DATE))
(void)index_mail_cache_sent_date(mail);
}
static int index_mail_parse_body_finish(struct index_mail *mail,
enum index_cache_field field)
{
if (message_parser_deinit(&mail->data.parser_ctx,
&mail->data.parts) < 0) {
mail_set_cache_corrupted(&mail->mail.mail,
MAIL_FETCH_MESSAGE_PARTS);
mail->data.parsed_bodystructure = FALSE;
return -1;
}
if (mail->data.no_caching) {
/* if we're here because we aborted parsing, don't get any
further or we may crash while generating output from
incomplete data */
return 0;
}
(void)get_cached_msgpart_sizes(mail);
index_mail_body_parsed_cache_flags(mail);
index_mail_body_parsed_cache_message_parts(mail);
index_mail_body_parsed_cache_bodystructure(mail, field);
index_mail_cache_sizes(mail);
index_mail_cache_dates(mail);
return 0;
}
static int index_mail_stream_check_failure(struct index_mail *mail)
{
if (mail->data.stream->stream_errno == 0)
return 0;
errno = mail->data.stream->stream_errno;
mail_storage_set_critical(mail->mail.mail.box->storage,
"read(%s) failed: %m (uid=%u)",
i_stream_get_name(mail->data.stream), mail->mail.mail.uid);
return -1;
}
static int index_mail_parse_body(struct index_mail *mail,
enum index_cache_field field)
{
struct index_mail_data *data = &mail->data;
uoff_t old_offset;
int ret;
i_assert(data->parser_ctx != NULL);
old_offset = data->stream->v_offset;
i_stream_seek(data->stream, data->hdr_size.physical_size);
if (data->save_bodystructure_body) {
/* bodystructure header is parsed, we want the body's mime
headers too */
i_assert(!data->save_bodystructure_header);
message_parser_parse_body(data->parser_ctx,
parse_bodystructure_part_header,
mail->data_pool);
data->save_bodystructure_body = FALSE;
data->parsed_bodystructure = TRUE;
} else {
message_parser_parse_body(data->parser_ctx,
null_message_part_header_callback, NULL);
}
ret = index_mail_stream_check_failure(mail);
if (index_mail_parse_body_finish(mail, field) < 0)
ret = -1;
i_stream_seek(data->stream, old_offset);
return ret;
}
static void index_mail_stream_destroy_callback(struct index_mail *mail)
{
i_assert(mail->data.destroying_stream);
mail->data.destroying_stream = FALSE;
}
void index_mail_set_read_buffer_size(struct mail *_mail, struct istream *input)
{
struct index_mail *mail = (struct index_mail *)_mail;
unsigned int block_size;
i_stream_set_max_buffer_size(input, MAIL_READ_FULL_BLOCK_SIZE);
block_size = (mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0 ?
MAIL_READ_FULL_BLOCK_SIZE : MAIL_READ_HDR_BLOCK_SIZE;
i_stream_set_init_buffer_size(input, block_size);
}
int index_mail_init_stream(struct index_mail *mail,
struct message_size *hdr_size,
struct message_size *body_size,
struct istream **stream_r)
{
struct mail *_mail = &mail->mail.mail;
struct index_mail_data *data = &mail->data;
struct istream *input;
bool has_nuls;
int ret;
if (!data->initialized_wrapper_stream &&
_mail->transaction->stats_track) {
input = i_stream_create_mail(_mail, data->stream,
!data->stream_has_only_header);
i_stream_unref(&data->stream);
data->stream = input;
data->initialized_wrapper_stream = TRUE;
}
if (!data->destroy_callback_set) {
/* do this only once in case a plugin changes the stream.
otherwise the check would break. */
data->destroy_callback_set = TRUE;
i_stream_set_destroy_callback(data->stream,
index_mail_stream_destroy_callback, mail);
}
if (hdr_size != NULL || body_size != NULL)
(void)get_cached_msgpart_sizes(mail);
if (hdr_size != NULL || body_size != NULL) {
i_stream_seek(data->stream, 0);
if (!data->hdr_size_set) {
if ((data->access_part & PARSE_HDR) != 0) {
(void)get_cached_parts(mail);
if (index_mail_parse_headers(mail, NULL) < 0)
return -1;
} else {
if (message_get_header_size(data->stream,
&data->hdr_size,
&has_nuls) < 0) {
mail_storage_set_critical(_mail->box->storage,
"read(%s) failed: %m",
i_stream_get_name(data->stream));
return -1;
}
data->hdr_size_set = TRUE;
}
}
if (hdr_size != NULL)
*hdr_size = data->hdr_size;
}
if (body_size != NULL) {
if (!data->body_size_set)
index_mail_get_cached_body_size(mail);
if (!data->body_size_set) {
i_stream_seek(data->stream,
data->hdr_size.physical_size);
if ((data->access_part & PARSE_BODY) != 0) {
if (index_mail_parse_body(mail, 0) < 0)
return -1;
} else {
if (message_get_body_size(data->stream,
&data->body_size,
&has_nuls) < 0) {
mail_storage_set_critical(_mail->box->storage,
"read(%s) failed: %m",
i_stream_get_name(data->stream));
return -1;
}
data->body_size_set = TRUE;
}
}
*body_size = data->body_size;
}
if (data->hdr_size_set && data->body_size_set) {
data->virtual_size = data->hdr_size.virtual_size +
data->body_size.virtual_size;
data->physical_size = data->hdr_size.physical_size +
data->body_size.physical_size;
}
ret = index_mail_stream_check_failure(mail);
i_stream_seek(data->stream, 0);
if (ret < 0)
return -1;
*stream_r = data->stream;
return 0;
}
static int index_mail_parse_bodystructure(struct index_mail *mail,
enum index_cache_field field)
{
struct index_mail_data *data = &mail->data;
string_t *str;
if (data->parsed_bodystructure) {
/* we have everything parsed already, but just not written to
a string */
index_mail_body_parsed_cache_bodystructure(mail, field);
} else {
if (data->save_bodystructure_header ||
!data->save_bodystructure_body) {
/* we haven't parsed the header yet */
data->save_bodystructure_header = TRUE;
data->save_bodystructure_body = TRUE;
(void)get_cached_parts(mail);
if (index_mail_parse_headers(mail, NULL) < 0)
return -1;
}
if (index_mail_parse_body(mail, field) < 0)
return -1;
}
i_assert(data->parts != NULL);
/* if we didn't want to have the body(structure) cached,
it's still not written. */
switch (field) {
case MAIL_CACHE_IMAP_BODY:
if (data->body == NULL) {
str = str_new(mail->data_pool, 128);
imap_bodystructure_write(data->parts, str, FALSE);
data->body = str_c(str);
}
break;
case MAIL_CACHE_IMAP_BODYSTRUCTURE:
if (data->bodystructure == NULL) {
str = str_new(mail->data_pool, 128);
imap_bodystructure_write(data->parts, str, TRUE);
data->bodystructure = str_c(str);
}
break;
default:
i_unreached();
}
return 0;
}
static void
index_mail_get_plain_bodystructure(struct index_mail *mail, string_t *str,
bool extended)
{
str_printfa(str, IMAP_BODY_PLAIN_7BIT_ASCII" %"PRIuUOFF_T" %u",
mail->data.parts->body_size.virtual_size,
mail->data.parts->body_size.lines);
if (extended)
str_append(str, " NIL NIL NIL NIL");
}
int index_mail_get_special(struct mail *_mail,
enum mail_fetch_field field, const char **value_r)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct index_mail_data *data = &mail->data;
const struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
string_t *str;
switch (field) {
case MAIL_FETCH_IMAP_BODY: {
const unsigned int body_cache_field =
cache_fields[MAIL_CACHE_IMAP_BODY].idx;
const unsigned int bodystructure_cache_field =
cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
if (data->body != NULL) {
*value_r = data->body;
return 0;
}
/* 1) use plain-7bit-ascii flag if it exists
2) get BODY if it exists
3) get it using BODYSTRUCTURE if it exists
4) parse body structure, and save BODY/BODYSTRUCTURE
depending on what we want cached */
str = str_new(mail->data_pool, 128);
if ((mail->data.cache_flags &
MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 &&
get_cached_parts(mail)) {
index_mail_get_plain_bodystructure(mail, str, FALSE);
data->body = str_c(str);
} else if (index_mail_cache_lookup_field(mail, str,
body_cache_field) > 0)
data->body = str_c(str);
else if (index_mail_cache_lookup_field(mail, str,
bodystructure_cache_field) > 0) {
data->bodystructure =
p_strdup(mail->data_pool, str_c(str));
str_truncate(str, 0);
if (imap_body_parse_from_bodystructure(
data->bodystructure, str))
data->body = str_c(str);
else {
/* broken, continue.. */
mail_set_cache_corrupted(_mail,
MAIL_FETCH_IMAP_BODYSTRUCTURE);
}
}
if (data->body == NULL) {
str_free(&str);
if (index_mail_parse_bodystructure(mail,
MAIL_CACHE_IMAP_BODY) < 0)
return -1;
}
i_assert(data->body != NULL);
*value_r = data->body;
return 0;
}
case MAIL_FETCH_IMAP_BODYSTRUCTURE: {
const unsigned int bodystructure_cache_field =
cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
if (data->bodystructure != NULL) {
*value_r = data->bodystructure;
return 0;
}
str = str_new(mail->data_pool, 128);
if ((mail->data.cache_flags &
MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 &&
get_cached_parts(mail)) {
index_mail_get_plain_bodystructure(mail, str, TRUE);
data->bodystructure = str_c(str);
} else if (index_mail_cache_lookup_field(mail, str,
bodystructure_cache_field) > 0) {
data->bodystructure = str_c(str);
} else {
str_free(&str);
if (index_mail_parse_bodystructure(mail,
MAIL_CACHE_IMAP_BODYSTRUCTURE) < 0)
return -1;
}
i_assert(data->bodystructure != NULL);
*value_r = data->bodystructure;
return 0;
}
case MAIL_FETCH_IMAP_ENVELOPE:
if (data->envelope == NULL) {
if (index_mail_headers_get_envelope(mail) < 0)
return -1;
}
*value_r = data->envelope;
return 0;
case MAIL_FETCH_FROM_ENVELOPE:
*value_r = data->from_envelope != NULL ?
data->from_envelope : "";
return 0;
case MAIL_FETCH_UIDL_FILE_NAME:
case MAIL_FETCH_UIDL_BACKEND:
case MAIL_FETCH_SEARCH_RELEVANCY:
case MAIL_FETCH_GUID:
case MAIL_FETCH_HEADER_MD5:
case MAIL_FETCH_POP3_ORDER:
*value_r = "";
return 0;
case MAIL_FETCH_MAILBOX_NAME:
*value_r = _mail->box->vname;
return 0;
default:
i_unreached();
return -1;
}
}
struct mail *index_mail_get_real_mail(struct mail *mail)
{
return mail;
}
struct mail *
index_mail_alloc(struct mailbox_transaction_context *t,
enum mail_fetch_field wanted_fields,
struct mailbox_header_lookup_ctx *wanted_headers)
{
struct index_mail *mail;
pool_t pool;
pool = pool_alloconly_create("mail", 2048);
mail = p_new(pool, struct index_mail, 1);
mail->mail.pool = pool;
index_mail_init(mail, t, wanted_fields, wanted_headers);
return &mail->mail.mail;
}
void index_mail_init(struct index_mail *mail,
struct mailbox_transaction_context *t,
enum mail_fetch_field wanted_fields,
struct mailbox_header_lookup_ctx *wanted_headers)
{
array_create(&mail->mail.module_contexts, mail->mail.pool,
sizeof(void *), 5);
mail->mail.v = *t->box->mail_vfuncs;
mail->mail.mail.box = t->box;
mail->mail.mail.transaction = t;
t->mail_ref_count++;
mail->data_pool = pool_alloconly_create("index_mail", 16384);
mail->ibox = INDEX_STORAGE_CONTEXT(t->box);
mail->mail.wanted_fields = wanted_fields;
if (wanted_headers != NULL) {
mail->mail.wanted_headers = wanted_headers;
mailbox_header_lookup_ref(wanted_headers);
}
}
static void index_mail_close_streams_full(struct index_mail *mail, bool closing)
{
struct index_mail_data *data = &mail->data;
struct message_part *parts;
if (data->parser_ctx != NULL) {
if (message_parser_deinit(&data->parser_ctx, &parts) < 0) {
mail_set_cache_corrupted(&mail->mail.mail,
MAIL_FETCH_MESSAGE_PARTS);
}
}
if (data->filter_stream != NULL)
i_stream_unref(&data->filter_stream);
if (data->stream != NULL) {
data->destroying_stream = TRUE;
if (!closing) {
/* we're replacing the stream with a new one. it's
allowed to have references until the mail is closed
(but we can't really check that) */
i_stream_unset_destroy_callback(data->stream);
}
i_stream_unref(&data->stream);
if (closing) {
/* there must be no references to the mail when the
mail is being closed. */
i_assert(!mail->data.destroying_stream);
} else {
data->destroying_stream = FALSE;
}
data->initialized_wrapper_stream = FALSE;
data->destroy_callback_set = FALSE;
}
}
void index_mail_close_streams(struct index_mail *mail)
{
index_mail_close_streams_full(mail, FALSE);
}
void index_mail_close(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *)_mail;
/* If uid == 0 but seq != 0, we came here from saving a (non-mbox)
message. If that happens, don't bother checking if anything should
be cached since it was already checked. Also by now the transaction
may have already been rollbacked and seq point to a nonexistent
message. */
if (mail->mail.mail.uid != 0) {
index_mail_cache_sizes(mail);
index_mail_cache_dates(mail);
}
index_mail_close_streams_full(mail, TRUE);
if (mail->data.wanted_headers != NULL)
mailbox_header_lookup_unref(&mail->data.wanted_headers);
}
static void index_mail_reset(struct index_mail *mail)
{
struct index_mail_data *data = &mail->data;
mail->mail.v.close(&mail->mail.mail);
memset(data, 0, sizeof(*data));
p_clear(mail->data_pool);
data->virtual_size = (uoff_t)-1;
data->physical_size = (uoff_t)-1;
data->save_date = (time_t)-1;
data->received_date = (time_t)-1;
data->sent_date.time = (uint32_t)-1;
data->dont_cache_field_idx = -1U;
data->wanted_fields = mail->mail.wanted_fields;
if (mail->mail.wanted_headers != NULL) {
data->wanted_headers = mail->mail.wanted_headers;
mailbox_header_lookup_ref(data->wanted_headers);
}
mail->mail.mail.seq = 0;
mail->mail.mail.uid = 0;
mail->mail.seq_pvt = 0;
mail->mail.mail.expunged = FALSE;
mail->mail.mail.has_nuls = FALSE;
mail->mail.mail.has_no_nuls = FALSE;
mail->mail.mail.saving = FALSE;
}
static void check_envelope(struct index_mail *mail)
{
struct mail *_mail = &mail->mail.mail;
const unsigned int cache_field_envelope =
mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx;
unsigned int cache_field_hdr;
if ((mail->data.access_part & PARSE_HDR) != 0) {
mail->data.save_envelope = TRUE;
return;
}
/* if "imap.envelope" is cached, that's all we need */
if (mail_cache_field_exists(_mail->transaction->cache_view,
_mail->seq, cache_field_envelope) > 0)
return;
/* don't waste time doing full checks for all required
headers. assume that if we have "hdr.message-id" cached,
we don't need to parse the header. */
cache_field_hdr = mail_cache_register_lookup(_mail->box->cache,
"hdr.message-id");
if (cache_field_hdr == (unsigned int)-1 ||
mail_cache_field_exists(_mail->transaction->cache_view,
_mail->seq, cache_field_hdr) <= 0)
mail->data.access_part |= PARSE_HDR;
mail->data.save_envelope = TRUE;
}
static void index_mail_update_access_parts(struct index_mail *mail)
{
struct mail *_mail = &mail->mail.mail;
struct index_mail_data *data = &mail->data;
const struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
struct mail_cache_view *cache_view = _mail->transaction->cache_view;
const struct mail_index_header *hdr;
struct istream *input;
if ((data->wanted_fields & (MAIL_FETCH_NUL_STATE |
MAIL_FETCH_IMAP_BODY |
MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0 &&
!_mail->has_nuls && !_mail->has_no_nuls) {
(void)index_mail_get_fixed_field(mail, MAIL_CACHE_FLAGS,
&data->cache_flags,
sizeof(data->cache_flags));
_mail->has_nuls =
(data->cache_flags & MAIL_CACHE_FLAG_HAS_NULS) != 0;
_mail->has_no_nuls =
(data->cache_flags & MAIL_CACHE_FLAG_HAS_NO_NULS) != 0;
/* we currently don't forcibly set the nul state. if it's not
already cached, the caller can figure out itself what to
do when neither is set */
}
/* see if wanted_fields can tell us if we need to read/parse
header/body */
if ((data->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0 &&
data->parts == NULL) {
const unsigned int cache_field =
cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
if (mail_cache_field_exists(cache_view, _mail->seq,
cache_field) <= 0) {
data->access_part |= PARSE_HDR | PARSE_BODY;
data->save_message_parts = TRUE;
}
}
if ((data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0 &&
data->envelope == NULL)
check_envelope(mail);
if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0 &&
(data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0 &&
data->body == NULL) {
/* we need either imap.body or imap.bodystructure */
const unsigned int cache_field1 =
cache_fields[MAIL_CACHE_IMAP_BODY].idx;
const unsigned int cache_field2 =
cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
if (mail_cache_field_exists(cache_view, _mail->seq,
cache_field1) <= 0 &&
mail_cache_field_exists(cache_view, _mail->seq,
cache_field2) <= 0) {
data->access_part |= PARSE_HDR | PARSE_BODY;
data->save_bodystructure_header = TRUE;
data->save_bodystructure_body = TRUE;
}
}
if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0 &&
(data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0 &&
data->bodystructure == NULL) {
const unsigned int cache_field =
cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
if (mail_cache_field_exists(cache_view, _mail->seq,
cache_field) <= 0) {
data->access_part |= PARSE_HDR | PARSE_BODY;
data->save_bodystructure_header = TRUE;
data->save_bodystructure_body = TRUE;
}
}
if ((data->wanted_fields & MAIL_FETCH_DATE) != 0 &&
data->sent_date.time == (uint32_t)-1) {
const unsigned int cache_field =
cache_fields[MAIL_CACHE_SENT_DATE].idx;
if (mail_cache_field_exists(cache_view, _mail->seq,
cache_field) <= 0) {
data->access_part |= PARSE_HDR;
data->save_sent_date = TRUE;
}
}
if ((data->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY)) != 0) {
/* open stream immediately to set expunged flag if
it's already lost */
if ((data->wanted_fields & MAIL_FETCH_STREAM_HEADER) != 0)
data->access_part |= READ_HDR;
if ((data->wanted_fields & MAIL_FETCH_STREAM_BODY) != 0)
data->access_part |= READ_BODY;
/* open the stream only if we didn't get here from
mailbox_save_init() */
hdr = mail_index_get_header(_mail->transaction->view);
if (!_mail->saving && _mail->uid < hdr->next_uid) {
if ((data->access_part & READ_BODY) != 0)
(void)mail_get_stream(_mail, NULL, NULL, &input);
else
(void)mail_get_hdr_stream(_mail, NULL, &input);
}
}
if ((data->wanted_fields & MAIL_FETCH_VIRTUAL_SIZE) != 0 &&
data->virtual_size == (uoff_t)-1 &&
_mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER &&
((data->access_part & (READ_HDR | PARSE_HDR)) == 0 ||
(data->access_part & (READ_BODY | PARSE_BODY)) == 0)) {
/* we want virtual size, and we'd prefer not to read the entire
message for it. see if it's possible. */
uoff_t vsize;
_mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE;
if (mail_get_virtual_size(_mail, &vsize) < 0)
mail->data.access_part |= READ_HDR | READ_BODY;
_mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
}
}
void index_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving)
{
struct index_mail *mail = (struct index_mail *)_mail;
if (mail->data.seq == seq)
return;
index_mail_reset(mail);
mail->data.seq = seq;
mail->mail.mail.seq = seq;
mail->mail.mail.saving = saving;
mail_index_lookup_uid(_mail->transaction->view, seq,
&mail->mail.mail.uid);
if (mail_index_view_is_inconsistent(_mail->transaction->view)) {
mail_set_expunged(&mail->mail.mail);
return;
}
index_mail_update_access_parts(mail);
mail->data.initialized = TRUE;
}
bool index_mail_prefetch(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *)_mail;
/* HAVE_POSIX_FADVISE alone isn't enough for CentOS 4.9 */
#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
struct mail_storage *storage = _mail->box->storage;
struct istream *input;
off_t len;
int fd;
if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG) == 0) {
/* we're handling only file-per-msg storages for now. */
return TRUE;
}
if (mail->data.access_part == 0) {
/* everything we need is cached */
return TRUE;
}
if (mail->data.stream == NULL) {
(void)mail_get_stream(_mail, NULL, NULL, &input);
if (mail->data.stream == NULL)
return TRUE;
}
/* tell OS to start reading the file into memory */
fd = i_stream_get_fd(mail->data.stream);
if (fd != -1) {
if ((mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0)
len = 0;
else
len = MAIL_READ_HDR_BLOCK_SIZE;
if (posix_fadvise(fd, 0, len, POSIX_FADV_WILLNEED) < 0) {
i_error("posix_fadvise(%s) failed: %m",
i_stream_get_name(mail->data.stream));
}
mail->data.prefetch_sent = TRUE;
}
#endif
return !mail->data.prefetch_sent;
}
bool index_mail_set_uid(struct mail *_mail, uint32_t uid)
{
struct index_mail *mail = (struct index_mail *)_mail;
uint32_t seq;
if (mail_index_lookup_seq(_mail->transaction->view, uid, &seq)) {
index_mail_set_seq(_mail, seq, FALSE);
return TRUE;
} else {
index_mail_reset(mail);
mail->mail.mail.uid = uid;
mail_set_expunged(&mail->mail.mail);
return FALSE;
}
}
void index_mail_add_temp_wanted_fields(struct mail *_mail,
enum mail_fetch_field fields,
struct mailbox_header_lookup_ctx *headers)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct index_mail_data *data = &mail->data;
ARRAY_TYPE(const_string) names;
unsigned int i;
data->wanted_fields |= fields;
if (headers == NULL) {
/* keep old ones */
} else if (data->wanted_headers == NULL) {
data->wanted_headers = headers;
mailbox_header_lookup_ref(headers);
} else {
/* merge headers */
t_array_init(&names, 32);
for (i = 0; i < data->wanted_headers->count; i++)
array_append(&names, &data->wanted_headers->name[i], 1);
for (i = 0; i < headers->count; i++)
array_append(&names, &headers->name[i], 1);
array_append_zero(&names);
data->wanted_headers =
mailbox_header_lookup_init(_mail->box,
array_idx(&names, 0));
}
index_mail_update_access_parts(mail);
}
void index_mail_set_uid_cache_updates(struct mail *_mail, bool set)
{
struct index_mail *mail = (struct index_mail *)_mail;
mail->data.no_caching = set || mail->data.forced_no_caching;
}
void index_mail_free(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct mailbox_header_lookup_ctx *headers_ctx =
(struct mailbox_header_lookup_ctx *)mail->mail.wanted_headers;
/* make sure mailbox_search_*() users don't try to free the mail
directly */
i_assert(!mail->search_mail);
mail->mail.v.close(_mail);
i_assert(_mail->transaction->mail_ref_count > 0);
_mail->transaction->mail_ref_count--;
if (mail->header_data != NULL)
buffer_free(&mail->header_data);
if (array_is_created(&mail->header_lines))
array_free(&mail->header_lines);
if (array_is_created(&mail->header_match))
array_free(&mail->header_match);
if (array_is_created(&mail->header_match_lines))
array_free(&mail->header_match_lines);
if (headers_ctx != NULL)
mailbox_header_lookup_unref(&headers_ctx);
pool_unref(&mail->data_pool);
pool_unref(&mail->mail.pool);
}
void index_mail_cache_parse_continue(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct message_block block;
while (message_parser_parse_next_block(mail->data.parser_ctx,
&block) > 0) {
if (block.size != 0)
continue;
if (!mail->data.header_parsed) {
index_mail_parse_header(block.part, block.hdr, mail);
if (block.hdr == NULL)
mail->data.header_parsed = TRUE;
} else {
imap_bodystructure_parse_header(mail->data_pool,
block.part, block.hdr);
}
}
}
void index_mail_cache_parse_deinit(struct mail *_mail, time_t received_date,
bool success)
{
struct index_mail *mail = (struct index_mail *)_mail;
if (!success) {
/* we're going to delete this mail anyway,
don't bother trying to update cache file */
mail->data.no_caching = TRUE;
mail->data.forced_no_caching = TRUE;
if (mail->data.parser_ctx == NULL) {
/* we didn't even start cache parsing */
return;
}
}
/* This is needed with 0 byte mails to get hdr=NULL call done. */
index_mail_cache_parse_continue(_mail);
if (mail->data.received_date == (time_t)-1)
mail->data.received_date = received_date;
if (mail->data.save_date == (time_t)-1) {
/* this save_date may not be exactly the same as what we get
in future, but then again neither mbox nor maildir
guarantees it anyway. */
mail->data.save_date = ioloop_time;
}
mail->data.save_bodystructure_body = FALSE;
mail->data.parsed_bodystructure = TRUE;
(void)index_mail_parse_body_finish(mail, 0);
}
static void index_mail_drop_recent_flag(struct mail *mail)
{
const struct mail_index_header *hdr;
uint32_t first_recent_uid = mail->uid + 1;
hdr = mail_index_get_header(mail->transaction->view);
if (hdr->first_recent_uid < first_recent_uid) {
mail_index_update_header(mail->transaction->itrans,
offsetof(struct mail_index_header, first_recent_uid),
&first_recent_uid, sizeof(first_recent_uid), FALSE);
}
}
void index_mail_update_flags(struct mail *_mail, enum modify_type modify_type,
enum mail_flags flags)
{
struct mail_private *mail = (struct mail_private *)_mail;
enum mail_flags pvt_flags_mask, pvt_flags = 0;
bool update_modseq = FALSE;
if ((flags & MAIL_RECENT) == 0 &&
index_mailbox_is_recent(_mail->box, _mail->uid))
index_mail_drop_recent_flag(_mail);
flags &= MAIL_FLAGS_NONRECENT | MAIL_INDEX_MAIL_FLAG_BACKEND;
if (_mail->box->view_pvt != NULL) {
/* mailbox has private flags */
pvt_flags_mask = mailbox_get_private_flags_mask(_mail->box);
pvt_flags = flags & pvt_flags_mask;
flags &= ~pvt_flags_mask;
if (index_mail_get_pvt(_mail) &&
(pvt_flags != 0 || modify_type == MODIFY_REPLACE)) {
mail_index_update_flags(_mail->transaction->itrans_pvt,
mail->seq_pvt,
modify_type, pvt_flags);
update_modseq = TRUE;
}
}
if (!update_modseq) {
/* no forced modseq update */
} else if (modify_type == MODIFY_REMOVE) {
/* add the modseq update separately */
mail_index_update_flags(_mail->transaction->itrans, _mail->seq,
MODIFY_ADD, (enum mail_flags )MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ);
} else {
/* add as part of the flag updates */
flags |= MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ;
}
mail_index_update_flags(_mail->transaction->itrans, _mail->seq,
modify_type, flags);
}
void index_mail_update_keywords(struct mail *mail, enum modify_type modify_type,
struct mail_keywords *keywords)
{
struct index_mail *imail = (struct index_mail *)mail;
if (array_is_created(&imail->data.keyword_indexes))
array_free(&imail->data.keyword_indexes);
if (array_is_created(&imail->data.keywords)) {
/* clear the keywords array so the next mail_get_keywords()
returns the updated keywords. don't free the array, because
then any existing mail_get_keywords() return values would
point to broken data. this won't leak memory because the
array is allocated from mail's memory pool. */
memset(&imail->data.keywords, 0,
sizeof(imail->data.keywords));
}
mail_index_update_keywords(mail->transaction->itrans, mail->seq,
modify_type, keywords);
}
void index_mail_update_modseq(struct mail *mail, uint64_t min_modseq)
{
mail_index_update_modseq(mail->transaction->itrans, mail->seq,
min_modseq);
}
void index_mail_expunge(struct mail *mail)
{
const char *value;
guid_128_t guid_128;
if (mail_get_special(mail, MAIL_FETCH_GUID, &value) < 0)
mail_index_expunge(mail->transaction->itrans, mail->seq);
else {
mail_generate_guid_128_hash(value, guid_128);
mail_index_expunge_guid(mail->transaction->itrans,
mail->seq, guid_128);
}
}
static void index_mail_parse(struct mail *mail, bool parse_body)
{
struct index_mail *imail = (struct index_mail *)mail;
imail->data.access_part |= PARSE_HDR;
if (index_mail_parse_headers(imail, NULL) == 0) {
if (parse_body) {
imail->data.access_part |= PARSE_BODY;
(void)index_mail_parse_body(imail, 0);
}
}
}
void index_mail_precache(struct mail *mail)
{
struct index_mail *imail = (struct index_mail *)mail;
enum mail_fetch_field cache;
time_t date;
uoff_t size;
const char *str;
if (mail_cache_field_exists_any(mail->transaction->cache_view,
mail->seq)) {
/* already cached this mail (we should get here only if FTS
plugin decreased the first precached seq) */
return;
}
cache = imail->data.wanted_fields;
if ((cache & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0)
index_mail_parse(mail, (cache & MAIL_FETCH_STREAM_BODY) != 0);
if ((cache & MAIL_FETCH_RECEIVED_DATE) != 0)
(void)mail_get_received_date(mail, &date);
if ((cache & MAIL_FETCH_SAVE_DATE) != 0)
(void)mail_get_save_date(mail, &date);
if ((cache & MAIL_FETCH_VIRTUAL_SIZE) != 0)
(void)mail_get_virtual_size(mail, &size);
if ((cache & MAIL_FETCH_PHYSICAL_SIZE) != 0)
(void)mail_get_physical_size(mail, &size);
if ((cache & MAIL_FETCH_UIDL_BACKEND) != 0)
(void)mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &str);
if ((cache & MAIL_FETCH_GUID) != 0)
(void)mail_get_special(mail, MAIL_FETCH_GUID, &str);
}
void index_mail_set_cache_corrupted(struct mail *mail,
enum mail_fetch_field field)
{
struct index_mail *imail = (struct index_mail *)mail;
const char *field_name;
switch ((int)field) {
case 0:
field_name = "fields";
break;
case MAIL_FETCH_PHYSICAL_SIZE:
field_name = "physical size";
imail->data.physical_size = (uoff_t)-1;
imail->data.virtual_size = (uoff_t)-1;
imail->data.parts = NULL;
break;
case MAIL_FETCH_VIRTUAL_SIZE:
field_name = "virtual size";
imail->data.physical_size = (uoff_t)-1;
imail->data.virtual_size = (uoff_t)-1;
imail->data.parts = NULL;
break;
case MAIL_FETCH_MESSAGE_PARTS:
field_name = "MIME parts";
imail->data.parts = NULL;
break;
case MAIL_FETCH_IMAP_BODY:
field_name = "IMAP BODY";
imail->data.body = NULL;
imail->data.bodystructure = NULL;
break;
case MAIL_FETCH_IMAP_BODYSTRUCTURE:
field_name = "IMAP BODYSTRUCTURE";
imail->data.body = NULL;
imail->data.bodystructure = NULL;
break;
default:
field_name = t_strdup_printf("#%x", field);
}
/* make sure we don't cache invalid values */
mail_cache_transaction_reset(mail->transaction->cache_trans);
imail->data.no_caching = TRUE;
imail->data.forced_no_caching = TRUE;
mail_cache_set_corrupted(mail->box->cache,
"Broken %s for mail UID %u",
field_name, mail->uid);
}
int index_mail_opened(struct mail *mail ATTR_UNUSED,
struct istream **stream ATTR_UNUSED)
{
return 0;
}
void index_mail_save_finish(struct mail_save_context *ctx)
{
struct index_mail *imail = (struct index_mail *)ctx->dest_mail;
if (imail == NULL)
return;
if (ctx->from_envelope != NULL && imail->data.from_envelope == NULL) {
imail->data.from_envelope =
p_strdup(imail->data_pool, ctx->from_envelope);
}
}