bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "lib.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "buffer.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "base64.h"
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen#include "str.h"
65988f5a8abed57e9894fec77105941e046d3490Timo Sirainen#include "unichar.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "charset-utf8.h"
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen#include "qp-decoder.h"
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen#include "rfc822-parser.h"
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen#include "rfc2231-parser.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "message-parser.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "message-header-decode.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "message-decoder.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen/* base64 takes max 4 bytes per character, q-p takes max 3. */
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen#define MAX_ENCODING_BUF_SIZE 3
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstruct message_decoder_context {
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen enum message_decoder_flags flags;
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen normalizer_func_t *normalizer;
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen struct message_part *prev_part;
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_header_line hdr;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_t *buf, *buf2;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen char *charset_trans_charset;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct charset_translation *charset_trans;
2c70dc3ca3f0e9e67b76065c4824aba6b2e4251cTimo Sirainen char translation_buf[CHARSET_MAX_PENDING_BUF_SIZE];
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t translation_size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen struct qp_decoder *qp;
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_t *encoding_buf;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen char *content_type, *content_charset;
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen enum message_cte message_cte;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool binary_input:1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen};
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainenstatic void
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainenmessage_decode_body_init_charset(struct message_decoder_context *ctx,
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen struct message_part *part);
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainenstruct message_decoder_context *
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainenmessage_decoder_init(normalizer_func_t *normalizer,
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen enum message_decoder_flags flags)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_decoder_context *ctx;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx = i_new(struct message_decoder_context, 1);
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen ctx->flags = flags;
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen ctx->normalizer = normalizer;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->buf = buffer_create_dynamic(default_pool, 8192);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->buf2 = buffer_create_dynamic(default_pool, 8192);
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen ctx->encoding_buf = buffer_create_dynamic(default_pool, 128);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return ctx;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenvoid message_decoder_deinit(struct message_decoder_context **_ctx)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_decoder_context *ctx = *_ctx;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen *_ctx = NULL;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen if (ctx->charset_trans != NULL)
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen charset_to_utf8_end(&ctx->charset_trans);
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen if (ctx->qp != NULL)
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen qp_decoder_deinit(&ctx->qp);
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_free(&ctx->encoding_buf);
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen buffer_free(&ctx->buf);
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen buffer_free(&ctx->buf2);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_free(ctx->charset_trans_charset);
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen i_free(ctx->content_type);
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen i_free(ctx->content_charset);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_free(ctx);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainenvoid message_decoder_set_return_binary(struct message_decoder_context *ctx,
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen bool set)
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen{
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen if (set)
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen ctx->flags |= MESSAGE_DECODER_FLAG_RETURN_BINARY;
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen else
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen ctx->flags &= ~MESSAGE_DECODER_FLAG_RETURN_BINARY;
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen message_decode_body_init_charset(ctx, ctx->prev_part);
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen}
7e50cca6b1dab5a7e2a90a8949678c62f4a0958aTimo Sirainen
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainenenum message_cte message_decoder_parse_cte(struct message_header_line *hdr)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen struct rfc822_parser_context parser;
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen enum message_cte message_cte;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen string_t *value;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen value = t_str_new(64);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
4e8d6d03c2ff85448df79b181a2ea850fb5d4199Timo Sirainen rfc822_skip_lwsp(&parser);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen (void)rfc822_parse_mime_token(&parser, value);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen message_cte = MESSAGE_CTE_UNKNOWN;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen switch (str_len(value)) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen case 4:
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen if (i_memcasecmp(str_data(value), "7bit", 4) == 0 ||
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen i_memcasecmp(str_data(value), "8bit", 4) == 0)
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen message_cte = MESSAGE_CTE_78BIT;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen break;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen case 6:
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen if (i_memcasecmp(str_data(value), "base64", 6) == 0)
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen message_cte = MESSAGE_CTE_BASE64;
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen else if (i_memcasecmp(str_data(value), "binary", 6) == 0)
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen message_cte = MESSAGE_CTE_BINARY;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen break;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen case 16:
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen if (i_memcasecmp(str_data(value), "quoted-printable", 16) == 0)
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen message_cte = MESSAGE_CTE_QP;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen break;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen return message_cte;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic void
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainenparse_content_type(struct message_decoder_context *ctx,
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen struct message_header_line *hdr)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen struct rfc822_parser_context parser;
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen const char *const *results;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen string_t *str;
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen int ret;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen if (ctx->content_type != NULL)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
4e8d6d03c2ff85448df79b181a2ea850fb5d4199Timo Sirainen rfc822_skip_lwsp(&parser);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen str = t_str_new(64);
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen ret = rfc822_parse_content_type(&parser, str);
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen ctx->content_type = i_strdup(str_c(str));
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen if (ret < 0)
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen return;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen rfc2231_parse(&parser, &results);
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen for (; *results != NULL; results += 2) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen if (strcasecmp(results[0], "charset") == 0) {
dabb4cec4cf9bdb34013de682b08f1284cfb670fTimo Sirainen ctx->content_charset = i_strdup(results[1]);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen break;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic bool message_decode_header(struct message_decoder_context *ctx,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_header_line *hdr,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_block *output)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen size_t value_len;
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (hdr->continues) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen hdr->use_full_value = TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return FALSE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (hdr->name_len == 12 &&
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen strcasecmp(hdr->name, "Content-Type") == 0)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen parse_content_type(ctx, hdr);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (hdr->name_len == 25 &&
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0)
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen ctx->message_cte = message_decoder_parse_cte(hdr);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_set_used_size(ctx->buf, 0);
72388282bf6718c39af34cfcf51438910f9d62daTimo Sirainen message_header_decode_utf8(hdr->full_value, hdr->full_value_len,
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen ctx->buf, ctx->normalizer);
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen value_len = ctx->buf->used;
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen if (ctx->normalizer != NULL) {
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen (void)ctx->normalizer(hdr->name, hdr->name_len, ctx->buf);
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen buffer_append_c(ctx->buf, '\0');
b516a7812b9acc04522869fead3aa6d2787dcdc6Timo Sirainen } else {
b516a7812b9acc04522869fead3aa6d2787dcdc6Timo Sirainen if (!uni_utf8_get_valid_data((const unsigned char *)hdr->name,
b516a7812b9acc04522869fead3aa6d2787dcdc6Timo Sirainen hdr->name_len, ctx->buf))
b516a7812b9acc04522869fead3aa6d2787dcdc6Timo Sirainen buffer_append_c(ctx->buf, '\0');
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->hdr = *hdr;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->hdr.full_value = ctx->buf->data;
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen ctx->hdr.full_value_len = value_len;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->hdr.value_len = 0;
b516a7812b9acc04522869fead3aa6d2787dcdc6Timo Sirainen if (ctx->buf->used != value_len) {
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen ctx->hdr.name = CONST_PTR_OFFSET(ctx->buf->data,
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen ctx->hdr.full_value_len);
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen ctx->hdr.name_len = ctx->buf->used - 1 - value_len;
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen output->hdr = &ctx->hdr;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic void translation_buf_decode(struct message_decoder_context *ctx,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen const unsigned char **data, size_t *size)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
2c70dc3ca3f0e9e67b76065c4824aba6b2e4251cTimo Sirainen unsigned char trans_buf[CHARSET_MAX_PENDING_BUF_SIZE+1];
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t data_wanted, skip;
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen size_t trans_size, orig_size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen /* @UNSAFE: move the previously untranslated bytes to trans_buf
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen and see if we have now enough data to get the next character
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen translated */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen memcpy(trans_buf, ctx->translation_buf, ctx->translation_size);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen data_wanted = sizeof(trans_buf) - ctx->translation_size;
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen if (data_wanted > *size)
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen data_wanted = *size;
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen memcpy(trans_buf + ctx->translation_size, *data, data_wanted);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen orig_size = trans_size = ctx->translation_size + data_wanted;
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen (void)charset_to_utf8(ctx->charset_trans, trans_buf,
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen &trans_size, ctx->buf2);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
3d92258c9897ffc9382c333964a1ab69cd1e5984Timo Sirainen if (trans_size <= ctx->translation_size) {
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen /* need more data to finish the translation. */
2c70dc3ca3f0e9e67b76065c4824aba6b2e4251cTimo Sirainen i_assert(orig_size < CHARSET_MAX_PENDING_BUF_SIZE);
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen memcpy(ctx->translation_buf, trans_buf, orig_size);
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen ctx->translation_size = orig_size;
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen *data += *size;
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen *size = 0;
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen return;
2e0fa95785f9b41c1c6aaf541245579a49f94841Timo Sirainen }
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen skip = trans_size - ctx->translation_size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_assert(*size >= skip);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen *data += skip;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen *size -= skip;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->translation_size = 0;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainenstatic void
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainenmessage_decode_body_init_charset(struct message_decoder_context *ctx,
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen struct message_part *part)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen{
87506860556bf42f656b13f4b14cf81b75261e95Timo Sirainen ctx->binary_input = ctx->content_charset == NULL &&
87506860556bf42f656b13f4b14cf81b75261e95Timo Sirainen (ctx->flags & MESSAGE_DECODER_FLAG_RETURN_BINARY) != 0 &&
87506860556bf42f656b13f4b14cf81b75261e95Timo Sirainen (part->flags & (MESSAGE_PART_FLAG_TEXT |
87506860556bf42f656b13f4b14cf81b75261e95Timo Sirainen MESSAGE_PART_FLAG_MESSAGE_RFC822)) == 0;
87506860556bf42f656b13f4b14cf81b75261e95Timo Sirainen
47e90cc0da86de98a8923802a29d844ab5159834Timo Sirainen if (ctx->binary_input)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
87506860556bf42f656b13f4b14cf81b75261e95Timo Sirainen if (ctx->charset_trans != NULL && ctx->content_charset != NULL &&
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen strcasecmp(ctx->content_charset, ctx->charset_trans_charset) == 0) {
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen /* already have the correct translation selected */
2d7ec47e669516fe224e61a4f778b8db48fd55efTimo Sirainen charset_to_utf8_reset(ctx->charset_trans);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen }
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen if (ctx->charset_trans != NULL)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen charset_to_utf8_end(&ctx->charset_trans);
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen i_free_and_null(ctx->charset_trans_charset);
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen ctx->charset_trans_charset = i_strdup(ctx->content_charset != NULL ?
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen ctx->content_charset : "UTF-8");
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen if (charset_to_utf8_begin(ctx->charset_trans_charset, ctx->normalizer,
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen &ctx->charset_trans) < 0)
c389f8bf1340ca004f3a8f94b3f020bf47fd2610Timo Sirainen ctx->charset_trans = charset_utf8_to_utf8_begin(ctx->normalizer);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen}
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic bool message_decode_body(struct message_decoder_context *ctx,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_block *input,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_block *output)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen const unsigned char *data = NULL;
e376e08040b5f21ff79a15ae728d2532a34207f6Timo Sirainen size_t pos = 0, size = 0;
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen const char *error;
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen int ret;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen if (ctx->encoding_buf->used != 0)
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_append(ctx->encoding_buf, input->data, input->size);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen switch (ctx->message_cte) {
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen case MESSAGE_CTE_UNKNOWN:
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* just skip this body */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return FALSE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen case MESSAGE_CTE_78BIT:
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen case MESSAGE_CTE_BINARY:
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen i_assert(ctx->encoding_buf->used == 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen data = input->data;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen size = pos = input->size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen break;
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen case MESSAGE_CTE_QP: {
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen i_assert(ctx->encoding_buf->used == 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_set_used_size(ctx->buf, 0);
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen if (ctx->qp == NULL)
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen ctx->qp = qp_decoder_init(ctx->buf);
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen (void)qp_decoder_more(ctx->qp, input->data, input->size,
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen &pos, &error);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen data = ctx->buf->data;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen size = ctx->buf->used;
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen /* eat away all input. qp-decoder buffers it internally. */
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen pos = input->size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen break;
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen }
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen case MESSAGE_CTE_BASE64:
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_set_used_size(ctx->buf, 0);
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen if (ctx->encoding_buf->used != 0) {
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen ret = base64_decode(ctx->encoding_buf->data,
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen ctx->encoding_buf->used,
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen &pos, ctx->buf);
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen } else {
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen ret = base64_decode(input->data, input->size,
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen &pos, ctx->buf);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen if (ret < 0) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* corrupted base64 data, don't bother with
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen the rest of it */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return FALSE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen if (ret == 0) {
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen /* end of base64 input */
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen pos = input->size;
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_set_used_size(ctx->encoding_buf, 0);
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen data = ctx->buf->data;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen size = ctx->buf->used;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen break;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen if (ctx->encoding_buf->used != 0)
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_delete(ctx->encoding_buf, 0, pos);
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen else if (pos != input->size) {
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_append(ctx->encoding_buf,
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen input->data + pos, input->size - pos);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen if (ctx->binary_input) {
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen output->data = data;
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen output->size = size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen } else {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_set_used_size(ctx->buf2, 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (ctx->translation_size != 0)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen translation_buf_decode(ctx, &data, &size);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen pos = size;
055f4599bba1874fa1148a8fa488517fa077619cTimo Sirainen (void)charset_to_utf8(ctx->charset_trans,
055f4599bba1874fa1148a8fa488517fa077619cTimo Sirainen data, &pos, ctx->buf2);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (pos != size) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->translation_size = size - pos;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_assert(ctx->translation_size <=
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sizeof(ctx->translation_buf));
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen memcpy(ctx->translation_buf, data + pos,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ctx->translation_size);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen output->data = ctx->buf2->data;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen output->size = ctx->buf2->used;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen output->hdr = NULL;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenbool message_decoder_decode_next_block(struct message_decoder_context *ctx,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_block *input,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct message_block *output)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen if (input->part != ctx->prev_part) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* MIME part changed. */
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen message_decoder_decode_reset(ctx);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen output->part = input->part;
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen ctx->prev_part = input->part;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
d60a5611b1db47f9ba14e82811170251a7dab6faTimo Sirainen if (input->hdr != NULL) {
d60a5611b1db47f9ba14e82811170251a7dab6faTimo Sirainen output->size = 0;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return message_decode_header(ctx, input->hdr, output);
d60a5611b1db47f9ba14e82811170251a7dab6faTimo Sirainen } else if (input->size != 0)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return message_decode_body(ctx, input, output);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen else {
9ae664e94e6eeb5c1f900bb90642052633031832Timo Sirainen output->hdr = NULL;
9ae664e94e6eeb5c1f900bb90642052633031832Timo Sirainen output->size = 0;
5cbefc6537aefbf1491416c433de00fc3e649a13Timo Sirainen message_decode_body_init_charset(ctx, input->part);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return TRUE;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen }
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen}
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainenconst char *
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainenmessage_decoder_current_content_type(struct message_decoder_context *ctx)
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen{
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen return ctx->content_type;
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen}
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainenvoid message_decoder_decode_reset(struct message_decoder_context *ctx)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen{
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen const char *error;
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen if (ctx->qp != NULL)
1a6a2a1317965035bec89ffbbc0e408fc8652621Timo Sirainen (void)qp_decoder_finish(ctx->qp, &error);
01eef06269e04d0b1b6d67c5055248fefb4f613eTimo Sirainen i_free_and_null(ctx->content_type);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_free_and_null(ctx->content_charset);
4e2d7dd2b201ae2083b6637d7ec0d37f49faca45Timo Sirainen ctx->message_cte = MESSAGE_CTE_78BIT;
db24d60c306c9d477392ff5b61cb3fc95fef3bb7Timo Sirainen buffer_set_used_size(ctx->encoding_buf, 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}