[dovecot-cvs] dovecot/src/lib-storage/index index-mail.c,NONE,1.1 index-mail.h,NONE,1.1
cras at procontrol.fi
cras at procontrol.fi
Tue Jan 21 07:36:34 EET 2003
Update of /home/cvs/dovecot/src/lib-storage/index
In directory danu:/tmp/cvs-serv4972
Added Files:
index-mail.c index-mail.h
Log Message:
Forgot to add
--- NEW FILE: index-mail.c ---
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
#include "istream.h"
#include "message-address.h"
#include "message-date.h"
#include "message-part-serialize.h"
#include "imap-bodystructure.h"
#include "imap-envelope.h"
#include "mail-index.h"
#include "mail-index-util.h"
#include "mail-custom-flags.h"
#include "index-storage.h"
#include "index-mail.h"
#include <ctype.h>
static int get_envelope_header_field(const char *name,
enum imap_envelope_field *ret)
{
*ret = (enum imap_envelope_field)-1;
switch (i_toupper(*name)) {
case 'B':
if (strcasecmp(name, "bcc") == 0)
*ret = IMAP_ENVELOPE_BCC;
break;
case 'C':
if (strcasecmp(name, "cc") == 0)
*ret = IMAP_ENVELOPE_CC;
break;
case 'D':
if (strcasecmp(name, "date") == 0)
*ret = IMAP_ENVELOPE_DATE;
break;
case 'F':
if (strcasecmp(name, "from") == 0)
*ret = IMAP_ENVELOPE_FROM;
break;
case 'I':
if (strcasecmp(name, "in-reply-to") == 0)
*ret = IMAP_ENVELOPE_IN_REPLY_TO;
break;
case 'M':
if (strcasecmp(name, "message-id") == 0)
*ret = IMAP_ENVELOPE_MESSAGE_ID;
break;
case 'R':
if (strcasecmp(name, "reply-to") == 0)
*ret = IMAP_ENVELOPE_REPLY_TO;
break;
case 'S':
if (strcasecmp(name, "subject") == 0)
*ret = IMAP_ENVELOPE_SUBJECT;
if (strcasecmp(name, "sender") == 0)
*ret = IMAP_ENVELOPE_SENDER;
break;
case 'T':
if (strcasecmp(name, "to") == 0)
*ret = IMAP_ENVELOPE_TO;
break;
}
return *ret != (enum imap_envelope_field)-1;
}
static struct message_part *get_cached_parts(struct index_mail *mail)
{
struct message_part *part;
const void *part_data;
const char *error;
size_t part_size;
part_data = mail->ibox->index->
lookup_field_raw(mail->ibox->index, mail->data.rec,
DATA_FIELD_MESSAGEPART, &part_size);
if (part_data == NULL) {
mail->ibox->index->cache_fields_later(mail->ibox->index,
DATA_FIELD_MESSAGEPART);
return NULL;
}
part = message_part_deserialize(mail->pool, part_data, part_size,
&error);
if (part == NULL) {
index_set_corrupted(mail->ibox->index,
"Corrupted cached message_part data (%s)", error);
return NULL;
}
return part;
}
static char *get_cached_field(struct index_mail *mail,
enum mail_data_field field)
{
const char *ret;
ret = mail->ibox->index->lookup_field(mail->ibox->index,
mail->data.rec, field);
if (ret == NULL)
mail->ibox->index->cache_fields_later(mail->ibox->index, field);
return p_strdup(mail->pool, ret);
}
static uoff_t get_cached_uoff_t(struct index_mail *mail,
enum mail_data_field field,
const char *field_name)
{
const uoff_t *uoff_p;
size_t size;
uoff_p = mail->ibox->index->
lookup_field_raw(mail->ibox->index, mail->data.rec,
field, &size);
if (uoff_p == NULL)
mail->ibox->index->cache_fields_later(mail->ibox->index, field);
else if (size != sizeof(*uoff_p)) {
index_set_corrupted(mail->ibox->index,
"Corrupted cached %s", field_name);
uoff_p = NULL;
}
return uoff_p == NULL ? (uoff_t)-1 : *uoff_p;
}
static uoff_t get_cached_virtual_size(struct index_mail *mail)
{
return get_cached_uoff_t(mail, DATA_HDR_VIRTUAL_SIZE, "virtual size");
}
static time_t get_cached_received_date(struct index_mail *mail)
{
const time_t *time_p;
size_t size;
time_p = mail->ibox->index->
lookup_field_raw(mail->ibox->index, mail->data.rec,
DATA_HDR_INTERNAL_DATE, &size);
if (time_p == NULL) {
mail->ibox->index->cache_fields_later(mail->ibox->index,
DATA_HDR_INTERNAL_DATE);
} else if (size != sizeof(*time_p)) {
index_set_corrupted(mail->ibox->index,
"Corrupted cached received time");
time_p = NULL;
}
return time_p == NULL ? (time_t)-1 : *time_p;
}
static int open_stream(struct index_mail *mail, uoff_t position)
{
int deleted;
if (mail->data.stream == NULL) {
mail->data.stream = mail->ibox->index->
open_mail(mail->ibox->index, mail->data.rec,
&mail->data.received_date, &deleted);
if (mail->data.stream == NULL)
return FALSE;
}
i_stream_seek(mail->data.stream, position);
return TRUE;
}
static void prepend_cached_header(struct index_mail *mail, const char *name)
{
struct cached_header *hdr;
hdr = p_new(mail->pool, struct cached_header, 1);
hdr->name = name;
hdr->name_len = strlen(name);
hdr->next = mail->data.headers;
mail->data.headers = hdr;
}
void index_mail_init_parse_header(struct index_mail *mail)
{
const char *const *tmp;
if (mail->wanted_headers != NULL) {
for (tmp = mail->wanted_headers; *tmp != NULL; tmp++)
prepend_cached_header(mail, *tmp);
}
}
void index_mail_parse_header(struct message_part *part __attr_unused__,
const unsigned char *name, size_t name_len,
const unsigned char *value, size_t value_len,
void *context)
{
struct index_mail *mail = context;
struct index_mail_data *data = &mail->data;
struct cached_header *hdr;
if (data->save_envelope) {
imap_envelope_parse_header(mail->pool, &data->envelope_data,
name, name_len, value, value_len);
}
if (name_len == 4 && data->save_sent_time &&
memcasecmp(name, "date",4) == 0) {
if (!message_date_parse(value, value_len, &data->sent_time,
&data->sent_timezone)) {
/* 0 == parse error */
data->sent_time = 0;
data->sent_timezone = 0;
}
data->save_sent_time = FALSE;
}
if (name_len == 0) {
/* end of headers */
if (data->save_sent_time) {
/* not found */
data->sent_time = 0;
data->sent_timezone = 0;
data->save_sent_time = FALSE;
}
}
for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
if (hdr->name_len == name_len &&
memcasecmp(hdr->name, name, name_len) == 0) {
/* save only the first header */
if (hdr->value == NULL) {
hdr->value = p_strndup(mail->pool,
value, value_len);
}
break;
}
}
}
static int parse_header(struct index_mail *mail)
{
if (!open_stream(mail, 0))
return FALSE;
index_mail_init_parse_header(mail);
message_parse_header(NULL, mail->data.stream, &mail->data.hdr_size,
index_mail_parse_header, mail);
mail->data.parse_header = FALSE;
mail->data.hdr_size_set = TRUE;
return TRUE;
}
static const struct mail_full_flags *get_flags(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
data->flags.flags = data->rec->msg_flags;
data->flags.custom_flags =
mail_custom_flags_list_get(mail->ibox->index->custom_flags);
data->flags.custom_flags_count = MAIL_CUSTOM_FLAGS_COUNT;
if (data->rec->uid >= mail->ibox->index->first_recent_uid)
data->flags.flags |= MAIL_RECENT;
return &data->flags;
}
static const struct message_part *get_parts(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
if (data->parts != NULL)
return data->parts;
if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) == 0) {
data->parts = get_cached_parts(mail);
if (data->parts != NULL)
return data->parts;
}
if (!open_stream(mail, 0))
return NULL;
index_mail_init_parse_header(mail);
data->parts = message_parse(mail->pool, data->stream,
index_mail_parse_header, mail);
return data->parts;
}
static time_t get_received_date(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
if (data->received_date != (time_t)-1)
return data->received_date;
if ((mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) == 0) {
data->received_date = get_cached_received_date(mail);
if (data->received_date != (time_t)-1)
return data->received_date;
}
data->received_date = mail->ibox->index->
get_internal_date(mail->ibox->index, mail->data.rec);
return data->received_date;
}
static time_t get_date(struct mail *_mail, int *timezone)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
const char *result;
if (data->sent_time != (time_t)-1) {
if (timezone != NULL)
*timezone = data->sent_timezone;
return data->sent_time;
}
if (data->parse_header || data->envelope == NULL) {
data->save_sent_time = TRUE;
parse_header(mail);
} else {
if (!imap_envelope_parse(data->envelope,
IMAP_ENVELOPE_DATE,
IMAP_ENVELOPE_RESULT_TYPE_STRING,
&result))
return (time_t)-1;
if (!message_date_parse((const unsigned char *) result,
(size_t)-1, &data->sent_time,
&data->sent_timezone))
data->sent_time = 0;
}
if (timezone != NULL)
*timezone = data->sent_timezone;
return data->sent_time;
}
static int get_msgpart_sizes(struct index_mail *mail)
{
struct index_mail_data *data = &mail->data;
if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0)
(void)get_parts(&mail->mail);
if (data->parts == NULL)
data->parts = get_cached_parts(mail);
if (data->parts != NULL) {
data->hdr_size = data->parts->header_size;
data->body_size = data->parts->body_size;
data->hdr_size_set = TRUE;
data->body_size_set = TRUE;
}
return data->parts != NULL;
}
static void get_binary_sizes(struct index_mail *mail)
{
uoff_t size;
if (!mail->data.hdr_size_set &&
(mail->data.rec->index_flags & INDEX_MAIL_FLAG_BINARY_HEADER)) {
size = get_cached_uoff_t(mail, DATA_HDR_HEADER_SIZE,
"header size");
if (size != (uoff_t)-1) {
mail->data.hdr_size.physical_size =
mail->data.hdr_size.virtual_size = size;
mail->data.hdr_size_set = TRUE;
}
}
if (!mail->data.body_size_set &&
(mail->data.rec->index_flags & INDEX_MAIL_FLAG_BINARY_BODY)) {
size = get_cached_uoff_t(mail, DATA_HDR_BODY_SIZE, "body size");
if (size != (uoff_t)-1) {
mail->data.body_size.physical_size =
mail->data.body_size.virtual_size = size;
mail->data.body_size_set = TRUE;
}
}
}
static uoff_t get_size(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
uoff_t hdr_size, body_size;
if (data->size != (uoff_t)-1)
return data->size;
if ((mail->wanted_fields & MAIL_FETCH_SIZE) == 0) {
data->size = get_cached_virtual_size(mail);
if (data->size != (uoff_t)-1)
return data->size;
}
if (!get_msgpart_sizes(mail)) {
if (data->parse_header)
parse_header(mail);
}
hdr_size = data->hdr_size_set ?
data->hdr_size.virtual_size : (uoff_t)-1;
body_size = data->body_size_set ?
data->body_size.virtual_size : (uoff_t)-1;
if (body_size != (uoff_t)-1 && hdr_size != (uoff_t)-1) {
data->size = hdr_size + body_size;
return data->size;
}
/* maybe it's binary */
get_binary_sizes(mail);
if (data->hdr_size_set && hdr_size == (uoff_t)-1)
hdr_size = data->hdr_size.virtual_size;
if (data->body_size_set && body_size == (uoff_t)-1)
body_size = data->body_size.virtual_size;
if (body_size != (uoff_t)-1 && hdr_size != (uoff_t)-1) {
data->size = hdr_size + body_size;
return data->size;
}
/* have to parse, slow.. */
if (!open_stream(mail, hdr_size != (uoff_t)-1 ? hdr_size : 0))
return (uoff_t)-1;
if (hdr_size == (uoff_t)-1) {
message_get_header_size(data->stream, &data->hdr_size);
hdr_size = data->hdr_size.virtual_size;
data->hdr_size_set = TRUE;
}
if (body_size == (uoff_t)-1) {
message_get_body_size(data->stream, &data->body_size,
(uoff_t)-1, NULL);
body_size = data->body_size.virtual_size;
data->body_size_set = TRUE;
}
data->size = hdr_size + body_size;
return data->size;
}
static const char *get_header(struct mail *_mail, const char *field)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
struct cached_header *hdr;
enum imap_envelope_field env_field = 0;
const char *str;
char *ret;
for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
if (strcasecmp(hdr->name, field) == 0)
return hdr->value;
}
if (data->parse_header || data->envelope == NULL ||
!get_envelope_header_field(field, &env_field)) {
/* if we have to parse the header, do it even if we could use
envelope - envelope parsing would just slow up. */
prepend_cached_header(mail, field);
parse_header(mail);
for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
if (strcasecmp(hdr->name, field) == 0)
return hdr->value;
}
return NULL;
} else {
t_push();
if (!imap_envelope_parse(data->envelope, env_field,
IMAP_ENVELOPE_RESULT_TYPE_STRING,
&str))
str = NULL;
ret = p_strdup(mail->pool, str);
t_pop();
return ret;
}
}
static const struct message_address *
get_address(struct mail *_mail, const char *field)
{
struct index_mail *mail = (struct index_mail *) _mail;
const char *str;
/* don't bother with checking envelope - we're most likely
creating it */
str = get_header(_mail, field);
if (str == NULL)
return NULL;
return message_address_parse(mail->pool, str, (size_t)-1, 1);
}
static const char *get_first_mailbox(struct mail *_mail, const char *field)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
enum imap_envelope_field env_field;
const char *str;
const char *ret = NULL;
if (data->envelope != NULL &&
get_envelope_header_field(field, &env_field)) {
/* prefer parsing envelope - faster than having to actually
parse the header field */
t_push();
if (!imap_envelope_parse(data->envelope, env_field,
IMAP_ENVELOPE_RESULT_TYPE_FIRST_MAILBOX,
&str))
str = NULL;
ret = p_strdup(mail->pool, str);
t_pop();
} else {
struct message_address *addr;
str = get_header(_mail, field);
if (str == NULL)
return NULL;
addr = message_address_parse(mail->pool, str,
(size_t)-1, 1);
if (addr != NULL)
ret = addr->mailbox;
}
return ret;
}
static struct istream *get_stream(struct mail *_mail,
struct message_size *hdr_size,
struct message_size *body_size)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
if (!open_stream(mail, 0))
return NULL;
if (hdr_size != NULL || body_size != NULL) {
if (!get_msgpart_sizes(mail))
get_binary_sizes(mail);
}
if (hdr_size != NULL) {
if (!data->hdr_size_set) {
message_get_header_size(data->stream, &data->hdr_size);
data->hdr_size_set = TRUE;
}
*hdr_size = data->hdr_size;
}
if (body_size != NULL) {
if (!data->body_size_set) {
i_stream_seek(data->stream,
data->hdr_size.physical_size);
message_get_body_size(data->stream, &data->body_size,
(uoff_t)-1, NULL);
data->body_size_set = TRUE;
}
*body_size = data->body_size;
}
i_stream_seek(data->stream, 0);
return data->stream;
}
static const char *get_special(struct mail *_mail, enum mail_fetch_field field)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
char *str;
switch (field) {
case MAIL_FETCH_IMAP_BODY:
if (data->body != NULL)
return data->body;
/* fall through */
case MAIL_FETCH_IMAP_BODYSTRUCTURE:
if (data->bodystructure != NULL) {
if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
return data->bodystructure;
/* create BODY from cached BODYSTRUCTURE */
t_push();
data->body = p_strdup(mail->pool,
imap_body_parse_from_bodystructure(
data->bodystructure));
t_pop();
return data->body;
}
if (data->parts == NULL)
data->parts = get_cached_parts(mail);
t_push();
str = p_strdup(mail->pool, imap_part_get_bodystructure(
mail->pool, &data->parts, data->stream,
field == MAIL_FETCH_IMAP_BODYSTRUCTURE));
t_pop();
if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
data->bodystructure = str;
else
data->body = str;
return str;
case MAIL_FETCH_IMAP_ENVELOPE:
if (data->envelope != NULL)
return data->envelope;
if ((mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) == 0) {
data->envelope = p_strdup(mail->pool,
get_cached_field(mail, DATA_FIELD_ENVELOPE));
if (data->envelope != NULL)
return data->envelope;
}
data->save_envelope = TRUE;
parse_header(mail);
return data->envelope;
default:
i_unreached();
return NULL;
}
}
static struct mail index_mail = {
0, 0, 0,
get_flags,
get_parts,
get_received_date,
get_date,
get_size,
get_header,
get_address,
get_first_mailbox,
get_stream,
get_special
};
void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail,
enum mail_fetch_field wanted_fields,
const char *const wanted_headers[])
{
mail->mail = index_mail;
mail->pool = pool_alloconly_create("index_mail", 4096);
mail->ibox = ibox;
mail->wanted_fields = wanted_fields;
mail->wanted_headers = wanted_headers;
}
int index_mail_next(struct index_mail *mail, struct mail_index_record *rec)
{
struct index_mail_data *data = &mail->data;
int open_mail, parse_header;
/* close the old one */
if (data->stream != NULL)
i_stream_unref(data->stream);
memset(data, 0, sizeof(*data));
p_clear(mail->pool);
data->rec = rec;
data->size = (uoff_t)-1;
data->received_date = data->sent_time = (time_t)-1;
/* if some wanted fields are cached, get them */
if (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS)
data->parts = get_cached_parts(mail);
if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE)
data->envelope = get_cached_field(mail, DATA_FIELD_ENVELOPE);
if (mail->wanted_fields & MAIL_FETCH_IMAP_BODY)
data->body = get_cached_field(mail, DATA_FIELD_BODY);
if (mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) {
data->bodystructure =
get_cached_field(mail, DATA_FIELD_BODYSTRUCTURE);
}
if (mail->wanted_fields & MAIL_FETCH_SIZE)
data->size = get_cached_virtual_size(mail);
/* see if we have to parse the message */
open_mail = parse_header = FALSE;
if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) &&
data->parts == NULL)
parse_header = TRUE;
else if ((mail->wanted_fields & (MAIL_FETCH_DATE |
MAIL_FETCH_IMAP_ENVELOPE)) &&
data->envelope == NULL)
parse_header = TRUE;
else if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) &&
data->bodystructure == NULL) {
if (data->parts == NULL)
data->parts = get_cached_parts(mail);
/* FIXME: this isn't helping really.. */
open_mail = TRUE;
parse_header = data->parts == NULL;
} else if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODY) &&
data->body == NULL && data->bodystructure == NULL) {
if (data->parts == NULL)
data->parts = get_cached_parts(mail);
open_mail = TRUE;
parse_header = data->parts == NULL;
} else if (mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY))
open_mail = TRUE;
else if ((mail->wanted_fields & MAIL_FETCH_SIZE) && data->size == 0)
open_mail = TRUE;
if (!parse_header && mail->wanted_headers != NULL) {
const char *const *tmp;
enum imap_envelope_field env_field;
int envelope_headers = FALSE;
for (tmp = mail->wanted_headers; *tmp != NULL; tmp++) {
if (get_envelope_header_field(*tmp, &env_field))
envelope_headers = TRUE;
else {
open_mail = TRUE;
parse_header = TRUE;
break;
}
}
if (!parse_header && envelope_headers &&
data->envelope == NULL) {
data->envelope =
get_cached_field(mail, DATA_FIELD_ENVELOPE);
if (data->envelope == NULL)
parse_header = TRUE;
}
}
if (open_mail || parse_header) {
int deleted;
data->stream = mail->ibox->index->
open_mail(mail->ibox->index, data->rec,
&data->received_date, &deleted);
if (data->stream == NULL)
return deleted ? 0 : -1;
}
if ((mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) &&
data->received_date == (time_t)-1) {
/* check this only after open_mail() */
data->received_date = get_cached_received_date(mail);
}
if (mail->wanted_fields & MAIL_FETCH_DATE)
data->save_sent_time = TRUE;
if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE)
data->save_envelope = TRUE;
data->parse_header = parse_header;
return 1;
}
void index_mail_deinit(struct index_mail *mail)
{
if (mail->data.stream != NULL) {
i_stream_unref(mail->data.stream);
mail->data.stream = NULL;
}
pool_unref(mail->pool);
mail->pool = NULL;
}
--- NEW FILE: index-mail.h ---
#ifndef __INDEX_MAIL_H
#define __INDEX_MAIL_H
struct cached_header {
struct cached_header *next;
size_t name_len;
const char *name, *value;
};
struct index_mail_data {
struct mail_full_flags flags;
time_t received_date;
uoff_t size;
time_t sent_time;
int sent_timezone;
struct cached_header *headers;
struct message_part *parts;
char *envelope, *body, *bodystructure;
struct message_part_envelope_data *envelope_data;
struct mail_index_record *rec;
struct istream *stream;
struct message_size hdr_size, body_size;
unsigned int parse_header:1;
unsigned int save_sent_time:1;
unsigned int save_envelope:1;
unsigned int hdr_size_set:1;
unsigned int body_size_set:1;
};
struct index_mail {
struct mail mail;
struct index_mail_data data;
pool_t pool;
struct index_mailbox *ibox;
enum mail_fetch_field wanted_fields;
const char *const *wanted_headers;
};
void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail,
enum mail_fetch_field wanted_fields,
const char *const wanted_headers[]);
int index_mail_next(struct index_mail *mail, struct mail_index_record *rec);
void index_mail_deinit(struct index_mail *mail);
void index_mail_init_parse_header(struct index_mail *mail);
void index_mail_parse_header(struct message_part *part,
const unsigned char *name, size_t name_len,
const unsigned char *value, size_t value_len,
void *context);
#endif
More information about the dovecot-cvs
mailing list