struct base64_decoder_istream {
struct istream_private istream;
- const struct base64_scheme *b64;
+ struct base64_decoder decoder;
};
static int i_stream_read_parent(struct istream_private *stream)
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;
- }
+ if (buffer_avail == 0)
+ return -2;
buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
buffer_avail);
- if (base64_scheme_decode(bstream->b64, data, size, &pos, &buf) < 0) {
+ if (base64_decode_more(&bstream->decoder, data, size, &pos, &buf) < 0) {
io_stream_set_error(&stream->iostream,
"Invalid base64 data: 0x%s",
binary_to_hex(data+pos, I_MIN(size-pos, 8)));
}
static void
-i_stream_base64_last_partial_block(struct base64_decoder_istream *bstream)
+i_stream_base64_finish_decode(struct base64_decoder_istream *bstream)
{
struct istream_private *stream = &bstream->istream;
- const unsigned char *data;
- size_t i, size;
- /* base64 input with a partial block */
- data = i_stream_get_data(stream->parent, &size);
- for (i = 0; i < size; i++) {
- if (!base64_scheme_is_valid_char(bstream->b64, data[i]))
- break;
- }
- if (i == size) {
+ i_assert(i_stream_get_data_size(stream->parent) == 0);
+
+ if (base64_decode_finish(&bstream->decoder) < 0) {
io_stream_set_error(&stream->iostream,
- "base64 input ends with a partial block: 0x%s",
- binary_to_hex(data, size));
+ "Base64 data ends prematurely");
stream->istream.stream_errno = EPIPE;
- } else {
- io_stream_set_error(&stream->iostream,
- "Invalid base64 data: 0x%s",
- binary_to_hex(data, size));
- stream->istream.stream_errno = EINVAL;
}
}
size_t pre_count, post_count;
int ret;
+ if (base64_decode_is_finished(&bstream->decoder)) {
+ stream->istream.eof = TRUE;
+ return -1;
+ }
+
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)
- i_stream_base64_last_partial_block(bstream);
+ if (ret < 0 && stream->istream.stream_errno == 0)
+ i_stream_base64_finish_decode(bstream);
return ret;
}
i_stream_base64_decoder_seek(struct istream_private *stream,
uoff_t v_offset, bool mark)
{
+ struct base64_decoder_istream *bstream =
+ (struct base64_decoder_istream *)stream;
+
if (v_offset < stream->istream.v_offset) {
/* seeking backwards - go back to beginning and seek
forward from there. */
stream->skip = stream->pos = 0;
stream->istream.v_offset = 0;
i_stream_seek(stream->parent, 0);
+
+ base64_decode_reset(&bstream->decoder);
}
i_stream_default_seek_nonseekable(stream, v_offset, mark);
}
struct base64_decoder_istream *bstream;
bstream = i_new(struct base64_decoder_istream, 1);
- bstream->b64 = b64;
bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
bstream->istream.read = i_stream_base64_decoder_read;
bstream->istream.istream.readable_fd = FALSE;
bstream->istream.istream.blocking = input->blocking;
bstream->istream.istream.seekable = input->seekable;
+
+ base64_decode_init(&bstream->decoder, b64, 0);
+
return i_stream_create(&bstream->istream, input,
i_stream_get_fd(input), 0);
}
{ "\n", "", 0 },
{ "\r\n", "", 0 },
{ " ", "", 0 },
- { "foo", "", EPIPE },
- { "foo ", "", EINVAL },
+ { "foo", "\x7e\x8a", EPIPE },
+ { "foo ","\x7e\x8a", EPIPE },
{ "Zm9vC", "foo", EPIPE },
{ "Zm9v!", "foo", EINVAL },
- { "Zm9!v", "", EINVAL },
- { "Zm9 v", "", EINVAL },
- { "Zm 9v", "", EINVAL },
- { "Z m9v", "", EINVAL },
+ { "Zm9!v", "fo", EINVAL },
+ { "Zm9 v", "foo", 0 },
+ { "Zm 9v", "foo", 0 },
+ { "Z m9v", "foo", 0 },
};
static const struct base64_istream_test base64url_tests[] = {
{ "\n", "", 0 },
{ "\r\n", "", 0 },
{ " ", "", 0 },
- { "foo", "", EPIPE },
- { "foo ", "", EINVAL },
+ { "foo", "\x7e\x8a", EPIPE },
+ { "foo ","\x7e\x8a", EPIPE },
{ "Zm9vC", "foo", EPIPE },
{ "Zm9v!", "foo", EINVAL },
- { "Zm9!v", "", EINVAL },
- { "Zm9 v", "", EINVAL },
- { "Zm 9v", "", EINVAL },
- { "Z m9v", "", EINVAL },
+ { "Zm9!v", "fo", EINVAL },
+ { "Zm9 v", "foo", 0 },
+ { "Zm 9v", "foo", 0 },
+ { "Z m9v", "foo", 0 },
};
static void