/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "array.h"
#include "str.h"
#include "rfc822-parser.h"
#include "test-common.h"
test_end();
}
+struct param {
+ const char *key, *value;
+};
+
+static void parse_content_type_param(const void *input, size_t input_len,
+ const char *content_type,
+ const struct param *params, size_t param_count,
+ bool expect_content_type, int expect_ret,
+ int idx)
+{
+ struct rfc822_parser_context parser;
+ const char *key;
+ string_t *value = t_str_new(64);
+ unsigned int i = 0;
+ int ret;
+
+ i_assert(params != NULL || param_count == 0);
+
+ rfc822_parser_init(&parser, input, input_len, NULL);
+ ret = rfc822_parse_content_type(&parser, value);
+ test_assert_idx((expect_content_type && ret == 1) ||
+ (!expect_content_type && ret == -1), idx);
+ test_assert_strcmp_idx(content_type, str_c(value), idx);
+
+ /* parse content type first */
+ while ((ret = rfc822_parse_content_param(&parser, &key, value)) > 0) {
+ if (i < param_count) {
+ test_assert_strcmp_idx(params[i].key, key, idx);
+ test_assert_strcmp_idx(params[i].value, str_c(value), idx);
+ }
+ i++;
+ }
+
+ test_assert_idx(expect_ret == ret, idx);
+ test_assert_idx(i == param_count, idx);
+ rfc822_parser_deinit(&parser);
+}
+
+#undef TEST_STRING
+#define TEST_STRING(a) (a), sizeof((a))-1
+
+#define X10(a) a a a a a a a a a a
+
+static void test_rfc822_parse_content_type_param(void)
+{
+ const char *input =
+ "(hello) text/plain ; (should we skip;comments=\"yes\")"
+ " param=value"
+ " ; param2=value2 (with comments (with comment) with;comment=\"yes\") "
+ " ; param3=\"value3 (with no comment ; or=value)\""
+ " ; param4=\"\xe7\xa8\xae\xe9\xa1\x9e\""
+ " ; \xe5\x90\xab\xe9\x87\x8f=\"\xe7\xa8\xae\xe9\xa1\x9e\""
+ " ; "X10(X10(X10("a")))"="X10(X10(X10("a")))
+ " ; "X10(X10(X10(X10("a"))))"="X10(X10(X10(X10("a"))))
+ " ; (comment) param7 (comment2) = (comment3) value7 (comment4) "
+ ;
+
+ const struct param output[] = {
+ { "param", "value" },
+ { "param2", "value2" },
+ { "param3", "value3 (with no comment ; or=value)" },
+ { "param4", "\xe7\xa8\xae\xe9\xa1\x9e" },
+ { "\xe5\x90\xab\xe9\x87\x8f", "\xe7\xa8\xae\xe9\xa1\x9e" },
+ { X10(X10(X10("a"))), X10(X10(X10("a"))) },
+ { X10(X10(X10(X10("a")))), X10(X10(X10(X10("a")))) },
+ { "param7", "value7" },
+ };
+ const struct param output5[] = {
+ { "charset", "" },
+ };
+
+ test_begin("rfc822 parse content type with params");
+
+ int idx = 0;
+ parse_content_type_param(input, strlen(input), "text/plain",
+ output, N_ELEMENTS(output), TRUE, 0, idx++);
+ parse_content_type_param(TEST_STRING("text/"), "", NULL, 0, FALSE, 0, idx++);
+ parse_content_type_param(
+ TEST_STRING("text/\0plain ;"), "", NULL, 0, FALSE, -1, idx++);
+ parse_content_type_param(
+ TEST_STRING("text/plain\0;charset=us-ascii"), "", NULL, 0, FALSE, -1, idx++);
+ parse_content_type_param(
+ TEST_STRING("text/plain;charset\0=us-ascii"), "text/plain", NULL, 0, TRUE, -1, idx++);
+ parse_content_type_param(
+ TEST_STRING("text/plain;charset="), "text/plain",
+ output5, N_ELEMENTS(output5), TRUE, 0, idx++);
+ parse_content_type_param(
+ TEST_STRING("text/plain ; ; charset=us-ascii"), "text/plain", NULL, 0, TRUE, -1, idx++);
+ /* build a large one */
+ ARRAY(struct param) output2;
+ t_array_init(&output2, 1000);
+ string_t *large = t_str_new(10000);
+ str_append(large, "text/plain");
+ for (unsigned int i = 0; i < 1000; i++) {
+ str_printfa(large, " ; param%u=\"value%u\"", i, i);
+ struct param *param = array_append_space(&output2);
+ param->key = t_strdup_printf("param%u", i);
+ param->value = t_strdup_printf("value%u", i);
+ }
+ parse_content_type_param(large->data, large->used,
+ "text/plain",
+ array_idx(&output2, 0), array_count(&output2),
+ TRUE, 0, idx++);
+ test_end();
+}
+
int main(void)
{
static void (*const test_functions[])(void) = {
test_rfc822_parse_domain_literal,
test_rfc822_parse_content_type,
test_rfc822_parse_content_param,
+ test_rfc822_parse_content_type_param,
NULL
};
return test_run(test_functions);