Mercurial > dovecot > original-hg > dovecot-1.2
changeset 408:e057845d94ca HEAD
Dropped sent_time and alignment from MailIndexRecord. SEARCH can now use
cached ENVELOPE data to search FROM, TO, CC, BCC, SUBJECT, HEADER MESSAGE-ID
and HEADER IN-REPLY-TO. They're never cached anymore now. Also SEARCH SENT*
had to be changed to use ENVELOPE (or fallback to Date-header parsing)
because sent_time was removed.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 12 Oct 2002 01:33:54 +0300 |
parents | 6edfb92319cf |
children | 849f3846212a |
files | src/lib-imap/imap-envelope.c src/lib-imap/imap-envelope.h src/lib-imap/imap-parser.c src/lib-index/mail-index-update.c src/lib-index/mail-index.h src/lib-mail/rfc822-date.c src/lib-mail/rfc822-date.h src/lib-storage/index/index-fetch.c src/lib-storage/index/index-search.c src/lib-storage/mail-search.c src/lib-storage/mail-search.h |
diffstat | 11 files changed, 323 insertions(+), 130 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-imap/imap-envelope.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-imap/imap-envelope.c Sat Oct 12 01:33:54 2002 +0300 @@ -1,8 +1,10 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" +#include "iobuffer.h" #include "temp-string.h" #include "rfc822-address.h" +#include "imap-parser.h" #include "imap-envelope.h" #include "imap-quote.h" @@ -116,3 +118,139 @@ imap_envelope_write_part_data(data, str); return str->str; } + +static int imap_address_arg_append(ImapArg *arg, TempString *str, int *in_group) +{ + ImapArgList *list; + const char *args[4]; + int i; + + if (arg->type != IMAP_ARG_LIST) + return FALSE; + list = arg->data.list; + + /* we require 4 arguments, strings or NILs */ + for (i = 0; i < 4; i++) { + if (list == NULL) + return FALSE; + + if (list->arg.type == IMAP_ARG_NIL) + args[i] = NULL; + else if (list->arg.type == IMAP_ARG_STRING) + args[i] = list->arg.data.str; + else + return FALSE; + } + + if (str->len > 0) + t_string_append(str, ", "); + + if (*in_group) { + if (args[0] == NULL && args[1] == NULL && + args[2] == NULL && args[3] == NULL) { + /* end of group */ + t_string_append_c(str, ';'); + *in_group = FALSE; + return TRUE; + } + } else { + if (args[0] == NULL && args[1] == NULL && + args[2] != NULL && args[3] == NULL) { + /* beginning of group */ + t_string_append(str, args[2]); + t_string_append(str, ": "); + *in_group = TRUE; + return TRUE; + } + } + + /* name <@route:mailbox@domain> */ + if (args[0] != NULL) { + t_string_append(str, args[0]); + t_string_append_c(str, ' '); + } + + t_string_append_c(str, '<'); + if (args[1] != NULL) { + t_string_append_c(str, '@'); + t_string_append(str, args[1]); + t_string_append_c(str, ':'); + } + if (args[2] != NULL) + t_string_append(str, args[2]); + if (args[3] != NULL) { + t_string_append_c(str, '@'); + t_string_append(str, args[3]); + } + t_string_append_c(str, '>'); + return TRUE; +} + +static const char *imap_envelope_parse_address(ImapArg *arg) +{ + ImapArgList *list; + TempString *str; + int in_group; + + if (arg->type != IMAP_ARG_LIST) + return NULL; + + in_group = FALSE; + str = t_string_new(128); + for (list = arg->data.list; list != NULL; list = list->next) { + if (!imap_address_arg_append(&list->arg, str, &in_group)) + return NULL; + } + + return str->str; +} + +static const char * +imap_envelope_parse_arg(ImapArg *arg, ImapEnvelopeField field, + const char *envelope) +{ + const char *value; + + if (arg->type == IMAP_ARG_NIL) + return ""; + + if (field >= IMAP_ENVELOPE_FROM && field <= IMAP_ENVELOPE_BCC) + value = imap_envelope_parse_address(arg); + else if (arg->type == IMAP_ARG_STRING || arg->type == IMAP_ARG_ATOM) + value = t_strdup(arg->data.str); + else + value = NULL; + + if (value == NULL) { + i_error("Invalid field %u in IMAP envelope: %s", + field, envelope); + } + + return value; +} + +const char *imap_envelope_parse(const char *envelope, ImapEnvelopeField field) +{ + IOBuffer *inbuf; + ImapParser *parser; + ImapArg *args; + const char *value; + int ret; + + i_assert(field < IMAP_ENVELOPE_FIELDS); + + inbuf = io_buffer_create_from_data(envelope, strlen(envelope), + data_stack_pool); + parser = imap_parser_create(inbuf, NULL); + + ret = imap_parser_read_args(parser, field, 0, &args); + if (ret < 0) + i_error("Error parsing IMAP envelope: %s", envelope); + + value = ret < (int)field ? NULL : + imap_envelope_parse_arg(&args[field], field, envelope); + + imap_parser_destroy(parser); + io_buffer_unref(inbuf); + return value; +}
--- a/src/lib-imap/imap-envelope.h Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-imap/imap-envelope.h Sat Oct 12 01:33:54 2002 +0300 @@ -3,12 +3,35 @@ typedef struct _MessagePartEnvelopeData MessagePartEnvelopeData; +typedef enum { + /* NOTE: in the same order as listed in ENVELOPE */ + IMAP_ENVELOPE_DATE = 0, + IMAP_ENVELOPE_SUBJECT, + IMAP_ENVELOPE_FROM, + IMAP_ENVELOPE_SENDER, + IMAP_ENVELOPE_REPLY_TO, + IMAP_ENVELOPE_TO, + IMAP_ENVELOPE_CC, + IMAP_ENVELOPE_BCC, + IMAP_ENVELOPE_IN_REPLY_TO, + IMAP_ENVELOPE_MESSAGE_ID, + + IMAP_ENVELOPE_FIELDS +} ImapEnvelopeField; + +/* Update envelope data based from given header field */ void imap_envelope_parse_header(Pool pool, MessagePartEnvelopeData **data, const char *name, const char *value, size_t value_len); +/* Write envelope to given string */ void imap_envelope_write_part_data(MessagePartEnvelopeData *data, TempString *str); +/* Return envelope. */ const char *imap_envelope_get_part_data(MessagePartEnvelopeData *data); +/* Parse envelope and return specified field unquoted, or NULL if error + occured. NILs are returned as "". */ +const char *imap_envelope_parse(const char *envelope, ImapEnvelopeField field); + #endif
--- a/src/lib-imap/imap-parser.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-imap/imap-parser.c Sat Oct 12 01:33:54 2002 +0300 @@ -285,8 +285,10 @@ return FALSE; } - io_buffer_send(parser->outbuf, "+ OK\r\n", 6); - io_buffer_send_flush(parser->outbuf); + if (parser->outbuf != NULL) { + io_buffer_send(parser->outbuf, "+ OK\r\n", 6); + io_buffer_send_flush(parser->outbuf); + } } parser->cur_type = ARG_PARSE_LITERAL_DATA;
--- a/src/lib-index/mail-index-update.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-index/mail-index-update.c Sat Oct 12 01:33:54 2002 +0300 @@ -170,10 +170,9 @@ /* corrupted data file - old value had a field larger than expected */ index_set_corrupted(update->index, - "full_field_size points outside " - "data_size (field %d?)", - update->index->filepath, - rec == NULL ? -1 : (int)rec->field); + "full_field_size points outside data_size " + "(field %u?)", update->index->filepath, + rec == NULL ? 0 : rec->field); return FALSE; } memcpy(destrec->data, src, src_size); @@ -197,7 +196,7 @@ if (fpos == 0) return FALSE; - /* update index file position - it's mmap()ed so it'll be writte + /* update index file position - it's mmap()ed so it'll be written into disk when index is unlocked. */ update->rec->data_position = fpos; update->rec->data_size = pos; @@ -283,45 +282,6 @@ update_field_full(update, field, value, size, 0); } -static MailField mail_header_get_field(const char *str, size_t len) -{ - 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, size_t len) -{ - char *ret, *p; - size_t 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; @@ -337,30 +297,11 @@ void *context) { HeaderUpdateContext *ctx = context; - MailField field; - const char *str; if (part != NULL && part->parent != NULL) return; - 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, &ctx->update->rec->sent_date)) - ctx->update->rec->sent_date = ioloop_time; - } - /* 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 (ctx->update->index->header->cache_fields & field) { - str = field_get_value(value, value_len); - ctx->update->index->update_field(ctx->update, - field, str, 0); - } - } - if (ctx->update->index->header->cache_fields & FIELD_TYPE_ENVELOPE) { if (ctx->envelope_pool == NULL) { ctx->envelope_pool = @@ -372,7 +313,6 @@ value, value_len); } - /* keep this last, it may break the parameter data by moving mmaping */ if (ctx->header_func != NULL) { ctx->header_func(part, name, name_len, value, value_len, ctx->context);
--- a/src/lib-index/mail-index.h Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-index/mail-index.h Sat Oct 12 01:33:54 2002 +0300 @@ -34,21 +34,16 @@ Location field is a good first field anyway, it's the one most often needed. With maildir format, it's the file name and with - mbox format it's the file position as a string. */ + mbox format it's the file position. */ FIELD_TYPE_LOCATION = 0x0001, FIELD_TYPE_ENVELOPE = 0x0002, FIELD_TYPE_BODY = 0x0004, FIELD_TYPE_BODYSTRUCTURE = 0x0008, - FIELD_TYPE_FROM = 0x0010, - FIELD_TYPE_TO = 0x0020, - FIELD_TYPE_CC = 0x0040, - FIELD_TYPE_BCC = 0x0080, - FIELD_TYPE_SUBJECT = 0x0100, - FIELD_TYPE_MD5 = 0x0200, - FIELD_TYPE_MESSAGEPART = 0x0400, + FIELD_TYPE_MD5 = 0x0010, + FIELD_TYPE_MESSAGEPART = 0x0020, - FIELD_TYPE_LAST = 0x0800, - FIELD_TYPE_MAX_BITS = 11 + FIELD_TYPE_LAST = 0x0040, + FIELD_TYPE_MAX_BITS = 6 } MailField; typedef enum { @@ -139,18 +134,15 @@ unsigned int uid; unsigned int msg_flags; /* MailFlags */ time_t internal_date; - time_t sent_date; - - uoff_t header_size; - uoff_t body_size; unsigned int index_flags; /* MailIndexMailFlags */ - unsigned int cached_fields; + unsigned int cached_fields; /* MailField */ + unsigned int data_size; uoff_t data_position; - unsigned int data_size; - unsigned int alignment; + uoff_t header_size; /* 0 if not known yet */ + uoff_t body_size; /* if header_size == 0, the size of full message */ }; struct _MailIndexDataRecord {
--- a/src/lib-mail/rfc822-date.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-mail/rfc822-date.c Sat Oct 12 01:33:54 2002 +0300 @@ -97,12 +97,11 @@ return ret; } -int rfc822_parse_date(const char *str, time_t *time) +int rfc822_parse_date(const char *str, time_t *time, int *timezone_offset) { struct tm tm; const Rfc822Token *tokens, *tok; size_t i; - int zone_offset; if (str == NULL || *str == '\0') return FALSE; @@ -198,14 +197,13 @@ if (tok == NULL || tok->token != 'A') return FALSE; - zone_offset = parse_timezone(tok->ptr, tok->len); + *timezone_offset = parse_timezone(tok->ptr, tok->len); tm.tm_isdst = -1; *time = mktime(&tm); if (*time < 0) return FALSE; - *time -= zone_offset * 60; return TRUE; }
--- a/src/lib-mail/rfc822-date.h Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-mail/rfc822-date.h Sat Oct 12 01:33:54 2002 +0300 @@ -1,7 +1,12 @@ #ifndef __RFC822_DATE #define __RFC822_DATE -int rfc822_parse_date(const char *str, time_t *time); +/* Parses RFC822 date/time string. time_t is filled with the date without + any timezone changes. timezone_offset is filled with the timezone's + difference to UTC in minutes. */ +int rfc822_parse_date(const char *str, time_t *time, int *timezone_offset); + +/* Create RFC822 date/time string from given time in local timezone. */ const char *rfc822_to_date(time_t time); #endif
--- a/src/lib-storage/index/index-fetch.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-storage/index/index-fetch.c Sat Oct 12 01:33:54 2002 +0300 @@ -226,12 +226,17 @@ mail_cache_context = index_msgcache_get_context(ctx->index, rec); - virtual_header_size = - (rec->index_flags & INDEX_MAIL_FLAG_BINARY_HEADER) ? - rec->header_size : 0; - virtual_body_size = - (rec->index_flags & INDEX_MAIL_FLAG_BINARY_BODY) ? - rec->body_size : 0; + if (rec->header_size == 0) { + virtual_header_size = 0; + virtual_body_size = 0; + } else { + virtual_header_size = + (rec->index_flags & INDEX_MAIL_FLAG_BINARY_HEADER) ? + rec->header_size : 0; + virtual_body_size = + (rec->index_flags & INDEX_MAIL_FLAG_BINARY_BODY) ? + rec->body_size : 0; + } imap_msgcache_open(ctx->cache, rec->uid, fields, virtual_header_size, virtual_body_size,
--- a/src/lib-storage/index/index-search.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-storage/index/index-search.c Sat Oct 12 01:33:54 2002 +0300 @@ -4,7 +4,10 @@ #include "iobuffer.h" #include "mmap-util.h" #include "rfc822-tokenize.h" +#include "rfc822-date.h" +#include "message-size.h" #include "imap-date.h" +#include "imap-envelope.h" #include "index-storage.h" #include "mail-index-util.h" #include "mail-modifylog.h" @@ -155,19 +158,6 @@ return FALSE; return rec->internal_date >= t; - case SEARCH_SENTBEFORE: - if (!imap_parse_date(value, &t)) - return FALSE; - return rec->sent_date < t; - case SEARCH_SENTON: - if (!imap_parse_date(value, &t)) - return FALSE; - return rec->sent_date >= t && rec->sent_date < t + 3600*24; - case SEARCH_SENTSINCE: - if (!imap_parse_date(value, &t)) - return FALSE; - return rec->sent_date >= t; - /* sizes, only with fastscanning */ case SEARCH_SMALLER: if (!mail_index_get_virtual_size(ibox->index, rec, TRUE, &size)) @@ -201,21 +191,42 @@ } } -static int match_field(MailIndex *index, MailIndexRecord *rec, - MailField field, const char *value) +static int search_sent(MailSearchArgType type, const char *value, + const char *sent_value) { - const char *field_value; - size_t i, value_len; + time_t search_time, sent_time; + int timezone_offset; + + if (!imap_parse_date(value, &search_time)) + return 0; + + /* NOTE: RFC2060 doesn't specify if timezones should affect + matching, so we ignore them. */ + if (!rfc822_parse_date(sent_value, &sent_time, &timezone_offset)) + return 0; - field_value = index->lookup_field(index, rec, field); - if (field_value == NULL) - return -1; + switch (type) { + case SEARCH_SENTBEFORE: + return sent_time < search_time; + case SEARCH_SENTON: + return sent_time >= search_time && + sent_time < search_time + 3600*24; + case SEARCH_SENTSINCE: + return sent_time >= search_time; + default: + i_assert(0); + } +} - /* note: value is already uppercased */ - value_len = strlen(value); - for (i = 0; field_value[i] != '\0'; i++) { - if (value[0] == i_toupper(field_value[i]) && - strncasecmp(value, field_value+i, value_len) == 0) +static int search_substr(const char *haystack, const char *needle) +{ + size_t i, needle_len; + + /* note: needle is already uppercased */ + needle_len = strlen(needle); + for (i = 0; haystack[i] != '\0'; i++) { + if (needle[0] == i_toupper(haystack[i]) && + strncasecmp(needle, haystack+i, needle_len) == 0) return 1; } @@ -226,20 +237,64 @@ static int search_arg_match_cached(MailIndex *index, MailIndexRecord *rec, MailSearchArgType type, const char *value) { + ImapEnvelopeField env_field; + const char *envelope, *field; + int ret; + switch (type) { + case SEARCH_SENTBEFORE: + case SEARCH_SENTON: + case SEARCH_SENTSINCE: + env_field = IMAP_ENVELOPE_DATE; + break; + case SEARCH_FROM: - return match_field(index, rec, FIELD_TYPE_FROM, value); + env_field = IMAP_ENVELOPE_FROM; + break; case SEARCH_TO: - return match_field(index, rec, FIELD_TYPE_TO, value); + env_field = IMAP_ENVELOPE_TO; + break; case SEARCH_CC: - return match_field(index, rec, FIELD_TYPE_CC, value); + env_field = IMAP_ENVELOPE_CC; + break; case SEARCH_BCC: - return match_field(index, rec, FIELD_TYPE_BCC, value); + env_field = IMAP_ENVELOPE_BCC; + break; case SEARCH_SUBJECT: - return match_field(index, rec, FIELD_TYPE_SUBJECT, value); + env_field = IMAP_ENVELOPE_SUBJECT; + break; + + case SEARCH_IN_REPLY_TO: + env_field = IMAP_ENVELOPE_IN_REPLY_TO; + break; + case SEARCH_MESSAGE_ID: + env_field = IMAP_ENVELOPE_MESSAGE_ID; + break; default: return -1; } + + t_push(); + + /* get field from hopefully cached envelope */ + envelope = index->lookup_field(index, rec, FIELD_TYPE_ENVELOPE); + field = envelope == NULL ? NULL : + imap_envelope_parse(envelope, env_field); + + if (field == NULL) + ret = -1; + else { + switch (type) { + case SEARCH_SENTBEFORE: + case SEARCH_SENTON: + case SEARCH_SENTSINCE: + ret = search_sent(type, value, field); + default: + ret = search_substr(field, value); + } + } + t_pop(); + return ret; } static void search_cached_arg(MailSearchArg *arg, void *context) @@ -351,6 +406,17 @@ /* first check that the field name matches to argument. */ switch (arg->type) { + case SEARCH_SENTBEFORE: + case SEARCH_SENTON: + case SEARCH_SENTSINCE: + /* date is handled differently than others */ + if (ctx->name_len == 4 && + strncasecmp(ctx->name, "Date", 4) == 0) { + search_sent(arg->type, arg->value.str, + t_strndup(ctx->value, ctx->value_len)); + } + return; + case SEARCH_FROM: if (ctx->name_len != 4 || strncasecmp(ctx->name, "From", 4) != 0) @@ -407,6 +473,7 @@ SearchHeaderContext *ctx = context; if ((name_len > 0 && ctx->custom_header) || + (name_len == 4 && strncasecmp(name, "Date", 4) == 0) || (name_len == 4 && strncasecmp(name, "From", 4) == 0) || (name_len == 2 && strncasecmp(name, "To", 2) == 0) || (name_len == 2 && strncasecmp(name, "Cc", 2) == 0) || @@ -505,6 +572,7 @@ MailSearchArg *args) { IOBuffer *inbuf; + MessageSize hdr_size; int have_headers, have_body, have_text; /* first check what we need to use */ @@ -524,7 +592,15 @@ /* header checks */ ctx.custom_header = TRUE; ctx.args = args; - message_parse_header(NULL, inbuf, NULL, search_header, &ctx); + message_parse_header(NULL, inbuf, &hdr_size, + search_header, &ctx); + + i_assert(rec->header_size == 0 || + hdr_size.physical_size == rec->header_size); + } else if (rec->header_size == 0) { + message_get_header_size(inbuf, &hdr_size); + } else { + hdr_size.physical_size = rec->header_size; } if (have_text) { @@ -536,15 +612,14 @@ } } - search_arg_match_data(inbuf, rec->header_size, + search_arg_match_data(inbuf, hdr_size.physical_size, args, search_text_header); } if (have_text || have_body) { - if (inbuf->offset != rec->header_size) { + if (inbuf->offset == 0) { /* skip over headers */ - i_assert(inbuf->offset == 0); - io_buffer_skip(inbuf, rec->header_size); + io_buffer_skip(inbuf, hdr_size.physical_size); } search_arg_match_data(inbuf, rec->body_size, args,
--- a/src/lib-storage/mail-search.c Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-storage/mail-search.c Sat Oct 12 01:33:54 2002 +0300 @@ -161,6 +161,12 @@ } else if (strcmp(key, "SUBJECT") == 0) { *args = (*args)->next; return ARG_NEW(SEARCH_SUBJECT, 1); + } else if (strcmp(key, "IN-REPLY-TO") == 0) { + *args = (*args)->next; + return ARG_NEW(SEARCH_IN_REPLY_TO, 1); + } else if (strcmp(key, "MESSAGE-ID") == 0) { + *args = (*args)->next; + return ARG_NEW(SEARCH_MESSAGE_ID, 1); } else { return ARG_NEW(SEARCH_HEADER, 2); } @@ -466,11 +472,16 @@ subarg = subarg->next; } break; + case SEARCH_SENTBEFORE: + case SEARCH_SENTON: + case SEARCH_SENTSINCE: case SEARCH_FROM: case SEARCH_TO: case SEARCH_CC: case SEARCH_BCC: case SEARCH_SUBJECT: + case SEARCH_IN_REPLY_TO: + case SEARCH_MESSAGE_ID: case SEARCH_HEADER: *have_headers = TRUE; break;
--- a/src/lib-storage/mail-search.h Fri Oct 11 15:46:18 2002 +0300 +++ b/src/lib-storage/mail-search.h Sat Oct 12 01:33:54 2002 +0300 @@ -44,7 +44,11 @@ /* body */ SEARCH_BODY, - SEARCH_TEXT + SEARCH_TEXT, + + /* our shortcuts for headers */ + SEARCH_IN_REPLY_TO, + SEARCH_MESSAGE_ID } MailSearchArgType; struct _MailSearchArg {