# HG changeset patch # User Timo Sirainen # Date 1287509451 -3600 # Node ID ce5bb3246ffb4f40d65bc7cd586eddf7a3d5f425 # Parent 21c70ff6b344bb7b6607c63a32886e47035a61d0 liblib: Added istream for reading binary data as base64-encoded. 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