dovecot-2.2: lib-mail: Added istream-attachment-[connector|extra...

dovecot at dovecot.org dovecot at dovecot.org
Fri Jun 29 08:01:25 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/ffab35ef2e5b
changeset: 14698:ffab35ef2e5b
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Jun 29 08:00:19 2012 +0300
description:
lib-mail: Added istream-attachment-[connector|extractor].
istream-attachment-extractor can be used to parse messages and extract
attachment MIME parts from them to wanted output streams. The attachments
are base64-decoded if they can be later re-encoded to original input without
any changes.

istream-attachment-connector does the reverse by taking the base istream and
attachment istreams and merging them together to create the original
istream.

diffstat:

 src/lib-mail/Makefile.am                    |   20 +-
 src/lib-mail/istream-attachment-connector.c |  123 ++++
 src/lib-mail/istream-attachment-connector.h |   26 +
 src/lib-mail/istream-attachment-extractor.c |  689 ++++++++++++++++++++++++++++
 src/lib-mail/istream-attachment-extractor.h |   59 ++
 src/lib-mail/test-istream-attachment.c      |  293 +++++++++++
 6 files changed, 1208 insertions(+), 2 deletions(-)

diffs (truncated from 1262 to 300 lines):

diff -r 69334bf138cf -r ffab35ef2e5b src/lib-mail/Makefile.am
--- a/src/lib-mail/Makefile.am	Fri Jun 29 07:56:02 2012 +0300
+++ b/src/lib-mail/Makefile.am	Fri Jun 29 08:00:19 2012 +0300
@@ -6,6 +6,8 @@
 	-I$(top_srcdir)/src/lib-charset
 
 libmail_la_SOURCES = \
+	istream-attachment-connector.c \
+	istream-attachment-extractor.c \
 	istream-binary-converter.c \
 	istream-dot.c \
 	istream-header-filter.c \
@@ -29,6 +31,8 @@
 	rfc822-parser.c
 
 headers = \
+	istream-attachment-connector.h \
+	istream-attachment-extractor.h \
 	istream-binary-converter.h \
 	istream-dot.h \
 	istream-header-filter.h \
@@ -57,6 +61,7 @@
 
 test_programs = \
 	test-istream-dot \
+	test-istream-attachment \
 	test-istream-binary-converter \
 	test-istream-header-filter \
 	test-mbox-from \
@@ -81,9 +86,20 @@
 test_istream_dot_LDADD = istream-dot.lo $(test_libs)
 test_istream_dot_DEPENDENCIES = istream-dot.lo $(test_libs)
 
+message_parser_objects = \
+	message-parser.lo \
+	message-header-parser.lo \
+	message-size.lo \
+	rfc822-parser.lo \
+	rfc2231-parser.lo
+
 test_istream_binary_converter_SOURCES = test-istream-binary-converter.c
-test_istream_binary_converter_LDADD = istream-binary-converter.lo message-parser.lo message-header-parser.lo message-size.lo rfc822-parser.lo rfc2231-parser.lo $(test_libs)
-test_istream_binary_converter_DEPENDENCIES = istream-binary-converter.lo message-parser.lo message-header-parser.lo message-size.lo rfc822-parser.lo rfc2231-parser.lo $(test_libs)
+test_istream_binary_converter_LDADD = istream-binary-converter.lo $(message_parser_objects)  $(test_libs)
+test_istream_binary_converter_DEPENDENCIES = istream-binary-converter.lo $(message_parser_objects)  $(test_libs)
+
+test_istream_attachment_SOURCES = test-istream-attachment.c
+test_istream_attachment_LDADD = istream-attachment-extractor.lo istream-attachment-connector.lo $(message_parser_objects) $(test_libs)
+test_istream_attachment_DEPENDENCIES = istream-attachment-extractor.lo istream-attachment-connector.lo $(message_parser_objects)  $(test_libs)
 
 test_istream_header_filter_SOURCES = test-istream-header-filter.c
 test_istream_header_filter_LDADD = istream-header-filter.lo message-header-parser.lo $(test_libs)
diff -r 69334bf138cf -r ffab35ef2e5b src/lib-mail/istream-attachment-connector.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/istream-attachment-connector.c	Fri Jun 29 08:00:19 2012 +0300
@@ -0,0 +1,123 @@
+/* Copyright (c) 2003-2012 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-encoder.h"
+#include "istream-attachment-connector.h"
+
+struct istream_attachment_connector {
+	pool_t pool;
+	struct istream *base_input;
+	uoff_t msg_size;
+
+	uoff_t encoded_offset;
+	ARRAY_DEFINE(streams, struct istream *);
+};
+
+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->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_limit(conn->base_input,
+					      base_prefix_size);
+		array_append(&conn->streams, &input, 1);
+		i_stream_skip(conn->base_input, 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);
+	}
+	input2 = i_stream_create_sized(input, encoded_size);
+	array_append(&conn->streams, &input2, 1);
+	i_stream_unref(&input);
+	return 0;
+}
+
+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->v_offset != conn->msg_size) {
+		i_assert(conn->base_input->v_offset < conn->msg_size);
+
+		trailer_size = conn->msg_size - conn->encoded_offset;
+		input = i_stream_create_limit(conn->base_input, trailer_size);
+		array_append(&conn->streams, &input, 1);
+	}
+	array_append_zero(&conn->streams);
+
+	inputs = array_idx_modifiable(&conn->streams, 0);
+	input = i_stream_create_concat(inputs);
+
+	i_stream_unref(&conn->base_input);
+	pool_unref(&conn->pool);
+	return input;
+}
+
+void istream_attachment_connector_abort(struct istream_attachment_connector **_conn)
+{
+	struct istream_attachment_connector *conn = *_conn;
+
+	*_conn = NULL;
+
+	i_stream_unref(&conn->base_input);
+	pool_unref(&conn->pool);
+}
diff -r 69334bf138cf -r ffab35ef2e5b src/lib-mail/istream-attachment-connector.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/istream-attachment-connector.h	Fri Jun 29 08:00:19 2012 +0300
@@ -0,0 +1,26 @@
+#ifndef ISTREAM_ATTACHMENT_CONNECTOR_H
+#define ISTREAM_ATTACHMENT_CONNECTOR_H
+
+/* Start building a message stream. The base_input contains the message
+   without attachments. The final stream must be exactly msg_size bytes. */
+struct istream_attachment_connector *
+istream_attachment_connector_begin(struct istream *base_input, uoff_t msg_size);
+
+/* Add the given input stream as attachment. The attachment starts at the given
+   start_offset in the (original) message. If base64_blocks_per_line is
+   non-zero, the input is base64-encoded with the given settings. The
+   (resulting base64-encoded) input must have exactly encoded_size bytes.
+
+   Returns 0 if the input was ok, -1 if we've already reached msg_size */
+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 *
+istream_attachment_connector_finish(struct istream_attachment_connector **conn);
+void istream_attachment_connector_abort(struct istream_attachment_connector **conn);
+
+#endif
diff -r 69334bf138cf -r ffab35ef2e5b src/lib-mail/istream-attachment-extractor.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/istream-attachment-extractor.c	Fri Jun 29 08:00:19 2012 +0300
@@ -0,0 +1,689 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream-private.h"
+#include "ostream.h"
+#include "base64.h"
+#include "buffer.h"
+#include "str.h"
+#include "hash-format.h"
+#include "rfc822-parser.h"
+#include "message-parser.h"
+#include "istream-attachment-extractor.h"
+
+#define BASE64_ATTACHMENT_MAX_EXTRA_BYTES 1024
+
+enum mail_attachment_state {
+	MAIL_ATTACHMENT_STATE_NO,
+	MAIL_ATTACHMENT_STATE_MAYBE,
+	MAIL_ATTACHMENT_STATE_YES
+};
+
+enum base64_state {
+	BASE64_STATE_0 = 0,
+	BASE64_STATE_1,
+	BASE64_STATE_2,
+	BASE64_STATE_3,
+	BASE64_STATE_CR,
+	BASE64_STATE_EOB,
+	BASE64_STATE_EOM
+};
+
+struct attachment_istream_part {
+	char *content_type, *content_disposition;
+	enum mail_attachment_state state;
+	/* start offset of the message part in the original input stream */
+	uoff_t start_offset;
+
+	/* for saving attachments base64-decoded: */
+	enum base64_state base64_state;
+	unsigned int base64_line_blocks, cur_base64_blocks;
+	uoff_t base64_bytes;
+	bool base64_have_crlf; /* CRLF linefeeds */
+	bool base64_failed;
+
+	int temp_fd;
+	struct ostream *temp_output;
+	buffer_t *part_buf;
+};
+
+struct attachment_istream {
+	struct istream_private istream;
+	pool_t pool;
+
+	struct istream_attachment_settings set;
+	void *context;
+
+	struct message_parser_ctx *parser;
+	struct message_part *cur_part;
+	struct attachment_istream_part part;
+
+	bool retry_read;
+};
+
+static void stream_add_data(struct attachment_istream *astream,
+			    const void *data, size_t size)
+{
+	if (size > 0) {
+		memcpy(i_stream_alloc(&astream->istream, size), data, size);
+		astream->istream.pos += size;
+	}
+}
+
+static void parse_content_type(struct attachment_istream *astream,
+			       const struct message_header_line *hdr)
+{
+	struct rfc822_parser_context parser;
+	string_t *content_type;
+
+	rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
+	rfc822_skip_lwsp(&parser);
+
+	T_BEGIN {
+		content_type = t_str_new(64);
+		if (rfc822_parse_content_type(&parser, content_type) >= 0) {
+			i_free(astream->part.content_type);
+			astream->part.content_type =
+				i_strdup(str_c(content_type));


More information about the dovecot-cvs mailing list