]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added istream-base64-decoder.
authorTimo Sirainen <tss@iki.fi>
Fri, 10 Aug 2012 03:45:25 +0000 (06:45 +0300)
committerTimo Sirainen <tss@iki.fi>
Fri, 10 Aug 2012 03:45:25 +0000 (06:45 +0300)
src/lib/Makefile.am
src/lib/istream-base64-decoder.c [new file with mode: 0644]
src/lib/istream-base64-encoder.c
src/lib/istream-base64.h [moved from src/lib/istream-base64-encoder.h with 71% similarity]
src/lib/test-istream-base64-decoder.c [new file with mode: 0644]
src/lib/test-istream-base64-encoder.c
src/lib/test-lib.c
src/lib/test-lib.h

index 36fbfbb195403630f80bfab50762f0bf7e681dd1..f4348592e19ff3c327d90f4899687cdb8b0f7052 100644 (file)
@@ -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 (file)
index 0000000..1138490
--- /dev/null
@@ -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));
+}
index d4cf26cd8f9402c1a08848170717df07a688e91d..5bcb804a62422f20337029df8de9cf63739c8bd1 100644 (file)
@@ -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;
similarity index 71%
rename from src/lib/istream-base64-encoder.h
rename to src/lib/istream-base64.h
index 841ba86d2ecb33cdbbbd180d0b7cf1cbfb7ab549..8639cf78bc98df5473b0d834986d6c057e329c4f 100644 (file)
@@ -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 (file)
index 0000000..06e7a03
--- /dev/null
@@ -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();
+}
index c0b8c99c6212de004c91c91fed4703c5cc6c91ff..fc40dcc0f614603256ddfc72730cb1b2a1bbc3b7 100644 (file)
@@ -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";
 
index 984b1d704bb49c0223452003decfd3b89923f9b6..dc2795c2b323e0f9a0eefe2a3f99a8c155dc90d3 100644 (file)
@@ -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,
index 9b9bfe3d59d939c26c1693a2829e582b3b3bb88c..9519cced6fe271cf1eb5133b3edc408c48618648 100644 (file)
@@ -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);