index-mail.c revision b0a901f1dbe9e05ac1c92a0974af6bce0274f31a
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "buffer.h"
#include "ioloop.h"
#include "istream.h"
#include "hex-binary.h"
#include "str.h"
#include "message-date.h"
#include "message-part-serialize.h"
#include "message-parser.h"
#include "imap-bodystructure.h"
#include "imap-envelope.h"
#include "mail-cache.h"
#include "mail-index-modseq.h"
#include "index-storage.h"
#include "istream-mail-stats.h"
#include "index-mail.h"
{ "date.sent", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
sizeof(struct mail_sent_date), 0 },
{ "date.received", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
sizeof(uint32_t), 0 },
{ "date.save", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
sizeof(uint32_t), 0 },
{ "size.virtual", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
sizeof(uoff_t), 0 },
{ "size.physical", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
sizeof(uoff_t), 0 },
{ "imap.body", 0, MAIL_CACHE_FIELD_STRING, 0, 0 },
{ "imap.bodystructure", 0, MAIL_CACHE_FIELD_STRING, 0, 0 },
{ "imap.envelope", 0, MAIL_CACHE_FIELD_STRING, 0, 0 },
{ "pop3.uidl", 0, MAIL_CACHE_FIELD_STRING, 0, 0 },
{ "guid", 0, MAIL_CACHE_FIELD_STRING, 0, 0 },
{ "mime.parts", 0, MAIL_CACHE_FIELD_VARIABLE_SIZE, 0, 0 }
};
enum index_cache_field field);
unsigned int field_idx)
{
int ret;
if (ret > 0)
return ret;
}
{
unsigned int field_idx =
struct message_part *parts;
const char *error;
int ret;
if (ret <= 0)
return NULL;
"Corrupted cached message_part data (%s)", error);
}
return parts;
}
{
struct message_part *part;
T_BEGIN {
} T_END;
return FALSE;
/* we know the NULs now, update them */
} else {
}
return TRUE;
}
enum index_cache_field field,
{
int ret;
T_BEGIN {
else {
}
} T_END;
return ret;
}
{
return index_mail_get_fixed_field(mail,
}
{
const struct mail_index_record *rec;
enum mail_flags flags;
flags |= MAIL_RECENT;
return flags;
}
{
}
{
const char *const *names;
const unsigned int *keyword_indexes;
unsigned int i, count, names_count;
(void)index_mail_get_keyword_indexes(_mail);
for (i = 0; i < count; i++) {
const char *name;
}
/* end with NULL */
}
const ARRAY_TYPE(keyword_indexes) *
{
&data->keyword_indexes);
}
return &data->keyword_indexes;
}
const struct message_part **parts_r)
{
return 0;
}
return -1;
}
if (index_mail_parse_body(mail, 0) < 0)
return -1;
return 0;
}
{
uint32_t t;
&t, sizeof(t)))
data->received_date = t;
}
}
{
uint32_t t;
&t, sizeof(t)))
}
}
{
const char *str;
time_t t;
return 0;
return ret;
if (ret == 0 ||
!message_date_parse((const unsigned char *)str,
/* 0 = not found / invalid */
t = 0;
tz = 0;
}
return 0;
}
{
return 0;
}
if (index_mail_cache_sent_date(mail) < 0)
return -1;
return 0;
}
{
}
}
{
&data->virtual_size)) {
if (!get_cached_msgpart_sizes(mail))
return FALSE;
}
}
}
return TRUE;
}
{
if (!data->hdr_size_set)
return;
/* we've already called get_cached_msgpart_sizes() and it didn't work.
try to do this by using cached virtual size and a quick physical
size lookup. */
return;
if (!data->body_size_set) {
return;
/* we should have everything now. try again. */
}
}
{
return 0;
return -1;
return 0;
}
{
&data->physical_size))
(void)get_cached_msgpart_sizes(mail);
}
}
{
}
{
const struct mail_index_header *hdr;
if (set->mail_cache_min_mail_count > 0) {
/* First check if we've configured caching not to be used with
low enough message count. */
return;
}
}
}
struct message_header_line *hdr,
{
}
{
MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0)
return TRUE;
return TRUE;
return TRUE;
return FALSE;
}
{
unsigned int cache_flags_idx;
bool want_cached;
if (data->parsed_bodystructure &&
/* we need message_parts cached to be able to
actually use it in BODY/BODYSTRUCTURE reply */
want_cached = TRUE;
}
/* cache flags should never get unset as long as the message doesn't
change, but try to handle it anyway */
} else {
}
&cache_flags, sizeof(cache_flags));
}
}
{
unsigned int cache_field =
if (data->messageparts_saved_to_cache ||
cache_field) != 0) {
/* already cached */
return;
}
/* we never want it cached */
return;
}
if (decision == MAIL_CACHE_DECISION_NO &&
!data->save_message_parts &&
/* we didn't really care about the message parts themselves,
just wanted to use something that depended on it */
return;
}
T_BEGIN {
} T_END;
}
static void
enum index_cache_field field)
{
unsigned int cache_field_parts =
unsigned int cache_field_body =
unsigned int cache_field_bodystructure =
enum mail_cache_decision_type dec;
bool bodystructure_cached = FALSE;
bool plain_bodystructure = FALSE;
bool cache_bodystructure, cache_body;
if (data->messageparts_saved_to_cache ||
cache_field_parts) > 0) {
/* cached it as flag + message_parts */
}
}
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)
else if (field == MAIL_CACHE_IMAP_BODYSTRUCTURE ||
} else {
}
if (cache_bodystructure) {
} else {
}
/* normally don't cache both BODY and BODYSTRUCTURE, but do it
if BODY is forced to be cached */
if (plain_bodystructure ||
cache_body = FALSE;
else if (field == MAIL_CACHE_IMAP_BODY) {
} else {
}
if (cache_body) {
}
}
static bool
{
enum mail_fetch_field fetch_field;
unsigned int cache_field;
switch (field) {
case MAIL_CACHE_SENT_DATE:
break;
case MAIL_CACHE_RECEIVED_DATE:
break;
case MAIL_CACHE_SAVE_DATE:
break;
break;
break;
default:
i_unreached();
}
return FALSE;
} else {
}
}
{
static enum index_cache_field size_fields[] = {
};
unsigned int i;
for (i = 0; i < N_ELEMENTS(size_fields); i++) {
}
}
}
{
static enum index_cache_field date_fields[] = {
};
unsigned int i;
uint32_t t;
for (i = 0; i < N_ELEMENTS(date_fields); i++) {
t = dates[i];
&t, sizeof(t));
}
}
(void)index_mail_cache_sent_date(mail);
}
enum index_cache_field field)
{
return -1;
}
/* 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);
return 0;
}
{
return 0;
return -1;
}
enum index_cache_field field)
{
int ret;
if (data->save_bodystructure_body) {
/* bodystructure header is parsed, we want the body's mime
headers too */
} else {
}
ret = -1;
return ret;
}
{
}
struct message_size *hdr_size,
struct message_size *body_size,
{
int ret;
}
(void)get_cached_msgpart_sizes(mail);
if (!data->hdr_size_set) {
(void)get_cached_parts(mail);
return -1;
} else {
}
}
}
if (!data->body_size_set)
if (!data->body_size_set) {
if (index_mail_parse_body(mail, 0) < 0)
return -1;
} else {
}
}
}
}
return ret;
}
enum index_cache_field field)
{
if (data->parsed_bodystructure) {
/* we have everything parsed already, but just not written to
a string */
} else {
if (data->save_bodystructure_header ||
/* we haven't parsed the header yet */
(void)get_cached_parts(mail);
return -1;
}
return -1;
}
/* if we didn't want to have the body(structure) cached,
it's still not written. */
switch (field) {
case MAIL_CACHE_IMAP_BODY:
}
break;
}
break;
default:
i_unreached();
}
return 0;
}
static void
bool extended)
{
if (extended)
}
{
const void *ext_data;
switch (field) {
case MAIL_FETCH_IMAP_BODY: {
unsigned int body_cache_field =
unsigned int bodystructure_cache_field =
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 */
get_cached_parts(mail)) {
body_cache_field) > 0)
bodystructure_cache_field) > 0) {
str_truncate(str, 0);
else {
/* broken, continue.. */
}
}
MAIL_CACHE_IMAP_BODY) < 0)
return -1;
}
return 0;
}
case MAIL_FETCH_IMAP_BODYSTRUCTURE: {
unsigned int bodystructure_cache_field =
return 0;
}
get_cached_parts(mail)) {
bodystructure_cache_field) > 0) {
} else {
return -1;
}
return 0;
}
case MAIL_FETCH_IMAP_ENVELOPE:
if (index_mail_headers_get_envelope(mail) < 0)
return -1;
}
return 0;
case MAIL_FETCH_FROM_ENVELOPE:
case MAIL_FETCH_UIDL_BACKEND:
case MAIL_FETCH_SEARCH_SCORE:
case MAIL_FETCH_GUID:
*value_r = "";
return 0;
case MAIL_FETCH_HEADER_MD5:
*value_r = "";
return 0;
}
return 0;
case MAIL_FETCH_MAILBOX_NAME:
return 0;
default:
i_unreached();
return -1;
}
}
struct mail *
index_mail_alloc(struct mailbox_transaction_context *t,
struct mailbox_header_lookup_ctx *wanted_headers)
{
struct index_mail *mail;
}
struct mailbox_transaction_context *_t,
struct mailbox_header_lookup_ctx *_wanted_headers)
{
struct index_transaction_context *t =
(struct index_transaction_context *)_t;
struct index_header_lookup_ctx *wanted_headers =
(struct index_header_lookup_ctx *)_wanted_headers;
const struct mail_index_header *hdr;
sizeof(void *), 5);
t->mail_ref_count++;
if (wanted_headers != NULL) {
}
}
{
struct message_part *parts;
/* 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 non-existing
message. */
}
}
}
}
}
{
}
{
unsigned int cache_field_envelope =
unsigned int cache_field_hdr;
return;
}
/* if "imap.envelope" is cached, that's all we need */
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. */
"hdr.message-id");
if (cache_field_hdr == (unsigned int)-1 ||
cache_field_hdr) <= 0)
}
{
const struct mail_index_header *hdr;
return;
return;
}
MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0) {
&data->cache_flags,
sizeof(data->cache_flags));
}
unsigned int cache_field =
cache_field) <= 0) {
}
}
unsigned int cache_field =
}
/* we need either imap.body or imap.bodystructure */
unsigned int cache_field1 =
unsigned int cache_field2 =
seq, cache_field1) <= 0 &&
seq, cache_field2) <= 0) {
}
}
unsigned int cache_field =
cache_field) <= 0) {
}
}
unsigned int cache_field =
cache_field) <= 0) {
}
}
MAIL_FETCH_STREAM_BODY)) != 0) {
/* open stream immediately to set expunged flag if
it's already lost */
/* open the stream only if we didn't get here from
mailbox_save_init() */
}
}
{
return TRUE;
} else {
return FALSE;
}
}
{
}
{
struct mailbox_header_lookup_ctx *headers_ctx =
if (headers_ctx != NULL)
}
{
struct message_block block;
int ret;
&block)) > 0) {
continue;
} else {
}
}
}
bool success)
{
if (!success) {
/* we're going to delete this mail anyway,
don't bother trying to update cache file */
}
/* This is needed with 0 byte mails to get hdr=NULL call done. */
/* 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. */
}
(void)index_mail_parse_body_finish(mail, 0);
}
enum mail_flags flags)
{
flags);
}
struct mail_keywords *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. */
}
keywords);
}
{
}
enum mail_fetch_field field)
{
const char *field_name;
switch (field) {
case 0:
field_name = "fields";
break;
case MAIL_FETCH_VIRTUAL_SIZE:
field_name = "virtual size";
break;
case MAIL_FETCH_MESSAGE_PARTS:
field_name = "MIME parts";
break;
case MAIL_FETCH_IMAP_BODY:
field_name = "IMAP BODY";
break;
field_name = "IMAP BODYSTRUCTURE";
break;
default:
}
/* make sure we don't cache invalid values */
"Broken %s for mail UID %u",
}
{
return (struct index_mail *)mail;
}