static void test_rfc822_parse_content_param(void) { const char *input = "; key1=value1#$!%&'*+-.^_`{|}~" "; key2=\" \\\"(),/:;<=>?@[\\\\]\""; const struct { const char *key, *value; } output[] = { { "key1", "value1#$!%&'*+-.^_`{|}~" }, { "key2", " \"(),/:;<=>?@[\\]" } }; struct rfc822_parser_context parser; const char *key, *value; unsigned int i = 0; int ret; test_begin("rfc822 parse content param"); rfc822_parser_init(&parser, (const void *)input, strlen(input), NULL); while ((ret = rfc822_parse_content_param(&parser, &key, &value)) > 0 && i < N_ELEMENTS(output)) { test_assert_idx(strcmp(output[i].key, key) == 0, i); test_assert_idx(strcmp(output[i].value, value) == 0, i); i++; } test_assert(ret == 0); test_assert(i == N_ELEMENTS(output)); test_end(); }
int rfc2231_parse(struct rfc822_parser_context *ctx, const char *const **result_r) { ARRAY_TYPE(const_string) result; ARRAY(struct rfc2231_parameter) rfc2231_params_arr; struct rfc2231_parameter rfc2231_param; const struct rfc2231_parameter *rfc2231_params; const char *key, *value, *p, *p2; string_t *str; unsigned int i, j, count, next, next_idx; bool ok, have_extended, broken = FALSE; 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) { if (ret < 0) { /* try to continue anyway.. */ broken = TRUE; if (ctx->data == ctx->end) break; ctx->data++; continue; } p = strchr(key, '*'); if (p != NULL) { p2 = p; if (p[1] != '\0') { 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 */ array_append_zero(&result); /* NULL-terminate */ *result_r = array_idx(&result, 0); return broken ? -1 : 0; } /* Merge the RFC 2231 parameters. Since their order isn't guaranteed to be ascending, start by sorting them. */ array_sort(&rfc2231_params_arr, rfc2231_parameter_cmp); rfc2231_params = array_get(&rfc2231_params_arr, &count); /* 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); } } array_append_zero(&result); /* NULL-terminate */ *result_r = array_idx(&result, 0); return broken ? -1 : 0; }