summaryrefslogtreecommitdiffstats
path: root/src/lib/istream-base64-encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/istream-base64-encoder.c')
-rw-r--r--src/lib/istream-base64-encoder.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/lib/istream-base64-encoder.c b/src/lib/istream-base64-encoder.c
new file mode 100644
index 0000000..22d2786
--- /dev/null
+++ b/src/lib/istream-base64-encoder.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2003-2018 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_encoder_istream {
+ struct istream_private istream;
+
+ struct base64_encoder encoder;
+};
+
+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 > 0)
+ return 1;
+
+ ret = i_stream_read_memarea(stream->parent);
+ if (ret <= 0) {
+ stream->istream.stream_errno = stream->parent->stream_errno;
+ return ret;
+ }
+ size = i_stream_get_data_size(stream->parent);
+ i_assert(size != 0);
+ return 1;
+}
+
+static int
+i_stream_base64_try_encode(struct base64_encoder_istream *bstream)
+{
+ struct istream_private *stream = &bstream->istream;
+ struct base64_encoder *b64enc = &bstream->encoder;
+ const unsigned char *data;
+ size_t size, pos, out_size, avail;
+ buffer_t buf;
+
+ data = i_stream_get_data(stream->parent, &size);
+ if (size == 0)
+ return 0;
+
+ out_size = base64_encode_get_size(b64enc, size);
+ if (!i_stream_try_alloc(stream, out_size, &avail))
+ return -2;
+
+ buffer_create_from_data(&buf, stream->w_buffer + stream->pos, avail);
+ base64_encode_more(b64enc, data, size, &pos, &buf);
+ i_assert(buf.used > 0);
+
+ stream->pos += buf.used;
+ i_stream_skip(stream->parent, pos);
+ return 1;
+}
+
+static int
+i_stream_base64_finish_encode(struct base64_encoder_istream *bstream)
+{
+ struct istream_private *stream = &bstream->istream;
+ struct base64_encoder *b64enc = &bstream->encoder;
+ size_t out_size, buffer_avail;
+ buffer_t buf;
+
+ out_size = base64_encode_get_size(b64enc, 0);
+ if (out_size == 0) {
+ if (base64_encode_finish(b64enc, NULL))
+ stream->istream.eof = TRUE;
+ return 1;
+ }
+
+ if (!i_stream_try_alloc(stream, out_size, &buffer_avail))
+ return -2;
+
+ buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
+ buffer_avail);
+ if (base64_encode_finish(b64enc, &buf))
+ stream->istream.eof = TRUE;
+ i_assert(buf.used > 0);
+
+ stream->pos += buf.used;
+ return 1;
+}
+
+static ssize_t i_stream_base64_encoder_read(struct istream_private *stream)
+{
+ struct base64_encoder_istream *bstream =
+ container_of(stream, struct base64_encoder_istream, istream);
+ size_t pre_count, post_count;
+ int ret;
+
+ if (base64_encode_is_finished(&bstream->encoder)) {
+ stream->istream.eof = TRUE;
+ return -1;
+ }
+
+ pre_count = post_count = 0;
+ do {
+ ret = i_stream_read_parent(stream);
+ if (ret == 0)
+ return 0;
+ if (ret < 0) {
+ if (stream->istream.stream_errno != 0)
+ return -1;
+ if (i_stream_get_data_size(stream->parent) == 0)
+ break;
+ /* add the final partial block */
+ }
+
+ /* encode as many lines as fits into destination buffer */
+ pre_count = stream->pos - stream->skip;
+ while ((ret = i_stream_base64_try_encode(bstream)) > 0) ;
+ post_count = stream->pos - stream->skip;
+ } while (ret == 0 && pre_count == post_count);
+
+ if (ret == -2) {
+ if (pre_count == post_count)
+ return -2;
+ } else if (ret < 0) {
+ if (i_stream_get_data_size(stream->parent) == 0) {
+ i_assert(post_count == pre_count);
+ pre_count = stream->pos - stream->skip;
+ ret = i_stream_base64_finish_encode(bstream);
+ post_count = stream->pos - stream->skip;
+ if (ret <= 0)
+ return ret;
+ }
+ if (pre_count == post_count) {
+ stream->istream.eof = TRUE;
+ return -1;
+ }
+ }
+
+ i_assert(post_count > pre_count);
+ return post_count - pre_count;
+}
+
+static void
+i_stream_base64_encoder_seek(struct istream_private *stream,
+ uoff_t v_offset, bool mark)
+{
+ struct base64_encoder_istream *bstream =
+ container_of(stream, struct base64_encoder_istream, istream);
+
+ 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);
+
+ base64_encode_reset(&bstream->encoder);
+ }
+ i_stream_default_seek_nonseekable(stream, v_offset, mark);
+}
+
+static int
+i_stream_base64_encoder_stat(struct istream_private *stream,
+ bool exact ATTR_UNUSED)
+{
+ struct base64_encoder_istream *bstream =
+ container_of(stream, struct base64_encoder_istream, istream);
+ const struct stat *st;
+
+ if (i_stream_stat(stream->parent, exact, &st) < 0) {
+ stream->istream.stream_errno = stream->parent->stream_errno;
+ return -1;
+ }
+
+ stream->statbuf = *st;
+ if (st->st_size == 0)
+ return 0;
+
+ stream->statbuf.st_size =
+ base64_get_full_encoded_size(&bstream->encoder, st->st_size);
+ return 0;
+}
+
+static struct istream *
+i_stream_create_base64_encoder_common(const struct base64_scheme *b64,
+ struct istream *input,
+ unsigned int chars_per_line, bool crlf)
+{
+ struct base64_encoder_istream *bstream;
+ enum base64_encode_flags b64_flags = 0;
+
+ i_assert(chars_per_line % 4 == 0);
+
+ bstream = i_new(struct base64_encoder_istream, 1);
+ bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+
+ bstream->istream.read = i_stream_base64_encoder_read;
+ bstream->istream.seek = i_stream_base64_encoder_seek;
+ bstream->istream.stat = i_stream_base64_encoder_stat;
+
+ bstream->istream.istream.readable_fd = FALSE;
+ bstream->istream.istream.blocking = input->blocking;
+ bstream->istream.istream.seekable = input->seekable;
+
+ if (crlf)
+ b64_flags |= BASE64_ENCODE_FLAG_CRLF;
+ base64_encode_init(&bstream->encoder, b64, b64_flags, chars_per_line);
+
+ return i_stream_create(&bstream->istream, input,
+ i_stream_get_fd(input), 0);
+}
+
+struct istream *
+i_stream_create_base64_encoder(struct istream *input,
+ unsigned int chars_per_line, bool crlf)
+{
+ return i_stream_create_base64_encoder_common(&base64_scheme, input,
+ chars_per_line, crlf);
+}
+
+struct istream *
+i_stream_create_base64url_encoder(struct istream *input,
+ unsigned int chars_per_line, bool crlf)
+{
+ return i_stream_create_base64_encoder_common(&base64url_scheme, input,
+ chars_per_line, crlf);
+}