]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Better handling for multiline headers. Before we skipped headers larger than
authorTimo Sirainen <tss@iki.fi>
Wed, 26 Mar 2003 17:29:01 +0000 (19:29 +0200)
committerTimo Sirainen <tss@iki.fi>
Wed, 26 Mar 2003 17:29:01 +0000 (19:29 +0200)
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.

--HG--
branch : HEAD

18 files changed:
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

index c901898348d3304c5bd11868958a59c800e5aabe..c1f37341fb9cabcbf09cc624ff80ff845ecd0b23 100644 (file)
@@ -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 @@ static int fetch_header_append(struct fetch_header_field_context *ctx,
        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 @@ static int fetch_header_fields(struct istream *input, const char *section,
        }
 
        ctx->dest_size = 0;
-       message_parse_header(NULL, input, NULL, fetch_header_field, ctx);
 
-       /* 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);
+       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;
+
+               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);
index 3dc55b11e7eb6c6ab10431a157004772c97f95ef..6fd6706d1f2a81971cb5c4d3d36e8fc904288cbd 100644 (file)
@@ -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,99 +142,114 @@ static void parse_content_language(const unsigned char *value, size_t value_len,
        message_tokenize_deinit(tok);
 }
 
-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)
+static void parse_content_header(struct message_part_body_data *d,
+                                struct message_header_line *hdr,
+                                pool_t pool)
 {
-       pool_t pool = context;
-       struct message_part_body_data *part_data;
-       int parent_rfc822;
+       const char *name = hdr->name;
+       const unsigned char *value;
+       size_t value_len;
 
-       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 (strncasecmp(name, "Content-", 8) != 0)
                return;
+       name += 8;
 
-       if (part->context == NULL) {
-               /* initialize message part data */
-               part->context = part_data =
-                       p_new(pool, struct message_part_body_data, 1);
-               part_data->pool = pool;
+       if (hdr->continues) {
+               hdr->use_full_value = TRUE;
+               return;
        }
-       part_data = part->context;
 
-       t_push();
+       value = hdr->full_value;
+       value_len = hdr->full_value_len;
 
-       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);
-               }
+       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 11:
-               if (memcasecmp(name, "Content-MD5", 11) == 0 &&
-                   part_data->content_md5 == NULL) {
-                       part_data->content_md5 =
-                               imap_quote(pool, value, value_len);
-               }
+       case 'm':
+       case 'M':
+               if (strcasecmp(name, "MD5") == 0 && d->content_md5 == NULL)
+                       d->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));
+       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 16:
-               if (memcasecmp(name, "Content-Language", 16) == 0)
-                       parse_content_language(value, value_len, part_data);
+       case 'l':
+       case 'L':
+               if (strcasecmp(name, "Language") == 0 &&
+                   d->content_language == NULL)
+                       parse_content_language(value, value_len, d);
                break;
 
-       case 19:
-               if (memcasecmp(name, "Content-Description", 19) == 0 &&
-                   part_data->content_description == NULL) {
-                       part_data->content_description =
+       case 'd':
+       case 'D':
+               if (strcasecmp(name, "Description") == 0 &&
+                   d->content_description == NULL) {
+                       d->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);
+               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,
-                                                    part_data);
-                       part_data->content_disposition_params =
-                               p_strdup_empty(pool, str_c(part_data->str));
+                                                    parse_save_params_list, d);
+                       d->content_disposition_params =
+                               p_strdup_empty(pool, str_c(d->str));
                }
                break;
+       }
+}
 
