Mercurial > dovecot > original-hg > dovecot-1.2
changeset 1322:97f8c00b8d4c HEAD
Better handling for multiline headers. Before we skipped headers larger than
input buffer size (8k with read (default), 256k with mmap). The skipping was
also a bit buggy.
Now we parse the lines one at a time. There's also a way to read the header
fully into memory before parsing it, if really needed.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 26 Mar 2003 19:29:01 +0200 |
parents | 1a5705eb23d4 |
children | c0ecdcaca2a6 |
files | src/imap/imap-fetch-body-section.c src/lib-imap/imap-bodystructure.c src/lib-imap/imap-envelope.c src/lib-imap/imap-envelope.h src/lib-imap/imap-quote.c src/lib-index/mail-index-update.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-index.h src/lib-index/mbox/mbox-rewrite.c src/lib-mail/message-body-search.c src/lib-mail/message-parser.c src/lib-mail/message-parser.h src/lib-storage/index/index-mail.c src/lib-storage/index/index-mail.h src/lib-storage/index/index-save.c src/lib-storage/index/index-search.c src/lib-storage/index/index-storage.h src/lib-storage/index/mbox/mbox-save.c |
diffstat | 18 files changed, 896 insertions(+), 679 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap/imap-fetch-body-section.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/imap/imap-fetch-body-section.c Wed Mar 26 19:29:01 2003 +0200 @@ -19,8 +19,6 @@ double-parsing. */ #define MAX_HEADER_BUFFER_SIZE (32*1024) -#define UNSIGNED_CRLF (const unsigned char *) "\r\n" - struct fetch_header_field_context { string_t *dest; struct ostream *output; @@ -216,53 +214,12 @@ return ctx->dest_size < ctx->max_size; } -static void fetch_header_field(struct message_part *part __attr_unused__, - const unsigned char *name, size_t name_len, - const unsigned char *value __attr_unused__, - size_t value_len __attr_unused__, - void *context) -{ - struct fetch_header_field_context *ctx = context; - const unsigned char *field_start, *field_end, *cr, *p; - - /* see if we want this field. */ - if (!ctx->match_func(ctx->fields, name, name_len) || name_len == 0) - return; - - /* add the field, inserting CRs when needed. FIXME: is this too - kludgy? we assume name continues with ": value". but otherwise - we wouldn't reply with correct LWSP around ":". */ - field_start = name; - field_end = value + value_len; - - cr = NULL; - for (p = field_start; p != field_end; p++) { - if (*p == '\r') - cr = p; - else if (*p == '\n' && cr != p-1) { - /* missing CR */ - if (!fetch_header_append(ctx, field_start, - (size_t) (p-field_start))) - return; - if (!fetch_header_append(ctx, UNSIGNED_CRLF, 2)) - return; - - field_start = p+1; - } - } - - if (field_start != field_end) { - if (!fetch_header_append(ctx, field_start, - (size_t) (field_end-field_start))) - return; - } - - (void)fetch_header_append(ctx, UNSIGNED_CRLF, 2); -} - static int fetch_header_fields(struct istream *input, const char *section, struct fetch_header_field_context *ctx) { + struct message_header_parser_ctx *hdr_ctx; + struct message_header_line *hdr; + if (strncmp(section, "HEADER.FIELDS ", 14) == 0) { ctx->fields = get_fields_array(section + 14); ctx->match_func = header_match; @@ -279,13 +236,31 @@ } ctx->dest_size = 0; - message_parse_header(NULL, input, NULL, fetch_header_field, ctx); + + hdr_ctx = message_parse_header_init(input, NULL); + while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) { + /* see if we want this field. + we always want the end-of-headers line */ + if (!ctx->match_func(ctx->fields, hdr->name, hdr->name_len) && + !hdr->eoh) + continue; - /* FIXME: The blank line must not be filtered, says RFC. However, we - shouldn't add it if it wasn't there in the first place. Not very - easy to know currently so we'll just do it always, it'll be present - in all sane messages anyway.. */ - (void)fetch_header_append(ctx, UNSIGNED_CRLF, 2); + if (!hdr->continued && !hdr->eoh) { + if (!fetch_header_append(ctx, hdr->name, hdr->name_len)) + break; + if (!fetch_header_append(ctx, + (const unsigned char *) ": ", 2)) + break; + } + if (!fetch_header_append(ctx, hdr->value, hdr->value_len)) + break; + if (!hdr->no_newline) { + if (!fetch_header_append(ctx, + (const unsigned char *) "\r\n", 2)) + break; + } + } + message_parse_header_deinit(hdr_ctx); i_assert(ctx->dest_size <= ctx->max_size); i_assert(ctx->dest == NULL || str_len(ctx->dest) == ctx->dest_size);
--- a/src/lib-imap/imap-bodystructure.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-imap/imap-bodystructure.c Wed Mar 26 19:29:01 2003 +0200 @@ -1,6 +1,7 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" +#include "buffer.h" #include "istream.h" #include "str.h" #include "message-parser.h" @@ -141,19 +142,97 @@ message_tokenize_deinit(tok); } +static void parse_content_header(struct message_part_body_data *d, + struct message_header_line *hdr, + pool_t pool) +{ + const char *name = hdr->name; + const unsigned char *value; + size_t value_len; + + if (strncasecmp(name, "Content-", 8) != 0) + return; + name += 8; + + if (hdr->continues) { + hdr->use_full_value = TRUE; + return; + } + + value = hdr->full_value; + value_len = hdr->full_value_len; + + switch (*name) { + case 'i': + case 'I': + if (strcasecmp(name, "ID") == 0 && d->content_id == NULL) + d->content_id = imap_quote(pool, value, value_len); + break; + + case 'm': + case 'M': + if (strcasecmp(name, "MD5") == 0 && d->content_md5 == NULL) + d->content_md5 = imap_quote(pool, value, value_len); + break; + + case 't': + case 'T': + if (strcasecmp(name, "Type") == 0 && d->content_type == NULL) { + d->str = t_str_new(256); + message_content_parse_header(value, value_len, + parse_content_type, + parse_save_params_list, d); + d->content_type_params = + p_strdup_empty(pool, str_c(d->str)); + } + if (strcasecmp(name, "Transfer-Encoding") == 0 && + d->content_transfer_encoding == NULL) { + message_content_parse_header(value, value_len, + parse_content_transfer_encoding, + NULL, d); + } + break; + + case 'l': + case 'L': + if (strcasecmp(name, "Language") == 0 && + d->content_language == NULL) + parse_content_language(value, value_len, d); + break; + + case 'd': + case 'D': + if (strcasecmp(name, "Description") == 0 && + d->content_description == NULL) { + d->content_description = + imap_quote(pool, value, value_len); + } + if (strcasecmp(name, "Disposition") == 0 && + d->content_disposition_params == NULL) { + d->str = t_str_new(256); + message_content_parse_header(value, value_len, + parse_content_disposition, + parse_save_params_list, d); + d->content_disposition_params = + p_strdup_empty(pool, str_c(d->str)); + } + break; + } +} + static void parse_header(struct message_part *part, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context) + struct message_header_line *hdr, void *context) { pool_t pool = context; struct message_part_body_data *part_data; int parent_rfc822; + if (hdr == NULL) + return; + parent_rfc822 = part->parent != NULL && (part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822); - if (!parent_rfc822 && (name_len <= 8 || - memcasecmp(name, "Content-", 8) != 0)) + if (!parent_rfc822 && strncasecmp(hdr->name, "Content-", 8) != 0) return; if (part->context == NULL) { @@ -166,74 +245,11 @@ t_push(); - switch (name_len) { - case 10: - if (memcasecmp(name, "Content-ID", 10) == 0 && - part_data->content_id == NULL) { - part_data->content_id = - imap_quote(pool, value, value_len); - } - break; - - case 11: - if (memcasecmp(name, "Content-MD5", 11) == 0 && - part_data->content_md5 == NULL) { - part_data->content_md5 = - imap_quote(pool, value, value_len); - } - break; - - case 12: - if (memcasecmp(name, "Content-Type", 12) != 0 || - part_data->content_type != NULL) - break; - - part_data->str = t_str_new(256); - message_content_parse_header(value, value_len, - parse_content_type, - parse_save_params_list, part_data); - part_data->content_type_params = - p_strdup_empty(pool, str_c(part_data->str)); - break; - - case 16: - if (memcasecmp(name, "Content-Language", 16) == 0) - parse_content_language(value, value_len, part_data); - break; - - case 19: - if (memcasecmp(name, "Content-Description", 19) == 0 && - part_data->content_description == NULL) { - part_data->content_description = - imap_quote(pool, value, value_len); - } - if (memcasecmp(name, "Content-Disposition", 19) == 0 && - part_data->content_disposition_params == NULL) { - part_data->str = t_str_new(256); - message_content_parse_header(value, value_len, - parse_content_disposition, - parse_save_params_list, - part_data); - part_data->content_disposition_params = - p_strdup_empty(pool, str_c(part_data->str)); - } - break; - - case 25: - if (memcasecmp(name, "Content-Transfer-Encoding", 25) != 0 || - part_data->content_transfer_encoding != NULL) - break; - - message_content_parse_header(value, value_len, - parse_content_transfer_encoding, - NULL, part_data); - break; - } + parse_content_header(part_data, hdr, pool); if (parent_rfc822) { /* message/rfc822, we need the envelope */ - imap_envelope_parse_header(pool, &part_data->envelope, - name, name_len, value, value_len); + imap_envelope_parse_header(pool, &part_data->envelope, hdr); } t_pop(); } @@ -249,7 +265,6 @@ (input->v_offset - start_offset)); message_parse_header(part, input, NULL, parse_header, pool); - if (part->children != NULL) { part_parse_headers(part->children, input, start_offset, pool);
--- a/src/lib-imap/imap-envelope.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-imap/imap-envelope.c Wed Mar 26 19:29:01 2003 +0200 @@ -4,6 +4,7 @@ #include "istream.h" #include "str.h" #include "message-address.h" +#include "message-parser.h" #include "imap-parser.h" #include "imap-envelope.h" #include "imap-quote.h" @@ -18,12 +19,80 @@ char *in_reply_to, *message_id; }; +int imap_envelope_get_field(const char *name, enum imap_envelope_field *ret) +{ + *ret = (enum imap_envelope_field)-1; + + switch (*name) { + case 'B': + case 'b': + if (strcasecmp(name, "Bcc") == 0) + *ret = IMAP_ENVELOPE_BCC; + break; + case 'C': + case 'c': + if (strcasecmp(name, "Cc") == 0) + *ret = IMAP_ENVELOPE_CC; + break; + case 'D': + case 'd': + if (strcasecmp(name, "Date") == 0) + *ret = IMAP_ENVELOPE_DATE; + break; + case 'F': + case 'f': + if (strcasecmp(name, "From") == 0) + *ret = IMAP_ENVELOPE_FROM; + break; + case 'I': + case 'i': + if (strcasecmp(name, "In-reply-to") == 0) + *ret = IMAP_ENVELOPE_IN_REPLY_TO; + break; + case 'M': + case 'm': + if (strcasecmp(name, "Message-id") == 0) + *ret = IMAP_ENVELOPE_MESSAGE_ID; + break; + case 'R': + case 'r': + if (strcasecmp(name, "Reply-to") == 0) + *ret = IMAP_ENVELOPE_REPLY_TO; + break; + case 'S': + case 's': + if (strcasecmp(name, "Subject") == 0) + *ret = IMAP_ENVELOPE_SUBJECT; + if (strcasecmp(name, "Sender") == 0) + *ret = IMAP_ENVELOPE_SENDER; + break; + case 'T': + case 't': + if (strcasecmp(name, "To") == 0) + *ret = IMAP_ENVELOPE_TO; + break; + } + + return *ret != (enum imap_envelope_field)-1; +} + void imap_envelope_parse_header(pool_t pool, struct message_part_envelope_data **data, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len) + struct message_header_line *hdr) { struct message_part_envelope_data *d; + enum imap_envelope_field field; + struct message_address **addr_p; + char **str_p; + + if (hdr == NULL || !imap_envelope_get_field(hdr->name, &field)) + return; + + if (hdr->continues) { + /* wait for full value */ + hdr->use_full_value = TRUE; + return; + } if (*data == NULL) { *data = p_new(pool, struct message_part_envelope_data, 1); @@ -31,61 +100,50 @@ } d = *data; - t_push(); - - switch (name_len) { - case 2: - if (memcasecmp(name, "To", 2) == 0 && d->to == NULL) { - d->to = message_address_parse(pool, value, - value_len, 0); - } else if (memcasecmp(name, "Cc", 2) == 0 && d->cc == NULL) { - d->cc = message_address_parse(pool, value, - value_len, 0); - } + addr_p = NULL; str_p = NULL; + switch (field) { + case IMAP_ENVELOPE_DATE: + str_p = &d->date; break; - case 3: - if (memcasecmp(name, "Bcc", 3) == 0 && d->bcc == NULL) { - d->bcc = message_address_parse(pool, value, - value_len, 0); - } + case IMAP_ENVELOPE_SUBJECT: + str_p = &d->subject; break; - case 4: - if (memcasecmp(name, "From", 4) == 0 && d->from == NULL) { - d->from = message_address_parse(pool, value, - value_len, 0); - } else if (memcasecmp(name, "Date", 4) == 0 && d->date == NULL) - d->date = imap_quote(pool, value, value_len); + case IMAP_ENVELOPE_MESSAGE_ID: + str_p = &d->message_id; + break; + case IMAP_ENVELOPE_IN_REPLY_TO: + str_p = &d->in_reply_to; break; - case 6: - if (memcasecmp(name, "Sender", 6) == 0 && d->sender == NULL) { - d->sender = message_address_parse(pool, value, - value_len, 0); - } + + case IMAP_ENVELOPE_CC: + addr_p = &d->cc; break; - case 7: - if (memcasecmp(name, "Subject", 7) == 0 && d->subject == NULL) - d->subject = imap_quote(pool, value, value_len); + case IMAP_ENVELOPE_BCC: + addr_p = &d->bcc; + break; + case IMAP_ENVELOPE_FROM: + addr_p = &d->from; break; - case 8: - if (memcasecmp(name, "Reply-To", 8) == 0 && - d->reply_to == NULL) { - d->reply_to = message_address_parse(pool, value, - value_len, 0); - } + case IMAP_ENVELOPE_SENDER: + addr_p = &d->sender; break; - case 10: - if (memcasecmp(name, "Message-Id", 10) == 0 && - d->message_id == NULL) - d->message_id = imap_quote(pool, value, value_len); + case IMAP_ENVELOPE_TO: + addr_p = &d->to; break; - case 11: - if (memcasecmp(name, "In-Reply-To", 11) == 0 && - d->in_reply_to == NULL) - d->in_reply_to = imap_quote(pool, value, value_len); + case IMAP_ENVELOPE_REPLY_TO: + addr_p = &d->reply_to; + break; + case IMAP_ENVELOPE_FIELDS: break; } - t_pop(); + if (addr_p != NULL) { + *addr_p = message_address_parse(pool, hdr->full_value, + hdr->full_value_len, 0); + } + + if (str_p != NULL) + *str_p = imap_quote(pool, hdr->full_value, hdr->full_value_len); } static void imap_write_address(string_t *str, struct message_address *addr)
--- a/src/lib-imap/imap-envelope.h Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-imap/imap-envelope.h Wed Mar 26 19:29:01 2003 +0200 @@ -1,6 +1,8 @@ #ifndef __IMAP_ENVELOPE_H #define __IMAP_ENVELOPE_H +struct message_header_line; + enum imap_envelope_field { /* NOTE: in the same order as listed in ENVELOPE */ IMAP_ENVELOPE_DATE = 0, @@ -24,11 +26,12 @@ struct message_part_envelope_data; +int imap_envelope_get_field(const char *name, enum imap_envelope_field *ret); + /* Update envelope data based from given header field */ void imap_envelope_parse_header(pool_t pool, struct message_part_envelope_data **data, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len); + struct message_header_line *hdr); /* Write envelope to given string */ void imap_envelope_write_part_data(struct message_part_envelope_data *data,
--- a/src/lib-imap/imap-quote.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-imap/imap-quote.c Wed Mar 26 19:29:01 2003 +0200 @@ -53,11 +53,18 @@ char *imap_quote(pool_t pool, const unsigned char *value, size_t value_len) { string_t *str; + char *ret; + + i_assert(pool != data_stack_pool); if (value == NULL) return "NIL"; + t_push(); str = t_str_new(value_len + MAX_INT_STRLEN + 5); imap_quote_append(str, value, value_len); - return p_strndup(pool, str_data(str), str_len(str)); + ret = p_strndup(pool, str_data(str), str_len(str)); + t_pop(); + + return ret; }
--- a/src/lib-index/mail-index-update.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-index/mail-index-update.c Wed Mar 26 19:29:01 2003 +0200 @@ -2,6 +2,7 @@ #include "lib.h" #include "buffer.h" +#include "str.h" #include "istream.h" #include "ioloop.h" #include "str.h" @@ -384,9 +385,7 @@ }; static void update_header_cb(struct message_part *part, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context) + struct message_header_line *hdr, void *context) { struct header_update_context *ctx = context; @@ -399,14 +398,12 @@ ctx->envelope_pool = pool_alloconly_create("index envelope", 2048); } - imap_envelope_parse_header(ctx->envelope_pool, &ctx->envelope, - name, name_len, value, value_len); + imap_envelope_parse_header(ctx->envelope_pool, + &ctx->envelope, hdr); } - if (ctx->header_cb != NULL) { - ctx->header_cb(part, name, name_len, - value, value_len, ctx->context); - } + if (ctx->header_cb != NULL) + ctx->header_cb(part, hdr, ctx->context); } void mail_index_update_headers(struct mail_index_update *update,
--- a/src/lib-index/mbox/mbox-index.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-index/mbox/mbox-index.c Wed Mar 26 19:29:01 2003 +0200 @@ -248,16 +248,14 @@ } void mbox_header_cb(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 message_header_line *hdr, void *context) { struct mbox_header_context *ctx = context; uoff_t start_offset, end_offset; size_t i; int fixed = FALSE; - if (name_len == 0) { + if (hdr == NULL) { /* End of headers */ if (!ctx->set_read_limit) return; @@ -281,99 +279,109 @@ return; } + if (hdr->eoh) + return; + /* Pretty much copy&pasted from popa3d by Solar Designer */ - switch (*name) { + switch (*hdr->name) { case 'R': case 'r': - if (!ctx->received && name_len == 8 && - memcasecmp(name, "Received", 8) == 0) { - ctx->received = TRUE; + if (!ctx->received && + strcasecmp(hdr->name, "Received") == 0) { + /* get only the first received-header */ fixed = TRUE; + if (!hdr->continues) + ctx->received = TRUE; } break; case 'C': case 'c': - if (name_len == 14 && ctx->set_read_limit && - memcasecmp(name, "Content-Length", 14) == 0) { + if (ctx->set_read_limit && + strcasecmp(hdr->name, "Content-Length") == 0) { /* manual parsing, so we can deal with uoff_t */ ctx->content_length = 0; - for (i = 0; i < value_len; i++) { - if (value[i] < '0' || value[i] > '9') { + for (i = 0; i < hdr->value_len; i++) { + if (hdr->value[i] < '0' || + hdr->value[i] > '9') { /* invalid */ ctx->content_length = 0; break; } ctx->content_length = ctx->content_length * 10 + - (value[i] - '0'); + (hdr->value[i] - '0'); } } break; case 'D': case 'd': - if (name_len == 12) - fixed = memcasecmp(name, "Delivered-To", 12) == 0; - else if (name_len == 4) { + if (strcasecmp(hdr->name, "Delivered-To") == 0) + fixed = TRUE; + else if (!ctx->received && strcasecmp(hdr->name, "Date") == 0) { /* Received-header contains date too, and more trusted one */ - fixed = !ctx->received && - memcasecmp(name, "Date", 4) == 0; + fixed = TRUE; } break; case 'M': case 'm': - if (name_len == 10) { + if (!ctx->received && + strcasecmp(hdr->name, "Message-ID") == 0) { /* Received-header contains unique ID too, and more trusted one */ - fixed = !ctx->received && - memcasecmp(name, "Message-ID", 10) == 0; + fixed = TRUE; } break; case 'S': case 's': - if (name_len == 6 && memcasecmp(name, "Status", 6) == 0) { + if (strcasecmp(hdr->name, "Status") == 0) { /* update message flags */ - ctx->flags |= mbox_get_status_flags(value, value_len); + ctx->flags |= mbox_get_status_flags(hdr->value, + hdr->value_len); } break; case 'X': case 'x': - if (name_len == 13) { + if (strcasecmp(hdr->name, "X-Delivery-ID:") == 0) { /* Let the local delivery agent help generate unique ID's but don't blindly trust this header alone as it could just as easily come from the remote. */ - fixed = memcasecmp(name, "X-Delivery-ID:", 13) == 0; - } else if (name_len == 5 && - memcasecmp(name, "X-UID", 5) == 0) { + fixed = TRUE; + } else if (strcasecmp(hdr->name, "X-UID") == 0) { ctx->uid = 0; - for (i = 0; i < value_len; i++) { - if (value[i] < '0' || value[i] > '9') + for (i = 0; i < hdr->value_len; i++) { + if (hdr->value[i] < '0' || + hdr->value[i] > '9') break; - ctx->uid = ctx->uid * 10 + (value[i]-'0'); + ctx->uid = ctx->uid * 10 + (hdr->value[i]-'0'); } - } else if (name_len == 8 && - memcasecmp(name, "X-Status", 8) == 0) { + } else if (strcasecmp(hdr->name, "X-Status") == 0) { /* update message flags */ - ctx->flags |= mbox_get_status_flags(value, value_len); - } else if (name_len == 10 && - memcasecmp(name, "X-Keywords", 10) == 0) { + ctx->flags |= mbox_get_status_flags(hdr->value, + hdr->value_len); + } else if (strcasecmp(hdr->name, "X-Keywords") == 0) { /* update custom message flags */ - ctx->flags |= mbox_get_keyword_flags(value, value_len, + ctx->flags |= mbox_get_keyword_flags(hdr->value, + hdr->value_len, ctx->custom_flags); - } else if (name_len == 10 && - memcasecmp(name, "X-IMAPbase", 10) == 0) { - mbox_parse_imapbase(value, value_len, ctx); + } else if (strcasecmp(hdr->name, "X-IMAPbase") == 0) { + if (hdr->continues) { + hdr->use_full_value = TRUE; + break; + } + mbox_parse_imapbase(hdr->full_value, + hdr->full_value_len, ctx); } break; } if (fixed) - md5_update(&ctx->md5, value, value_len); + md5_update(&ctx->md5, hdr->value, hdr->value_len); } void mbox_keywords_parse(const unsigned char *value, size_t len,
--- a/src/lib-index/mbox/mbox-index.h Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-index/mbox/mbox-index.h Wed Mar 26 19:29:01 2003 +0200 @@ -33,10 +33,8 @@ struct mail_index *index, struct istream *input); void mbox_header_free_context(struct mbox_header_context *ctx); -void mbox_header_cb(struct message_part *part __attr_unused__, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context); +void mbox_header_cb(struct message_part *part, + struct message_header_line *hdr, void *context); void mbox_keywords_parse(const unsigned char *value, size_t len, const char *custom_flags[MAIL_CUSTOM_FLAGS_COUNT], void (*func)(const unsigned char *, size_t,
--- a/src/lib-index/mbox/mbox-rewrite.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-index/mbox/mbox-rewrite.c Wed Mar 26 19:29:01 2003 +0200 @@ -20,7 +20,6 @@ struct mbox_rewrite_context { struct ostream *output; - int failed; uoff_t content_length; unsigned int seq, uid; @@ -258,51 +257,96 @@ return str_len(str) == 0 ? NULL : str_c(str); } -static void header_cb(struct message_part *part __attr_unused__, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context) +static int write_header(struct mbox_rewrite_context *ctx, + struct message_header_line *hdr) { - struct mbox_rewrite_context *ctx = context; const char *str; - if (ctx->failed) - return; + switch (hdr->name_len) { + case 5: + if (strcasecmp(hdr->name, "X-UID") == 0) { + if (ctx->xuid_found) + return TRUE; + + ctx->xuid_found = TRUE; + return mbox_write_xuid(ctx); + } + break; + case 6: + if (strcasecmp(hdr->name, "Status") == 0) { + if (ctx->status_found) + return TRUE; + if (hdr->continues) { + hdr->use_full_value = TRUE; + return TRUE; + } + + ctx->status_found = TRUE; + str = strip_chars(hdr->full_value, + hdr->full_value_len, "RO"); + return mbox_write_status(ctx, str); + } + break; + case 8: + if (strcasecmp(hdr->name, "X-Status") == 0) { + if (ctx->xstatus_found) + return TRUE; + if (hdr->continues) { + hdr->use_full_value = TRUE; + return TRUE; + } - if (name_len == 6 && memcasecmp(name, "Status", 6) == 0) { - ctx->status_found = TRUE; - str = strip_chars(value, value_len, "RO"); - (void)mbox_write_status(ctx, str); - } else if (name_len == 8 && memcasecmp(name, "X-Status", 8) == 0) { - ctx->xstatus_found = TRUE; - str = strip_chars(value, value_len, "ADFT"); - (void)mbox_write_xstatus(ctx, str); - } else if (name_len == 10 && memcasecmp(name, "X-Keywords", 10) == 0) { - ctx->xkeywords_found = TRUE; - str = strip_custom_flags(value, value_len, ctx); - (void)mbox_write_xkeywords(ctx, str); - } else if (name_len == 10 && memcasecmp(name, "X-IMAPbase", 10) == 0) { - if (ctx->seq == 1) { + ctx->xstatus_found = TRUE; + str = strip_chars(hdr->full_value, + hdr->full_value_len, "ADFT"); + return mbox_write_xstatus(ctx, str); + } + break; + case 10: + if (strcasecmp(hdr->name, "X-Keywords") == 0) { + if (ctx->xkeywords_found) + return TRUE; + if (hdr->continues) { + hdr->use_full_value = TRUE; + return TRUE; + } + + ctx->xkeywords_found = TRUE; + str = strip_custom_flags(hdr->full_value, + hdr->full_value_len, ctx); + return mbox_write_xkeywords(ctx, str); + } else if (strcasecmp(hdr->name, "X-IMAPbase") == 0) { + if (ctx->seq != 1 || ctx->ximapbase_found) + return TRUE; + ctx->ximapbase_found = TRUE; - (void)mbox_write_ximapbase(ctx); + return mbox_write_ximapbase(ctx); } - } else if (name_len == 5 && memcasecmp(name, "X-UID", 5) == 0) { - ctx->xuid_found = TRUE; - (void)mbox_write_xuid(ctx); - } else if (name_len == 14 && - memcasecmp(name, "Content-Length", 14) == 0) { - ctx->content_length_found = TRUE; - (void)mbox_write_content_length(ctx); - } else if (name_len > 0) { - /* save this header */ - (void)o_stream_send(ctx->output, name, name_len); - (void)o_stream_send(ctx->output, ": ", 2); - (void)o_stream_send(ctx->output, value, value_len); - (void)o_stream_send(ctx->output, "\n", 1); + break; + case 14: + if (strcasecmp(hdr->name, "Content-Length") == 0) { + if (ctx->content_length_found) + return TRUE; + + ctx->content_length_found = TRUE; + return mbox_write_content_length(ctx); + } + break; } - if (ctx->output->closed) - ctx->failed = TRUE; + if (!hdr->eoh) { + /* save this header */ + if (!hdr->continued) { + (void)o_stream_send(ctx->output, hdr->name, + hdr->name_len); + (void)o_stream_send(ctx->output, ": ", 2); + } + (void)o_stream_send(ctx->output, hdr->value, hdr->value_len); + if (!hdr->no_newline) + (void)o_stream_send(ctx->output, "\n", 1); + } + + return !ctx->output->closed; } static int mbox_write_header(struct mail_index *index, @@ -323,6 +367,8 @@ Last used UID is also not updated, and set to 0 initially. */ struct mbox_rewrite_context ctx; + struct message_header_parser_ctx *hdr_ctx; + struct message_header_line *hdr; struct message_size hdr_parsed_size; if (input->v_offset >= end_offset) { @@ -346,7 +392,12 @@ ctx.custom_flags = mail_custom_flags_list_get(index->custom_flags); i_stream_set_read_limit(input, input->v_offset + hdr_size); - message_parse_header(NULL, input, &hdr_parsed_size, header_cb, &ctx); + + hdr_ctx = message_parse_header_init(input, &hdr_parsed_size); + while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) + write_header(&ctx, hdr); + message_parse_header_deinit(hdr_ctx); + i_stream_set_read_limit(input, 0); i_assert(hdr_parsed_size.physical_size == hdr_size);
--- a/src/lib-mail/message-body-search.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-mail/message-body-search.c Wed Mar 26 19:29:01 2003 +0200 @@ -28,7 +28,6 @@ struct part_search_context { struct body_search_context *body_ctx; - struct header_search_context *hdr_search_ctx; struct charset_translation *translation; buffer_t *decode_buf; @@ -42,7 +41,6 @@ unsigned int content_unknown:1; unsigned int content_type_text:1; /* text/any or message/any */ unsigned int ignore_header:1; - unsigned int found:1; }; static void parse_content_type(const unsigned char *value, size_t value_len, @@ -101,47 +99,59 @@ } } -static void header_find(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 part_search_context *ctx = context; - - if (ctx->found) - return; - - if (!ctx->ignore_header) { - ctx->found = message_header_search(value, value_len, - ctx->hdr_search_ctx); - } - - if (name_len == 12 && memcasecmp(name, "Content-Type", 12) == 0) { - message_content_parse_header(value, value_len, - parse_content_type, - parse_content_type_param, - ctx); - } else if (name_len == 25 && - memcasecmp(name, "Content-Transfer-Encoding", 25) == 0) { - message_content_parse_header(value, value_len, - parse_content_encoding, - NULL, ctx); - } -} - static int message_search_header(struct part_search_context *ctx, struct istream *input) { - ctx->hdr_search_ctx = message_header_search_init(data_stack_pool, - ctx->body_ctx->key, - ctx->body_ctx->charset, - NULL); + struct header_search_context *hdr_search_ctx; + struct message_header_parser_ctx *hdr_ctx; + struct message_header_line *hdr; + int found = FALSE; + + hdr_search_ctx = message_header_search_init(data_stack_pool, + ctx->body_ctx->key, + ctx->body_ctx->charset, + NULL); /* we default to text content-type */ ctx->content_type_text = TRUE; - message_parse_header(NULL, input, NULL, header_find, ctx); + + hdr_ctx = message_parse_header_init(input, NULL); + while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) { + if (!ctx->ignore_header) { + if (message_header_search(hdr->value, hdr->value_len, + hdr_search_ctx)) { + found = TRUE; + break; + } + } - return ctx->found; + if (hdr->name_len == 12 && + strcasecmp(hdr->name, "Content-Type") == 0) { + if (hdr->continues) { + hdr->use_full_value = TRUE; + continue; + } + message_content_parse_header(hdr->full_value, + hdr->full_value_len, + parse_content_type, + parse_content_type_param, + ctx); + } else if (hdr->name_len == 25 && + strcasecmp(hdr->name, + "Content-Transfer-Encoding") == 0) { + if (hdr->continues) { + hdr->use_full_value = TRUE; + continue; + } + message_content_parse_header(hdr->full_value, + hdr->full_value_len, + parse_content_encoding, + NULL, ctx); + } + } + message_parse_header_deinit(hdr_ctx); + + return found; } static int message_search_decoded_block(struct part_search_context *ctx,
--- a/src/lib-mail/message-parser.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-mail/message-parser.c Wed Mar 26 19:29:01 2003 +0200 @@ -1,7 +1,9 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" +#include "buffer.h" #include "istream.h" +#include "str.h" #include "strescape.h" #include "message-content-parser.h" #include "message-parser.h" @@ -27,6 +29,17 @@ void *context; }; +struct message_header_parser_ctx { + struct message_header_line line; + + struct istream *input; + struct message_size *hdr_size; + + string_t *name; + buffer_t *value_buf; + size_t skip; +}; + static struct message_part * message_parse_part(struct istream *input, struct parser_context *parser_ctx); @@ -119,28 +132,6 @@ } } -static void parse_header_field(struct message_part *part, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context) -{ - struct parser_context *parser_ctx = context; - - /* call the user-defined header parser */ - if (parser_ctx->callback != NULL) { - parser_ctx->callback(part, name, name_len, value, value_len, - parser_ctx->context); - } - - if (name_len == 12 && memcasecmp(name, "Content-Type", 12) == 0) { - /* we need to know the boundary */ - message_content_parse_header(value, value_len, - parse_content_type, - parse_content_type_param, - parser_ctx); - } -} - static struct message_part * message_parse_multipart(struct istream *input, struct parser_context *parser_ctx) @@ -197,12 +188,38 @@ static struct message_part * message_parse_part(struct istream *input, struct parser_context *parser_ctx) { + struct message_header_parser_ctx *hdr_ctx; + struct message_header_line *hdr; struct message_part *next_part, *part; uoff_t hdr_size; - message_parse_header(parser_ctx->part, input, - &parser_ctx->part->header_size, - parse_header_field, parser_ctx); + hdr_ctx = message_parse_header_init(input, + &parser_ctx->part->header_size); + while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) { + /* call the user-defined header parser */ + if (parser_ctx->callback != NULL) { + parser_ctx->callback(parser_ctx->part, hdr, + parser_ctx->context); + } + + if (strcasecmp(hdr->name, "Content-Type") == 0) { + if (hdr->continues) { + hdr->use_full_value = TRUE; + continue; + } + /* we need to know the boundary */ + message_content_parse_header(hdr->full_value, + hdr->full_value_len, + parse_content_type, + parse_content_type_param, + parser_ctx); + } + } + if (parser_ctx->callback != NULL) { + parser_ctx->callback(parser_ctx->part, NULL, + parser_ctx->context); + } + message_parse_header_deinit(hdr_ctx); i_assert((parser_ctx->part->flags & MUTEX_FLAGS) != MUTEX_FLAGS); @@ -252,23 +269,6 @@ return next_part; } -struct message_part *message_parse(pool_t pool, struct istream *input, - message_header_callback_t *callback, - void *context) -{ - struct message_part *part; - struct parser_context parser_ctx; - - memset(&parser_ctx, 0, sizeof(parser_ctx)); - parser_ctx.pool = pool; - parser_ctx.callback = callback; - parser_ctx.context = context; - parser_ctx.part = part = p_new(pool, struct message_part, 1); - - message_parse_part(input, &parser_ctx); - return part; -} - static void message_skip_line(struct istream *input, struct message_size *msg_size, int skip_lf) { @@ -315,135 +315,6 @@ } } -void message_parse_header(struct message_part *part, struct istream *input, - struct message_size *hdr_size, - message_header_callback_t *callback, void *context) -{ - const unsigned char *msg; - size_t i, size, parse_size, startpos, missing_cr_count; - size_t line_start, colon_pos, end_pos, name_len, value_len; - int ret; - - if (hdr_size != NULL) - memset(hdr_size, 0, sizeof(struct message_size)); - - missing_cr_count = startpos = line_start = 0; - colon_pos = UINT_MAX; - for (;;) { - ret = i_stream_read_data(input, &msg, &size, startpos+1); - if (ret == -2) { - /* overflow, line is too long. just skip it. */ - i_assert(size > 2); - - message_skip_line(input, hdr_size, TRUE); - startpos = line_start = 0; - colon_pos = UINT_MAX; - continue; - } - - if (ret < 0 || (ret <= 0 && size == startpos)) { - /* EOF and nothing in buffer. the later check is - needed only when there's no message body */ - break; - } - - parse_size = size <= startpos+1 ? size : size-1; - for (i = startpos; i < parse_size; i++) { - if (msg[i] == ':' && colon_pos == UINT_MAX) { - colon_pos = i; - continue; - } - - if (msg[i] != '\n') - continue; - - if (hdr_size != NULL) - hdr_size->lines++; - - if (i == 0 || msg[i-1] != '\r') { - /* missing CR */ - missing_cr_count++; - } - - if (i == 0 || (i == 1 && msg[i-1] == '\r')) { - /* no headers at all */ - break; - } - - if ((i > 0 && msg[i-1] == '\n') || - (i > 1 && msg[i-2] == '\n' && msg[i-1] == '\r')) { - /* \n\n or \n\r\n - end of headers */ - break; - } - - /* make sure the header doesn't continue to next line */ - if (i+1 == size || !IS_LWSP(msg[i+1])) { - if (colon_pos != UINT_MAX && - colon_pos != line_start && - callback != NULL && - !IS_LWSP(msg[line_start])) { - /* we have a valid header line */ - - /* get length of name-field */ - end_pos = colon_pos-1; - while (end_pos > line_start && - IS_LWSP(msg[end_pos])) - end_pos--; - name_len = end_pos - line_start + 1; - - /* get length of value field. - skip all LWSP after ':'. */ - colon_pos++; - while (colon_pos < i && - IS_LWSP(msg[colon_pos])) - colon_pos++; - value_len = i - colon_pos; - if (msg[i-1] == '\r') value_len--; - - /* and finally call the function */ - callback(part, - msg + line_start, name_len, - msg + colon_pos, value_len, - context); - } - - colon_pos = UINT_MAX; - line_start = i+1; - } - } - - if (i < parse_size) { - /* end of header */ - startpos = i+1; - break; - } - - /* leave the last line to buffer */ - if (colon_pos != UINT_MAX) - colon_pos -= line_start; - if (hdr_size != NULL) - hdr_size->physical_size += line_start; - i_stream_skip(input, line_start); - - startpos = i-line_start; - line_start = 0; - } - - i_stream_skip(input, startpos); - - if (hdr_size != NULL) { - hdr_size->physical_size += startpos; - hdr_size->virtual_size += - hdr_size->physical_size + missing_cr_count; - i_assert(hdr_size->virtual_size >= hdr_size->physical_size); - } - - if (callback != NULL) { - /* "end of headers" notify */ - callback(part, NULL, 0, NULL, 0, context); - } -} - static struct message_boundary * boundary_find(struct message_boundary *boundaries, const unsigned char *msg, size_t len) @@ -616,3 +487,245 @@ return boundary == NULL ? NULL : boundary->part; } + +struct message_part *message_parse(pool_t pool, struct istream *input, + message_header_callback_t *callback, + void *context) +{ + struct message_part *part; + struct parser_context parser_ctx; + + memset(&parser_ctx, 0, sizeof(parser_ctx)); + parser_ctx.pool = pool; + parser_ctx.callback = callback; + parser_ctx.context = context; + parser_ctx.part = part = p_new(pool, struct message_part, 1); + + message_parse_part(input, &parser_ctx); + return part; +} + +void message_parse_header(struct message_part *part, struct istream *input, + struct message_size *hdr_size, + message_header_callback_t *callback, void *context) +{ + struct message_header_parser_ctx *hdr_ctx; + struct message_header_line *hdr; + + hdr_ctx = message_parse_header_init(input, hdr_size); + while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) + callback(part, hdr, context); + callback(part, NULL, context); + message_parse_header_deinit(hdr_ctx); +} + +struct message_header_parser_ctx * +message_parse_header_init(struct istream *input, struct message_size *hdr_size) +{ + struct message_header_parser_ctx *ctx; + + ctx = i_new(struct message_header_parser_ctx, 1); + ctx->input = input; + ctx->hdr_size = hdr_size; + ctx->name = str_new(default_pool, 128); + + if (hdr_size != NULL) + memset(hdr_size, 0, sizeof(*hdr_size)); + return ctx; +} + +void message_parse_header_deinit(struct message_header_parser_ctx *ctx) +{ + i_stream_skip(ctx->input, ctx->skip); + if (ctx->value_buf != NULL) + buffer_free(ctx->value_buf); + str_free(ctx->name); + i_free(ctx); +} + +struct message_header_line * +message_parse_header_next(struct message_header_parser_ctx *ctx) +{ + struct message_header_line *line = &ctx->line; + const unsigned char *msg; + size_t i, size, startpos, colon_pos, parse_size; + int ret; + + if (line->eoh) + return NULL; + + if (ctx->skip > 0) { + i_stream_skip(ctx->input, ctx->skip); + ctx->skip = 0; + } + + startpos = 0; colon_pos = UINT_MAX; + + line->no_newline = FALSE; + + if (line->continues) { + if (line->use_full_value && !line->continued) { + /* save the first line */ + if (ctx->value_buf != NULL) + buffer_set_used_size(ctx->value_buf, 0); + else { + ctx->value_buf = + buffer_create_dynamic(default_pool, + 4096, (size_t)-1); + } + buffer_append(ctx->value_buf, + line->value, line->value_len); + } + + line->continued = TRUE; + line->continues = FALSE; + colon_pos = 0; + } else { + /* new header line */ + line->continued = FALSE; + } + + for (;;) { + ret = i_stream_read_data(ctx->input, &msg, &size, startpos+1); + + if (ret != 0) { + /* we want to know one byte in advance to find out + if it's multiline header */ + parse_size = size-1; + } else { + parse_size = size; + } + + if (ret <= 0 && (ret != 0 || startpos == size)) { + if (ret == -1) { + /* error / EOF with no bytes */ + return NULL; + } + + /* a) line is larger than input buffer + b) header ended unexpectedly */ + if (colon_pos == UINT_MAX) { + /* header name is huge. just skip it. */ + message_skip_line(ctx->input, ctx->hdr_size, + TRUE); + continue; + } + + /* go back to last LWSP if found. */ + for (i = size-1; i > colon_pos; i--) { + if (IS_LWSP(msg[i])) { + size = i; + break; + } + } + + line->no_newline = TRUE; + line->continues = TRUE; + ctx->skip = size; + break; + } + + /* find ':' */ + if (colon_pos == UINT_MAX) { + for (i = startpos; i < parse_size; i++) { + if (msg[i] <= ':') { + if (msg[i] == ':') { + colon_pos = i; + break; + } + if (msg[i] == '\n') { + /* end of headers, or error */ + break; + } + } + } + } + + /* find '\n' */ + for (i = startpos; i < parse_size; i++) { + if (msg[i] == '\n') + break; + } + + if (i < parse_size) { + /* got a line */ + line->continues = i+1 < size && IS_LWSP(msg[i+1]); + + if (ctx->hdr_size != NULL) + ctx->hdr_size->lines++; + if (i == 0 || msg[i-1] != '\r') { + /* missing CR */ + if (ctx->hdr_size != NULL) + ctx->hdr_size->virtual_size++; + size = i; + } else { + size = i-1; + } + + ctx->skip = i+1; + break; + } + + startpos = i; + } + + if (size == 0 || (size == 1 && msg[0] == '\r')) { + /* end of headers */ + line->eoh = TRUE; + line->name_len = line->value_len = 0; + } else if (line->continued) { + line->value = msg; + line->value_len = size; + } else if (colon_pos == UINT_MAX) { + /* missing ':', assume the whole line is name */ + line->value = NULL; + line->value_len = 0; + + str_truncate(ctx->name, 0); + str_append_n(ctx->name, msg, size); + line->name = str_c(ctx->name); + line->name_len = str_len(ctx->name); + } else { + /* get value, skip only first LWSP after ':' */ + line->value = msg + colon_pos+1; + line->value_len = size - colon_pos - 1; + if (line->value_len > 0 && + IS_LWSP(line->value[0])) { + line->value++; + line->value_len--; + } + + /* get name, skip LWSP before ':' */ + while (colon_pos > 0 && IS_LWSP(msg[colon_pos-1])) + colon_pos--; + + str_truncate(ctx->name, 0); + str_append_n(ctx->name, msg, colon_pos); + line->name = str_c(ctx->name); + line->name_len = str_len(ctx->name); + } + + if (!line->continued) { + /* first header line, set full_value = value */ + line->full_value = line->value; + line->full_value_len = line->value_len; + } else if (line->use_full_value) { + /* continue saving the full value */ + buffer_append(ctx->value_buf, line->value, line->value_len); + line->full_value = buffer_get_data(ctx->value_buf, + &line->full_value_len); + } else { + /* we didn't want full_value, and this is a continued line. */ + line->full_value = NULL; + line->full_value_len = 0; + } + + /* always reset it */ + line->use_full_value = FALSE; + + if (ctx->hdr_size != NULL) { + ctx->hdr_size->physical_size += ctx->skip; + ctx->hdr_size->virtual_size += ctx->skip; + } + return line; +}
--- a/src/lib-mail/message-parser.h Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-mail/message-parser.h Wed Mar 26 19:29:01 2003 +0200 @@ -31,27 +31,44 @@ void *context; }; -/* NOTE: name and value aren't \0-terminated. Also called once at end of - headers with name_len = value_len = 0. */ +struct message_header_parser_ctx; + +struct message_header_line { + const char *name; + size_t name_len; + + const unsigned char *value; + size_t value_len; + + const unsigned char *full_value; + size_t full_value_len; + + unsigned int continues:1; /* multiline header, continues in next line */ + unsigned int continued:1; /* multiline header, continues */ + unsigned int eoh:1; /* "end of headers" line */ + unsigned int no_newline:1; /* no \n after this line */ + unsigned int use_full_value:1; /* set if you want full_value */ +}; + +/* called once with hdr = NULL at end of headers */ typedef void message_header_callback_t(struct message_part *part, - const unsigned char *name, - size_t name_len, - const unsigned char *value, - size_t value_len, + struct message_header_line *hdr, void *context); /* callback is called for each field in message header. */ struct message_part *message_parse(pool_t pool, struct istream *input, message_header_callback_t *callback, void *context); - -/* Call callback for each field in message header. Fills the hdr_size. - part can be NULL, just make sure your header function works with it. - This function doesn't use data stack so your header function may save - values to it. When finished, input will point to beginning of message - body. */ void message_parse_header(struct message_part *part, struct istream *input, struct message_size *hdr_size, message_header_callback_t *callback, void *context); +struct message_header_parser_ctx * +message_parse_header_init(struct istream *input, struct message_size *hdr_size); +void message_parse_header_deinit(struct message_header_parser_ctx *ctx); + +/* Read and return next header line. */ +struct message_header_line * +message_parse_header_next(struct message_header_parser_ctx *ctx); + #endif
--- a/src/lib-storage/index/index-mail.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-storage/index/index-mail.c Wed Mar 26 19:29:01 2003 +0200 @@ -16,55 +16,6 @@ #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; @@ -192,20 +143,17 @@ } 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 message_header_line *hdr, void *context) { struct index_mail *mail = context; struct index_mail_data *data = &mail->data; - struct cached_header *hdr; + struct cached_header *cached_hdr; if (data->save_envelope) { + imap_envelope_parse_header(mail->pool, + &data->envelope_data, hdr); - imap_envelope_parse_header(mail->pool, &data->envelope_data, - name, name_len, value, value_len); - - if (name_len == 0) { + if (hdr == NULL) { /* finalize the envelope */ string_t *str; @@ -215,9 +163,24 @@ } } - if (name_len == 4 && data->save_sent_time && - memcasecmp(name, "date",4) == 0) { - if (!message_date_parse(value, value_len, &data->sent_time, + if (hdr == NULL) { + /* end of headers */ + if (data->save_sent_time) { + /* not found */ + data->sent_time = 0; + data->sent_timezone = 0; + data->save_sent_time = FALSE; + } + return; + } + + if (data->save_sent_time && strcasecmp(hdr->name, "Date") == 0) { + if (hdr->continues) { + hdr->use_full_value = TRUE; + return; + } + if (!message_date_parse(hdr->full_value, hdr->full_value_len, + &data->sent_time, &data->sent_timezone)) { /* 0 == parse error */ data->sent_time = 0; @@ -226,26 +189,25 @@ 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; - } - } + cached_hdr = data->headers; + while (cached_hdr != NULL) { + if (cached_hdr->name_len == hdr->name_len && + memcasecmp(hdr->name, hdr->name, hdr->name_len) == 0) { + /* save only the first header */ + if (cached_hdr->value != NULL) + break; - 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); + if (hdr->continues) { + hdr->use_full_value = TRUE; + break; } + + cached_hdr->value = p_strndup(mail->pool, + hdr->full_value, + hdr->full_value_len); break; } + cached_hdr = cached_hdr->next; } } @@ -479,7 +441,7 @@ } if (data->parse_header || data->envelope == NULL || - !get_envelope_header_field(field, &env_field)) { + !imap_envelope_get_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); @@ -528,7 +490,7 @@ const char *ret = NULL; if (data->envelope != NULL && - get_envelope_header_field(field, &env_field)) { + imap_envelope_get_field(field, &env_field)) { /* prefer parsing envelope - faster than having to actually parse the header field */ t_push(); @@ -748,7 +710,7 @@ int envelope_headers = FALSE; for (tmp = mail->wanted_headers; *tmp != NULL; tmp++) { - if (get_envelope_header_field(*tmp, &env_field)) + if (imap_envelope_get_field(*tmp, &env_field)) envelope_headers = TRUE; else { open_mail = TRUE;
--- a/src/lib-storage/index/index-mail.h Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-storage/index/index-mail.h Wed Mar 26 19:29:01 2003 +0200 @@ -38,6 +38,7 @@ pool_t pool; struct index_mailbox *ibox; + buffer_t *header_buf; enum mail_fetch_field wanted_fields; const char *const *wanted_headers; @@ -51,8 +52,6 @@ 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); + struct message_header_line *hdr, void *context); #endif
--- a/src/lib-storage/index/index-save.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-storage/index/index-save.c Wed Mar 26 19:29:01 2003 +0200 @@ -9,19 +9,6 @@ #include <stdlib.h> #include <unistd.h> -struct save_header_context { - struct mail_storage *storage; - const char *path; - - struct ostream *output; - write_func_t *write_func; - - header_callback_t *header_callback; - void *context; - - int failed; -}; - static int write_with_crlf(struct ostream *output, const void *v_data, size_t size) { @@ -103,45 +90,53 @@ } } -static void save_header_callback(struct message_part *part __attr_unused__, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context) +static int save_headers(struct istream *input, struct ostream *output, + header_callback_t *header_callback, void *context, + write_func_t *write_func) { - struct save_header_context *ctx = context; - int ret; + struct message_header_parser_ctx *hdr_ctx; + struct message_header_line *hdr; + int ret, failed = FALSE; - if (ctx->failed) - return; + hdr_ctx = message_parse_header_init(input, NULL); + while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) { + ret = header_callback(hdr->name, write_func, context); + if (ret <= 0) { + if (ret < 0) { + failed = TRUE; + break; + } + continue; + } - ret = ctx->header_callback(name, name_len, ctx->write_func, - ctx->context); - if (ret <= 0) { - if (ret < 0) - ctx->failed = TRUE; - return; + if (!hdr->eoh) { + if (!hdr->continued) { + (void)o_stream_send(output, hdr->name, + hdr->name_len); + (void)o_stream_send(output, ": ", 2); + } + (void)o_stream_send(output, hdr->value, hdr->value_len); + if (!hdr->no_newline) + write_func(output, "\n", 1); + } } + if (!failed) { + if (header_callback(NULL, write_func, context) < 0) + failed = TRUE; - if (name_len == 0) { - name = "\n"; value_len = 1; - } else { - if (value[value_len] == '\r') - value_len++; - i_assert(value[value_len] == '\n'); - value_len += (size_t) (value-name) + 1; + /* end of headers */ + write_func(output, "\n", 1); } + message_parse_header_deinit(hdr_ctx); - if (ctx->write_func(ctx->output, name, value_len) < 0) { - set_write_error(ctx->storage, ctx->output, ctx->path); - ctx->failed = TRUE; - } + return !failed; } int index_storage_save(struct mail_storage *storage, const char *path, struct istream *input, struct ostream *output, header_callback_t *header_callback, void *context) { - int (*write_func)(struct ostream *, const void *, size_t); + write_func_t *write_func; const unsigned char *data; size_t size; ssize_t ret; @@ -150,20 +145,8 @@ write_func = getenv("MAIL_SAVE_CRLF") ? write_with_crlf : write_with_lf; if (header_callback != NULL) { - struct save_header_context ctx; - - memset(&ctx, 0, sizeof(ctx)); - ctx.storage = storage; - ctx.output = output; - ctx.path = path; - ctx.write_func = write_func; - ctx.header_callback = header_callback; - ctx.context = context; - - message_parse_header(NULL, input, NULL, - save_header_callback, &ctx); - - if (ctx.failed) + if (!save_headers(input, output, header_callback, + context, write_func)) return FALSE; }
--- a/src/lib-storage/index/index-search.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-storage/index/index-search.c Wed Mar 26 19:29:01 2003 +0200 @@ -41,8 +41,7 @@ struct mail_search_context *index_context; struct mail_search_arg *args; - const unsigned char *name, *value; - size_t name_len, value_len; + struct message_header_line *hdr; unsigned int custom_header:1; unsigned int threading:1; @@ -283,7 +282,7 @@ } static int search_sent(enum mail_search_arg_type type, const char *search_value, - const char *sent_value) + const unsigned char *sent_value, size_t sent_value_len) { time_t search_time, sent_time; int timezone_offset; @@ -296,7 +295,7 @@ /* NOTE: Latest IMAP4rev1 draft specifies that timezone is ignored in searches. sent_time is returned as UTC, so change it. */ - if (!message_date_parse((const unsigned char *) sent_value, (size_t)-1, + if (!message_date_parse(sent_value, sent_value_len, &sent_time, &timezone_offset)) return 0; sent_time -= timezone_offset * 60; @@ -404,7 +403,9 @@ case SEARCH_SENTBEFORE: case SEARCH_SENTON: case SEARCH_SENTSINCE: - ret = search_sent(arg->type, arg->value.str, field); + ret = search_sent(arg->type, arg->value.str, + (const unsigned char *) field, + (size_t)-1); break; default: if (arg->value.str[0] == '\0') { @@ -457,7 +458,6 @@ { struct search_header_context *ctx = context; struct header_search_context *hdr_search_ctx; - size_t len; int ret; /* first check that the field name matches to argument. */ @@ -466,41 +466,50 @@ case SEARCH_SENTON: case SEARCH_SENTSINCE: /* date is handled differently than others */ - if (ctx->name_len == 4 && - memcasecmp(ctx->name, "Date", 4) == 0) { + if (strcasecmp(ctx->hdr->name, "Date") == 0) { + if (ctx->hdr->continues) { + ctx->hdr->use_full_value = TRUE; + return; + } ret = search_sent(arg->type, arg->value.str, - t_strndup(ctx->value, ctx->value_len)); + ctx->hdr->full_value, + ctx->hdr->full_value_len); ARG_SET_RESULT(arg, ret); } return; case SEARCH_FROM: - if (ctx->name_len != 4 || memcasecmp(ctx->name, "From", 4) != 0) + if (strcasecmp(ctx->hdr->name, "From") != 0) return; break; case SEARCH_TO: - if (ctx->name_len != 2 || memcasecmp(ctx->name, "To", 2) != 0) + if (strcasecmp(ctx->hdr->name, "To") != 0) return; break; case SEARCH_CC: - if (ctx->name_len != 2 || memcasecmp(ctx->name, "Cc", 2) != 0) + if (strcasecmp(ctx->hdr->name, "Cc") != 0) return; break; case SEARCH_BCC: - if (ctx->name_len != 3 || memcasecmp(ctx->name, "Bcc", 3) != 0) + if (strcasecmp(ctx->hdr->name, "Bcc") != 0) return; break; case SEARCH_SUBJECT: - if (ctx->name_len != 7 || - memcasecmp(ctx->name, "Subject", 7) != 0) + if (strcasecmp(ctx->hdr->name, "Subject") != 0) + return; + break; + case SEARCH_IN_REPLY_TO: + if (strcasecmp(ctx->hdr->name, "In-Reply-To") != 0) + return; + break; + case SEARCH_MESSAGE_ID: + if (strcasecmp(ctx->hdr->name, "Message-ID") != 0) return; break; case SEARCH_HEADER: ctx->custom_header = TRUE; - len = strlen(arg->hdr_field_name); - if (ctx->name_len != len || - memcasecmp(ctx->name, arg->hdr_field_name, len) != 0) + if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0) return; case SEARCH_TEXT: /* TEXT goes through all headers */ @@ -514,6 +523,11 @@ /* we're just testing existence of the field. always matches. */ ret = 1; } else { + if (ctx->hdr->continues) { + ctx->hdr->use_full_value = TRUE; + return; + } + t_push(); hdr_search_ctx = search_header_context(ctx->index_context, arg); @@ -526,14 +540,16 @@ string_t *str; addr = message_address_parse(data_stack_pool, - ctx->value, ctx->value_len, + ctx->hdr->full_value, + ctx->hdr->full_value_len, 0); - str = t_str_new(ctx->value_len); + str = t_str_new(ctx->hdr->value_len); message_address_write(str, addr); ret = message_header_search(str_data(str), str_len(str), hdr_search_ctx) ? 1 : 0; } else { - ret = message_header_search(ctx->value, ctx->value_len, + ret = message_header_search(ctx->hdr->full_value, + ctx->hdr->full_value_len, hdr_search_ctx) ? 1 : 0; } t_pop(); @@ -565,6 +581,8 @@ case SEARCH_BCC: case SEARCH_SUBJECT: case SEARCH_HEADER: + case SEARCH_IN_REPLY_TO: + case SEARCH_MESSAGE_ID: ARG_SET_RESULT(arg, 0); break; default: @@ -573,32 +591,34 @@ } static void search_header(struct message_part *part, - const unsigned char *name, size_t name_len, - const unsigned char *value, size_t value_len, - void *context) + struct message_header_line *hdr, void *context) { struct search_header_context *ctx = context; - index_mail_parse_header(part, name, name_len, value, value_len, - ctx->index_context->mail); + if (hdr == NULL) { + /* end of headers, mark all unknown SEARCH_HEADERs unmatched */ + mail_search_args_foreach(ctx->args, search_header_unmatch, ctx); + return; + } + + if (hdr->eoh) + return; + + index_mail_parse_header(part, hdr, ctx->index_context->mail); - if ((ctx->custom_header && name_len > 0) || - (name_len == 4 && memcasecmp(name, "Date", 4) == 0) || - (name_len == 4 && memcasecmp(name, "From", 4) == 0) || - (name_len == 2 && memcasecmp(name, "To", 2) == 0) || - (name_len == 2 && memcasecmp(name, "Cc", 2) == 0) || - (name_len == 3 && memcasecmp(name, "Bcc", 3) == 0) || - (name_len == 7 && memcasecmp(name, "Subject", 7) == 0)) { - ctx->name = name; - ctx->value = value; - ctx->name_len = name_len; - ctx->value_len = value_len; + if (ctx->custom_header || + strcasecmp(hdr->name, "Date") == 0 || + strcasecmp(hdr->name, "From") == 0 || + strcasecmp(hdr->name, "To") == 0 || + strcasecmp(hdr->name, "Cc") == 0 || + strcasecmp(hdr->name, "Bcc") == 0 || + strcasecmp(hdr->name, "Subject") == 0 || + strcasecmp(hdr->name, "In-Reply-To") == 0 || + strcasecmp(hdr->name, "Message-ID") == 0) { + ctx->hdr = hdr; ctx->custom_header = FALSE; mail_search_args_foreach(ctx->args, search_header_arg, ctx); - } else if (name_len == 0) { - /* last header, mark all unknown SEARCH_HEADERs unmatched */ - mail_search_args_foreach(ctx->args, search_header_unmatch, ctx); } }
--- a/src/lib-storage/index/index-storage.h Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-storage/index/index-storage.h Wed Mar 26 19:29:01 2003 +0200 @@ -8,7 +8,7 @@ typedef int write_func_t(struct ostream *, const void *, size_t); /* Return -1 = failure, 0 = don't write the header, 1 = write it */ -typedef int header_callback_t(const unsigned char *name, size_t len, +typedef int header_callback_t(const char *name, write_func_t *write_func, void *context); struct index_autosync_file {
--- a/src/lib-storage/index/mbox/mbox-save.c Wed Mar 26 17:40:16 2003 +0200 +++ b/src/lib-storage/index/mbox/mbox-save.c Wed Mar 26 19:29:01 2003 +0200 @@ -169,8 +169,8 @@ return str_c(str); } -static int save_header_callback(const unsigned char *name, size_t len, - write_func_t *write_func, void *context) +static int save_header_callback(const char *name, write_func_t *write_func, + void *context) { static const char *content_length = "Content-Length: "; struct mail_save_context *ctx = context; @@ -178,8 +178,7 @@ char *buf; size_t space; - switch (len) { - case 0: + if (name == NULL) { /* write system flags */ str = get_system_flags(ctx->flags->flags); if (write_func(ctx->output, str, strlen(str)) < 0) @@ -209,27 +208,29 @@ return -1; } ctx->eoh_offset = ctx->output->offset; - break; - case 5: - if (memcasecmp(name, "X-UID", 5) == 0) - return 0; - break; - case 6: - if (memcasecmp(name, "Status", 6) == 0) + return 1; + } + + switch (*name) { + case 'C': + case 'c': + if (strcasecmp(name, "Content-Length") == 0) return 0; break; - case 8: - if (memcasecmp(name, "X-Status", 8) == 0) + case 'S': + case 's': + if (strcasecmp(name, "Status") == 0) return 0; break; - case 10: - if (memcasecmp(name, "X-Keywords", 10) == 0) + case 'X': + case 'x': + if (strcasecmp(name, "X-UID") == 0) return 0; - if (memcasecmp(name, "X-IMAPbase", 10) == 0) + if (strcasecmp(name, "X-Status") == 0) return 0; - break; - case 14: - if (memcasecmp(name, "Content-Length", 14) == 0) + if (strcasecmp(name, "X-Keywords") == 0) + return 0; + if (strcasecmp(name, "X-IMAPbase") == 0) return 0; break; }