getting better..
--HG--
branch : HEAD
unsigned int charset_found:1;
};
-static void part_write_bodystructure(struct message_part *part,
- string_t *str, int extended);
-
static void parse_content_type(const unsigned char *value, size_t value_len,
void *context)
{
}
if (part->children != NULL)
- part_write_bodystructure(part->children, str, extended);
+ imap_bodystructure_write(part->children, str, extended);
else {
/* no parts in multipart message,
that's not allowed. write a single
imap_envelope_write_part_data(env_data, str);
str_append(str, ") ");
- part_write_bodystructure(part->children, str, extended);
+ imap_bodystructure_write(part->children, str, extended);
str_printfa(str, " %u", part->body_size.lines);
}
}
}
-static void part_write_bodystructure(struct message_part *part,
- string_t *str, int extended)
+void imap_bodystructure_write(struct message_part *part,
+ string_t *dest, int extended)
{
i_assert(part->parent != NULL || part->next == NULL);
while (part != NULL) {
if (part->parent != NULL)
- str_append_c(str, '(');
+ str_append_c(dest, '(');
if (part->flags & MESSAGE_PART_FLAG_MULTIPART)
- part_write_body_multipart(part, str, extended);
+ part_write_body_multipart(part, dest, extended);
else
- part_write_body(part, str, extended);
+ part_write_body(part, dest, extended);
if (part->parent != NULL)
- str_append_c(str, ')');
+ str_append_c(dest, ')');
part = part->next;
}
}
-const char *imap_bodystructure_parse_finish(struct message_part *root,
- int extended)
-{
- string_t *str;
-
- str = t_str_new(2048);
- part_write_bodystructure(root, str, extended);
- return str_c(str);
-}
-
static int str_append_imap_arg(string_t *str, const struct imap_arg *arg)
{
switch (arg->type) {
return TRUE;
}
-const char *imap_body_parse_from_bodystructure(const char *bodystructure)
+int imap_body_parse_from_bodystructure(const char *bodystructure,
+ string_t *dest)
{
struct istream *input;
struct imap_parser *parser;
struct imap_arg *args;
- string_t *str;
- const char *value;
- size_t len;
int ret;
- len = strlen(bodystructure);
- str = t_str_new(len);
-
input = i_stream_create_from_data(pool_datastack_create(),
- bodystructure, len);
+ bodystructure, strlen(bodystructure));
(void)i_stream_read(input);
parser = imap_parser_create(input, NULL, (size_t)-1);
ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE |
IMAP_PARSE_FLAG_LITERAL_TYPE, &args);
- if (ret <= 0 || !imap_parse_bodystructure_args(args, str))
- value = NULL;
- else
- value = str_c(str);
+ ret = ret > 0 && imap_parse_bodystructure_args(args, dest);
- if (value == NULL)
+ if (!ret)
i_error("Error parsing IMAP bodystructure: %s", bodystructure);
imap_parser_destroy(parser);
i_stream_unref(input);
- return value;
+ return ret;
}
void imap_bodystructure_parse_header(pool_t pool, struct message_part *part,
struct message_header_line *hdr);
-const char *imap_bodystructure_parse_finish(struct message_part *root,
- int extended);
+void imap_bodystructure_write(struct message_part *part,
+ string_t *dest, int extended);
/* Return BODY part from BODYSTRUCTURE */
-const char *imap_body_parse_from_bodystructure(const char *bodystructure);
+int imap_body_parse_from_bodystructure(const char *bodystructure,
+ string_t *dest);
#endif
static unsigned char null4[4] = { 0, 0, 0, 0 };
-static const struct mail_cache_record *
-mail_cache_compress_record(struct mail_cache_view *view, uint32_t seq,
- enum mail_cache_field orig_cached_fields,
- int header_idx, uint32_t *size_r)
+struct mail_cache_copy_context {
+ int new_msg;
+ char field_seen[32], keep_fields[32], temp_fields[32];
+ buffer_t *buffer, *header;
+};
+
+static int
+mail_cache_compress_callback(struct mail_cache_view *view __attr_unused__,
+ enum mail_cache_field field,
+ const void *data, size_t data_size, void *context)
{
- enum mail_cache_field cached_fields, field;
- struct mail_cache_record cache_rec;
- buffer_t *buffer;
- pool_t pool;
- const void *data;
- size_t size, pos;
- uint32_t *p, size32 = 0;
+ struct mail_cache_copy_context *ctx = context;
+ uint32_t size32;
int i;
- memset(&cache_rec, 0, sizeof(cache_rec));
- pool = pool_datastack_create();
- buffer = buffer_create_dynamic(pool, 4096, (size_t)-1);
-
- cached_fields = orig_cached_fields & ~MAIL_CACHE_HEADERS_MASK;
- buffer_append(buffer, &cache_rec, sizeof(cache_rec));
- for (i = 0, field = 1; i < 31; i++, field <<= 1) {
- if ((cached_fields & field) == 0)
- continue;
-
- pos = buffer_get_used_size(buffer);
- if ((field & MAIL_CACHE_FIXED_MASK) == 0)
- buffer_append(buffer, &size32, sizeof(size32));
-
- if (!mail_cache_lookup_field(view, buffer, seq, field)) {
- cached_fields &= ~field;
- buffer_set_used_size(buffer, pos);
- continue;
- }
-
- if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
- p = buffer_get_space_unsafe(buffer, pos,
- sizeof(uint32_t));
- *p = (uint32_t)size;
- }
-
- if ((size & 3) != 0)
- buffer_append(buffer, null4, 4 - (size & 3));
+ if (ctx->new_msg) {
+ if (!ctx->temp_fields[field])
+ return 1;
+ } else {
+ if (!ctx->keep_fields[field])
+ return 1;
}
- /* now merge all the headers if we have them all */
- if ((orig_cached_fields & mail_cache_header_fields[header_idx]) != 0) {
- size32 = 0;
- pos = buffer_get_used_size(buffer);
- buffer_append(buffer, &size32, sizeof(size32));
-
- for (i = 0; i <= header_idx; i++) {
- field = mail_cache_header_fields[i];
- if (mail_cache_lookup_field(view, buffer, seq, field)) {
- /* remove terminating \0 */
- buffer_set_used_size(buffer,
- buffer_get_used_size(buffer)-1);
+ if (ctx->field_seen[field]) {
+ /* drop duplicates */
+ return 1;
+ }
+ ctx->field_seen[field] = TRUE;
+
+ for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
+ if (mail_cache_header_fields[i] == field) {
+ /* it's header - save it into header field */
+ size32 = buffer_get_used_size(ctx->header);
+ if (size32 > 0) {
+ /* remove old terminating \0 */
+ buffer_set_used_size(ctx->header, size32-1);
}
+ buffer_append(ctx->header, data, data_size);
+ return 1;
}
- buffer_append(buffer, null4, 1);
+ }
- size32 = buffer_get_used_size(buffer) - pos;
- if ((size32 & 3) != 0)
- buffer_append(buffer, null4, 4 - (size32 & 3));
- buffer_write(buffer, pos, &size32, sizeof(size32));
+ buffer_append(ctx->buffer, &field, sizeof(field));
- cached_fields |= MAIL_CACHE_HEADERS1;
+ if (mail_cache_field_sizes[field] == (unsigned int)-1) {
+ size32 = (uint32_t)data_size;
+ buffer_append(ctx->buffer, &size32, sizeof(size32));
}
- cache_rec.fields = cached_fields;
- cache_rec.size = buffer_get_used_size(buffer);
- buffer_write(buffer, 0, &cache_rec, sizeof(cache_rec));
-
- data = buffer_get_data(buffer, &size);
- *size_r = size;
- return data;
+ buffer_append(ctx->buffer, data, data_size);
+ if ((data_size & 3) != 0)
+ buffer_append(ctx->buffer, null4, 4 - (data_size & 3));
+ return 1;
}
static int
mail_cache_copy(struct mail_cache *cache, struct mail_index_view *view, int fd)
{
+ struct mail_cache_copy_context ctx;
struct mail_cache_view *cache_view;
struct mail_index_transaction *t;
const struct mail_index_header *idx_hdr;
- const struct mail_cache_record *cache_rec;
struct mail_cache_header hdr;
+ struct mail_cache_record cache_rec;
+ enum mail_cache_field field;
struct ostream *output;
- enum mail_cache_field keep_fields, temp_fields;
- enum mail_cache_field cached_fields, new_fields;
const char *str;
- uint32_t size, size32, message_count, seq, first_new_seq;
+ uint32_t size32, message_count, seq, first_new_seq, old_offset;
uoff_t offset;
- int i, header_idx, ret;
+ int i, ret, header_idx;
/* get sequence of first message which doesn't need it's temp fields
removed. */
memcpy(hdr.field_usage_last_used,
cache->hdr->field_usage_last_used,
sizeof(hdr.field_usage_last_used));
+ } else {
+ memcpy(hdr.field_usage_decision_type,
+ cache->default_field_usage_decision_type,
+ sizeof(hdr.field_usage_decision_type));
+ }
- keep_fields = temp_fields = 0;
- for (i = 0; i < 32; i++) {
- if (cache->hdr->field_usage_decision_type[i] &
- MAIL_CACHE_DECISION_YES)
- keep_fields |= 1 << i;
- else if (cache->hdr->field_usage_decision_type[i] &
- MAIL_CACHE_DECISION_TEMP)
- temp_fields |= 1 << i;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.buffer = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+ ctx.header = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+
+ for (i = 0; i < 32; i++) {
+ if (hdr.field_usage_decision_type[i] & MAIL_CACHE_DECISION_YES)
+ ctx.keep_fields[i] = TRUE;
+ else if (hdr.field_usage_decision_type[i] &
+ MAIL_CACHE_DECISION_TEMP) {
+ ctx.temp_fields[i] = TRUE;
+ ctx.keep_fields[i] = TRUE;
}
}
ret = 0;
for (seq = 1; seq <= message_count; seq++) {
- cache_rec = mail_cache_lookup(cache_view, seq);
- if (cache_rec == NULL)
- continue;
-
- cached_fields = mail_cache_get_fields(cache_view, seq);
- new_fields = cached_fields & keep_fields;
- if ((cached_fields & temp_fields) != 0 &&
- seq >= first_new_seq) {
- /* new message, keep temp fields */
- new_fields |= cached_fields & temp_fields;
+ ctx.new_msg = seq >= first_new_seq;
+ buffer_set_used_size(ctx.buffer, 0);
+ buffer_set_used_size(ctx.header, 0);
+ memset(ctx.field_seen, 0, sizeof(ctx.field_seen));
+
+ memset(&cache_rec, 0, sizeof(cache_rec));
+ buffer_append(ctx.buffer, &cache_rec, sizeof(cache_rec));
+
+ mail_cache_foreach(cache_view, seq,
+ mail_cache_compress_callback, &ctx);
+
+ size32 = buffer_get_used_size(ctx.header);
+ if (size32 > 0 && ctx.field_seen[header_idx]) {
+ field = MAIL_CACHE_HEADERS1;
+ buffer_append(ctx.buffer, &field, sizeof(field));
+ buffer_append(ctx.buffer, &size32, sizeof(size32));
+ buffer_append(ctx.buffer,
+ buffer_get_data(ctx.header, NULL),
+ size32);
+ if ((size32 & 3) != 0) {
+ buffer_append(ctx.buffer, null4,
+ 4 - (size32 & 3));
+ }
}
- if (keep_fields == cached_fields &&
- cache_rec->prev_offset == 0) {
- /* just one unmodified block, save it */
- mail_index_update_cache(t, seq, hdr.file_seq,
- output->offset, NULL);
- o_stream_send(output, cache_rec, cache_rec->size);
+ if (buffer_get_used_size(ctx.buffer) == sizeof(cache_rec))
+ continue;
- if ((cache_rec->size & 3) != 0) {
- o_stream_send(output, null4,
- 4 - (cache_rec->size & 3));
- }
- } else {
- /* a) dropping fields
- b) multiple blocks, sort them into buffer */
- mail_index_update_cache(t, seq, hdr.file_seq,
- output->offset, NULL);
-
- t_push();
- cache_rec = mail_cache_compress_record(cache_view, seq,
- keep_fields,
- header_idx,
- &size);
- o_stream_send(output, cache_rec, size);
- t_pop();
- }
+ mail_index_update_cache(t, seq, hdr.file_seq,
+ output->offset, &old_offset);
+ o_stream_send(output, buffer_get_data(ctx.buffer, NULL),
+ buffer_get_used_size(ctx.buffer));
}
hdr.used_file_size = output->offset;
+ buffer_free(ctx.buffer);
+ buffer_free(ctx.header);
o_stream_seek(output, 0);
o_stream_send(output, &hdr, sizeof(hdr));
return -1;
}
- // FIXME: check that cache file was just recreated
+ // FIXME: check that cache file wasn't just recreated
ret = 0;
if (mail_cache_copy(cache, view, fd) < 0) {
#include <stddef.h>
static void
-mail_cache_set_decision_type(struct mail_cache *cache, uint32_t idx,
+mail_cache_set_decision_type(struct mail_cache *cache,
+ enum mail_cache_field field,
enum mail_cache_decision_type type)
{
uint8_t value = type;
will be corrected sometimes later, not too bad.. */
if (pwrite_full(cache->fd, &value, 1,
offsetof(struct mail_cache_header,
- field_usage_decision_type) + idx) < 0) {
+ field_usage_decision_type) + field) < 0) {
mail_cache_set_syscall_error(cache, "pwrite_full()");
}
}
-void mail_cache_handle_decisions(struct mail_cache_view *view, uint32_t seq,
- enum mail_cache_field field)
+void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field)
{
const struct mail_index_header *hdr;
- unsigned int idx;
uint32_t uid;
- idx = mail_cache_field_index(field);
- if (view->cache->hdr->field_usage_decision_type[idx] !=
+ if (view->cache->hdr->field_usage_decision_type[field] !=
MAIL_CACHE_DECISION_TEMP) {
/* a) forced decision
b) not cached, mail_cache_mark_missing() will handle this
mail_index_get_header(view->view, &hdr) < 0)
return;
- if (uid < view->cache->field_usage_uid_highwater[idx] ||
+ if (uid < view->cache->field_usage_uid_highwater[field] ||
uid < hdr->day_first_uid[7]) {
/* a) nonordered access within this session. if client doesn't
request messages in growing order, we assume it doesn't
client with no local cache. if it was just a new client
generating the local cache for the first time, we'll
drop back to TEMP within few months. */
- mail_cache_set_decision_type(view->cache, idx,
+ mail_cache_set_decision_type(view->cache, field,
MAIL_CACHE_DECISION_YES);
} else {
- view->cache->field_usage_uid_highwater[idx] = uid;
+ view->cache->field_usage_uid_highwater[field] = uid;
}
}
-void mail_cache_mark_missing(struct mail_cache_view *view, uint32_t seq,
+void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq,
enum mail_cache_field field)
{
- unsigned int idx;
uint32_t uid;
if (MAIL_CACHE_IS_UNUSABLE(view->cache))
return;
- idx = mail_cache_field_index(field);
- if (view->cache->hdr->field_usage_decision_type[idx] !=
+ if (view->cache->hdr->field_usage_decision_type[field] !=
MAIL_CACHE_DECISION_NO) {
/* a) forced decision
b) we're already caching it, so it just wasn't in cache */
}
/* field used the first time */
- mail_cache_set_decision_type(view->cache, idx,
+ mail_cache_set_decision_type(view->cache, field,
MAIL_CACHE_DECISION_TEMP);
if (mail_index_lookup_uid(view->view, seq, &uid) == 0)
- view->cache->field_usage_uid_highwater[idx] = uid;
+ view->cache->field_usage_uid_highwater[field] = uid;
}
return 0;
}
-struct mail_cache_record *
-mail_cache_lookup(struct mail_cache_view *view, uint32_t seq)
+int mail_cache_foreach(struct mail_cache_view *view, uint32_t seq,
+ int (*callback)(struct mail_cache_view *view,
+ enum mail_cache_field field,
+ const void *data, size_t data_size,
+ void *context), void *context)
{
- uint32_t offset;
-
- // FIXME: check transactions too
+ const struct mail_cache_record *cache_rec;
+ size_t pos, next_pos, max_size, data_size;
+ uint32_t offset, field;
+ int ret;
if (MAIL_CACHE_IS_UNUSABLE(view->cache))
- return NULL;
+ return 0;
- if (mail_cache_lookup_offset(view, seq, &offset) <= 0)
- return NULL;
+ if ((ret = mail_cache_lookup_offset(view, seq, &offset)) <= 0)
+ return ret;
- return mail_cache_get_record(view->cache, offset);
-}
+ cache_rec = mail_cache_get_record(view->cache, offset);
+ while (cache_rec != NULL) {
+ max_size = cache_rec->size;
+ if (max_size < sizeof(*cache_rec) + sizeof(uint32_t)*2) {
+ mail_cache_set_corrupted(view->cache,
+ "record has invalid size");
+ return -1;
+ }
+ max_size -= sizeof(uint32_t);
+
+ for (pos = sizeof(*cache_rec); pos < max_size; ) {
+ field = *((const uint32_t *)
+ CONST_PTR_OFFSET(cache_rec, pos));
+ pos += sizeof(uint32_t);
+
+ data_size = mail_cache_field_sizes[field];
+ if (data_size == (unsigned int)-1) {
+ data_size = *((const uint32_t *)
+ CONST_PTR_OFFSET(cache_rec, pos));
+ pos += sizeof(uint32_t);
+ }
-enum mail_cache_field
-mail_cache_get_fields(struct mail_cache_view *view, uint32_t seq)
-{
- struct mail_cache_record *cache_rec;
- enum mail_cache_field fields = 0;
+ next_pos = pos + ((data_size + 3) & ~3);
+ if (next_pos > cache_rec->size) {
+ mail_cache_set_corrupted(view->cache,
+ "Record continues outside it's "
+ "allocated size");
+ return -1;
+ }
- cache_rec = mail_cache_lookup(view, seq);
- while (cache_rec != NULL) {
- fields |= cache_rec->fields;
+ ret = callback(view, field,
+ CONST_PTR_OFFSET(cache_rec, pos),
+ data_size, context);
+ if (ret <= 0)
+ return ret;
+
+ pos = next_pos;
+ }
cache_rec = mail_cache_get_record(view->cache,
cache_rec->prev_offset);
}
- return fields;
+ if (view->transaction != NULL) {
+ // FIXME: update
+ }
+ return 1;
}
-static int cache_get_field(struct mail_cache *cache,
- const struct mail_cache_record *cache_rec,
- enum mail_cache_field field, buffer_t *dest_buf)
+static int mail_cache_seq_callback(struct mail_cache_view *view,
+ enum mail_cache_field field,
+ const void *data __attr_unused__,
+ size_t data_size __attr_unused__,
+ void *context __attr_unused__)
{
- unsigned int mask;
- uint32_t data_size;
- size_t offset, prev_offset;
- int i;
+ view->cached_exists[field] = TRUE;
+ return 1;
+}
- offset = sizeof(*cache_rec);
+static int mail_cache_seq(struct mail_cache_view *view, uint32_t seq)
+{
+ int ret;
- for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
- if ((cache_rec->fields & mask) == 0)
- continue;
+ view->cached_exists_seq = seq;
+ memset(view->cached_exists, 0, sizeof(view->cached_exists));
- /* all records are at least 32bit. we have to check this
- before getting data_size. */
- if (offset + sizeof(uint32_t) > cache_rec->size) {
- mail_cache_set_corrupted(cache,
- "Record continues outside it's allocated size");
- return FALSE;
- }
+ ret = mail_cache_foreach(view, seq, mail_cache_seq_callback, NULL);
+ return ret < 0 ? -1 : 0;
+}
- if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
- data_size = mail_cache_field_sizes[i];
- else {
- memcpy(&data_size, CONST_PTR_OFFSET(cache_rec, offset),
- sizeof(data_size));
- offset += sizeof(data_size);
- }
+int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field)
+{
+ i_assert(field < MAIL_CACHE_FIELD_COUNT);
- prev_offset = offset + ((data_size + 3) & ~3);
- if (prev_offset > cache_rec->size) {
- mail_cache_set_corrupted(cache,
- "Record continues outside it's allocated size");
- return FALSE;
- }
+ if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+ return 0;
- if (field == mask) {
- if (data_size == 0) {
- mail_cache_set_corrupted(cache,
- "Field size is 0");
- return FALSE;
- }
- buffer_append(dest_buf,
- CONST_PTR_OFFSET(cache_rec, offset),
- data_size);
- return TRUE;
- }
- offset = prev_offset;
+ if (view->cached_exists_seq != seq) {
+ if (mail_cache_seq(view, seq) < 0)
+ return -1;
}
+ return view->cached_exists[field];
+}
- i_unreached();
- return FALSE;
+enum mail_cache_decision_type
+mail_cache_field_get_decision(struct mail_cache *cache,
+ enum mail_cache_field field)
+{
+ i_assert(field < MAIL_CACHE_FIELD_COUNT);
+
+ if (MAIL_CACHE_IS_UNUSABLE(cache))
+ return cache->default_field_usage_decision_type[field];
+
+ return cache->hdr->field_usage_decision_type[field];
+}
+
+struct mail_cache_lookup_context {
+ buffer_t *dest_buf;
+ enum mail_cache_field field;
+};
+
+static int
+mail_cache_lookup_callback(struct mail_cache_view *view __attr_unused__,
+ enum mail_cache_field field,
+ const void *data, size_t data_size, void *context)
+{
+ struct mail_cache_lookup_context *ctx = context;
+
+ if (ctx->field != field)
+ return 1;
+
+ buffer_append(ctx->dest_buf, data, data_size);
+ return 0;
}
int mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf,
uint32_t seq, enum mail_cache_field field)
{
- struct mail_cache_record *cache_rec;
+ struct mail_cache_lookup_context ctx;
- mail_cache_handle_decisions(view, seq, field);
+ i_assert(field < MAIL_CACHE_FIELD_COUNT);
- cache_rec = mail_cache_lookup(view, seq);
- while (cache_rec != NULL) {
- if ((cache_rec->fields & field) != 0) {
- return cache_get_field(view->cache, cache_rec, field,
- dest_buf);
- }
- cache_rec = mail_cache_get_record(view->cache,
- cache_rec->prev_offset);
+ if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+ return 0;
+
+ mail_cache_decision_lookup(view, seq, field);
+
+ if (view->cached_exists_seq != seq) {
+ if (mail_cache_seq(view, seq) < 0)
+ return -1;
}
- return FALSE;
+ if (!view->cached_exists[field])
+ return 0;
+
+ /* should exist. find it. */
+ ctx.field = field;
+ ctx.dest_buf = dest_buf;
+ return mail_cache_foreach(view, seq, mail_cache_lookup_callback,
+ &ctx) == 0;
}
int mail_cache_lookup_string_field(struct mail_cache_view *view, string_t *dest,
{
size_t old_size, new_size;
- i_assert((field & MAIL_CACHE_STRING_MASK) != 0);
+ i_assert(field < MAIL_CACHE_FIELD_COUNT);
+
+ if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+ return 0;
old_size = str_len(dest);
if (!mail_cache_lookup_field(view, dest, seq, field))
- return FALSE;
+ return 0;
new_size = str_len(dest);
if (old_size == new_size ||
str_data(dest)[new_size-1] != '\0') {
mail_cache_set_corrupted(view->cache,
"String field %x doesn't end with NUL", field);
- return FALSE;
+ return -1;
}
str_truncate(dest, new_size-1);
- return TRUE;
+ return 1;
}
enum mail_cache_record_flag
#define MAIL_CACHE_IS_UNUSABLE(cache) \
((cache)->hdr == NULL)
-enum mail_cache_decision_type {
- /* Not needed currently */
- MAIL_CACHE_DECISION_NO = 0x00,
- /* Needed only for new mails. Drop when compressing. */
- MAIL_CACHE_DECISION_TEMP = 0x01,
- /* Needed. */
- MAIL_CACHE_DECISION_YES = 0x02,
-
- /* This decision has been forced manually, don't change it. */
- MAIL_CACHE_DECISION_FORCED = 0x80
-};
-
struct mail_cache_header {
/* version is increased only when you can't have backwards
compatibility. */
};
struct mail_cache_record {
- uint32_t fields; /* enum mail_cache_field */
uint32_t prev_offset;
uint32_t size; /* full record size, including this header */
+ /* array of { uint32_t field; [ uint32_t size; ] { .. } } */
};
struct mail_cache_hole_header {
uint32_t split_offsets[MAIL_CACHE_HEADERS_COUNT];
const char *const *split_headers[MAIL_CACHE_HEADERS_COUNT];
- enum mail_cache_field default_cache_fields;
- enum mail_cache_field never_cache_fields;
-
+ uint8_t default_field_usage_decision_type[32];
uint32_t field_usage_uid_highwater[32];
unsigned int locked:1;
struct mail_cache *cache;
struct mail_index_view *view;
- unsigned int broken:1;
+ struct mail_cache_transaction_ctx *transaction;
+ char cached_exists[32];
+ uint32_t cached_exists_seq;
};
extern unsigned int mail_cache_field_sizes[32];
uint32_t mail_cache_uint32_to_offset(uint32_t offset);
uint32_t mail_cache_offset_to_uint32(uint32_t offset);
-unsigned int mail_cache_field_index(enum mail_cache_field field);
/* Explicitly lock the cache file. Returns -1 if error, 1 if ok, 0 if we
couldn't lock */
struct mail_cache_record *
mail_cache_get_record(struct mail_cache *cache, uint32_t offset);
-struct mail_cache_record *
-mail_cache_lookup(struct mail_cache_view *view, uint32_t seq);
+int mail_cache_foreach(struct mail_cache_view *view, uint32_t seq,
+ int (*callback)(struct mail_cache_view *view,
+ enum mail_cache_field field,
+ const void *data, size_t data_size,
+ void *context), void *context);
int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx);
void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx);
/* Mark record in given offset to be deleted. */
int mail_cache_delete(struct mail_cache *cache, uint32_t offset);
-void mail_cache_handle_decisions(struct mail_cache_view *view, uint32_t seq,
- enum mail_cache_field field);
+void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field);
+void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field);
void mail_cache_set_syscall_error(struct mail_cache *cache,
const char *function);
uint32_t last_grow_size;
uint32_t first_seq, last_seq;
- enum mail_cache_field fields;
+ enum mail_cache_field fields[32];
unsigned int changes:1;
};
ctx->reservations =
buffer_create_dynamic(system_pool, 256, (size_t)-1);
+ i_assert(view->transaction == NULL);
+ view->transaction = ctx;
+
t->cache_trans_ctx = ctx;
return ctx;
}
static void mail_cache_transaction_free(struct mail_cache_transaction_ctx *ctx)
{
+ ctx->view->transaction = NULL;
+
buffer_free(ctx->cache_data);
buffer_free(ctx->cache_data_seq);
buffer_free(ctx->reservations);
struct mail_cache *cache = ctx->cache;
const struct mail_cache_record *rec, *tmp_rec;
const uint32_t *seq;
- uint32_t write_offset, old_offset, rec_offset;
+ uint32_t write_offset, old_offset, rec_pos;
size_t size, max_size, seq_idx, seq_limit, seq_count;
int commit;
seq = buffer_get_data(ctx->cache_data_seq, &seq_count);
seq_count /= sizeof(*seq);
+ seq_limit = 0;
- for (seq_idx = 0, rec_offset = 0; rec_offset < ctx->prev_pos;) {
- max_size = ctx->prev_pos - rec_offset;
+ for (seq_idx = 0, rec_pos = 0; rec_pos < ctx->prev_pos;) {
+ max_size = ctx->prev_pos - rec_pos;
write_offset = mail_cache_transaction_get_space(ctx, rec->size,
max_size,
&max_size,
return ctx->prev_pos == 0 ? 0 : -1;
}
- if (max_size < ctx->prev_pos) {
+ if (rec_pos + max_size < ctx->prev_pos) {
/* see how much we can really write there */
tmp_rec = rec;
for (size = 0; size + tmp_rec->size <= max_size; ) {
}
write_offset += rec->size;
- rec_offset += rec->size;
+ rec_pos += rec->size;
rec = CONST_PTR_OFFSET(rec, rec->size);
}
}
/* drop the written data from buffer */
buffer_copy(ctx->cache_data, 0,
ctx->cache_data, ctx->prev_pos, (size_t)-1);
- buffer_set_used_size(ctx->cache_data, size - ctx->prev_pos);
+ buffer_set_used_size(ctx->cache_data,
+ buffer_get_used_size(ctx->cache_data) -
+ ctx->prev_pos);
+ ctx->prev_pos = 0;
buffer_set_used_size(ctx->cache_data_seq, 0);
return 0;
data = buffer_get_modifyable_data(ctx->cache_data, &size);
rec = PTR_OFFSET(data, ctx->prev_pos);
rec->size = size - ctx->prev_pos;
+ i_assert(rec->size != 0);
buffer_append(ctx->cache_data_seq, &ctx->prev_seq,
sizeof(ctx->prev_seq));
return offset > 0;
}
-static size_t
-mail_cache_transaction_get_insert_pos(struct mail_cache_transaction_ctx *ctx,
- enum mail_cache_field field)
-{
- const struct mail_cache_record *cache_rec;
- const void *data;
- unsigned int mask;
- uint32_t data_size;
- size_t pos;
- int i;
-
- data = buffer_get_data(ctx->cache_data, NULL);
- cache_rec = CONST_PTR_OFFSET(data, ctx->prev_pos);
-
- pos = ctx->prev_pos + sizeof(*cache_rec);
- for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
- if ((field & mask) != 0)
- return pos;
-
- if ((cache_rec->fields & mask) != 0) {
- if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
- data_size = mail_cache_field_sizes[i];
- else {
- memcpy(&data_size, CONST_PTR_OFFSET(data, pos),
- sizeof(data_size));
- pos += sizeof(data_size);
- }
- pos += (data_size + 3) & ~3;
- }
- }
-
- i_unreached();
- return pos;
-}
-
void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
enum mail_cache_field field,
const void *data, size_t data_size)
{
- struct mail_cache_record *cache_rec;
- unsigned char *buf;
- size_t full_size, pos;
- uint32_t data_size32;
- unsigned int field_idx;
+ uint32_t fixed_size, data_size32;
+ size_t full_size;
+ i_assert(field < MAIL_CACHE_FIELD_COUNT);
i_assert(data_size > 0);
i_assert(data_size < (uint32_t)-1);
- data_size32 = (uint32_t)data_size;
+ mail_cache_decision_add(ctx->view, seq, field);
- if ((field & MAIL_CACHE_FIXED_MASK) != 0) {
- field_idx = mail_cache_field_index(field);
- i_assert(mail_cache_field_sizes[field_idx] == data_size);
- } else if ((field & MAIL_CACHE_STRING_MASK) != 0) {
- i_assert(((char *) data)[data_size-1] == '\0');
- }
+ fixed_size = mail_cache_field_sizes[field];
+ i_assert(fixed_size == (unsigned int)-1 || fixed_size == data_size);
+
+ data_size32 = (uint32_t)data_size;
if (ctx->prev_seq != seq) {
mail_cache_transaction_switch_seq(ctx);
ctx->first_seq = seq;
if (seq > ctx->last_seq)
ctx->last_seq = seq;
- ctx->fields |= field;
+ ctx->view->cached_exists[field] = TRUE;
+ ctx->fields[field] = TRUE;
}
full_size = (data_size + 3) & ~3;
- if ((field & MAIL_CACHE_FIXED_MASK) == 0)
+ if (fixed_size == (unsigned int)-1)
full_size += sizeof(data_size32);
if (buffer_get_used_size(ctx->cache_data) + full_size >
return;
}
- /* fields must be ordered. find where to insert it. */
- pos = mail_cache_transaction_get_insert_pos(ctx, field);
- buffer_copy(ctx->cache_data, pos + full_size,
- ctx->cache_data, pos, (size_t)-1);
-
- cache_rec = buffer_get_space_unsafe(ctx->cache_data, ctx->prev_pos,
- sizeof(*cache_rec));
- cache_rec->fields |= field;
-
- /* @UNSAFE */
- buf = buffer_get_space_unsafe(ctx->cache_data, pos, full_size);
- if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
- memcpy(buf, &data_size32, sizeof(data_size32));
- buf += sizeof(data_size32);
+ buffer_append(ctx->cache_data, &field, sizeof(field));
+ if (fixed_size == (unsigned int)-1) {
+ buffer_append(ctx->cache_data, &data_size32,
+ sizeof(data_size32));
}
- memcpy(buf, data, data_size); buf += data_size;
+
+ buffer_append(ctx->cache_data, data, data_size);
if ((data_size & 3) != 0)
- memset(buf, 0, 4 - (data_size & 3));
+ buffer_append(ctx->cache_data, null4, 4 - (data_size & 3));
}
int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
sizeof(time_t),
sizeof(uoff_t),
- 0, 0, 0, 0, 0, 0, 0, 0,
-
/* variable sized */
(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
+ (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
+ (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1
};
(((uint32_t)buf[0] & 0x7f) << 23);
}
-unsigned int mail_cache_field_index(enum mail_cache_field field)
-{
- unsigned int i, num;
-
- for (i = 0, num = 1; i < 32; i++, num <<= 1) {
- if (field == num)
- return i;
- }
- i_unreached();
-}
-
void mail_cache_set_syscall_error(struct mail_cache *cache,
const char *function)
{
}
void mail_cache_set_defaults(struct mail_cache *cache,
- enum mail_cache_field default_cache_fields,
- enum mail_cache_field never_cache_fields)
+ const enum mail_cache_decision_type dec[32])
{
- cache->default_cache_fields = default_cache_fields;
- cache->never_cache_fields = never_cache_fields;
+ memcpy(cache->default_field_usage_decision_type, dec,
+ sizeof(cache->default_field_usage_decision_type));
}
int mail_cache_lock(struct mail_cache *cache)
struct mail_cache_view;
struct mail_cache_transaction_ctx;
+enum mail_cache_decision_type {
+ /* Not needed currently */
+ MAIL_CACHE_DECISION_NO = 0x00,
+ /* Needed only for new mails. Drop when compressing. */
+ MAIL_CACHE_DECISION_TEMP = 0x01,
+ /* Needed. */
+ MAIL_CACHE_DECISION_YES = 0x02,
+
+ /* This decision has been forced manually, don't change it. */
+ MAIL_CACHE_DECISION_FORCED = 0x80
+};
+
enum mail_cache_record_flag {
/* If binary flags are set, it's not checked whether mail is
missing CRs. So this flag may be set as an optimization for
/* when modifying, remember to update mail_cache_field_sizes[] too */
enum mail_cache_field {
/* fixed size fields */
- MAIL_CACHE_INDEX_FLAGS = 0x00000001,
- MAIL_CACHE_SENT_DATE = 0x00000002,
- MAIL_CACHE_RECEIVED_DATE = 0x00000004,
- MAIL_CACHE_VIRTUAL_FULL_SIZE = 0x00000008,
+ MAIL_CACHE_INDEX_FLAGS = 0,
+ MAIL_CACHE_SENT_DATE,
+ MAIL_CACHE_RECEIVED_DATE,
+ MAIL_CACHE_VIRTUAL_FULL_SIZE,
/* variable sized field */
- MAIL_CACHE_HEADERS1 = 0x40000000,
- MAIL_CACHE_HEADERS2 = 0x20000000,
- MAIL_CACHE_HEADERS3 = 0x10000000,
- MAIL_CACHE_HEADERS4 = 0x08000000,
- MAIL_CACHE_LOCATION = 0x04000000,
- MAIL_CACHE_BODY = 0x02000000,
- MAIL_CACHE_BODYSTRUCTURE = 0x01000000,
- MAIL_CACHE_ENVELOPE = 0x00800000,
- MAIL_CACHE_MESSAGEPART = 0x00400000,
- MAIL_CACHE_UID_STRING = 0x00200000,
-
- MAIL_CACHE_FIXED_MASK = MAIL_CACHE_INDEX_FLAGS |
- MAIL_CACHE_SENT_DATE |
- MAIL_CACHE_RECEIVED_DATE |
- MAIL_CACHE_VIRTUAL_FULL_SIZE,
- MAIL_CACHE_HEADERS_MASK = MAIL_CACHE_HEADERS1 |
- MAIL_CACHE_HEADERS2 |
- MAIL_CACHE_HEADERS3 |
- MAIL_CACHE_HEADERS4,
- MAIL_CACHE_STRING_MASK = MAIL_CACHE_HEADERS_MASK |
- MAIL_CACHE_LOCATION |
- MAIL_CACHE_BODY |
- MAIL_CACHE_BODYSTRUCTURE |
- MAIL_CACHE_ENVELOPE |
- MAIL_CACHE_UID_STRING,
- MAIL_CACHE_BODYSTRUCTURE_MASK = MAIL_CACHE_BODY |
- MAIL_CACHE_BODYSTRUCTURE |
- MAIL_CACHE_MESSAGEPART
+ MAIL_CACHE_HEADERS1,
+ MAIL_CACHE_HEADERS2,
+ MAIL_CACHE_HEADERS3,
+ MAIL_CACHE_HEADERS4,
+ MAIL_CACHE_BODY,
+ MAIL_CACHE_BODYSTRUCTURE,
+ MAIL_CACHE_ENVELOPE,
+ MAIL_CACHE_MESSAGEPART,
+ MAIL_CACHE_UID_STRING,
+
+ MAIL_CACHE_FIELD_COUNT
};
struct mail_sent_date {
void mail_cache_free(struct mail_cache *cache);
void mail_cache_set_defaults(struct mail_cache *cache,
- enum mail_cache_field default_cache_fields,
- enum mail_cache_field never_cache_fields);
+ const enum mail_cache_decision_type dec[32]);
/* Returns TRUE if cache should be compressed. */
int mail_cache_need_compress(struct mail_cache *cache);
enum mail_cache_field field,
const void *data, size_t data_size);
-/* Return all fields that are currently cached for record. */
-enum mail_cache_field
-mail_cache_get_fields(struct mail_cache_view *view, uint32_t seq);
+/* Retursn TRUE if field exists. */
+int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field);
+/* Returns current caching decision for given field. */
+enum mail_cache_decision_type
+mail_cache_field_get_decision(struct mail_cache *cache,
+ enum mail_cache_field field);
/* Set data_r and size_r to point to wanted field in cache file.
Returns TRUE if field was found. If field contains multiple fields,
int mail_cache_lookup_string_field(struct mail_cache_view *view, string_t *dest,
uint32_t seq, enum mail_cache_field field);
-/* Mark given field as missing, ie. it should be cached when possible. */
-void mail_cache_mark_missing(struct mail_cache_view *view, uint32_t uid,
- enum mail_cache_field field);
-
/* Return record flags. */
enum mail_cache_record_flag
mail_cache_get_record_flags(struct mail_cache_view *view, uint32_t seq);
{
struct index_transaction_context *t =
(struct index_transaction_context *)_t;
- const struct mail_index_record *rec;
-
- if (mail_index_lookup(t->trans_view, seq, &rec) < 0) {
- mail_storage_set_index_error(t->ibox);
- return NULL;
- }
-
- if (rec == NULL)
- return NULL;
if (t->fetch_mail.pool != NULL)
index_mail_deinit(&t->fetch_mail);
index_mail_init(t, &t->fetch_mail, wanted_fields, NULL);
- if (index_mail_next(&t->fetch_mail, rec, seq, FALSE) <= 0)
+ if (index_mail_next(&t->fetch_mail, seq) < 0)
return NULL;
-
return &t->fetch_mail.mail;
}
return -1;
for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
- if ((mail->data.cached_fields &
- mail_cache_header_fields[idx]) != 0)
+ if (mail_cache_field_exists(mail->trans->cache_view,
+ mail->data.seq,
+ mail_cache_header_fields[idx]) > 0)
return idx;
}
data->save_sent_date = FALSE;
}
if (data->sent_date.time != (time_t)-1) {
- index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
- &data->sent_date,
- sizeof(data->sent_date));
+ mail_cache_add(mail->trans->cache_trans, data->seq,
+ MAIL_CACHE_SENT_DATE, &data->sent_date,
+ sizeof(data->sent_date));
}
cached_headers_mark_fully_saved(mail);
static int index_mail_can_cache_headers(struct index_mail *mail)
{
- if ((mail->data.cached_fields &
- mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT-1]) != 0)
+ enum mail_cache_field field;
+
+ field = mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT-1];
+ if (mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
+ field) != 0)
return FALSE; /* all headers used */
/* FIXME: add some smart checks here. we don't necessarily want to
if (mail->data.header_data == NULL)
mail->data.header_data = str_new(mail->pool, 4096);
- /* can_cache_headers() locks the cache file. it must be done before
- we can expect cached headers to stay the same. it's not a good idea
- to cache some headers twice because of race conditions.. */
if (!data->header_fully_parsed && index_mail_can_cache_headers(mail)) {
if (data->header_data_cached_partial) {
/* too difficult to handle efficiently, trash it */
if (idx != -2) {
if (idx >= 0) {
for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
- if ((data->cached_fields &
- mail_cache_header_fields[idx]) != 0)
+ if (mail_cache_field_exists(
+ mail->trans->cache_view,
+ data->seq,
+ mail_cache_header_fields[idx]) > 0)
break;
}
}
#include "index-storage.h"
#include "index-mail.h"
-static void index_mail_parse_body(struct index_mail *mail);
+static void index_mail_parse_body(struct index_mail *mail, int need_parts);
static struct message_part *get_cached_parts(struct index_mail *mail)
{
buffer_t *part_buf;
const char *error;
- if ((mail->data.cached_fields & MAIL_CACHE_MESSAGEPART) == 0) {
- mail_cache_mark_missing(mail->trans->cache_view, mail->data.seq,
- MAIL_CACHE_MESSAGEPART);
- return NULL;
- }
-
+ t_push();
part_buf = buffer_create_dynamic(pool_datastack_create(),
128, (size_t)-1);
- if (!mail_cache_lookup_field(mail->trans->cache_view, part_buf,
- mail->data.seq, MAIL_CACHE_MESSAGEPART)) {
- /* unexpected - must be an error */
+ if (mail_cache_lookup_field(mail->trans->cache_view, part_buf,
+ mail->data.seq,
+ MAIL_CACHE_MESSAGEPART) <= 0) {
+ t_pop();
return NULL;
}
buffer_get_data(part_buf, NULL),
buffer_get_used_size(part_buf),
&error);
+ t_pop();
+
if (part == NULL) {
mail_cache_set_corrupted(mail->ibox->cache,
"Corrupted cached message_part data (%s)", error);
{
string_t *str;
- if ((mail->data.cached_fields & field) == 0) {
- mail_cache_mark_missing(mail->trans->cache_view,
- mail->data.seq, field);
- return NULL;
- }
-
str = str_new(mail->pool, 32);
- if (!mail_cache_lookup_string_field(mail->trans->cache_view, str,
- mail->data.seq, field))
+ if (mail_cache_lookup_string_field(mail->trans->cache_view, str,
+ mail->data.seq, field) <= 0) {
+ p_free(mail->pool, str);
return NULL;
+ }
return str_c(str);
}
t_push();
buf = buffer_create_data(pool_datastack_create(), data, data_size);
- if (!mail_cache_lookup_field(mail->trans->cache_view, buf,
- mail->data.seq, field)) {
- mail_cache_mark_missing(mail->trans->cache_view,
- mail->data.seq, field);
+ if (mail_cache_lookup_field(mail->trans->cache_view, buf,
+ mail->data.seq, field) <= 0) {
ret = FALSE;
} else {
i_assert(buffer_get_used_size(buf) == data_size);
}
}
-static int index_mail_cache_can_add(struct index_mail *mail,
- enum mail_cache_field field)
-{
- if ((mail->data.cached_fields & field) != 0)
- return FALSE;
-
- // FIXME: check if we really want to cache this
-
- index_mail_cache_transaction_begin(mail);
-
- /* cached_fields may have changed, recheck */
- if ((mail->data.cached_fields & field) != 0)
- return FALSE;
-
- return TRUE;
-}
-
-void index_mail_cache_add(struct index_mail *mail, enum mail_cache_field field,
- const void *data, size_t size)
-{
- if (!index_mail_cache_can_add(mail, field))
- return;
-
- mail_cache_add(mail->trans->cache_trans, mail->data.seq,
- field, data, size);
-
- mail->data.cached_fields |= field;
-}
-
const struct mail_full_flags *index_mail_get_flags(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *) _mail;
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;
- }
+ data->parts = get_cached_parts(mail);
+ if (data->parts != NULL)
+ return data->parts;
if (data->parser_ctx == NULL) {
if (!index_mail_parse_headers(mail))
return NULL;
}
- index_mail_parse_body(mail);
+ index_mail_parse_body(mail, TRUE);
return data->parts;
}
tz = 0;
}
data->sent_date.timezone = tz;
- index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
- &data->sent_date,
- sizeof(data->sent_date));
+ mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+ MAIL_CACHE_SENT_DATE, &data->sent_date,
+ sizeof(data->sent_date));
}
}
{
struct index_mail_data *data = &mail->data;
- if (data->parts == NULL) {
- if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0)
- (void)index_mail_get_parts(&mail->mail);
- else
- data->parts = get_cached_parts(mail);
- }
+ if (data->parts == NULL)
+ (void)index_mail_get_parts(&mail->mail);
if (data->parts != NULL) {
data->hdr_size = data->parts->header_size;
if (data->size != (uoff_t)-1)
return data->size;
- if ((mail->wanted_fields & MAIL_FETCH_SIZE) == 0) {
- data->size = index_mail_get_cached_virtual_size(mail);
- if (data->size != (uoff_t)-1)
- return data->size;
- }
+ data->size = index_mail_get_cached_virtual_size(mail);
+ if (data->size != (uoff_t)-1)
+ return data->size;
if (get_msgpart_sizes(mail))
return data->size;
imap_bodystructure_parse_header(pool, part, hdr);
}
-static void index_mail_parse_body(struct index_mail *mail)
+static void index_mail_parse_body(struct index_mail *mail, int need_parts)
{
struct index_mail_data *data = &mail->data;
enum mail_cache_record_flag cache_flags;
+ enum mail_cache_decision_type decision;
buffer_t *buffer;
const void *buf_data;
size_t buf_size;
i_stream_seek(data->stream, data->hdr_size.physical_size);
if (data->bodystructure_header_parsed) {
+ /* bodystructure header is parsed, we want the body's mime
+ headers too */
message_parser_parse_body(data->parser_ctx,
parse_bodystructure_header,
NULL, mail->pool);
data->body_size = data->parts->body_size;
data->body_size_set = TRUE;
- if (mail->mail.has_nuls || mail->mail.has_no_nuls)
- return;
+ index_mail_cache_transaction_begin(mail);
- /* we know the NULs now, update them */
- if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
- mail->mail.has_nuls = TRUE;
- mail->mail.has_no_nuls = FALSE;
- } else {
- mail->mail.has_nuls = FALSE;
- mail->mail.has_no_nuls = TRUE;
- }
+ if (!mail->mail.has_nuls && !mail->mail.has_no_nuls) {
+ /* we know the NULs now, update them */
+ if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
+ mail->mail.has_nuls = TRUE;
+ mail->mail.has_no_nuls = FALSE;
+ } else {
+ mail->mail.has_nuls = FALSE;
+ mail->mail.has_no_nuls = TRUE;
+ }
- index_mail_cache_transaction_begin(mail);
+ /* update cache_flags */
+ cache_flags =
+ mail_cache_get_record_flags(mail->trans->cache_view,
+ mail->data.seq);
+ if (mail->mail.has_nuls)
+ cache_flags |= MAIL_INDEX_FLAG_HAS_NULS;
+ else
+ cache_flags |= MAIL_INDEX_FLAG_HAS_NO_NULS;
- /* update cache_flags */
- cache_flags = mail_cache_get_record_flags(mail->trans->cache_view,
- mail->data.seq);
- if (mail->mail.has_nuls)
- cache_flags |= MAIL_INDEX_FLAG_HAS_NULS;
- else
- cache_flags |= MAIL_INDEX_FLAG_HAS_NO_NULS;
+ (void)mail_cache_update_record_flags(mail->trans->cache_view,
+ mail->data.seq,
+ cache_flags);
+ }
- if (!mail_cache_update_record_flags(mail->trans->cache_view,
- mail->data.seq, cache_flags))
+ /* see if we want to cache the message part */
+ if (mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
+ MAIL_CACHE_MESSAGEPART) != 0)
return;
- if (index_mail_cache_can_add(mail, MAIL_CACHE_MESSAGEPART)) {
+ decision = mail_cache_field_get_decision(mail->ibox->cache,
+ MAIL_CACHE_MESSAGEPART);
+ if (decision != (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED) &&
+ (decision != MAIL_CACHE_DECISION_NO || need_parts ||
+ (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0)) {
t_push();
buffer = buffer_create_dynamic(pool_datastack_create(),
1024, (size_t)-1);
message_part_serialize(mail->data.parts, buffer);
buf_data = buffer_get_data(buffer, &buf_size);
- index_mail_cache_add(mail, MAIL_CACHE_MESSAGEPART,
- buf_data, buf_size);
+ mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+ MAIL_CACHE_MESSAGEPART, buf_data, buf_size);
t_pop();
}
}
if (body_size != NULL) {
if (!data->body_size_set)
- index_mail_parse_body(mail);
+ index_mail_parse_body(mail, FALSE);
*body_size = data->body_size;
}
return data->stream;
}
+static void index_mail_parse_bodystructure(struct index_mail *mail,
+ enum mail_cache_field field)
+{
+ struct index_mail_data *data = &mail->data;
+ enum mail_cache_decision_type dec;
+ string_t *str;
+ int bodystructure_cached = FALSE;
+
+ if (!data->bodystructure_header_parsed) {
+ data->bodystructure_header_want = TRUE;
+ if (!index_mail_parse_headers(mail))
+ return;
+ }
+
+ if (data->parts != NULL) {
+ i_assert(data->parts->next == NULL);
+ message_parse_from_parts(data->parts->children, data->stream,
+ parse_bodystructure_header,
+ mail->pool);
+ } else {
+ index_mail_parse_body(mail, FALSE);
+ }
+
+ dec = mail_cache_field_get_decision(mail->ibox->cache,
+ MAIL_CACHE_BODYSTRUCTURE);
+ if (field == MAIL_CACHE_BODYSTRUCTURE ||
+ ((dec & ~MAIL_CACHE_DECISION_FORCED) != MAIL_CACHE_DECISION_NO &&
+ mail_cache_field_exists(mail->trans->cache_view, data->seq,
+ MAIL_CACHE_BODYSTRUCTURE)) == 0) {
+ str = str_new(mail->pool, 128);
+ imap_bodystructure_write(data->parts, str, TRUE);
+ data->bodystructure = str_c(str);
+
+ if (dec !=
+ (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
+ mail_cache_add(mail->trans->cache_trans, data->seq,
+ MAIL_CACHE_BODYSTRUCTURE,
+ str_c(str), str_len(str)+1);
+ bodystructure_cached = TRUE;
+ }
+ }
+
+ dec = mail_cache_field_get_decision(mail->ibox->cache, MAIL_CACHE_BODY);
+ if (field == MAIL_CACHE_BODY ||
+ ((dec & ~MAIL_CACHE_DECISION_FORCED) != MAIL_CACHE_DECISION_NO &&
+ mail_cache_field_exists(mail->trans->cache_view, data->seq,
+ MAIL_CACHE_BODY)) == 0) {
+ str = str_new(mail->pool, 128);
+ imap_bodystructure_write(data->parts, str, FALSE);
+ data->body = str_c(str);
+
+ if (!bodystructure_cached && dec !=
+ (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
+ mail_cache_add(mail->trans->cache_trans, data->seq,
+ MAIL_CACHE_BODY,
+ str_c(str), str_len(str)+1);
+ }
+ }
+}
+
const char *index_mail_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;
- struct mail_cache *cache = mail->ibox->cache;
- enum mail_cache_field cache_field;
- char *str;
+ string_t *str;
switch (field) {
case MAIL_FETCH_IMAP_BODY:
- if ((data->cached_fields & MAIL_CACHE_BODY) &&
- data->body == NULL) {
- data->body = index_mail_get_cached_string(mail,
- MAIL_CACHE_BODY);
- }
if (data->body != NULL)
return data->body;
- /* fall through */
- case MAIL_FETCH_IMAP_BODYSTRUCTURE:
- if ((data->cached_fields & MAIL_CACHE_BODYSTRUCTURE) &&
- data->bodystructure == NULL) {
- data->bodystructure = index_mail_get_cached_string(mail,
- MAIL_CACHE_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();
+ /* 1) get BODY if it exists
+ 2) get it using BODYSTRUCTURE if it exists
+ 3) parse body structure, and save BODY/BODYSTRUCTURE
+ depending on what we want cached */
- if (data->body == NULL) {
- mail_cache_set_corrupted(cache,
- "Corrupted BODYSTRUCTURE");
- }
+ str = str_new(mail->pool, 128);
+ if (mail_cache_lookup_string_field(mail->trans->cache_view,
+ str, mail->data.seq,
+ MAIL_CACHE_BODY) > 0) {
+ data->body = str_c(str);
return data->body;
}
+ if (mail_cache_lookup_string_field(mail->trans->cache_view,
+ str, mail->data.seq,
+ MAIL_CACHE_BODYSTRUCTURE) > 0) {
+ data->bodystructure = str_c(str);
+ str_truncate(str, 0);
+
+ if (imap_body_parse_from_bodystructure(
+ data->bodystructure, str)) {
+ data->body = str_c(str);
+ return data->body;
+ }
- if (!data->bodystructure_header_parsed) {
- data->bodystructure_header_want = TRUE;
- if (!index_mail_parse_headers(mail))
- return NULL;
+ /* broken, continue.. */
+ data->bodystructure = NULL;
+ mail_cache_set_corrupted(mail->ibox->cache,
+ "Corrupted BODYSTRUCTURE for mail %u",
+ mail->mail.uid);
}
+ p_free(mail->pool, str);
- if (data->parts != NULL) {
- i_assert(data->parts->next == NULL);
- message_parse_from_parts(data->parts->children,
- data->stream,
- parse_bodystructure_header,
- mail->pool);
- } else {
- index_mail_parse_body(mail);
+ index_mail_parse_bodystructure(mail, MAIL_CACHE_BODY);
+ return data->body;
+ case MAIL_FETCH_IMAP_BODYSTRUCTURE:
+ if (data->bodystructure != NULL)
+ return data->bodystructure;
+
+ str = str_new(mail->pool, 128);
+ if (mail_cache_lookup_string_field(mail->trans->cache_view,
+ str, mail->data.seq,
+ MAIL_CACHE_BODYSTRUCTURE) > 0) {
+ data->bodystructure = str_c(str);
+ return data->bodystructure;
}
+ p_free(mail->pool, str);
- t_push();
- str = p_strdup(mail->pool, imap_bodystructure_parse_finish(
- data->parts, field == MAIL_FETCH_IMAP_BODYSTRUCTURE));
- t_pop();
-
- /* should never fail */
- i_assert(str != NULL);
-
- cache_field = field == MAIL_FETCH_IMAP_BODYSTRUCTURE ?
- MAIL_CACHE_BODYSTRUCTURE : MAIL_CACHE_BODY;
- index_mail_cache_add(mail, cache_field, str, strlen(str)+1);
-
- if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
- data->bodystructure = str;
- else
- data->body = str;
- return str;
+ index_mail_parse_bodystructure(mail, MAIL_CACHE_BODYSTRUCTURE);
+ return data->bodystructure;
case MAIL_FETCH_IMAP_ENVELOPE:
if (data->envelope != NULL)
return data->envelope;
index_mail_headers_close(mail);
}
-int index_mail_next(struct index_mail *mail,
- const struct mail_index_record *rec,
- uint32_t seq, int delay_open)
+int index_mail_next(struct index_mail *mail, uint32_t seq)
{
struct index_mail_data *data = &mail->data;
+ const struct mail_index_record *rec;
enum mail_cache_record_flag cache_flags;
- int ret, open_mail;
+
+ if (mail_index_lookup(mail->trans->trans_view, seq, &rec) < 0) {
+ mail_storage_set_index_error(mail->ibox);
+ return -1;
+ }
t_push();
memset(data, 0, sizeof(*data));
p_clear(mail->pool);
- data->cached_fields =
- mail_cache_get_fields(mail->trans->cache_view, seq);
- cache_flags = (data->cached_fields & MAIL_CACHE_INDEX_FLAGS) == 0 ? 0 :
- mail_cache_get_record_flags(mail->trans->cache_view, seq);
+ cache_flags = mail_cache_get_record_flags(mail->trans->cache_view, seq);
mail->mail.seq = seq;
mail->mail.uid = rec->uid;
get_cached_sent_date(mail, &data->sent_date);
/* see if we have to parse the message */
- open_mail = FALSE;
if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) &&
data->parts == NULL)
data->parse_header = TRUE;
data->bodystructure == NULL) {
if (data->parts == NULL)
data->parts = get_cached_parts(mail);
- open_mail = TRUE;
+ data->open_mail = TRUE;
data->parse_header = data->parts == NULL;
- data->bodystructure_header_want = TRUE;
+ data->bodystructure_header_want = TRUE;
} 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;
+ data->open_mail = TRUE;
data->parse_header = data->parts == NULL;
data->bodystructure_header_want = TRUE;
} else if (mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY))
- open_mail = TRUE;
+ data->open_mail = TRUE;
index_mail_headers_init_next(mail);
- if ((open_mail || data->parse_header) && !delay_open) {
- if (mail->mail.get_stream(&mail->mail, NULL, NULL) == NULL)
- ret = data->deleted ? 0 : -1;
- else
- ret = 1;
- } else {
- if (mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) {
- /* check this only after open_mail() */
- data->received_date =
- index_mail_get_cached_received_date(mail);
- }
- ret = 1;
- }
-
if ((mail->wanted_fields & MAIL_FETCH_DATE) &&
data->sent_date.time == (time_t)-1)
data->save_sent_date = TRUE;
data->save_envelope = TRUE;
t_pop();
- return ret;
+ return 0;
}
void index_mail_deinit(struct index_mail *mail)
time_t date, received_date;
uoff_t size;
- enum mail_cache_field cached_fields;
struct mail_sent_date sent_date;
buffer_t *headers;
unsigned int header_data_cached_partial:1;
unsigned int header_fully_parsed:1;
unsigned int header_save:1;
+ unsigned int open_mail:1;
};
struct index_mail {
struct index_mail *mail,
enum mail_fetch_field wanted_fields,
const char *const wanted_headers[]);
-int index_mail_next(struct index_mail *mail,
- const struct mail_index_record *rec,
- uint32_t seq, int delay_open);
+int index_mail_next(struct index_mail *mail, uint32_t seq);
void index_mail_deinit(struct index_mail *mail);
void index_mail_parse_header_init(struct index_mail *mail,
struct index_mail *mail);
void index_mail_cache_transaction_begin(struct index_mail *mail);
-void index_mail_cache_add(struct index_mail *mail, enum mail_cache_field field,
- const void *data, size_t size);
-
int index_mail_parse_headers(struct index_mail *mail);
void index_mail_headers_init(struct index_mail *mail);
struct mail *index_storage_search_next(struct mail_search_context *_ctx)
{
struct index_search_context *ctx = (struct index_search_context *)_ctx;
- const struct mail_index_record *rec;
int ret;
ret = 0;
while (ctx->seq1 <= ctx->seq2) {
- if (mail_index_lookup(ctx->view, ctx->seq1, &rec) < 0) {
+ if (index_mail_next(&ctx->imail, ctx->seq1++) < 0) {
ctx->failed = TRUE;
- mail_storage_set_index_error(ctx->ibox);
return NULL;
}
- ret = index_mail_next(&ctx->imail, rec, ctx->seq1++, TRUE);
- if (ret < 0)
- break;
-
t_push();
ret = search_match_next(ctx);
t_pop();
/* Copyright (C) 2002-2003 Timo Sirainen */
#include "lib.h"
+#include "buffer.h"
#include "ioloop.h"
#include "mail-index.h"
#include "index-storage.h"
destroy_unrefed(TRUE);
}
-static enum mail_cache_field get_cache_fields(const char *fields)
+static void set_cache_fields(const char *fields,
+ enum mail_cache_decision_type dest[32],
+ enum mail_cache_decision_type dec)
{
- static enum mail_cache_field field_masks[] = {
+ static enum mail_cache_field field_enums[] = {
MAIL_CACHE_SENT_DATE,
MAIL_CACHE_RECEIVED_DATE,
MAIL_CACHE_VIRTUAL_FULL_SIZE,
MAIL_CACHE_BODY,
MAIL_CACHE_BODYSTRUCTURE,
- MAIL_CACHE_MESSAGEPART,
+ MAIL_CACHE_MESSAGEPART
};
static const char *field_names[] = {
"sent_date",
};
const char *const *arr;
- enum mail_cache_field ret;
int i;
if (fields == NULL || *fields == '\0')
- return 0;
+ return;
- ret = 0;
for (arr = t_strsplit_spaces(fields, " ,"); *arr != NULL; arr++) {
for (i = 0; field_names[i] != NULL; i++) {
if (strcasecmp(field_names[i], *arr) == 0) {
- ret |= field_masks[i];
+ dest[field_enums[i]] = dec;
break;
}
}
*arr);
}
}
-
- return ret;
-}
-
-static enum mail_cache_field get_default_cache_fields(void)
-{
- static enum mail_cache_field ret = 0;
- static int ret_set = FALSE;
-
- if (ret_set)
- return ret;
-
- ret = get_cache_fields(getenv("MAIL_CACHE_FIELDS"));
- ret_set = TRUE;
- return ret;
}
-static enum mail_cache_field get_never_cache_fields(void)
+static const enum mail_cache_decision_type *get_default_cache_decisions(void)
{
- static enum mail_cache_field ret = 0;
- static int ret_set = FALSE;
-
- if (ret_set)
- return ret;
-
- ret = get_cache_fields(getenv("MAIL_NEVER_CACHE_FIELDS"));
- ret_set = TRUE;
- return ret;
+ static enum mail_cache_decision_type dec[32];
+ static int dec_set = FALSE;
+
+ if (dec_set)
+ return dec;
+
+ memset(dec, 0, sizeof(dec));
+ set_cache_fields(getenv("MAIL_CACHE_FIELDS"), dec,
+ MAIL_CACHE_DECISION_TEMP);
+ set_cache_fields(getenv("MAIL_NEVER_CACHE_FIELDS"), dec,
+ MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED);
+ return dec;
}
void index_storage_lock_notify(struct index_mailbox *ibox,
ibox->cache = mail_index_get_cache(index);
mail_cache_set_defaults(ibox->cache,
- get_default_cache_fields(),
- get_never_cache_fields());
-
+ get_default_cache_decisions());
ibox->view = mail_index_view_open(index);
return ibox;
} while (0);
if (data->received_date != (time_t)-1)
return data->received_date;
+ if (data->open_mail && data->stream == NULL) {
+ /* we're going to open the mail anyway */
+ (void)_mail->get_stream(_mail, NULL, NULL);
+ }
+
if (data->stream != NULL) {
fd = i_stream_get_fd(data->stream);
i_assert(fd != -1);
}
data->received_date = st.st_mtime;
- index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
- &data->received_date, sizeof(data->received_date));
+ mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+ MAIL_CACHE_RECEIVED_DATE,
+ &data->received_date, sizeof(data->received_date));
return data->received_date;
}
}
if (*p == ':' || *p == ',' || *p == '\0') {
- index_mail_cache_add(mail, MAIL_CACHE_VIRTUAL_FULL_SIZE,
- &virtual_size,
- sizeof(virtual_size));
+ mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+ MAIL_CACHE_VIRTUAL_FULL_SIZE,
+ &virtual_size, sizeof(virtual_size));
return virtual_size;
}
}
if (data->stream == NULL) {
data->stream = maildir_open_mail(mail->ibox, mail->mail.uid,
&deleted);
- if (data->stream == NULL)
+ if (data->stream == NULL) {
+ data->deleted = deleted;
return NULL;
+ }
}
return index_mail_init_stream(mail, hdr_size, body_size);
t_pop();
if (mail_r != NULL) {
- const struct mail_index_record *rec;
-
- if (mail_index_lookup(t->ictx.trans_view, seq, &rec) < 0)
- return -1;
- if (index_mail_next(&ctx->mail, rec, seq, FALSE) <= 0)
+ if (index_mail_next(&ctx->mail, seq) < 0)
return -1;
*mail_r = &ctx->mail.mail;
}
uint64_t offset;
int ret;
+ if (mail->data.deleted)
+ return 0;
+
if (ibox->mbox_lock_type == F_UNLCK) {
if (mbox_sync(ibox, FALSE, FALSE, TRUE) < 0)
return -1;
if (ret <= 0) {
if (ret < 0)
mail_storage_set_index_error(ibox);
- return -1;
+ else
+ mail->data.deleted = TRUE;
+ return ret;
}
offset = *((const uint64_t *)data);
mail_index_mark_corrupted(ibox->index);
return -1;
}
- return 0;
+ return 1;
}
static const struct mail_full_flags *mbox_mail_get_flags(struct mail *_mail)
if (data->received_date != (time_t)-1)
return data->received_date;
- if (mbox_mail_seek(mail) < 0)
+ if (mbox_mail_seek(mail) <= 0)
return (time_t)-1;
data->received_date =
istream_raw_mbox_get_received_time(mail->ibox->mbox_stream);
data->received_date = 0;
}
- index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
- &data->received_date,
- sizeof(data->received_date));
+ mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+ MAIL_CACHE_RECEIVED_DATE,
+ &data->received_date, sizeof(data->received_date));
return data->received_date;
}
struct index_mail *mail = (struct index_mail *)_mail;
if (field == MAIL_FETCH_FROM_ENVELOPE) {
- if (mbox_mail_seek(mail) < 0)
+ if (mbox_mail_seek(mail) <= 0)
return NULL;
return istream_raw_mbox_get_sender(mail->ibox->mbox_stream);
uoff_t offset;
if (data->stream == NULL) {
- if (mbox_mail_seek(mail) < 0)
+ if (mbox_mail_seek(mail) <= 0)
return NULL;
- // FIXME: need to hide the headers
raw_stream = mail->ibox->mbox_stream;
offset = istream_raw_mbox_get_header_offset(raw_stream);
raw_stream = i_stream_create_limit(default_pool, raw_stream,
t_pop();
if (mail_r != NULL) {
- const struct mail_index_record *rec;
-
- if (mail_index_lookup(t->ictx.trans_view, seq, &rec) < 0)
- return -1;
- if (index_mail_next(&ctx->mail, rec, seq, FALSE) <= 0)
+ if (index_mail_next(&ctx->mail, seq) < 0)
return -1;
*mail_r = &ctx->mail.mail;
}