bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "lib.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "istream-private.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "ostream.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "base64.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "buffer.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "str.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "hash-format.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "rfc822-parser.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "message-parser.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "istream-attachment-extractor.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#define BASE64_ATTACHMENT_MAX_EXTRA_BYTES 1024
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenenum mail_attachment_state {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen MAIL_ATTACHMENT_STATE_NO,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen MAIL_ATTACHMENT_STATE_MAYBE,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen MAIL_ATTACHMENT_STATE_YES
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen};
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenenum base64_state {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_0 = 0,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_1,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_2,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_3,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_CR,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_EOB,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_STATE_EOM
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen};
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstruct attachment_istream_part {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen char *content_type, *content_disposition;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen enum mail_attachment_state state;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* start offset of the message part in the original input stream */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen uoff_t start_offset;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* for saving attachments base64-decoded: */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen enum base64_state base64_state;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen unsigned int base64_line_blocks, cur_base64_blocks;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen uoff_t base64_bytes;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen bool base64_have_crlf; /* CRLF linefeeds */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen bool base64_failed;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen int temp_fd;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct ostream *temp_output;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_t *part_buf;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen};
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstruct attachment_istream {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream_private istream;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen pool_t pool;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream_attachment_settings set;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen void *context;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct message_parser_ctx *parser;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct message_part *cur_part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream_part part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen bool retry_read;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen};
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void stream_add_data(struct attachment_istream *astream,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const void *data, size_t size)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (size > 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen memcpy(i_stream_alloc(&astream->istream, size), data, size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->istream.pos += size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void parse_content_type(struct attachment_istream *astream,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const struct message_header_line *hdr)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct rfc822_parser_context parser;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen string_t *content_type;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen if (astream->part.content_type != NULL)
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen return;
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen rfc822_skip_lwsp(&parser);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen T_BEGIN {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen content_type = t_str_new(64);
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen (void)rfc822_parse_content_type(&parser, content_type);
13961419ca9710eb80c254e00510c58c466f3c08Timo Sirainen astream->part.content_type = i_strdup(str_c(content_type));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } T_END;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenparse_content_disposition(struct attachment_istream *astream,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const struct message_header_line *hdr)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* just pass it without parsing to is_attachment() callback */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_free(astream->part.content_disposition);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->part.content_disposition =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_strndup(hdr->full_value, hdr->full_value_len);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void astream_parse_header(struct attachment_istream *astream,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct message_header_line *hdr)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (!hdr->continued) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, hdr->name, hdr->name_len);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, hdr->middle, hdr->middle_len);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, hdr->value, hdr->value_len);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (!hdr->no_newline) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (hdr->crlf_newline)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, "\r\n", 2);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, "\n", 1);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (hdr->continues) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hdr->use_full_value = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (strcasecmp(hdr->name, "Content-Type") == 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen parse_content_type(astream, hdr);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else if (strcasecmp(hdr->name, "Content-Disposition") == 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen parse_content_disposition(astream, hdr);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic bool astream_want_attachment(struct attachment_istream *astream,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct message_part *part)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream_attachment_header ahdr;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* multiparts may contain attachments as children,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen but they're never themselves */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return FALSE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (astream->set.want_attachment == NULL)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&ahdr);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ahdr.part = part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ahdr.content_type = astream->part.content_type;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ahdr.content_disposition = astream->part.content_disposition;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return astream->set.want_attachment(&ahdr, astream->context);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic int astream_base64_decode_lf(struct attachment_istream_part *part)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen if (part->base64_have_crlf && part->base64_state != BASE64_STATE_CR) {
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen /* mixed LF vs CRLFs */
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen return -1;
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->cur_base64_blocks < part->base64_line_blocks) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* last line */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_EOM;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if (part->base64_line_blocks == 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* first line */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->cur_base64_blocks == 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_line_blocks = part->cur_base64_blocks;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if (part->cur_base64_blocks == part->base64_line_blocks) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* line is ok */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->cur_base64_blocks = 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic int
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenastream_try_base64_decode_char(struct attachment_istream_part *part,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t pos, char chr)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen switch (part->base64_state) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_0:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (base64_is_valid_char(chr))
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else if (chr == '\r')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_CR;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else if (chr == '\n') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return astream_base64_decode_lf(part);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_1:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (!base64_is_valid_char(chr))
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_2:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (base64_is_valid_char(chr))
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else if (chr == '=')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_EOB;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_3:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_bytes = part->temp_output->offset + pos + 1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (base64_is_valid_char(chr)) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->cur_base64_blocks++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if (chr == '=') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_EOM;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->cur_base64_blocks++;
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen if (part->cur_base64_blocks > part->base64_line_blocks &&
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen part->base64_line_blocks > 0) {
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen /* too many blocks */
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen return -1;
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_CR:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (chr != '\n')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen if (!part->base64_have_crlf) {
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen if (part->base64_line_blocks != 0) {
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen /* mixed LF vs CRLFs */
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen return -1;
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen }
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen part->base64_have_crlf = TRUE;
ba8c3c767dad12e9b998613a311c3502f9aaa565Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return astream_base64_decode_lf(part);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_EOB:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (chr != '=')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_bytes = part->temp_output->offset + pos + 1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_EOM;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->cur_base64_blocks++;
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen if (part->cur_base64_blocks > part->base64_line_blocks &&
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen part->base64_line_blocks > 0) {
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen /* too many blocks */
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen return -1;
dce10c2f67cd90f6d5fef9691d2493fdea5b42a7Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case BASE64_STATE_EOM:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_unreached();
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenastream_try_base64_decode(struct attachment_istream_part *part,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const unsigned char *data, size_t size)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t i;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen int ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->base64_failed || part->base64_state == BASE64_STATE_EOM)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen for (i = 0; i < size; i++) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ret = astream_try_base64_decode_char(part, i, (char)data[i]);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (ret <= 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (ret < 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic int astream_open_output(struct attachment_istream *astream)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen int fd;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(astream->part.temp_fd == -1);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen fd = astream->set.open_temp_fd(astream->context);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (fd == -1)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->part.temp_fd = fd;
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi astream->part.temp_output = o_stream_create_fd(fd, 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_cork(astream->part.temp_output);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void astream_add_body(struct attachment_istream *astream,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const struct message_block *block)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream_part *part = &astream->part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_t *part_buf;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t new_size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen switch (part->state) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case MAIL_ATTACHMENT_STATE_NO:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, block->data, block->size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case MAIL_ATTACHMENT_STATE_MAYBE:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* we'll write data to in-memory buffer until we reach
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen attachment min_size */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->part_buf == NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->part_buf =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_create_dynamic(default_pool,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->set.min_size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part_buf = part->part_buf;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen new_size = part_buf->used + block->size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (new_size < astream->set.min_size) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_append(part_buf, block->data, block->size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* attachment is large enough. we'll first copy the buffered
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen data from memory to temp file */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (astream_open_output(astream) < 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* failed, fallback to just saving it inline */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->state = MAIL_ATTACHMENT_STATE_NO;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, part_buf->data, part_buf->used);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, block->data, block->size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->state = MAIL_ATTACHMENT_STATE_YES;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream_try_base64_decode(part, part_buf->data, part_buf->used);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_loop(astream->set.hash_format,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part_buf->data, part_buf->used);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_nsend(part->temp_output,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part_buf->data, part_buf->used);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_set_used_size(part_buf, 0);
f784d5bb8edbec88829524135cfa100129f5384dTimo Sirainen /* fall through - write the new data to temp file */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case MAIL_ATTACHMENT_STATE_YES:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream_try_base64_decode(part, block->data, block->size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_loop(astream->set.hash_format,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen block->data, block->size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_nsend(part->temp_output, block->data, block->size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainenstatic int astream_decode_base64(struct attachment_istream *astream,
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen buffer_t **extra_buf_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream_part *part = &astream->part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream *input, *base64_input;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct ostream *output;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const unsigned char *data;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ssize_t ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_t *buf;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen int outfd;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen bool failed = FALSE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen *extra_buf_r = NULL;
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->base64_bytes < astream->set.min_size ||
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->temp_output->offset > part->base64_bytes +
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen BASE64_ATTACHMENT_MAX_EXTRA_BYTES) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* only a small part of the MIME part is base64-encoded. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->base64_line_blocks == 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* only one line of base64 */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_line_blocks = part->cur_base64_blocks;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(part->base64_line_blocks > 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* decode base64 data and write it to another temp file */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen outfd = astream->set.open_temp_fd(astream->context);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (outfd == -1)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buf = buffer_create_dynamic(default_pool, 1024);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen base64_input = i_stream_create_limit(input, part->base64_bytes);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen output = o_stream_create_fd_file(outfd, 0, FALSE);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_cork(output);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_reset(astream->set.hash_format);
9b5fa7fdd9b9f1f61eaddda48036df200fc5e56eTimo Sirainen size_t bytes_needed = 1;
9b5fa7fdd9b9f1f61eaddda48036df200fc5e56eTimo Sirainen while ((ret = i_stream_read_bytes(base64_input, &data, &size,
9b5fa7fdd9b9f1f61eaddda48036df200fc5e56eTimo Sirainen bytes_needed)) > 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_set_used_size(buf, 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (base64_decode(data, size, &size, buf) < 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_error("istream-attachment: BUG: "
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen "Attachment base64 data unexpectedly broke");
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_stream_skip(base64_input, size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_nsend(output, buf->data, buf->used);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_loop(astream->set.hash_format,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buf->data, buf->used);
9b5fa7fdd9b9f1f61eaddda48036df200fc5e56eTimo Sirainen bytes_needed = i_stream_get_data_size(base64_input) + 1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (ret != -1) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(failed);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if (base64_input->stream_errno != 0) {
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_error("istream-attachment: read(%s) failed: %s",
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_stream_get_name(base64_input),
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_stream_get_error(base64_input));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen if (o_stream_finish(output) < 0) {
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_error("istream-attachment: write(%s) failed: %s",
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen o_stream_get_name(output), o_stream_get_error(output));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen buffer_free(&buf);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_stream_unref(&base64_input);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_unref(&output);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (input->v_offset != part->temp_output->offset && !failed) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* write the rest of the data to the message stream */
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen *extra_buf_r = buffer_create_dynamic(default_pool, 1024);
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody while ((ret = i_stream_read_more(input, &data, &size)) > 0) {
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen buffer_append(*extra_buf_r, data, size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_stream_skip(input, size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(ret == -1);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (input->stream_errno != 0) {
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_error("istream-attachment: read(%s) failed: %s",
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_stream_get_name(input),
9bc1a7f4aa77af2f78e71be129cefd1406fb5e9dTimo Sirainen i_stream_get_error(input));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_stream_unref(&input);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (failed) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_close_fd(&outfd);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* successfully wrote it. switch to using it. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_destroy(&part->temp_output);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_close_fd(&part->temp_fd);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->temp_fd = outfd;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainenstatic int
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainenastream_part_finish(struct attachment_istream *astream, const char **error_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream_part *part = &astream->part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream_attachment_info info;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream *input;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct ostream *output;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen string_t *digest_str;
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen buffer_t *extra_buf = NULL;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const unsigned char *data;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen int ret = 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen if (o_stream_finish(part->temp_output) < 0) {
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen *error_r = t_strdup_printf("write(%s) failed: %s",
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen o_stream_get_name(part->temp_output),
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen o_stream_get_error(part->temp_output));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&info);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.start_offset = astream->part.start_offset;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* base64_bytes contains how many valid base64 bytes there are so far.
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if the base64 ends properly, it'll specify how much of the MIME part
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen is saved as an attachment. the rest of the data (typically
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen linefeeds) is added back to main stream */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.encoded_size = part->base64_bytes;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* get the hash before base64-decoder resets it */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen digest_str = t_str_new(128);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_write(astream->set.hash_format, digest_str);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.hash = str_c(digest_str);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* if it looks like we can decode base64 without any data loss,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen do it and write the decoded data to another temp file. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (!part->base64_failed) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->base64_state == BASE64_STATE_0 &&
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_bytes > 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* there is no trailing LF or '=' characters,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen but it's not completely empty */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_state = BASE64_STATE_EOM;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->base64_state == BASE64_STATE_EOM) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* base64 data looks ok. */
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen if (astream_decode_base64(astream, &extra_buf) < 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->base64_failed = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* open attachment output file */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.part = astream->cur_part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (!part->base64_failed) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.base64_blocks_per_line = part->base64_line_blocks;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.base64_have_crlf = part->base64_have_crlf;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* base64-decoder updated the hash, use it */
97c8ebb3d0ff7e51f8e04539b372786704d37a96Timo Sirainen str_truncate(digest_str, 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_write(astream->set.hash_format, digest_str);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.hash = str_c(digest_str);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* couldn't decode base64, so write the entire MIME part
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen as attachment */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen info.encoded_size = part->temp_output->offset;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen if (astream->set.open_attachment_ostream(&info, &output, error_r,
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen astream->context) < 0) {
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen buffer_free(&extra_buf);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* copy data to attachment from temp file */
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE);
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody while (i_stream_read_more(input, &data, &size) > 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen o_stream_nsend(output, data, size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_stream_skip(input, size);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (input->stream_errno != 0) {
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen *error_r = t_strdup_printf("read(%s) failed: %s",
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen i_stream_get_name(input), i_stream_get_error(input));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ret = -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_stream_destroy(&input);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen if (astream->set.close_attachment_ostream(output, ret == 0, error_r,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->context) < 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ret = -1;
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen if (ret == 0 && extra_buf != NULL)
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen stream_add_data(astream, extra_buf->data, extra_buf->used);
f5d8e05492a3d2552b54129344e37ca01d241832Timo Sirainen buffer_free(&extra_buf);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic void astream_part_reset(struct attachment_istream *astream)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream_part *part = &astream->part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek o_stream_destroy(&part->temp_output);
7b032348d7bbb93ff96188289d3dfc1899b9abb3Josef 'Jeff' Sipek i_close_fd(&part->temp_fd);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_free_and_null(part->content_type);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_free_and_null(part->content_disposition);
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipek buffer_free(&part->part_buf);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(part);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->temp_fd = -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_reset(astream->set.hash_format);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic int
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainenastream_end_of_part(struct attachment_istream *astream, const char **error_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream_part *part = &astream->part;
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen size_t old_size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen int ret = 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* MIME part changed. we're now parsing the end of a boundary,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen possibly followed by message epilogue */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen switch (part->state) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case MAIL_ATTACHMENT_STATE_NO:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case MAIL_ATTACHMENT_STATE_MAYBE:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* MIME part wasn't large enough to be an attachment */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (part->part_buf != NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream_add_data(astream, part->part_buf->data,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->part_buf->used);
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen ret = part->part_buf->used > 0 ? 1 : 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case MAIL_ATTACHMENT_STATE_YES:
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen old_size = astream->istream.pos - astream->istream.skip;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen if (astream_part_finish(astream, error_r) < 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ret = -1;
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen else {
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen /* finished base64 may have added a few more trailing
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen bytes to the stream */
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen ret = astream->istream.pos -
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen astream->istream.skip - old_size;
a3736354b7eb292620cff0fbd8dc8e3ebaf31f4eTimo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen part->state = MAIL_ATTACHMENT_STATE_NO;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream_part_reset(astream);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
edb39c4e2bec31d329816abfd3e2772dd515da07Timo Sirainenstatic int astream_read_next(struct attachment_istream *astream, bool *retry_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream_private *stream = &astream->istream;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct message_block block;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t old_size, new_size;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen const char *error;
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen int ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *retry_r = FALSE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream))
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -2;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen old_size = stream->pos - stream->skip;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen switch (message_parser_parse_next_block(astream->parser, &block)) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case -1:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* done / error */
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen ret = astream_end_of_part(astream, &error);
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen if (ret > 0) {
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen /* final data */
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen new_size = stream->pos - stream->skip;
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen return new_size - old_size;
95ba0bc53d54a31dd74998eb9043799413c71057Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream->istream.eof = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen if (ret < 0) {
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen io_stream_set_error(&stream->iostream, "%s", error);
3979d59aa4e45a49f101eb8a1301e4c2e1eee727Timo Sirainen stream->istream.stream_errno = EIO;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->cur_part = NULL;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen case 0:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* need more data */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen default:
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (block.part != astream->cur_part && astream->cur_part != NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* end of a MIME part */
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen if (astream_end_of_part(astream, &error) < 0) {
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen io_stream_set_error(&stream->iostream, "%s", error);
3979d59aa4e45a49f101eb8a1301e4c2e1eee727Timo Sirainen stream->istream.stream_errno = EIO;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->cur_part = block.part;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (block.hdr != NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* parsing a header */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream_parse_header(astream, block.hdr);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if (block.size == 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* end of headers */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (astream_want_attachment(astream, block.part)) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->part.state = MAIL_ATTACHMENT_STATE_MAYBE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->part.start_offset = stream->parent->v_offset;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream_add_body(astream, &block);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen new_size = stream->pos - stream->skip;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *retry_r = new_size == old_size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return new_size - old_size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
edb39c4e2bec31d329816abfd3e2772dd515da07Timo Sirainenstatic ssize_t
edb39c4e2bec31d329816abfd3e2772dd515da07Timo Siraineni_stream_attachment_extractor_read(struct istream_private *stream)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream *astream =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen (struct attachment_istream *)stream;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen bool retry;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ssize_t ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen do {
edb39c4e2bec31d329816abfd3e2772dd515da07Timo Sirainen ret = astream_read_next(astream, &retry);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } while (retry && astream->set.drain_parent_input);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->retry_read = retry;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return ret;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void i_stream_attachment_extractor_close(struct iostream_private *stream,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen bool close_parent)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream *astream =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen (struct attachment_istream *)stream;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct message_part *parts;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (astream->parser != NULL) {
12e5ac049bd74f8b98d9dc62adcb0bf3217beef6Martti Rannanjärvi message_parser_deinit(&astream->parser, &parts);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen hash_format_deinit_free(&astream->set.hash_format);
c69a177207ed18d0f0210347430a60957136bd6cJosef 'Jeff' Sipek pool_unref(&astream->pool);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (close_parent)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen i_stream_close(astream->istream.parent);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstruct istream *
8aeae03f9f447c8a792b215c9fb954468053c556Timo Siraineni_stream_create_attachment_extractor(struct istream *input,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct istream_attachment_settings *set,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen void *context)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream *astream;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(set->min_size > 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(set->hash_format != NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(set->open_attachment_ostream != NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(set->close_attachment_ostream != NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream = i_new(struct attachment_istream, 1);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->part.temp_fd = -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->set = *set;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->context = context;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->retry_read = TRUE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* make sure the caller doesn't try to double-free this */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen set->hash_format = NULL;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->istream.max_buffer_size = input->real_stream->max_buffer_size;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
edb39c4e2bec31d329816abfd3e2772dd515da07Timo Sirainen astream->istream.read = i_stream_attachment_extractor_read;
edb39c4e2bec31d329816abfd3e2772dd515da07Timo Sirainen astream->istream.iostream.close = i_stream_attachment_extractor_close;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->istream.istream.readable_fd = FALSE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->istream.istream.blocking = input->blocking;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->istream.istream.seekable = FALSE;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
c6f644533ef64e758fcb654641c668e73e654da7Timo Sirainen astream->pool = pool_alloconly_create("istream attachment", 1024);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen astream->parser = message_parser_init(astream->pool, input, 0,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return i_stream_create(&astream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenbool i_stream_attachment_extractor_can_retry(struct istream *input)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct attachment_istream *astream =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen (struct attachment_istream *)input->real_stream;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return astream->retry_read;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}