dovecot-2.0: Added support for saving base64-encoded attachments...

dovecot at dovecot.org dovecot at dovecot.org
Sat Oct 2 14:34:02 EEST 2010


details:   http://hg.dovecot.org/dovecot-2.0/rev/3ef0ac874fd7
changeset: 12254:3ef0ac874fd7
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Aug 23 19:04:10 2010 +0100
description:
Added support for saving base64-encoded attachments de-base64d

diffstat:

 src/lib-storage/index/Makefile.am                   |    2 +
 src/lib-storage/index/dbox-common/dbox-attachment.c |   96 +++++-
 src/lib-storage/index/index-attachment.c            |  427 +++++++++++++++++++++---
 src/lib-storage/index/index-attachment.h            |    8 +
 src/lib-storage/index/istream-attachment.c          |    9 -
 src/lib-storage/index/istream-base64-encoder.c      |  148 ++++++++
 src/lib-storage/index/istream-base64-encoder.h      |    8 +
 7 files changed, 615 insertions(+), 83 deletions(-)

diffs (truncated from 995 to 300 lines):

diff -r 0078f598d1fc -r 3ef0ac874fd7 src/lib-storage/index/Makefile.am
--- a/src/lib-storage/index/Makefile.am	Mon Aug 23 19:03:16 2010 +0100
+++ b/src/lib-storage/index/Makefile.am	Mon Aug 23 19:04:10 2010 +0100
@@ -13,6 +13,7 @@
 
 libstorage_index_la_SOURCES = \
 	istream-attachment.c \
+	istream-base64-encoder.c \
 	istream-mail-stats.c \
 	index-attachment.c \
 	index-fetch.c \
@@ -38,6 +39,7 @@
 
 headers = \
 	istream-attachment.h \
+	istream-base64-encoder.h \
 	istream-mail-stats.h \
 	index-attachment.h \
 	index-mail.h \
diff -r 0078f598d1fc -r 3ef0ac874fd7 src/lib-storage/index/dbox-common/dbox-attachment.c
--- a/src/lib-storage/index/dbox-common/dbox-attachment.c	Mon Aug 23 19:03:16 2010 +0100
+++ b/src/lib-storage/index/dbox-common/dbox-attachment.c	Mon Aug 23 19:04:10 2010 +0100
@@ -5,16 +5,24 @@
 #include "istream-concat.h"
 #include "str.h"
 #include "istream-attachment.h"
+#include "istream-base64-encoder.h"
 #include "dbox-file.h"
 #include "dbox-save.h"
 #include "dbox-attachment.h"
 
+enum dbox_attachment_decode_option {
+	DBOX_ATTACHMENT_DECODE_OPTION_NONE = '-',
+	DBOX_ATTACHMENT_DECODE_OPTION_BASE64 = 'B',
+	DBOX_ATTACHMENT_DECODE_OPTION_CRLF = 'C'
+};
+
 void dbox_attachment_save_write_metadata(struct mail_save_context *ctx,
 					 string_t *str)
 {
 	const ARRAY_TYPE(mail_attachment_extref) *extrefs;
 	const struct mail_attachment_extref *extref;
 	bool add_space = FALSE;
+	unsigned int startpos;
 
 	extrefs = index_attachment_save_get_extrefs(ctx);
 	if (extrefs == NULL || array_count(extrefs) == 0)
@@ -26,13 +34,61 @@
 			add_space = TRUE;
 		else
 			str_append_c(str, ' ');
-		str_printfa(str, "%"PRIuUOFF_T" %"PRIuUOFF_T" %s",
-			    extref->start_offset, extref->size, extref->path);
+		str_printfa(str, "%"PRIuUOFF_T" %"PRIuUOFF_T" ",
+			    extref->start_offset, extref->size);
+
+		startpos = str_len(str);
+		if (extref->base64_have_crlf)
+			str_append_c(str, DBOX_ATTACHMENT_DECODE_OPTION_CRLF);
+		if (extref->base64_blocks_per_line > 0) {
+			str_printfa(str, "%c%u",
+				    DBOX_ATTACHMENT_DECODE_OPTION_BASE64,
+				    extref->base64_blocks_per_line * 4);
+		}
+		if (startpos == str_len(str)) {
+			/* make it clear there are no options */
+			str_append_c(str, DBOX_ATTACHMENT_DECODE_OPTION_NONE);
+		}
+		str_append_c(str, ' ');
+		str_append(str, extref->path);
 	}
 	str_append_c(str, '\n');
 }
 
 static bool
