dovecot-1.2: Merge RFC 2231 header continuations in BODY/BODYSTR...

dovecot at dovecot.org dovecot at dovecot.org
Tue Jun 24 16:49:42 EEST 2008


details:   http://hg.dovecot.org/dovecot-1.2/rev/3412c43d6707
changeset: 7950:3412c43d6707
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Jun 24 16:49:36 2008 +0300
description:
Merge RFC 2231 header continuations in BODY/BODYSTRUCTURE replies. Also use
them internally while parsing messages.

diffstat:

7 files changed, 245 insertions(+), 21 deletions(-)
src/lib-imap/imap-bodystructure.c |   27 +++---
src/lib-mail/Makefile.am          |    2 
src/lib-mail/message-decoder.c    |   12 +-
src/lib-mail/message-parser.c     |   11 +-
src/lib-mail/rfc2231-parser.c     |  161 +++++++++++++++++++++++++++++++++++++
src/lib-mail/rfc2231-parser.h     |   11 ++
src/tests/test-mail.c             |   42 +++++++++

diffs (truncated from 404 to 300 lines):

diff -r e787f6cae97c -r 3412c43d6707 src/lib-imap/imap-bodystructure.c
--- a/src/lib-imap/imap-bodystructure.c	Tue Jun 24 13:37:55 2008 +0300
+++ b/src/lib-imap/imap-bodystructure.c	Tue Jun 24 16:49:36 2008 +0300
@@ -6,6 +6,7 @@
 #include "str.h"
 #include "message-parser.h"
 #include "rfc822-parser.h"
+#include "rfc2231-parser.h"
 #include "imap-parser.h"
 #include "imap-quote.h"
 #include "imap-envelope.h"
