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;
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;
}
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);
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
+#include "buffer.h"
#include "istream.h"
#include "str.h"
#include "message-parser.h"
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();
}
(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);
#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"
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);
}
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)
#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,
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,
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;
}
#include "lib.h"
#include "buffer.h"
+#include "str.h"
#include "istream.h"
#include "ioloop.h"
#include "str.h"
};
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;
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,
}
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;
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,
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,
struct mbox_rewrite_context {
struct ostream *output;
- int failed;
uoff_t content_length;
unsigned int seq, uid;
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,
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) {
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);
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;
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,
}
}
-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,
/* 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"
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);
}
}
-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)
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);
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)
{
}
}
-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)
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;
+}
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
#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;
}
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;
}
}
- 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;
}
}
}
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);
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();
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;
pool_t pool;
struct index_mailbox *ibox;
+ buffer_t *header_buf;
enum mail_fetch_field wanted_fields;
const char *const *wanted_headers;
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
#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)
{
}
}
-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;
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;
}
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;
}
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;
/* 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;
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') {
{
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. */
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 */
/* 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);
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();
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:
}
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);
}
}
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 {
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;
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)
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;
}