+parse_extref_decode_options(const char *str,
+			    struct mail_attachment_extref *extref)
+{
+	unsigned int num;
+
+	if (*str == DBOX_ATTACHMENT_DECODE_OPTION_NONE)
+		return str[1] == '\0';
+
+	while (*str != '\0') {
+		switch (*str) {
+		case DBOX_ATTACHMENT_DECODE_OPTION_BASE64:
+			str++; num = 0;
+			while (*str >= '0' && *str <= '9') {
+				num = num*10 + (*str-'0');
+				str++;
+			}
+			if (num == 0 || num % 4 != 0)
+				return FALSE;
+
+			extref->base64_blocks_per_line = num/4;
+			break;
+		case DBOX_ATTACHMENT_DECODE_OPTION_CRLF:
+			extref->base64_have_crlf = TRUE;
+			str++;
+			break;
+		default:
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+static bool
 dbox_attachment_parse_extref_real(const char *line, pool_t pool,
 				  ARRAY_TYPE(mail_attachment_extref) *extrefs)
 {
@@ -43,20 +99,27 @@
 
 	args = t_strsplit(line, " ");
 	len = str_array_length(args);
-	if ((len % 3) != 0)
+	if ((len % 4) != 0)
 		return FALSE;
 
 	last_voffset = 0;
-	memset(&extref, 0, sizeof(extref));
-	for (i = 0; args[i] != NULL; i += 3) {
-		if (str_to_uoff(args[i+0], &extref.start_offset) < 0 ||
-		    str_to_uoff(args[i+1], &extref.size) < 0 ||
-		    extref.start_offset < last_voffset)
+	for (i = 0; args[i] != NULL; i += 4) {
+		const char *start_offset_str = args[i+0];
+		const char *size_str = args[i+1];
+		const char *decode_options = args[i+2];
+		const char *path = args[i+3];
+
+		memset(&extref, 0, sizeof(extref));
+		if (str_to_uoff(start_offset_str, &extref.start_offset) < 0 ||
+		    str_to_uoff(size_str, &extref.size) < 0 ||
+		    extref.start_offset < last_voffset ||
+		    !parse_extref_decode_options(decode_options, &extref))
 			return FALSE;
+
 		last_voffset += extref.size +
 			(extref.start_offset - last_voffset);
 
-		extref.path = p_strdup(pool, args[i+2]);
+		extref.path = p_strdup(pool, path);
 		array_append(extrefs, &extref, 1);
 	}
 	return TRUE;
@@ -109,6 +172,15 @@
 
 		last_voffset += extref->size;
 		input2 = i_stream_create_file(path, IO_BLOCK_SIZE);
+
+		if (extref->base64_blocks_per_line > 0) {
+			input = i_stream_create_base64_encoder(input2,
+					extref->base64_blocks_per_line*4,
+					extref->base64_have_crlf);
+			i_stream_unref(&input2);
+			input2 = input;
+		}
+
 		input = i_stream_create_attachment(input2, extref->size);
 		i_stream_unref(&input2);
 		array_append(&streams, &input, 1);
@@ -154,7 +226,9 @@
 		ret = dbox_attachment_file_get_stream_from(file, ext_refs,
 							   stream_r);
 	} T_END;
-	if (ret == 0)
-		dbox_file_set_corrupted(file, "Ext refs metadata corrupted");
+	if (ret == 0) {
+		dbox_file_set_corrupted(file, "Ext refs metadata corrupted: %s",
+					ext_refs);
+	}
 	return ret;
 }
diff -r 0078f598d1fc -r 3ef0ac874fd7 src/lib-storage/index/index-attachment.c
--- a/src/lib-storage/index/index-attachment.c	Mon Aug 23 19:03:16 2010 +0100
+++ b/src/lib-storage/index/index-attachment.c	Mon Aug 23 19:04:10 2010 +0100
@@ -5,6 +5,7 @@
 #include "fs-api.h"
 #include "istream.h"
 #include "ostream.h"
+#include "base64.h"
 #include "sha1.h"
 #include "str.h"
 #include "hex-binary.h"
@@ -14,28 +15,53 @@
 #include "index-mail.h"
 #include "index-attachment.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 mail_save_attachment_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, base64_prev_state;
+	unsigned int base64_line_blocks, cur_base64_blocks;
+	unsigned int base64_last_newline_size;
+	uoff_t base64_bytes;
+	bool base64_have_crlf; /* CRLF linefeeds */
+	bool base64_failed;
+
+	int temp_fd;
+	struct ostream *output;
+	struct sha1_ctxt part_hash;
+	buffer_t *part_buf;
+};
+
 struct mail_save_attachment {
 	pool_t pool;
 	struct message_parser_ctx *parser;
 	struct fs *fs;
 	struct istream *input;
 
+	/* per-MIME part data */
+	struct mail_save_attachment_part part;
 	struct message_part *prev_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;
-
-	int temp_fd;
-	struct ostream *output;
-	struct sha1_ctxt part_hash;
-	buffer_t *part_buf;
 
 	ARRAY_TYPE(mail_attachment_extref) extrefs;
 };
@@ -79,8 +105,8 @@
 	T_BEGIN {
 		content_type = t_str_new(64);
 		if (rfc822_parse_content_type(&parser, content_type) >= 0) {
-			i_free(ctx->attach->content_type);
-			ctx->attach->content_type =
+			i_free(ctx->attach->part.content_type);
+			ctx->attach->part.content_type =
 				i_strdup(str_c(content_type));
 		}
 	} T_END;
@@ -91,8 +117,8 @@
 			  const struct message_header_line *hdr)
 {
 	/* just pass it as-is to backend. */
-	i_free(ctx->attach->content_disposition);
-	ctx->attach->content_disposition =
+	i_free(ctx->attach->part.content_disposition);
+	ctx->attach->part.content_disposition =
 		i_strndup(hdr->full_value, hdr->full_value_len);
 }
 
@@ -136,9 +162,8 @@
 		return box->v.save_is_attachment(ctx, part);
 }
 
-static int index_attachment_save_temp_open(struct mail_save_context *ctx)
+static int index_attachment_save_temp_open_fd(struct mail_storage *storage)
 {
-	struct mail_storage *storage = ctx->transaction->box->storage;
 	string_t *temp_path;
 	int fd;
 
@@ -156,15 +181,150 @@
 		(void)close(fd);
 		return -1;
 	}
+	return fd;
+}
 
-	ctx->attach->temp_fd = fd;
-	ctx->attach->output = o_stream_create_fd(fd, 0, FALSE);
-	sha1_init(&ctx->attach->part_hash);
+static int index_attachment_save_temp_open(struct mail_save_context *ctx)
+{
+	int fd;
+
+	fd = index_attachment_save_temp_open_fd(ctx->transaction->box->storage);
+	if (fd == -1)
+		return -1;
+
+	ctx->attach->part.temp_fd = fd;


More information about the dovecot-cvs mailing list