istream-base64-decoder.c revision 36a50bc13192383ee374be3c68c18842aa597f62
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen#include "buffer.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "base64.h"
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include "istream-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream-base64.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct base64_decoder_istream {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct istream_private istream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int i_stream_read_parent(struct istream_private *stream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ssize_t ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size = i_stream_get_data_size(stream->parent);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (size >= 4)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we have less than one base64 block.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen see if there is more data available. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = i_stream_read(stream->parent);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret <= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.eof = stream->parent->eof;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen size = i_stream_get_data_size(stream->parent);
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen i_assert(size != 0);
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen return 1;
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen}
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainenstatic int
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Siraineni_stream_base64_try_decode_block(struct base64_decoder_istream *bstream)
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen{
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen struct istream_private *stream = &bstream->istream;
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen const unsigned char *data;
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen size_t size, avail, buffer_avail, pos;
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen buffer_t buf;
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen data = i_stream_get_data(stream->parent, &size);
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen if (size == 0)
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen i_stream_try_alloc(stream, (size+3)/4*3, &avail);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen buffer_avail = stream->buffer_size - stream->pos;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if ((size + 3) / 4 * 3 > buffer_avail) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* can't fit everything to destination buffer.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen write as much as we can. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size = (buffer_avail / 3) * 4;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (size == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -2;
3b32bc12710240f86465a00fbb2bd1ef030e6c40Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen buffer_create_data(&buf, stream->w_buffer + stream->pos, buffer_avail);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (base64_decode(data, size, &pos, &buf) < 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen stream->istream.stream_errno = EINVAL;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return -1;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen stream->pos += buf.used;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen i_stream_skip(stream->parent, pos);
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen return pos > 0 ? 1 : 0;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen}
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic ssize_t i_stream_base64_decoder_read(struct istream_private *stream)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct base64_decoder_istream *bstream =
d22301419109ed4a38351715e6760011421dadecTimo Sirainen (struct base64_decoder_istream *)stream;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen size_t pre_count, post_count;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen do {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = i_stream_read_parent(stream);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret <= 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (ret < 0 && stream->istream.stream_errno == 0 &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_get_data_size(stream->parent) > 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen /* base64 input with a partial block */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.stream_errno = EINVAL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen /* encode as many blocks as fits into destination buffer */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pre_count = stream->pos - stream->skip;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen while ((ret = i_stream_base64_try_decode_block(bstream)) > 0) ;
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen post_count = stream->pos - stream->skip;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } while (ret == 0 && pre_count == post_count);
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen if (ret < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(post_count > pre_count);
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen return post_count - pre_count;
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen}
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainenstatic void
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Siraineni_stream_base64_decoder_seek(struct istream_private *stream,
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen uoff_t v_offset, bool mark)
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen{
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen if (v_offset < stream->istream.v_offset) {
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen /* seeking backwards - go back to beginning and seek
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen forward from there. */
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen stream->parent_expected_offset = stream->parent_start_offset;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->skip = stream->pos = 0;
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen stream->istream.v_offset = 0;
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen i_stream_seek(stream->parent, 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_default_seek_nonseekable(stream, v_offset, mark);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct istream *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Siraineni_stream_create_base64_decoder(struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct base64_decoder_istream *bstream;
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream = i_new(struct base64_decoder_istream, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream->istream.read = i_stream_base64_decoder_read;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream->istream.seek = i_stream_base64_decoder_seek;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream->istream.istream.readable_fd = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream->istream.istream.blocking = input->blocking;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bstream->istream.istream.seekable = input->seekable;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return i_stream_create(&bstream->istream, input,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_get_fd(input));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen