dovecot-2.0: liblib: Added istream for reading binary data as ba...

dovecot at dovecot.org dovecot at dovecot.org
Tue Oct 19 20:54:58 EEST 2010


details:   http://hg.dovecot.org/dovecot-2.0/rev/ce5bb3246ffb
changeset: 12311:ce5bb3246ffb
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Oct 19 18:30:51 2010 +0100
description:
liblib: Added istream for reading binary data as base64-encoded.

diffstat:

 src/lib/Makefile.am              |    2 +
 src/lib/istream-base64-encoder.c |  148 +++++++++++++++++++++++++++++++++++++
 src/lib/istream-base64-encoder.h |    8 ++
 3 files changed, 158 insertions(+), 0 deletions(-)

diffs (183 lines):

diff -r 21c70ff6b344 -r ce5bb3246ffb src/lib/Makefile.am
--- a/src/lib/Makefile.am	Tue Oct 19 18:27:30 2010 +0100
+++ b/src/lib/Makefile.am	Tue Oct 19 18:30:51 2010 +0100
@@ -47,6 +47,7 @@
 	imem.c \
 	iostream.c \
 	istream.c \
+	istream-base64-encoder.c \
 	istream-concat.c \
 	istream-crlf.c \
 	istream-data.c \
@@ -158,6 +159,7 @@
 	imem.h \
 	iostream-internal.h \
 	istream.h \
+	istream-base64-encoder.h \
 	istream-concat.h \
 	istream-crlf.h \
 	istream-internal.h \
diff -r 21c70ff6b344 -r ce5bb3246ffb src/lib/istream-base64-encoder.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/istream-base64-encoder.c	Tue Oct 19 18:30:51 2010 +0100
@@ -0,0 +1,148 @@
+/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "base64.h"
+#include "istream-internal.h"
+#include "istream-base64-encoder.h"
+
+struct base64_encoder_istream {
+	struct istream_private istream;
+
+	/* current encoded line length. */
+	unsigned int cur_line_len;
+
+	unsigned int chars_per_line;
+	bool crlf;
+};
+
+static int i_stream_read_parent(struct istream_private *stream)
+{
+	size_t size;
+	ssize_t ret;
+
+	(void)i_stream_get_data(stream->parent, &size);
+	if (size >= 4)
+		return 1;
+
+	/* we have less than one base64 block.
+	   see if there is more data available. */
+	ret = i_stream_read(stream->parent);
+	if (ret <= 0) {
+		stream->istream.stream_errno = stream->parent->stream_errno;
+		stream->istream.eof = stream->parent->eof;
+		return size > 0 ? 1 : ret;
+	}
+	(void)i_stream_get_data(stream->parent, &size);
+	i_assert(size != 0);
+	return 1;
+}
+
+static bool
+i_stream_base64_try_encode_line(struct base64_encoder_istream *bstream)
+{
+	struct istream_private *stream = &bstream->istream;
+	const unsigned char *data;
+	size_t size, buffer_avail;
+	buffer_t buf;
+
+	if (bstream->cur_line_len == bstream->chars_per_line) {
+		/* @UNSAFE: end of line, add newline */
+		if (!i_stream_get_buffer_space(stream,
+					       bstream->crlf ? 2 : 1, NULL))
+			return FALSE;
+
+		if (bstream->crlf)
+			stream->w_buffer[stream->pos++] = '\r';
+		stream->w_buffer[stream->pos++] = '\n';
+		bstream->cur_line_len = 0;
+	}
+	data = i_stream_get_data(stream->parent, &size);
+	if (size == 0)
+		return FALSE;
+
+	i_stream_get_buffer_space(stream, (size+2)/3*4, NULL);
+	buffer_avail = stream->buffer_size - stream->pos;
+
+	if ((size + 2) / 3 * 4 > buffer_avail) {
+		/* can't fit everything to destination buffer.
+		   write as much as we can. */
+		size = (buffer_avail / 4) * 3;
+	} else if (!stream->parent->eof && size % 3 != 0) {
+		/* encode 3 chars at a time, so base64_encode() doesn't
+		   add '=' characters in the middle of the stream */
+		size -= (size % 3);
+	}
+	if (size == 0)
+		return FALSE;
+
+	if (bstream->cur_line_len + (size+2)/3*4 > bstream->chars_per_line) {
+		size = (bstream->chars_per_line - bstream->cur_line_len)/4 * 3;
+		i_assert(size != 0);
+	}
+
+	buffer_create_data(&buf, stream->w_buffer + stream->pos, buffer_avail);
+	base64_encode(data, size, &buf);
+	i_assert(buf.used > 0);
+
+	bstream->cur_line_len += buf.used;
+	i_assert(bstream->cur_line_len <= bstream->chars_per_line);
+	stream->pos += buf.used;
+	i_stream_skip(stream->parent, size);
+	return TRUE;
+}
+
+static ssize_t i_stream_base64_encoder_read(struct istream_private *stream)
+{
+	struct base64_encoder_istream *bstream =
+		(struct base64_encoder_istream *)stream;
+	size_t pre_count, post_count;
+	int ret;
+
+	ret = i_stream_read_parent(stream);
+	if (ret <= 0)
+		return ret;
+
+	/* encode as many lines as fits into destination buffer */
+	pre_count = stream->pos - stream->skip;
+	while (i_stream_base64_try_encode_line(bstream)) ;
+	post_count = stream->pos - stream->skip;
+
+	if (pre_count == post_count) {
+		i_assert(stream->buffer_size - stream->pos < 4);
+		return -2;
+	}
+
+	i_assert(post_count > pre_count);
+	return post_count - pre_count;
+}
+
+static const struct stat *
+i_stream_base64_encoder_stat(struct istream_private *stream, bool exact)
+{
+	return i_stream_stat(stream->parent, exact);
+}
+
+struct istream *
+i_stream_create_base64_encoder(struct istream *input,
+			       unsigned int chars_per_line, bool crlf)
+{
+	struct base64_encoder_istream *bstream;
+
+	i_assert(chars_per_line % 4 == 0);
+
+	bstream = i_new(struct base64_encoder_istream, 1);
+	bstream->chars_per_line = chars_per_line;
+	bstream->crlf = crlf;
+	bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+
+	bstream->istream.parent = input;
+	bstream->istream.read = i_stream_base64_encoder_read;
+	bstream->istream.stat = i_stream_base64_encoder_stat;
+
+	bstream->istream.istream.readable_fd = FALSE;
+	bstream->istream.istream.blocking = input->blocking;
+	bstream->istream.istream.seekable = FALSE;
+	return i_stream_create(&bstream->istream, input,
+			       i_stream_get_fd(input));
+}
diff -r 21c70ff6b344 -r ce5bb3246ffb src/lib/istream-base64-encoder.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/istream-base64-encoder.h	Tue Oct 19 18:30:51 2010 +0100
@@ -0,0 +1,8 @@
+#ifndef ISTREAM_BASE64_H
+#define ISTREAM_BASE64_H
+
+struct istream *
+i_stream_create_base64_encoder(struct istream *input,
+			       unsigned int chars_per_line, bool crlf);
+
+#endif


More information about the dovecot-cvs mailing list