-       case 25:
-               if (memcasecmp(name, "Content-Transfer-Encoding", 25) != 0 ||
-                   part_data->content_transfer_encoding != NULL)
-                       break;
+static void parse_header(struct message_part *part,
+                         struct message_header_line *hdr, void *context)
+{
+       pool_t pool = context;
+       struct message_part_body_data *part_data;
+       int parent_rfc822;
 
-               message_content_parse_header(value, value_len,
-                                            parse_content_transfer_encoding,
-                                            NULL, part_data);
-               break;
+       if (hdr == NULL)
+               return;
+
+       parent_rfc822 = part->parent != NULL &&
+               (part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822);
+       if (!parent_rfc822 && strncasecmp(hdr->name, "Content-", 8) != 0)
+               return;
+
+       if (part->context == NULL) {
+               /* initialize message part data */
+               part->context = part_data =
+                       p_new(pool, struct message_part_body_data, 1);
+               part_data->pool = pool;
        }
+       part_data = part->context;
+
+       t_push();
+
+       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 @@ static void part_parse_headers(struct message_part *part, struct istream *input,
                              (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);
index ac980af80cd1c3212141044bf513dbece456c441..7aa97f155df3f16612db6f5cf2a969f2a777a418 100644 (file)
@@ -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 @@ struct message_part_envelope_data {
        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 @@ void imap_envelope_parse_header(pool_t pool,
        }
        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 6:
-               if (memcasecmp(name, "Sender", 6) == 0 && d->sender == NULL) {
-                       d->sender = message_address_parse(pool, value,
-                                                         value_len, 0);
-               }
+       case IMAP_ENVELOPE_IN_REPLY_TO:
+               str_p = &d->in_reply_to;
                break;
-       case 7:
-               if (memcasecmp(name, "Subject", 7) == 0 && d->subject == NULL)
-                       d->subject = imap_quote(pool, value, value_len);
+
+       case IMAP_ENVELOPE_CC:
+               addr_p = &d->cc;
                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_BCC:
+               addr_p = &d->bcc;
+               break;
+       case IMAP_ENVELOPE_FROM:
+               addr_p = &d->from;
+               break;
+       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;
+       }
+
+       if (addr_p != NULL) {
+               *addr_p = message_address_parse(pool, hdr->full_value,
+                                               hdr->full_value_len, 0);
        }
 
-       t_pop();
+       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)
index 21916e365e87b58c819cfbdccada6360debd5f9e..9952cd71adbb3081489772d19e7b619c9ec72c15 100644 (file)
@@ -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 @@ enum imap_envelope_result_type {
 
 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,
index 3a7c02070d78bf96521b3d8898d28d4b02db70b0..b58cc78cd7e4280ce6e5d867f2917ddc8fb3c242 100644 (file)
@@ -53,11 +53,18 @@ void imap_quote_append(string_t *str, const unsigned char *value,
 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;
 }
index 89014c0a527afc883b864f97e3f0c90916223eed..11a30e1ac6f1b4c25c572998672c2fbff0ee610f 100644 (file)
@@ -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 @@ struct header_update_context {
 };
 
 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 @@ static void update_header_cb(struct message_part *part,
                        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,
index cd11edf71cb7ba718b2498244e393cc09cbb42a5..7f81565792427f6129dfd7d5bee63b981f3b3242 100644 (file)
@@ -248,16 +248,14 @@ static void mbox_parse_imapbase(const unsigned char *value, size_t len,
 }
 
 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 @@ void mbox_header_cb(struct message_part *part __attr_unused__,
                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,
index 75fba443d36146ed1df8273dff8ee74d382b8e0f..b7adc828e2841a9b9859d66323833b989bbec9a4 100644 (file)
@@ -33,10 +33,8 @@ void mbox_header_init_context(struct mbox_header_context *ctx,
                              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,
index 025e13f4617281971c7e6d1a4338390c45bee306..cacc6355698411d5453446299b228aac6f54fbfc 100644 (file)
@@ -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 @@ static const char *strip_custom_flags(const unsigned char *value, size_t len,
        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;
-
-       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) {
+       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;
+                       }
+
+                       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) {
+               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 (!hdr->eoh) {
                /* 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);
+               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);
        }
 
-       if (ctx->output->closed)
-               ctx->failed = TRUE;
+       return !ctx->output->closed;
 }
 
 static int mbox_write_header(struct mail_index *index,
@@ -323,6 +367,8 @@ static int mbox_write_header(struct mail_index *index,
           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 @@ static int mbox_write_header(struct mail_index *index,
        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);
index bb98017ad1e8b9436804eefdc29c9f01ec6ac47d..18d0b8f8fdd03ddea3ce5bd3b71b9b063635dd82 100644 (file)
@@ -28,7 +28,6 @@ struct body_search_context {
 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 @@ struct part_search_context {
        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 parse_content_encoding(const unsigned char *value, size_t value_len,
        }
 }
 
-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);
 
-       return ctx->found;
+       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;
+                       }
+               }
+
+               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,
index c28ff4b40ad4a8e6223d8b84fc29bba734a6e760..d52a0af52d64118ac1dfec1d549788c795044656 100644 (file)
@@ -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 @@ struct parser_context {
        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 @@ parse_content_type_param(const unsigned char *name, size_t name_len,
        }
 }
 
-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 @@ message_parse_multipart(struct istream *input,
 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 @@ message_parse_part(struct istream *input, struct parser_context *parser_ctx)
        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 @@ __break:
        }
 }
 
-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 @@ message_skip_boundary(struct istream *input,
 
        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;
+}
index baf8abb01c3ded7997b86b95cc50064ea22e5229..d68e0fc745a3436e59ccf9a9e033a21afeb69b01 100644 (file)
@@ -31,27 +31,44 @@ struct message_part {
        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
index 9e3aaf43e8ffe7d7af3ec506acfd49197f44614a..b23a00e905c4dadcbe87f21e64ce384b202e9f4d 100644 (file)
 
 #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_init_parse_header(struct index_mail *mail)
 }
 
 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,37 +163,51 @@ void index_mail_parse_header(struct message_part *part __attr_unused__,
                }
        }
 
-       if (name_len == 4 && data->save_sent_time &&
-           memcasecmp(name, "date",4) == 0) {
-               if (!message_date_parse(value, value_len, &data->sent_time,
-                                       &data->sent_timezone)) {
-                       /* 0 == parse error */
+       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;
                }
-               data->save_sent_time = FALSE;
+               return;
        }
 
-       if (name_len == 0) {
-               /* end of headers */
-               if (data->save_sent_time) {
-                       /* not found */
+       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;
                        data->sent_timezone = 0;
-                       data->save_sent_time = FALSE;
                }
+               data->save_sent_time = FALSE;
        }
 
-       for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
-               if (hdr->name_len == name_len &&
-                   memcasecmp(hdr->name, name, name_len) == 0) {
+        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 (hdr->value == NULL) {
-                               hdr->value = p_strndup(mail->pool,
-                                                      value, value_len);
+                       if (cached_hdr->value != NULL)
+                               break;
+
+                       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 @@ static const char *get_header(struct mail *_mail, const char *field)
        }
 
        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 @@ static const char *get_first_mailbox(struct mail *_mail, const char *field)
        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 index_mail_next(struct index_mail *mail, struct mail_index_record *rec)
                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;
index 6d57363e234e0809532000f755576e83dd76d0dc..7c8e10213234e10e5aae14c057da9bc4a6324377 100644 (file)
@@ -38,6 +38,7 @@ struct index_mail {
 
        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_deinit(struct index_mail *mail);
 
 void index_mail_init_parse_header(struct index_mail *mail);
 void index_mail_parse_header(struct message_part *part,
-                            const unsigned char *name, size_t name_len,
-                            const unsigned char *value, size_t value_len,
-                            void *context);
+                            struct message_header_line *hdr, void *context);
 
 #endif
index c18151201ffe4b79d180f346d6ce035a915da763..6d070a623c6922c6375913a318527f3b4ce1086c 100644 (file)
@@ -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 set_write_error(struct mail_storage *storage,
        }
 }
 
-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;
-
-       if (ctx->failed)
-               return;
-
-       ret = ctx->header_callback(name, name_len, ctx->write_func,
-                                  ctx->context);
-       if (ret <= 0) {
-               if (ret < 0)
-                       ctx->failed = TRUE;
-               return;
-       }
+       struct message_header_parser_ctx *hdr_ctx;
+       struct message_header_line *hdr;
+       int ret, failed = FALSE;
+
+       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;
+               }
 
-       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;
+               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 (ctx->write_func(ctx->output, name, value_len) < 0) {
-               set_write_error(ctx->storage, ctx->output, ctx->path);
-               ctx->failed = TRUE;
+               /* end of headers */
+               write_func(output, "\n", 1);
        }
+       message_parse_header_deinit(hdr_ctx);
+
+       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 @@ int index_storage_save(struct mail_storage *storage, const char *path,
        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;
        }
 
index a791f4f6f0507ab9617babad38a745e92ca26180..7d624d166970429c3886ee9b5c714aaaee6694bd 100644 (file)
@@ -41,8 +41,7 @@ struct search_header_context {
         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 void search_cached_arg(struct mail_search_arg *arg, void *context)
 }
 
 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 @@ static int search_sent(enum mail_search_arg_type type, const char *search_value,
 
        /* 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 @@ static int search_arg_match_envelope(struct mail_search_context *ctx,
                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 @@ static void search_header_arg(struct mail_search_arg *arg, void *context)
 {
        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 @@ static void search_header_arg(struct mail_search_arg *arg, void *context)
        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 @@ static void search_header_arg(struct mail_search_arg *arg, void *context)
                /* 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 @@ static void search_header_arg(struct mail_search_arg *arg, void *context)
                        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 @@ static void search_header_unmatch(struct mail_search_arg *arg,
        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_unmatch(struct mail_search_arg *arg,
 }
 
 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 ((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 (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 ||
+           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);
        }
 }
 
index 7bf527ec42c7b28fb1173d691fa3ace158907a83..b4d37a0c924a4400986c9cbe5f874d72f2f9044a 100644 (file)
@@ -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 {
index 46460ca8c685ebd7f0b3345a63eeb39e72585d05..b95adc70cb5639d88a12bace2cafb8b2dc69dde6 100644 (file)
@@ -169,8 +169,8 @@ static const char *get_custom_flags(const struct mail_full_flags *flags)
        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 @@ static int save_header_callback(const unsigned char *name, size_t len,
        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 @@ static int save_header_callback(const unsigned char *name, size_t len,
                        return -1;
                }
                ctx->eoh_offset = ctx->output->offset;
-               break;
-       case 5:
-               if (memcasecmp(name, "X-UID", 5) == 0)
+               return 1;
+       }
+
+       switch (*name) {
+       case 'C':
+       case 'c':
+               if (strcasecmp(name, "Content-Length") == 0)
                        return 0;
                break;
-       case 6:
-               if (memcasecmp(name, "Status", 6) == 0)
+       case 'S':
+       case 's':
+               if (strcasecmp(name, "Status") == 0)
                        return 0;
                break;
-       case 8:
-               if (memcasecmp(name, "X-Status", 8) == 0)
+       case 'X':
+       case 'x':
+               if (strcasecmp(name, "X-UID") == 0)
                        return 0;
-               break;
-       case 10:
-               if (memcasecmp(name, "X-Keywords", 10) == 0)
+               if (strcasecmp(name, "X-Status") == 0)
                        return 0;
-               if (memcasecmp(name, "X-IMAPbase", 10) == 0)
+               if (strcasecmp(name, "X-Keywords") == 0)
                        return 0;
-               break;
-       case 14:
-               if (memcasecmp(name, "Content-Length", 14) == 0)
+               if (strcasecmp(name, "X-IMAPbase") == 0)
                        return 0;
                break;
        }