From: Timo Sirainen Date: Fri, 10 Aug 2012 03:45:25 +0000 (+0300) Subject: Added istream-base64-decoder. X-Git-Tag: 2.2.alpha1~383 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3700eab7818bcf8a68424c418485ae4e8770865d;p=thirdparty%2Fdovecot%2Fcore.git Added istream-base64-decoder. --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 36fbfbb195..f4348592e1 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -54,6 +54,7 @@ liblib_la_SOURCES = \ iostream-rawlog.c \ iso8601-date.c \ istream.c \ + istream-base64-decoder.c \ istream-base64-encoder.c \ istream-chain.c \ istream-concat.c \ @@ -178,7 +179,7 @@ headers = \ iostream-rawlog-private.h \ iso8601-date.h \ istream.h \ - istream-base64-encoder.h \ + istream-base64.h \ istream-chain.h \ istream-concat.h \ istream-crlf.h \ @@ -263,6 +264,7 @@ test_lib_SOURCES = \ test-hash-format.c \ test-hex-binary.c \ test-iso8601-date.c \ + test-istream-base64-decoder.c \ test-istream-base64-encoder.c \ test-istream-concat.c \ test-istream-crlf.c \ diff --git a/src/lib/istream-base64-decoder.c b/src/lib/istream-base64-decoder.c new file mode 100644 index 0000000000..1138490717 --- /dev/null +++ b/src/lib/istream-base64-decoder.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "base64.h" +#include "istream-private.h" +#include "istream-base64.h" + +struct base64_decoder_istream { + struct istream_private istream; +}; + +static int i_stream_read_parent(struct istream_private *stream) +{ + size_t size; + ssize_t ret; + + size = i_stream_get_data_size(stream->parent); + 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 ret; + } + size = i_stream_get_data_size(stream->parent); + i_assert(size != 0); + return 1; +} + +static int +i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream) +{ + struct istream_private *stream = &bstream->istream; + const unsigned char *data; + size_t size, avail, buffer_avail, pos; + buffer_t buf; + + data = i_stream_get_data(stream->parent, &size); + if (size == 0) + return 0; + + i_stream_try_alloc(stream, (size+3)/4*3, &avail); + buffer_avail = stream->buffer_size - stream->pos; + + if ((size + 3) / 4 * 3 > buffer_avail) { + /* can't fit everything to destination buffer. + write as much as we can. */ + size = (buffer_avail / 3) * 4; + if (size == 0) + return -2; + } + + buffer_create_data(&buf, stream->w_buffer + stream->pos, buffer_avail); + if (base64_decode(data, size, &pos, &buf) < 0) { + stream->istream.stream_errno = EINVAL; + return -1; + } + + stream->pos += buf.used; + i_stream_skip(stream->parent, pos); + return pos > 0 ? 1 : 0; +} + +static ssize_t i_stream_base64_decoder_read(struct istream_private *stream) +{ + struct base64_decoder_istream *bstream = + (struct base64_decoder_istream *)stream; + size_t pre_count, post_count; + int ret; + + do { + ret = i_stream_read_parent(stream); + if (ret <= 0) { + if (ret < 0 && stream->istream.stream_errno == 0 && + i_stream_get_data_size(stream->parent) > 0) { + /* base64 input with a partial block */ + stream->istream.stream_errno = EINVAL; + } + return ret; + } + + /* encode as many blocks as fits into destination buffer */ + pre_count = stream->pos - stream->skip; + while ((ret = i_stream_base64_try_decode_block(bstream)) > 0) ; + post_count = stream->pos - stream->skip; + } while (ret == 0); + + if (ret < 0) + return ret; + + i_assert(post_count > pre_count); + return post_count - pre_count; +} + +static void +i_stream_base64_decoder_seek(struct istream_private *stream, + uoff_t v_offset, bool mark) +{ + if (v_offset < stream->istream.v_offset) { + /* seeking backwards - go back to beginning and seek + forward from there. */ + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + i_stream_seek(stream->parent, 0); + } + i_stream_default_seek_nonseekable(stream, v_offset, mark); +} + +struct istream * +i_stream_create_base64_decoder(struct istream *input) +{ + struct base64_decoder_istream *bstream; + + bstream = i_new(struct base64_decoder_istream, 1); + bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + bstream->istream.read = i_stream_base64_decoder_read; + bstream->istream.seek = i_stream_base64_decoder_seek; + + bstream->istream.istream.readable_fd = FALSE; + bstream->istream.istream.blocking = input->blocking; + bstream->istream.istream.seekable = input->seekable; + return i_stream_create(&bstream->istream, input, + i_stream_get_fd(input)); +} diff --git a/src/lib/istream-base64-encoder.c b/src/lib/istream-base64-encoder.c index d4cf26cd8f..5bcb804a62 100644 --- a/src/lib/istream-base64-encoder.c +++ b/src/lib/istream-base64-encoder.c @@ -4,7 +4,7 @@ #include "buffer.h" #include "base64.h" #include "istream-private.h" -#include "istream-base64-encoder.h" +#include "istream-base64.h" struct base64_encoder_istream { struct istream_private istream; diff --git a/src/lib/istream-base64-encoder.h b/src/lib/istream-base64.h similarity index 71% rename from src/lib/istream-base64-encoder.h rename to src/lib/istream-base64.h index 841ba86d2e..8639cf78bc 100644 --- a/src/lib/istream-base64-encoder.h +++ b/src/lib/istream-base64.h @@ -4,5 +4,7 @@ struct istream * i_stream_create_base64_encoder(struct istream *input, unsigned int chars_per_line, bool crlf); +struct istream * +i_stream_create_base64_decoder(struct istream *input); #endif diff --git a/src/lib/test-istream-base64-decoder.c b/src/lib/test-istream-base64-decoder.c new file mode 100644 index 0000000000..06e7a032a4 --- /dev/null +++ b/src/lib/test-istream-base64-decoder.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-private.h" +#include "istream-base64.h" + +struct { + const char *input; + const char *output; +} tests[] = { + { "aGVsbG8gd29ybGQ=", "hello world" }, + { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world" }, + { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n", "hello world" }, +}; + +static void +decode_test(const char *base64_input, const char *output, bool broken_input) +{ + unsigned int base64_input_len = strlen(base64_input); + struct istream *input_data, *input; + const unsigned char *data; + size_t i, size; + int ret = 0; + + input_data = test_istream_create_data(base64_input, base64_input_len); + test_istream_set_allow_eof(input_data, FALSE); + input = i_stream_create_base64_decoder(input_data); + + for (i = 1; i <= base64_input_len; i++) { + test_istream_set_size(input_data, i); + while ((ret = i_stream_read(input)) > 0) ; + if (ret == -1 && broken_input) + break; + test_assert(ret == 0); + } + if (ret == 0) { + test_istream_set_allow_eof(input_data, TRUE); + while ((ret = i_stream_read(input)) > 0) ; + } + test_assert(ret == -1); + test_assert((input->stream_errno == 0 && !broken_input) || + (input->stream_errno == EINVAL && broken_input)); + + data = i_stream_get_data(input, &size); + test_assert(size == strlen(output) && memcmp(data, output, size) == 0); + i_stream_unref(&input); + i_stream_unref(&input_data); +} + +void test_istream_base64_decoder(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(tests); i++) { + test_begin(t_strdup_printf("istream base64 decoder %u", i+1)); + decode_test(tests[i].input, tests[i].output, FALSE); + test_end(); + } + test_begin("istream base64 decoder error"); + decode_test("foo", "", TRUE); + decode_test("Zm9vC", "foo", TRUE); + decode_test("Zm9v!", "foo", TRUE); + decode_test("Zm9!v", "", TRUE); + decode_test("Zm9 v", "", TRUE); + decode_test("Zm 9v", "", TRUE); + decode_test("Z m9v", "", TRUE); + test_end(); +} diff --git a/src/lib/test-istream-base64-encoder.c b/src/lib/test-istream-base64-encoder.c index c0b8c99c62..fc40dcc0f6 100644 --- a/src/lib/test-istream-base64-encoder.c +++ b/src/lib/test-istream-base64-encoder.c @@ -3,7 +3,7 @@ #include "test-lib.h" #include "str.h" #include "istream-private.h" -#include "istream-base64-encoder.h" +#include "istream-base64.h" static const char *hello = "hello world"; diff --git a/src/lib/test-lib.c b/src/lib/test-lib.c index 984b1d704b..dc2795c2b3 100644 --- a/src/lib/test-lib.c +++ b/src/lib/test-lib.c @@ -14,6 +14,7 @@ int main(void) test_hash_format, test_hex_binary, test_iso8601_date, + test_istream_base64_decoder, test_istream_base64_encoder, test_istream_concat, test_istream_crlf, diff --git a/src/lib/test-lib.h b/src/lib/test-lib.h index 9b9bfe3d59..9519cced6f 100644 --- a/src/lib/test-lib.h +++ b/src/lib/test-lib.h @@ -13,6 +13,7 @@ void test_crc32(void); void test_hash_format(void); void test_hex_binary(void); void test_iso8601_date(void); +void test_istream_base64_decoder(void); void test_istream_base64_encoder(void); void test_istream_concat(void); void test_istream_crlf(void);