/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "istream-concat.h" #include "istream-sized.h" #include "istream-base64.h" #include "istream-attachment-connector.h" struct istream_attachment_connector { pool_t pool; struct istream *base_input; uoff_t base_input_offset, msg_size; uoff_t encoded_offset; ARRAY(struct istream *) streams; }; struct istream_attachment_connector * istream_attachment_connector_begin(struct istream *base_input, uoff_t msg_size) { struct istream_attachment_connector *conn; pool_t pool; pool = pool_alloconly_create("istream-attachment-connector", 1024); conn = p_new(pool, struct istream_attachment_connector, 1); conn->pool = pool; conn->base_input = base_input; conn->base_input_offset = base_input->v_offset; conn->msg_size = msg_size; p_array_init(&conn->streams, pool, 8); i_stream_ref(conn->base_input); return conn; } int istream_attachment_connector_add(struct istream_attachment_connector *conn, struct istream *decoded_input, uoff_t start_offset, uoff_t encoded_size, unsigned int base64_blocks_per_line, bool base64_have_crlf, const char **error_r) { struct istream *input, *input2; uoff_t base_prefix_size; if (start_offset < conn->encoded_offset) { *error_r = t_strdup_printf( "Attachment %s points before the previous attachment " "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", i_stream_get_name(decoded_input), start_offset, conn->encoded_offset); return -1; } base_prefix_size = start_offset - conn->encoded_offset; if (start_offset + encoded_size > conn->msg_size) { *error_r = t_strdup_printf( "Attachment %s points outside message " "(%"PRIuUOFF_T" + %"PRIuUOFF_T" > %"PRIuUOFF_T")", i_stream_get_name(decoded_input), start_offset, encoded_size, conn->msg_size); return -1; } if (base_prefix_size > 0) { /* add a part of the base message before the attachment */ input = i_stream_create_min_sized_range(conn->base_input, conn->base_input_offset, base_prefix_size); i_stream_set_name(input, t_strdup_printf("%s middle", i_stream_get_name(conn->base_input))); array_push_back(&conn->streams, &input); conn->base_input_offset += base_prefix_size; conn->encoded_offset += base_prefix_size; } conn->encoded_offset += encoded_size; if (base64_blocks_per_line == 0) { input = decoded_input; i_stream_ref(input); } else { input = i_stream_create_base64_encoder(decoded_input, base64_blocks_per_line*4, base64_have_crlf); i_stream_set_name(input, t_strdup_printf("%s[base64:%u b/l%s]", i_stream_get_name(decoded_input), base64_blocks_per_line, base64_have_crlf ? ",crlf" : "")); } input2 = i_stream_create_sized(input, encoded_size); array_push_back(&conn->streams, &input2); i_stream_unref(&input); return 0; } static void istream_attachment_connector_free(struct istream_attachment_connector *conn) { struct istream *stream; array_foreach_elem(&conn->streams, stream) i_stream_unref(&stream); i_stream_unref(&conn->base_input); pool_unref(&conn->pool); } struct istream * istream_attachment_connector_finish(struct istream_attachment_connector **_conn) { struct istream_attachment_connector *conn = *_conn; struct istream **inputs, *input; uoff_t trailer_size; *_conn = NULL; if (conn->base_input_offset != conn->msg_size) { i_assert(conn->base_input_offset < conn->msg_size); if (conn->msg_size != UOFF_T_MAX) { trailer_size = conn->msg_size - conn->encoded_offset; input = i_stream_create_sized_range(conn->base_input, conn->base_input_offset, trailer_size); i_stream_set_name(input, t_strdup_printf( "%s trailer", i_stream_get_name(conn->base_input))); } else { input = i_stream_create_range(conn->base_input, conn->base_input_offset, UOFF_T_MAX); } array_push_back(&conn->streams, &input); } array_append_zero(&conn->streams); inputs = array_front_modifiable(&conn->streams); input = i_stream_create_concat(inputs); istream_attachment_connector_free(conn); return input; } void istream_attachment_connector_abort(struct istream_attachment_connector **_conn) { struct istream_attachment_connector *conn = *_conn; *_conn = NULL; istream_attachment_connector_free(conn); }