Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-index/mail-index-update.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children | 1b34ec11fff8 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "ioloop.h" #include "rfc822-date.h" #include "rfc822-tokenize.h" #include "message-parser.h" #include "message-size.h" #include "imap-envelope.h" #include "imap-bodystructure.h" #include "mail-index.h" #include "mail-index-data.h" struct _MailIndexUpdate { Pool pool; MailIndex *index; MailIndexRecord *rec; unsigned int updated_fields; char *fields[FIELD_TYPE_MAX_BITS]; unsigned int field_sizes[FIELD_TYPE_MAX_BITS]; unsigned int field_extra_sizes[FIELD_TYPE_MAX_BITS]; }; MailIndexUpdate *mail_index_update_begin(MailIndex *index, MailIndexRecord *rec) { Pool pool; MailIndexUpdate *update; i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE); pool = pool_create("MailIndexUpdate", 1024, FALSE); update = p_new(pool, MailIndexUpdate, 1); update->pool = pool; update->index = index; update->rec = rec; return update; } static int mail_field_get_index(MailField field) { unsigned int i, mask; for (i = 0, mask = 1; i < FIELD_TYPE_MAX_BITS; i++, mask <<= 1) { if (field == mask) return i; } return -1; } static int have_new_fields(MailIndexUpdate *update) { MailField field; if (update->rec->cached_fields == 0) { /* new record */ return TRUE; } for (field = 1; field != FIELD_TYPE_LAST; field <<= 1) { if ((update->updated_fields & field) && (update->rec->cached_fields & field) == 0) return TRUE; } return FALSE; } static int have_too_large_fields(MailIndexUpdate *update) { MailIndexDataRecord *rec; int index; /* start from the first data field - it's required to exist */ rec = mail_index_data_lookup(update->index->data, update->rec, 1); while (rec != NULL) { if (rec->field & update->updated_fields) { /* field was changed */ index = mail_field_get_index(rec->field); i_assert(index >= 0); if (update->field_sizes[index] >= rec->full_field_size) return TRUE; } rec = mail_index_data_next(update->index->data, update->rec, rec); } return FALSE; } /* Append all the data at the end of the data file and update the index's data position */ static int update_by_append(MailIndexUpdate *update) { MailIndexDataRecord *rec, *destrec; MailField field; off_t fpos; void *mem; unsigned int max_size, pos; int i; /* allocate the old size + also the new size of all changed or added fields. this is more than required, but it's much easier than calculating the exact size. */ max_size = update->rec->data_size; for (i = 0; i < FIELD_TYPE_MAX_BITS; i++) { max_size += sizeof(MailIndexDataRecord)-sizeof(rec->data) + update->field_sizes[i] + update->field_extra_sizes[i] + MEM_ALIGN_SIZE-1; } mem = p_malloc(update->pool, max_size); pos = 0; rec = mail_index_data_lookup(update->index->data, update->rec, 1); for (i = 0, field = 1; field != FIELD_TYPE_LAST; i++, field <<= 1) { destrec = (MailIndexDataRecord *) ((char *) mem + pos); if (update->fields[i] != NULL) { /* value was modified - use it */ destrec->full_field_size = update->field_sizes[i] + update->field_extra_sizes[i]; memcpy(destrec->data, update->fields[i], update->field_sizes[i]); } else if (rec != NULL) { /* use the old value */ destrec->full_field_size = rec->full_field_size; memcpy(destrec->data, rec->data, rec->full_field_size); } else { /* the field doesn't exist, jump to next */ continue; } /* memory alignment fix */ destrec->full_field_size = MEM_ALIGN(destrec->full_field_size); destrec->field = field; pos += DATA_RECORD_SIZE(destrec); if (rec != NULL && rec->field == field) { rec = mail_index_data_next(update->index->data, update->rec, rec); } } i_assert(pos <= max_size); /* append the data at the end of the data file */ fpos = mail_index_data_append(update->index->data, mem, pos); if (fpos == (off_t)-1) return FALSE; /* update index file position - it's mmap()ed so it'll be writte into disk when index is unlocked. */ update->rec->data_position = fpos; update->rec->data_size = pos; return TRUE; } /* Replace the modified fields in the file - assumes there's enough space to do it */ static void update_by_replace(MailIndexUpdate *update) { MailIndexDataRecord *rec; int index; /* start from the first data field - it's required to exist */ rec = mail_index_data_lookup(update->index->data, update->rec, 1); while (rec != NULL) { if (rec->field & update->updated_fields) { /* field was changed */ index = mail_field_get_index(rec->field); i_assert(index >= 0); i_assert(update->field_sizes[index] < rec->full_field_size); strcpy(rec->data, update->fields[index]); } rec = mail_index_data_next(update->index->data, update->rec, rec); } } int mail_index_update_end(MailIndexUpdate *update) { int failed; i_assert(update->index->lock_type == MAIL_LOCK_EXCLUSIVE); /* if any of the fields were newly added, or have grown larger than their old max. size, we need to move the record to end of file. */ if (have_new_fields(update) || have_too_large_fields(update)) failed = !update_by_append(update); else { update_by_replace(update); failed = FALSE; } if (!failed) { /* update cached fields mask */ update->rec->cached_fields |= update->updated_fields; } pool_unref(update->pool); return !failed; } void mail_index_update_field(MailIndexUpdate *update, MailField field, const char *value, unsigned int extra_space) { unsigned int size; int index; index = mail_field_get_index(field); i_assert(index >= 0); size = strlen(value)+1; update->updated_fields |= field; update->field_sizes[index] = size; update->field_extra_sizes[index] = extra_space; update->fields[index] = p_malloc(update->pool, size); memcpy(update->fields[index], value, size); } static MailField mail_header_get_field(const char *str, unsigned int len) { if (len == 10 && strncasecmp(str, "Message-ID", 10) == 0) return FIELD_TYPE_MESSAGEID; if (len == 7 && strncasecmp(str, "Subject", 7) == 0) return FIELD_TYPE_SUBJECT; if (len == 4 && strncasecmp(str, "From", 4) == 0) return FIELD_TYPE_FROM; if (len == 2 && strncasecmp(str, "To", 2) == 0) return FIELD_TYPE_TO; if (len == 2 && strncasecmp(str, "Cc", 2) == 0) return FIELD_TYPE_CC; if (len == 3 && strncasecmp(str, "Bcc", 3) == 0) return FIELD_TYPE_BCC; return 0; } static const char *field_get_value(const char *value, unsigned int len) { char *ret, *p; unsigned int i; ret = t_malloc(len+1); /* compress the long headers (remove \r?\n before space or tab) */ for (i = 0, p = ret; i < len; i++) { if (value[i] == '\r' && i+1 != len && value[i+1] == '\n') i++; if (value[i] == '\n') { i_assert(IS_LWSP(value[i+1])); } else { *p++ = value[i]; } } *p = '\0'; return ret; } typedef struct { MailIndexUpdate *update; Pool envelope_pool; MessagePartEnvelopeData *envelope; MessageHeaderFunc header_func; void *user_data; } HeaderUpdateData; static void update_header_func(MessagePart *part, const char *name, unsigned int name_len, const char *value, unsigned int value_len, void *user_data) { HeaderUpdateData *data = user_data; MailField field; const char *str; if (part != NULL && part->parent != NULL) return; if (data->header_func != NULL) { data->header_func(part, name, name_len, value, value_len, data->user_data); } if (name_len == 4 && strncasecmp(name, "Date", 4) == 0) { /* date is stored into index record itself */ str = field_get_value(value, value_len); if (!rfc822_parse_date(str, &data->update->rec->sent_date)) data->update->rec->sent_date = ioloop_time; return; } /* see if we can do anything with this field */ field = mail_header_get_field(name, name_len); if (field != 0) { /* do we want to store this? */ if (data->update->index->header->cache_fields & field) { str = field_get_value(value, value_len); data->update->index->update_field(data->update, field, str, 0); } } if (data->update->index->header->cache_fields & FIELD_TYPE_ENVELOPE) { if (data->envelope_pool == NULL) { data->envelope_pool = pool_create("index envelope", 2048, FALSE); } imap_envelope_parse_header(data->envelope_pool, &data->envelope, t_strndup(name, name_len), value, value_len); } } void mail_index_update_headers(MailIndexUpdate *update, const char *msg, size_t size, MessageHeaderFunc header_func, void *user_data) { HeaderUpdateData data; MailField cache_fields; MessagePart *part; MessageSize hdr_size, body_size; Pool pool; const char *value; data.update = update; data.envelope_pool = NULL; data.envelope = NULL; data.header_func = header_func; data.user_data = user_data; cache_fields = update->index->header->cache_fields; if ((cache_fields & (FIELD_TYPE_BODY|FIELD_TYPE_BODYSTRUCTURE)) != 0) { /* for body / bodystructure, we need need to fully parse the message */ pool = pool_create("index message parser", 2048, FALSE); part = message_parse(pool, msg, size, update_header_func, &data); /* update our sizes */ update->rec->header_size = part->header_size.physical_size; update->rec->body_size = part->body_size.physical_size; update->rec->full_virtual_size = part->header_size.virtual_size + part->body_size.virtual_size; if (cache_fields & FIELD_TYPE_BODY) { t_push(); value = imap_part_get_bodystructure(pool, &part, msg, size, FALSE); update->index->update_field(update, FIELD_TYPE_BODY, value, 0); t_pop(); } if (cache_fields & FIELD_TYPE_BODYSTRUCTURE) { t_push(); value = imap_part_get_bodystructure(pool, &part, msg, size, TRUE); update->index->update_field(update, FIELD_TYPE_BODYSTRUCTURE, value, 0); t_pop(); } pool_unref(pool); } else { message_parse_header(NULL, msg, size, &hdr_size, update_header_func, &data); update->rec->header_size = hdr_size.physical_size; update->rec->body_size = size - hdr_size.physical_size; if (update->rec->full_virtual_size == 0) { /* we need to calculate virtual size of the body as well */ message_get_body_size(msg + hdr_size.physical_size, size - hdr_size.physical_size, &body_size); update->rec->full_virtual_size = hdr_size.virtual_size + body_size.virtual_size; } } if (data.envelope != NULL) { t_push(); value = imap_envelope_get_part_data(data.envelope); update->index->update_field(update, FIELD_TYPE_ENVELOPE, value, 0); t_pop(); pool_unref(data.envelope_pool); } }