summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/rfc2231-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-mail/rfc2231-parser.c')
-rw-r--r--src/lib-mail/rfc2231-parser.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/lib-mail/rfc2231-parser.c b/src/lib-mail/rfc2231-parser.c
new file mode 100644
index 0000000..119d48d
--- /dev/null
+++ b/src/lib-mail/rfc2231-parser.c
@@ -0,0 +1,179 @@
+/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "rfc822-parser.h"
+#include "rfc2231-parser.h"
+
+
+struct rfc2231_parameter {
+ const char *key, *value;
+ unsigned int idx;
+ bool extended;
+};
+
+static int rfc2231_parameter_cmp(const struct rfc2231_parameter *r1,
+ const struct rfc2231_parameter *r2)
+{
+ 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(struct rfc2231_parameter) rfc2231_params_arr;
+ struct rfc2231_parameter rfc2231_param;
+ const struct rfc2231_parameter *rfc2231_params;
+ const char *key, *p, *p2;
+ string_t *str;
+ unsigned int i, j, count, next, next_idx;
+ bool ok, have_extended, broken = FALSE;
+ const char *prev_replacement_str;
+ int ret;
+
+ /* Temporarily replace the nul_replacement_char while we're parsing
+ the content-params. It'll be restored before we return. */
+ prev_replacement_str = ctx->nul_replacement_str;
+ ctx->nul_replacement_str = RFC822_NUL_REPLACEMENT_STR;
+
+ /* 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. */
+ i_zero(&rfc2231_param);
+ t_array_init(&result, 8);
+ t_array_init(&rfc2231_params_arr, 8);
+ str = t_str_new(64);
+ while ((ret = rfc822_parse_content_param(ctx, &key, str)) != 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 = t_strdup(str_c(str));
+ array_push_back(&rfc2231_params_arr,
+ &rfc2231_param);
+ }
+ }
+ if (p == NULL) {
+ const char *value = t_strdup(str_c(str));
+ array_push_back(&result, &key);
+ array_push_back(&result, &value);
+ }
+ }
+ ctx->nul_replacement_str = prev_replacement_str;
+
+ if (array_count(&rfc2231_params_arr) == 0) {
+ /* No RFC 2231 parameters */
+ array_append_zero(&result); /* NULL-terminate */
+ *result_r = array_front(&result);
+ 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. */
+ 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_push_back(&result, &key);
+ array_push_back(&result,
+ &rfc2231_params[j].value);
+ }
+ } 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);
+ const char *value = t_strdup(str_c(str));
+ array_push_back(&result, &key);
+ array_push_back(&result, &value);
+ }
+ }
+ array_append_zero(&result); /* NULL-terminate */
+ *result_r = array_front(&result);
+ return broken ? -1 : 0;
+}