Ejemplo n.º 1
0
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;
}