them internally while parsing messages.
--HG--
branch : HEAD
#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"
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;
/* 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 &&
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);
/* 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 =
message-send.c \
message-size.c \
quoted-printable.c \
+ rfc2231-parser.c \
rfc822-parser.c
headers = \
message-send.h \
message-size.h \
quoted-printable.h \
+ rfc2231-parser.h \
rfc822-parser.h
if INSTALL_HEADERS
#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"
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)
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;
}
}
#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.
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)
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
+/* 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
+#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
#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"
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)
{
test_message_address();
test_message_date_parse();
test_message_parser();
+ test_rfc2231_parser();
test_istream_filter();
return test_deinit();
}