message-decoder.c revision 9ae664e94e6eeb5c1f900bb90642052633031832
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2006-2007 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
18398a5d21c88cbb34c601c6b6c1f9dea502e1caTimo Sirainen#include "buffer.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "base64.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "unichar.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "charset-utf8.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "quoted-printable.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "rfc822-parser.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "message-parser.h"
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen#include "message-header-decode.h"
366eb7178f2c90d97134e0c2d1958f93fcdaba12Timo Sirainen#include "message-decoder.h"
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainenenum content_type {
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen CONTENT_TYPE_UNKNOWN = 0,
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen CONTENT_TYPE_BINARY,
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainen CONTENT_TYPE_QP,
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainen CONTENT_TYPE_BASE64
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainen};
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen/* base64 takes max 4 bytes per character, q-p takes max 3. */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#define MAX_ENCODING_BUF_SIZE 3
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen/* UTF-8 takes max 5 bytes per character. Not sure about others, but I'd think
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen 10 is more than enough for everyone.. */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#define MAX_TRANSLATION_BUF_SIZE 10
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenstruct message_decoder_context {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct message_part *prev_part;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct message_header_line hdr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_t *buf, *buf2;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen char *charset_trans_charset;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct charset_translation *charset_trans;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char translation_buf[MAX_TRANSLATION_BUF_SIZE];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int translation_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char encoding_buf[MAX_ENCODING_BUF_SIZE];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int encoding_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
55a210942dc7da58b2fd0b11bed8da6b030af5c1Timo Sirainen char *content_charset;
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen enum content_type content_type;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int dtcase:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int charset_utf8:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct message_decoder_context *message_decoder_init(bool dtcase)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct message_decoder_context *ctx;
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen ctx = i_new(struct message_decoder_context, 1);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen ctx->dtcase = dtcase;
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen ctx->buf = buffer_create_dynamic(default_pool, 8192);
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen ctx->buf2 = buffer_create_dynamic(default_pool, 8192);
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen return ctx;
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainenvoid message_decoder_deinit(struct message_decoder_context **_ctx)
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen{
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen struct message_decoder_context *ctx = *_ctx;
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen *_ctx = NULL;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (ctx->charset_trans != NULL)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen charset_to_utf8_end(&ctx->charset_trans);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen buffer_free(&ctx->buf);
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen buffer_free(&ctx->buf2);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(ctx->charset_trans_charset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(ctx->content_charset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(ctx);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void
45e62043058738e294f89504c319d852e25943ccTimo Sirainenparse_content_transfer_encoding(struct message_decoder_context *ctx,
45e62043058738e294f89504c319d852e25943ccTimo Sirainen struct message_header_line *hdr)
45e62043058738e294f89504c319d852e25943ccTimo Sirainen{
45e62043058738e294f89504c319d852e25943ccTimo Sirainen struct rfc822_parser_context parser;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen string_t *value;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen value = t_str_new(64);
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen (void)rfc822_skip_lwsp(&parser);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen (void)rfc822_parse_mime_token(&parser, value);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen switch (str_len(value)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case 4:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (i_memcasecmp(str_data(value), "7bit", 4) == 0 ||
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen i_memcasecmp(str_data(value), "8bit", 4) == 0)
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen ctx->content_type = CONTENT_TYPE_BINARY;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen case 6:
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (i_memcasecmp(str_data(value), "base64", 6) == 0)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen ctx->content_type = CONTENT_TYPE_BASE64;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen else if (i_memcasecmp(str_data(value), "binary", 6) == 0)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen ctx->content_type = CONTENT_TYPE_BINARY;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen break;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen case 16:
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (i_memcasecmp(str_data(value), "quoted-printable", 16) == 0)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ctx->content_type = CONTENT_TYPE_QP;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen break;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen}
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainenparse_content_type(struct message_decoder_context *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct message_header_line *hdr)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen struct rfc822_parser_context parser;
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen const char *key, *value;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen string_t *str;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen if (ctx->content_charset != NULL)
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen return;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (void)rfc822_skip_lwsp(&parser);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen str = t_str_new(64);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (rfc822_parse_content_type(&parser, str) <= 0)
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen return;
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen while (rfc822_parse_content_param(&parser, &key, &value) > 0) {
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (strcasecmp(key, "charset") == 0) {
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen ctx->content_charset = i_strdup(value);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen ctx->charset_utf8 = charset_is_utf8(value);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen break;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen}
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenstatic bool message_decode_header(struct message_decoder_context *ctx,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct message_header_line *hdr,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct message_block *output)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
size_t value_len;
if (hdr->continues) {
hdr->use_full_value = TRUE;
return FALSE;
}
T_FRAME(
if (hdr->name_len == 12 &&
strcasecmp(hdr->name, "Content-Type") == 0)
parse_content_type(ctx, hdr);
if (hdr->name_len == 25 &&
strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0)
parse_content_transfer_encoding(ctx, hdr);
);
buffer_set_used_size(ctx->buf, 0);
message_header_decode_utf8(hdr->full_value, hdr->full_value_len,
ctx->buf, ctx->dtcase);
value_len = ctx->buf->used;
if (ctx->dtcase) {
(void)uni_utf8_to_decomposed_titlecase(hdr->name, hdr->name_len,
ctx->buf);
buffer_append_c(ctx->buf, '\0');
}
ctx->hdr = *hdr;
ctx->hdr.full_value = ctx->buf->data;
ctx->hdr.full_value_len = value_len;
ctx->hdr.value_len = 0;
if (ctx->dtcase) {
ctx->hdr.name = CONST_PTR_OFFSET(ctx->buf->data,
ctx->hdr.full_value_len);
ctx->hdr.name_len = ctx->buf->used - 1 - value_len;
}
output->hdr = &ctx->hdr;
return TRUE;
}
static void translation_buf_decode(struct message_decoder_context *ctx,
const unsigned char **data, size_t *size)
{
unsigned char trans_buf[MAX_TRANSLATION_BUF_SIZE+1];
unsigned int data_wanted, skip;
size_t trans_size;
/* @UNSAFE: move the previously untranslated bytes to trans_buf
and see if we have now enough data to get the next character
translated */
memcpy(trans_buf, ctx->translation_buf, ctx->translation_size);
data_wanted = sizeof(trans_buf) - ctx->translation_size;
if (data_wanted > *size)
data_wanted = *size;
memcpy(trans_buf + ctx->translation_size, *data, data_wanted);
trans_size = ctx->translation_size + data_wanted;
(void)charset_to_utf8(ctx->charset_trans, trans_buf,
&trans_size, ctx->buf2);
i_assert(trans_size > ctx->translation_size);
skip = trans_size - ctx->translation_size;
i_assert(*size >= skip);
*data += skip;
*size -= skip;
ctx->translation_size = 0;
}
static inline unsigned int
is_valid_utf8_seq(const unsigned char *input, unsigned int size)
{
size_t i, len;
len = uni_utf8_char_bytes(input[0]);
if (unlikely(len > size))
return 0;
for (i = 0; i < len; i++) {
if (unlikely(uni_utf8_char_bytes(input[i]) != len-i))
return 0;
}
return len;
}
static const unsigned char *
get_valid_utf8(const unsigned char *input, size_t size, buffer_t *tmpbuf,
size_t *output_size_r)
{
size_t i, len;
/* find the first invalid utf8 sequence */
for (i = 0; i < size;) {
if (input[i] < 0x80)
i++;
else {
len = is_valid_utf8_seq(input + i, size-i);
if (unlikely(len == 0))
goto broken;
i += len;
}
}
/* we can use it as-is */
*output_size_r = size;
return input;
broken:
/* broken utf-8 input - skip the broken characters */
buffer_set_used_size(tmpbuf, 0);
buffer_append(tmpbuf, input, i++);
while (i < size) {
if (input[i] < 0x80) {
buffer_append_c(tmpbuf, input[i++]);
continue;
}
len = is_valid_utf8_seq(input + i, size-i);
if (len == 0) {
i++;
continue;
}
buffer_append(tmpbuf, input + i, len);
i += len;
}
*output_size_r = tmpbuf->used;
return tmpbuf->data;
}
static void message_decode_body_init_charset(struct message_decoder_context *ctx)
{
enum charset_flags flags;
if (ctx->charset_utf8)
return;
if (ctx->charset_trans != NULL &&
strcasecmp(ctx->content_charset, ctx->charset_trans_charset) == 0) {
/* already have the correct translation selected */
return;
}
if (ctx->charset_trans != NULL)
charset_to_utf8_end(&ctx->charset_trans);
i_free(ctx->charset_trans_charset);
flags = ctx->dtcase ? CHARSET_FLAG_DECOMP_TITLECASE : 0;
ctx->charset_trans_charset = i_strdup(ctx->content_charset != NULL ?
ctx->content_charset : "UTF-8");
if (charset_to_utf8_begin(ctx->charset_trans_charset,
flags, &ctx->charset_trans) < 0)
ctx->charset_trans = NULL;
}
static bool message_decode_body(struct message_decoder_context *ctx,
struct message_block *input,
struct message_block *output)
{
unsigned char new_buf[MAX_ENCODING_BUF_SIZE+1];
const unsigned char *data = NULL;
size_t pos, size = 0, skip = 0;
int ret;
if (ctx->encoding_size != 0) {
/* @UNSAFE */
memcpy(new_buf, ctx->encoding_buf, ctx->encoding_size);
skip = sizeof(new_buf) - ctx->encoding_size;
if (skip > input->size)
skip = input->size;
memcpy(new_buf + ctx->encoding_size, input->data, skip);
}
switch (ctx->content_type) {
case CONTENT_TYPE_UNKNOWN:
/* just skip this body */
return FALSE;
case CONTENT_TYPE_BINARY:
data = input->data;
size = pos = input->size;
break;
case CONTENT_TYPE_QP:
buffer_set_used_size(ctx->buf, 0);
if (ctx->encoding_size != 0) {
quoted_printable_decode(new_buf,
ctx->encoding_size + skip,
&pos, ctx->buf);
i_assert(pos >= ctx->encoding_size);
skip = pos - ctx->encoding_size;
}
quoted_printable_decode(input->data + skip, input->size - skip,
&pos, ctx->buf);
pos += skip;
data = ctx->buf->data;
size = ctx->buf->used;
break;
case CONTENT_TYPE_BASE64:
buffer_set_used_size(ctx->buf, 0);
if (ctx->encoding_size != 0) {
if (base64_decode(new_buf, ctx->encoding_size + skip,
&pos, ctx->buf) < 0) {
/* corrupted base64 data, don't bother with
the rest of it */
return FALSE;
}
i_assert(pos >= ctx->encoding_size);
skip = pos - ctx->encoding_size;
}
ret = base64_decode(input->data + skip, input->size - skip,
&pos, ctx->buf);
if (ret < 0) {
/* corrupted base64 data, don't bother with
the rest of it */
return FALSE;
}
if (ret == 0) {
/* end of base64 input */
pos = input->size - skip;
}
pos += skip;
data = ctx->buf->data;
size = ctx->buf->used;
break;
}
if (pos != input->size) {
/* @UNSAFE */
i_assert(pos < input->size);
ctx->encoding_size = input->size - pos;
i_assert(ctx->encoding_size <= sizeof(ctx->encoding_buf));
memcpy(ctx->encoding_buf, input->data + pos,
ctx->encoding_size);
} else {
ctx->encoding_size = 0;
}
if (ctx->charset_utf8) {
if (ctx->dtcase) {
buffer_set_used_size(ctx->buf2, 0);
(void)uni_utf8_to_decomposed_titlecase(data, size,
ctx->buf2);
output->data = ctx->buf2->data;
output->size = ctx->buf2->used;
} else {
output->data = get_valid_utf8(data, size, ctx->buf2,
&output->size);
}
} else if (ctx->charset_trans == NULL) {
/* unknown charset */
output->data = get_valid_utf8(data, size, ctx->buf2,
&output->size);
} else {
buffer_set_used_size(ctx->buf2, 0);
if (ctx->translation_size != 0)
translation_buf_decode(ctx, &data, &size);
pos = size;
(void)charset_to_utf8(ctx->charset_trans,
data, &pos, ctx->buf2);
if (pos != size) {
ctx->translation_size = size - pos;
i_assert(ctx->translation_size <=
sizeof(ctx->translation_buf));
memcpy(ctx->translation_buf, data + pos,
ctx->translation_size);
}
output->data = ctx->buf2->data;
output->size = ctx->buf2->used;
}
output->hdr = NULL;
return TRUE;
}
bool message_decoder_decode_next_block(struct message_decoder_context *ctx,
struct message_block *input,
struct message_block *output)
{
if (input->part != ctx->prev_part) {
/* MIME part changed. */
message_decoder_decode_reset(ctx);
}
output->part = input->part;
ctx->prev_part = input->part;
if (input->hdr != NULL)
return message_decode_header(ctx, input->hdr, output);
else if (input->size != 0)
return message_decode_body(ctx, input, output);
else {
output->hdr = NULL;
output->size = 0;
message_decode_body_init_charset(ctx);
return TRUE;
}
}
void message_decoder_decode_reset(struct message_decoder_context *ctx)
{
i_free_and_null(ctx->content_charset);
ctx->content_type = CONTENT_TYPE_BINARY;
ctx->charset_utf8 = TRUE;
ctx->encoding_size = 0;
}