bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen#define BASE64_BLOCKS_PER_LINE (76/BASE64_BLOCK_SIZE)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen char base64_delayed[BASE64_BLOCK_INPUT_SIZE-1];
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainenstatic void stream_add_data(struct binary_converter_istream *bstream,
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainenstatic bool part_can_convert(const struct message_part *part)
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 Sirainenstream_finish_convert_decision(struct binary_converter_istream *bstream)
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen if (!part_can_convert(bstream->convert_part)) {
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen stream_add_data(bstream, buf->data, buf->used);
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen data = CONST_PTR_OFFSET(buf->data, bstream->cte_header_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic void stream_add_data(struct binary_converter_istream *bstream,
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen if (bstream->hdr_buf->used + size <= MAX_HDR_BUFFER_SIZE) {
2c26b19c543706fff723c3f52b401817507a0ee0Timo Sirainen /* buffer is getting too large. just finish the decision. */
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen memcpy(i_stream_alloc(&bstream->istream, size), data, size);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic void stream_encode_base64(struct binary_converter_istream *bstream,
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen struct istream_private *stream = &bstream->istream;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen unsigned char base64_block[BASE64_BLOCK_INPUT_SIZE];
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen unsigned int base64_block_len, missing_len, encode_blocks;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_delayed_len == 1 && size == 1) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen base64_block_len = bstream->base64_delayed_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* finish base64 */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen missing_len = BASE64_BLOCK_INPUT_SIZE - base64_block_len;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) {
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);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) {
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_size = encode_blocks * BASE64_BLOCK_INPUT_SIZE;
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 /* encode these when more data is available */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic void stream_add_hdr(struct binary_converter_istream *bstream,
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, hdr->name, hdr->name_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, hdr->middle, hdr->middle_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, hdr->value, hdr->value_len);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstatic ssize_t i_stream_binary_converter_read(struct istream_private *stream)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* @UNSAFE */
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream))
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen switch (message_parser_parse_next_block(bstream->parser, &block)) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* done / error */
1e6170b4c4bc8ed15a6ca47d8dbc056a1e8c9db4Timo Sirainen /* flush any pending base64 output */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* need more data */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* end of base64 encoded part */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* parsing a header */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (strcasecmp(block.hdr->name, "Content-Type") == 0)
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen if (strcasecmp(block.hdr->name, "Content-Transfer-Encoding") == 0 &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen !block.hdr->continued && !block.hdr->continues &&
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen i_memcasecmp(block.hdr->value, "binary", 6) == 0 &&
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 bstream->hdr_buf = buffer_create_dynamic(default_pool, 512);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->cte_header_len = bstream->hdr_buf->used;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen } else if (block.hdr->eoh && bstream->hdr_buf != NULL) {
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* finish the decision about decoding */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* end of header */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen /* message has no body */
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen stream_add_data(bstream, bstream->hdr_buf->data,
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 stream_add_data(bstream, block.data, block.size);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen return i_stream_binary_converter_read(stream);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void i_stream_binary_converter_close(struct iostream_private *stream,
12e5ac049bd74f8b98d9dc62adcb0bf3217beef6Martti Rannanjärvi message_parser_deinit(&bstream->parser, &parts);
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainenstruct istream *i_stream_create_binary_converter(struct istream *input)
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 bstream->istream.read = i_stream_binary_converter_read;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.iostream.close = i_stream_binary_converter_close;
d51bddedf8c02326124935e1ffbee5da2275f8d4Timo Sirainen bstream->istream.istream.blocking = input->blocking;
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 |