rfc2231-parser.c revision efe78d3ba24fc866af1c79b9223dc0809ba26cad
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2008-2016 Dovecot authors, see the included COPYING file */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen#include "lib.h"
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen#include "array.h"
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen#include "str.h"
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen#include "rfc822-parser.h"
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen#include "rfc2231-parser.h"
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainenstruct rfc2231_parameter {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen const char *key, *value;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen unsigned int idx;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen bool extended;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen};
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainenstatic int rfc2231_parameter_cmp(const struct rfc2231_parameter *r1,
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen const struct rfc2231_parameter *r2)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen{
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen int ret;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen ret = strcmp(r1->key, r2->key);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (ret != 0)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen return ret;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen return r1->idx < r2->idx ? -1 :
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen (r1-> idx > r2->idx ? 1 : 0);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen}
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainenstatic void rfc2231_escape(string_t *dest, const char *src)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen{
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen for (; *src != '\0'; src++) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (*src == '%')
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen str_append(dest, "%25");
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen else
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen str_append_c(dest, *src);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen}
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainenint rfc2231_parse(struct rfc822_parser_context *ctx,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen const char *const **result_r)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen{
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen ARRAY_TYPE(const_string) result;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ARRAY(struct rfc2231_parameter) rfc2231_params_arr;
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen struct rfc2231_parameter rfc2231_param;
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen const struct rfc2231_parameter *rfc2231_params;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen const char *key, *value, *p, *p2;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen string_t *str;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen unsigned int i, j, count, next, next_idx;
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen bool ok, have_extended, broken = FALSE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen int ret;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* Get a list of all parameters. RFC 2231 uses key*<n>[*]=value pairs,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen which we want to merge to a key[*]=value pair. Save them to a
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen separate array. */
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&rfc2231_param);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen t_array_init(&result, 8);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen t_array_init(&rfc2231_params_arr, 8);
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen while ((ret = rfc822_parse_content_param(ctx, &key, &value)) != 0) {
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen if (ret < 0) {
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen /* try to continue anyway.. */
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen broken = TRUE;
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen if (ctx->data == ctx->end)
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen break;
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen ctx->data++;
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen continue;
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen p = strchr(key, '*');
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (p != NULL) {
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen p2 = p;
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen if (p[1] != '\0') {
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen p++;
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen rfc2231_param.idx = 0;
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen for (; *p >= '0' && *p <= '9'; p++) {
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen rfc2231_param.idx =
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen rfc2231_param.idx*10 + *p - '0';
ec23a602b0230eea29e64e2f283a9cf215be1f82Timo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (*p != '*')
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_param.extended = FALSE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen else {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_param.extended = TRUE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen p++;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (*p != '\0')
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen p = NULL;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen else {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_param.key = t_strdup_until(key, p2);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_param.value = value;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&rfc2231_params_arr,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen &rfc2231_param, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (p == NULL) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&result, &key, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&result, &value, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (array_count(&rfc2231_params_arr) == 0) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* No RFC 2231 parameters */
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen array_append_zero(&result); /* NULL-terminate */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen *result_r = array_idx(&result, 0);
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen return broken ? -1 : 0;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* Merge the RFC 2231 parameters. Since their order isn't guaranteed to
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen be ascending, start by sorting them. */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&rfc2231_params_arr, rfc2231_parameter_cmp);
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen rfc2231_params = array_get(&rfc2231_params_arr, &count);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* keys are now sorted primarily by their name and secondarily by
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen their index. If any indexes are missing, fallback to assuming
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen these aren't RFC 2231 encoded parameters. */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen str = t_str_new(64);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen for (i = 0; i < count; i = next) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen ok = TRUE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen have_extended = FALSE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen next_idx = 0;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen for (j = i; j < count; j++) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (strcasecmp(rfc2231_params[i].key,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_params[j].key) != 0)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen break;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (rfc2231_params[j].idx != next_idx) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* missing indexes */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen ok = FALSE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (rfc2231_params[j].extended)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen have_extended = TRUE;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen next_idx++;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen next = j;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (!ok) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* missing indexes */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen for (j = i; j < next; j++) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen key = t_strdup_printf(
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_params[j].extended ?
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen "%s*%u*" : "%s*%u",
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_params[j].key,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_params[j].idx);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&result, &key, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&result,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen &rfc2231_params[j].value, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen } else {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen /* everything was successful */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen str_truncate(str, 0);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (!rfc2231_params[i].extended && have_extended)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen str_append(str, "''");
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen for (j = i; j < next; j++) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (!rfc2231_params[j].extended &&
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen have_extended) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_escape(str,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_params[j].value);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen } else {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen str_append(str,
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen rfc2231_params[j].value);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen key = rfc2231_params[i].key;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (have_extended)
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen key = t_strconcat(key, "*", NULL);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen value = t_strdup(str_c(str));
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&result, &key, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen array_append(&result, &value, 1);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen }
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen array_append_zero(&result); /* NULL-terminate */
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen *result_r = array_idx(&result, 0);
7a62083331fbbb73121d16d6e13fc3dbc5720aa7Timo Sirainen return broken ? -1 : 0;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen}