bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#include "lib.h"
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#include "buffer.h"
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#include "base64.h"
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#include "istream-private.h"
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#include "message-parser.h"
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#include "istream-binary-converter.h"
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#define BASE64_BLOCK_INPUT_SIZE 3
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#define BASE64_BLOCK_SIZE 4
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#define BASE64_BLOCKS_PER_LINE (76/BASE64_BLOCK_SIZE)
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen#define MAX_HDR_BUFFER_SIZE (1024*32)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstruct binary_converter_istream {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct istream_private istream;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen pool_t pool;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct message_parser_ctx *parser;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct message_part *convert_part;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen char base64_delayed[BASE64_BLOCK_INPUT_SIZE-1];
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen unsigned int base64_delayed_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen unsigned int base64_block_pos;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen buffer_t *hdr_buf;
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t cte_header_len;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool content_type_seen:1;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen};
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainenstatic void stream_add_data(struct binary_converter_istream *bstream,
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen const void *data, size_t size);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainenstatic bool part_can_convert(const struct message_part *part)
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen{
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen /* some MUAs use "c-t-e: binary" for multiparts.
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen we don't want to convert them. */
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen return (part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen}
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainenstatic void
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainenstream_finish_convert_decision(struct binary_converter_istream *bstream)
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen{
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen buffer_t *buf = bstream->hdr_buf;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen const unsigned char *data;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen bstream->hdr_buf = NULL;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen if (!part_can_convert(bstream->convert_part)) {
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen bstream->convert_part = NULL;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen stream_add_data(bstream, buf->data, buf->used);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen } else {
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen stream_add_data(bstream,
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen "Content-Transfer-Encoding: base64\r\n", 35);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen data = CONST_PTR_OFFSET(buf->data, bstream->cte_header_len);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen stream_add_data(bstream, data,
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen buf->used - bstream->cte_header_len);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen }
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen buffer_free(&buf);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen}
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic void stream_add_data(struct binary_converter_istream *bstream,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen const void *data, size_t size)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen{
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen if (size == 0)
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen return;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->hdr_buf != NULL) {
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen if (bstream->hdr_buf->used + size <= MAX_HDR_BUFFER_SIZE) {
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen buffer_append(bstream->hdr_buf, data, size);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen return;
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen }
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen /* buffer is getting too large. just finish the decision. */
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen stream_finish_convert_decision(bstream);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen memcpy(i_stream_alloc(&bstream->istream, size), data, size);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen bstream->istream.pos += size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen}
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic void stream_encode_base64(struct binary_converter_istream *bstream,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen const void *_data, size_t size)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen{
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen struct istream_private *stream = &bstream->istream;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen const unsigned char *data = _data;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen buffer_t buf;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen void *dest;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen size_t encode_size, max_encoded_size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen unsigned char base64_block[BASE64_BLOCK_INPUT_SIZE];
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen unsigned int base64_block_len, missing_len, encode_blocks;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_delayed_len > 0) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_delayed_len == 1 && size == 1) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_delayed[1] = data[0];
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_delayed_len++;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen memcpy(base64_block, bstream->base64_delayed,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_delayed_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen base64_block_len = bstream->base64_delayed_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (size == 0) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* finish base64 */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen missing_len = BASE64_BLOCK_INPUT_SIZE - base64_block_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen i_assert(size >= missing_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen memcpy(base64_block + base64_block_len,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen data, missing_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen data += missing_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen size -= missing_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen base64_block_len = BASE64_BLOCK_INPUT_SIZE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) {
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen memcpy(i_stream_alloc(stream, 2), "\r\n", 2);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen stream->pos += 2;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_block_pos = 0;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen dest = i_stream_alloc(stream, BASE64_BLOCK_SIZE);
3281669db44d09a087a203201248abbc81b3cc1aTimo Sirainen buffer_create_from_data(&buf, dest, BASE64_BLOCK_SIZE);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen base64_encode(base64_block, base64_block_len, &buf);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen stream->pos += buf.used;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_block_pos++;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_delayed_len = 0;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen while (size >= BASE64_BLOCK_INPUT_SIZE) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) {
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen memcpy(i_stream_alloc(stream, 2), "\r\n", 2);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen stream->pos += 2;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_block_pos = 0;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* try to encode one full line of base64 blocks */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen encode_size = I_MIN(size, BASE64_BLOCKS_PER_LINE*BASE64_BLOCK_SIZE);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (encode_size % BASE64_BLOCK_INPUT_SIZE != 0)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen encode_size -= encode_size % BASE64_BLOCK_INPUT_SIZE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen encode_blocks = encode_size/BASE64_BLOCK_INPUT_SIZE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_block_pos + encode_blocks > BASE64_BLOCKS_PER_LINE) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen encode_blocks = BASE64_BLOCKS_PER_LINE -
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_block_pos;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen encode_size = encode_blocks * BASE64_BLOCK_INPUT_SIZE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen max_encoded_size = MAX_BASE64_ENCODED_SIZE(encode_size);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen dest = i_stream_alloc(stream, max_encoded_size);
3281669db44d09a087a203201248abbc81b3cc1aTimo Sirainen buffer_create_from_data(&buf, dest, max_encoded_size);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen base64_encode(data, encode_size, &buf);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen stream->pos += buf.used;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_block_pos += encode_blocks;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen data += encode_size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen size -= encode_size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (size > 0) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* encode these when more data is available */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen i_assert(size < BASE64_BLOCK_INPUT_SIZE);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen memcpy(bstream->base64_delayed, data, size);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_delayed_len = size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen}
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic void stream_add_hdr(struct binary_converter_istream *bstream,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen const struct message_header_line *hdr)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen{
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (!hdr->continued) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, hdr->name, hdr->name_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, hdr->middle, hdr->middle_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, hdr->value, hdr->value_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (!hdr->no_newline)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, "\r\n", 2);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen}
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic ssize_t i_stream_binary_converter_read(struct istream_private *stream)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen{
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* @UNSAFE */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct binary_converter_istream *bstream =
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen (struct binary_converter_istream *)stream;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct message_block block;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen size_t old_size, new_size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream))
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return -2;
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen old_size = stream->pos - stream->skip;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen switch (message_parser_parse_next_block(bstream->parser, &block)) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen case -1:
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* done / error */
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen if (bstream->convert_part != NULL &&
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen bstream->base64_delayed_len > 0) {
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen /* flush any pending base64 output */
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen stream_encode_base64(bstream, "", 0);
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen new_size = stream->pos - stream->skip;
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen i_assert(old_size != new_size);
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen return new_size - old_size;
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream->istream.eof = TRUE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return -1;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen case 0:
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* need more data */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return 0;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen default:
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen break;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (block.part != bstream->convert_part &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->convert_part != NULL) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* end of base64 encoded part */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen stream_encode_base64(bstream, "", 0);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (block.hdr != NULL) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* parsing a header */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (strcasecmp(block.hdr->name, "Content-Type") == 0)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->content_type_seen = TRUE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (strcasecmp(block.hdr->name, "Content-Transfer-Encoding") == 0 &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen !block.hdr->continued && !block.hdr->continues &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen block.hdr->value_len == 6 &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen i_memcasecmp(block.hdr->value, "binary", 6) == 0 &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen part_can_convert(block.part) &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->convert_part != block.part) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* looks like we want to convert this body part to
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen base64, but if we haven't seen Content-Type yet
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen delay the decision until we've read the rest of
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen the header */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen i_assert(block.part != NULL);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->convert_part = block.part;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->base64_block_pos = 0;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (!bstream->content_type_seen) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen i_assert(bstream->hdr_buf == NULL);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->hdr_buf = buffer_create_dynamic(default_pool, 512);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_hdr(bstream, block.hdr);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->cte_header_len = bstream->hdr_buf->used;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen "Content-Transfer-Encoding: base64\r\n", 35);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else if (block.hdr->eoh && bstream->hdr_buf != NULL) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* finish the decision about decoding */
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen stream_finish_convert_decision(bstream);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, "\r\n", 2);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_hdr(bstream, block.hdr);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else if (block.size == 0) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* end of header */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->hdr_buf != NULL) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* message has no body */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->convert_part = NULL;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, bstream->hdr_buf->data,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->hdr_buf->used);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen buffer_free(&bstream->hdr_buf);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->content_type_seen = FALSE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else if (block.part == bstream->convert_part) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* convert body part to base64 */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_encode_base64(bstream, block.data, block.size);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, block.data, block.size);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen }
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen new_size = stream->pos - stream->skip;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (new_size == old_size)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return i_stream_binary_converter_read(stream);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return new_size - old_size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen}
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void i_stream_binary_converter_close(struct iostream_private *stream,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen bool close_parent)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen{
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct binary_converter_istream *bstream =
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen (struct binary_converter_istream *)stream;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct message_part *parts;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
dd59c7a1f0ebb254b0daac79fc3d0379793b2198Timo Sirainen if (bstream->parser != NULL) {
12e5ac049bd74f8b98d9dc62adcb0bf3217beef6Martti Rannanjärvi message_parser_deinit(&bstream->parser, &parts);
dd59c7a1f0ebb254b0daac79fc3d0379793b2198Timo Sirainen }
c69a177207ed18d0f0210347430a60957136bd6cJosef 'Jeff' Sipek pool_unref(&bstream->pool);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (close_parent)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen i_stream_close(bstream->istream.parent);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen}
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstruct istream *i_stream_create_binary_converter(struct istream *input)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen{
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen struct binary_converter_istream *bstream;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream = i_new(struct binary_converter_istream, 1);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.read = i_stream_binary_converter_read;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.iostream.close = i_stream_binary_converter_close;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.istream.readable_fd = FALSE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.istream.blocking = input->blocking;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.istream.seekable = FALSE;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->pool = pool_alloconly_create("istream binary converter", 128);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->parser = message_parser_init(bstream->pool, input, 0,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return i_stream_create(&bstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen}