Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7950:3412c43d6707 HEAD
Merge RFC 2231 header continuations in BODY/BODYSTRUCTURE replies. Also use
them internally while parsing messages.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 24 Jun 2008 16:49:36 +0300 |
parents | e787f6cae97c |
children | 5e3b995372d4 |
files | src/lib-imap/imap-bodystructure.c src/lib-mail/Makefile.am src/lib-mail/message-decoder.c src/lib-mail/message-parser.c src/lib-mail/rfc2231-parser.c src/lib-mail/rfc2231-parser.h src/tests/test-mail.c |
diffstat | 7 files changed, 242 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- 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 @@ 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 @@ /* 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); + imap_quote_append_string(str, results[0], TRUE); str_append_c(str, ' '); - imap_quote_append_string(str, value, TRUE); + imap_quote_append_string(str, results[1], TRUE); } if (!charset_found && @@ -106,7 +108,7 @@ 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 @@ /* parse parameters and save them */ str_truncate(str, 0); - while (rfc822_parse_content_param(&parser, &key, &value) > 0) { + (void)rfc2231_parse(&parser, &results); + for (; *results != NULL; results += 2) { str_append_c(str, ' '); - imap_quote_append_string(str, key, TRUE); + imap_quote_append_string(str, results[0], TRUE); str_append_c(str, ' '); - imap_quote_append_string(str, value, TRUE); + imap_quote_append_string(str, results[1], TRUE); } if (str_len(str) > 0) { data->content_disposition_params =
--- 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 @@ message-send.c \ message-size.c \ quoted-printable.c \ + rfc2231-parser.c \ rfc822-parser.c headers = \ @@ -37,6 +38,7 @@ message-send.h \ message-size.h \ quoted-printable.h \ + rfc2231-parser.h \ rfc822-parser.h if INSTALL_HEADERS
--- 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 @@ 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 @@ 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; } }
--- 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 @@ 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 @@ 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; } }
--- /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) + str_append(str, "''"); + for (j = i; j < next; j++) { + if (!rfc2231_params[j].extended && + have_extended) { + rfc2231_escape(str, + rfc2231_params[j].value); + } else { + str_append(str, + rfc2231_params[j].value); + } + } + key = rfc2231_params[i].key; + if (have_extended) + key = t_strconcat(key, "*", NULL); + value = t_strdup(str_c(str)); + array_append(&result, &key, 1); + array_append(&result, &value, 1); + } + } + (void)array_append_space(&result); /* NULL-terminate */ + *result_r = array_idx(&result, 0); + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-mail/rfc2231-parser.h Tue Jun 24 16:49:36 2008 +0300 @@ -0,0 +1,11 @@ +#ifndef RFC2231_PARSER_H +#define RFC2231_PARSER_H + +/* Parse all content parameters using rfc822_parse_content_param() and return + them as a NULL-terminated [key, value] array. RFC 2231-style continuations + are merged to a single key. Returns -1 if some of the input was invalid + (but valid key/value pairs are still returned), 0 if everything looked ok. */ +int rfc2231_parse(struct rfc822_parser_context *ctx, + const char *const **result_r); + +#endif
--- a/src/tests/test-mail.c Tue Jun 24 13:37:55 2008 +0300 +++ b/src/tests/test-mail.c Tue Jun 24 16:49:36 2008 +0300 @@ -3,6 +3,8 @@ #include "lib.h" #include "str.h" #include "istream.h" +#include "rfc822-parser.h" +#include "rfc2231-parser.h" #include "message-address.h" #include "message-date.h" #include "message-parser.h" @@ -249,6 +251,45 @@ test_out("message_parser()", success); } +static void test_rfc2231_parser(void) +{ + const char *input = + "; key*2=ba%" + "; key2*0=a" + "; key3*0*=us-ascii'en'xyz" + "; key*0=\"foo\"" + "; key2*1*=b%25" + "; key3*1=plop%" + "; key*1=baz"; + const char *output[] = { + "key", + "foobazba%", + "key2*", + "''ab%25", + "key3*", + "us-ascii'en'xyzplop%25", + NULL + }; + struct rfc822_parser_context parser; + const char *const *result; + unsigned int i; + bool success; + + rfc822_parser_init(&parser, (const void *)input, strlen(input), NULL); + if (rfc2231_parse(&parser, &result) < 0) + success = FALSE; + else { + success = TRUE; + for (i = 0; output[i] != NULL && result[i] != NULL; i++) { + if (strcmp(output[i], result[i]) != 0) + break; + } + if (output[i] != NULL || result[i] != NULL) + success = FALSE; + } + test_out("rfc2231_parse()", success); +} + static void filter_callback(struct message_header_line *hdr, bool *matched, void *context ATTR_UNUSED) { @@ -309,6 +350,7 @@ test_message_address(); test_message_date_parse(); test_message_parser(); + test_rfc2231_parser(); test_istream_filter(); return test_deinit(); }