@@ -37,7 +38,7 @@ static void parse_content_type(struct me
 			       struct message_header_line *hdr)
 {
 	struct rfc822_parser_context parser;
-	const char *key, *value;
+	const char *value, *const *results;
 	string_t *str;
 	unsigned int i;
 	bool charset_found = FALSE;
@@ -64,14 +65,15 @@ static void parse_content_type(struct me
 
 	/* parse parameters and save them */
 	str_truncate(str, 0);
-	while (rfc822_parse_content_param(&parser, &key, &value) > 0) {
-		if (strcasecmp(key, "charset") == 0)
+	(void)rfc2231_parse(&parser, &results);
+	for (; *results != NULL; results += 2) {
+		if (strcasecmp(results[0], "charset") == 0)
 			charset_found = TRUE;
 
 		str_append_c(str, ' ');
-		imap_quote_append_string(str, key, TRUE);
-		str_append_c(str, ' ');
-		imap_quote_append_string(str, value, TRUE);
+		imap_quote_append_string(str, results[0], TRUE);
+		str_append_c(str, ' ');
+		imap_quote_append_string(str, results[1], TRUE);
 	}
 
 	if (!charset_found &&
@@ -106,7 +108,7 @@ static void parse_content_disposition(st
 				      struct message_header_line *hdr)
 {
 	struct rfc822_parser_context parser;
-	const char *key, *value;
+	const char *const *results;
 	string_t *str;
 
 	rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
@@ -120,11 +122,12 @@ static void parse_content_disposition(st
 
 	/* parse parameters and save them */
 	str_truncate(str, 0);
-	while (rfc822_parse_content_param(&parser, &key, &value) > 0) {
-		str_append_c(str, ' ');
-		imap_quote_append_string(str, key, TRUE);
-		str_append_c(str, ' ');
-		imap_quote_append_string(str, value, TRUE);
+	(void)rfc2231_parse(&parser, &results);
+	for (; *results != NULL; results += 2) {
+		str_append_c(str, ' ');
+		imap_quote_append_string(str, results[0], TRUE);
+		str_append_c(str, ' ');
+		imap_quote_append_string(str, results[1], TRUE);
 	}
 	if (str_len(str) > 0) {
 		data->content_disposition_params =
diff -r e787f6cae97c -r 3412c43d6707 src/lib-mail/Makefile.am
--- a/src/lib-mail/Makefile.am	Tue Jun 24 13:37:55 2008 +0300
+++ b/src/lib-mail/Makefile.am	Tue Jun 24 16:49:36 2008 +0300
@@ -19,6 +19,7 @@ libmail_a_SOURCES = \
 	message-send.c \
 	message-size.c \
 	quoted-printable.c \
+	rfc2231-parser.c \
 	rfc822-parser.c
 
 headers = \
@@ -37,6 +38,7 @@ headers = \
 	message-send.h \
 	message-size.h \
 	quoted-printable.h \
+	rfc2231-parser.h \
 	rfc822-parser.h
 
 if INSTALL_HEADERS
diff -r e787f6cae97c -r 3412c43d6707 src/lib-mail/message-decoder.c
--- a/src/lib-mail/message-decoder.c	Tue Jun 24 13:37:55 2008 +0300
+++ b/src/lib-mail/message-decoder.c	Tue Jun 24 16:49:36 2008 +0300
@@ -8,6 +8,7 @@
 #include "charset-utf8.h"
 #include "quoted-printable.h"
 #include "rfc822-parser.h"
+#include "rfc2231-parser.h"
 #include "message-parser.h"
 #include "message-header-decode.h"
 #include "message-decoder.h"
@@ -112,7 +113,7 @@ parse_content_type(struct message_decode
 		   struct message_header_line *hdr)
 {
 	struct rfc822_parser_context parser;
-	const char *key, *value;
+	const char *const *results;
 	string_t *str;
 
 	if (ctx->content_charset != NULL)
@@ -124,10 +125,11 @@ parse_content_type(struct message_decode
 	if (rfc822_parse_content_type(&parser, str) <= 0)
 		return;
 
-	while (rfc822_parse_content_param(&parser, &key, &value) > 0) {
-		if (strcasecmp(key, "charset") == 0) {
-			ctx->content_charset = i_strdup(value);
-			ctx->charset_utf8 = charset_is_utf8(value);
+	(void)rfc2231_parse(&parser, &results);
+	for (; *results != NULL; results += 2) {
+		if (strcasecmp(results[0], "charset") == 0) {
+			ctx->content_charset = i_strdup(results[1]);
+			ctx->charset_utf8 = charset_is_utf8(results[1]);
 			break;
 		}
 	}
diff -r e787f6cae97c -r 3412c43d6707 src/lib-mail/message-parser.c
--- a/src/lib-mail/message-parser.c	Tue Jun 24 13:37:55 2008 +0300
+++ b/src/lib-mail/message-parser.c	Tue Jun 24 16:49:36 2008 +0300
@@ -4,6 +4,7 @@
 #include "str.h"
 #include "istream.h"
 #include "rfc822-parser.h"
+#include "rfc2231-parser.h"
 #include "message-parser.h"
 
 /* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
@@ -410,7 +411,7 @@ static void parse_content_type(struct me
 			       struct message_header_line *hdr)
 {
 	struct rfc822_parser_context parser;
-	const char *key, *value;
+	const char *const *results;
 	string_t *content_type;
 
 	if (ctx->part_seen_content_type)
@@ -441,9 +442,11 @@ static void parse_content_type(struct me
 	    ctx->last_boundary != NULL)
 		return;
 
-	while (rfc822_parse_content_param(&parser, &key, &value) > 0) {
-		if (strcasecmp(key, "boundary") == 0) {
-			ctx->last_boundary = p_strdup(ctx->parser_pool, value);
+	(void)rfc2231_parse(&parser, &results);
+	for (; *results != NULL; results += 2) {
+		if (strcasecmp(results[0], "boundary") == 0) {
+			ctx->last_boundary =
+				p_strdup(ctx->parser_pool, results[1]);
 			break;
 		}
 	}
diff -r e787f6cae97c -r 3412c43d6707 src/lib-mail/rfc2231-parser.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/rfc2231-parser.c	Tue Jun 24 16:49:36 2008 +0300
@@ -0,0 +1,161 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "rfc822-parser.h"
+#include "rfc2231-parser.h"
+
+#include <stdlib.h>
+
+struct rfc2231_parameter {
+	const char *key, *value;
+	unsigned int idx;
+	bool extended;
+};
+
+static int rfc2231_parameter_cmp(const void *p1, const void *p2)
+{
+	const struct rfc2231_parameter *r1 = p1, *r2 = p2;
+	int ret;
+
+	ret = strcmp(r1->key, r2->key);
+	if (ret != 0)
+		return ret;
+
+	return r1->idx < r2->idx ? -1 :
+		(r1-> idx > r2->idx ? 1 : 0);
+}
+
+static void rfc2231_escape(string_t *dest, const char *src)
+{
+	for (; *src != '\0'; src++) {
+		if (*src == '%')
+			str_append(dest, "%25");
+		else
+			str_append_c(dest, *src);
+	}
+}
+
+int rfc2231_parse(struct rfc822_parser_context *ctx,
+		  const char *const **result_r)
+{
+	ARRAY_TYPE(const_string) result;
+	ARRAY_DEFINE(rfc2231_params_arr, struct rfc2231_parameter);
+	struct rfc2231_parameter rfc2231_param, *rfc2231_params;
+	const char *key, *value, *p, *p2;
+	string_t *str;
+	unsigned int i, j, count, next, next_idx;
+	bool ok, have_extended;
+	int ret;
+
+	/* Get a list of all parameters. RFC 2231 uses key*<n>[*]=value pairs,
+	   which we want to merge to a key[*]=value pair. Save them to a
+	   separate array. */
+	memset(&rfc2231_param, 0, sizeof(rfc2231_param));
+	t_array_init(&result, 8);
+	t_array_init(&rfc2231_params_arr, 8);
+	while ((ret = rfc822_parse_content_param(ctx, &key, &value)) > 0) {
+		p = strchr(key, '*');
+		if (p != NULL) {
+			p2 = p++;
+			rfc2231_param.idx = 0;
+			for (; *p >= '0' && *p <= '9'; p++) {
+				rfc2231_param.idx =
+					rfc2231_param.idx*10 + *p - '0';
+			}
+			if (*p != '*')
+				rfc2231_param.extended = FALSE;
+			else {
+				rfc2231_param.extended = TRUE;
+				p++;
+			}
+			if (*p != '\0')
+				p = NULL;
+			else {
+				rfc2231_param.key = t_strdup_until(key, p2);
+				rfc2231_param.value = value;
+				array_append(&rfc2231_params_arr,
+					     &rfc2231_param, 1);
+			}
+		}
+		if (p == NULL) {
+			array_append(&result, &key, 1);
+			array_append(&result, &value, 1);
+		}
+	}
+
+	if (array_count(&rfc2231_params_arr) == 0) {
+		/* No RFC 2231 parameters */
+		(void)array_append_space(&result); /* NULL-terminate */
+		*result_r = array_idx(&result, 0);
+		return ret;
+	}
+
+	/* Merge the RFC 2231 parameters. Since their order isn't guaranteed to
+	   be ascending, start by sorting them. */
+	rfc2231_params = array_get_modifiable(&rfc2231_params_arr, &count);
+	qsort(rfc2231_params, count, sizeof(*rfc2231_params),
+	      rfc2231_parameter_cmp);
+
+	/* keys are now sorted primarily by their name and secondarily by
+	   their index. If any indexes are missing, fallback to assuming
+	   these aren't RFC 2231 encoded parameters. */
+	str = t_str_new(64);
+	for (i = 0; i < count; i = next) {
+		ok = TRUE;
+		have_extended = FALSE;
+		next_idx = 0;
+		for (j = i; j < count; j++) {
+			if (strcasecmp(rfc2231_params[i].key,
+				       rfc2231_params[j].key) != 0)
+				break;
+			if (rfc2231_params[j].idx != next_idx) {
+				/* missing indexes */
+				ok = FALSE;
+			}
+			if (rfc2231_params[j].extended)
+				have_extended = TRUE;
+			next_idx++;
+		}
+		next = j;
+
+		if (!ok) {
+			/* missing indexes */
+			for (j = i; j < next; j++) {
+				key = t_strdup_printf(
+					rfc2231_params[j].extended ?
+					"%s*%u*" : "%s*%u",
+					rfc2231_params[j].key,
+					rfc2231_params[j].idx);
+				array_append(&result, &key, 1);
+				array_append(&result,
+					     &rfc2231_params[j].value, 1);
+			}
+		} else {
+			/* everything was successful */
+			str_truncate(str, 0);
+			if (!rfc2231_params[i].extended && have_extended)


More information about the dovecot-cvs mailing list