summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/istream-attachment-connector.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-mail/istream-attachment-connector.c')
-rw-r--r--src/lib-mail/istream-attachment-connector.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/lib-mail/istream-attachment-connector.c b/src/lib-mail/istream-attachment-connector.c
new file mode 100644
index 0000000..cb66563
--- /dev/null
+++ b/src/lib-mail/istream-attachment-connector.c
@@ -0,0 +1,149 @@
+/* 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);
+}