struct base64_decoder_istream {
struct istream_private istream;
+
+ const struct base64_scheme *b64;
};
static int i_stream_read_parent(struct istream_private *stream)
buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
buffer_avail);
- if (base64_decode(data, size, &pos, &buf) < 0) {
+ if (base64_scheme_decode(bstream->b64, 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)));
return pos > 0 ? 1 : 0;
}
-static void i_stream_base64_last_partial_block(struct istream_private *stream)
+static void
+i_stream_base64_last_partial_block(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_is_valid_char(data[i]))
+ if (!base64_scheme_is_valid_char(bstream->b64, data[i]))
break;
}
if (i == size) {
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(stream);
+ i_stream_base64_last_partial_block(bstream);
return ret;
}
i_stream_default_seek_nonseekable(stream, v_offset, mark);
}
-struct istream *
-i_stream_create_base64_decoder(struct istream *input)
+static struct istream *
+i_stream_create_base64_decoder_common(const struct base64_scheme *b64,
+ struct istream *input)
{
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;
return i_stream_create(&bstream->istream, input,
i_stream_get_fd(input), 0);
}
+
+struct istream *
+i_stream_create_base64_decoder(struct istream *input)
+{
+ return i_stream_create_base64_decoder_common(&base64_scheme, input);
+}
+
+struct istream *
+i_stream_create_base64url_decoder(struct istream *input)
+{
+ return i_stream_create_base64_decoder_common(&base64url_scheme, input);
+}
struct base64_encoder_istream {
struct istream_private istream;
+ const struct base64_scheme *b64;
+
/* current encoded line length. */
size_t cur_line_len;
buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
buffer_avail);
- base64_encode(data, size, &buf);
+ base64_scheme_encode(bstream->b64, data, size, &buf);
i_assert(buf.used > 0);
bstream->cur_line_len += buf.used;
return 0;
}
-struct istream *
-i_stream_create_base64_encoder(struct istream *input,
- unsigned int chars_per_line, bool crlf)
+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;
i_assert(chars_per_line % 4 == 0);
bstream = i_new(struct base64_encoder_istream, 1);
+ bstream->b64 = b64;
bstream->chars_per_line = chars_per_line;
bstream->crlf = crlf;
bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
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);
+}
i_stream_create_base64_encoder(struct istream *input,
unsigned int chars_per_line, bool crlf);
struct istream *
+i_stream_create_base64url_encoder(struct istream *input,
+ unsigned int chars_per_line, bool crlf);
+
+struct istream *
i_stream_create_base64_decoder(struct istream *input);
+struct istream *
+i_stream_create_base64url_decoder(struct istream *input);
#endif
"\x81\xd1\x82\x2e", 0, UINT_MAX },
};
string_t *str;
+ buffer_t buf;
unsigned int i;
size_t src_pos;
int ret;
test_begin("base64url_decode()");
- str = t_str_new(256);
for (i = 0; i < N_ELEMENTS(tests); i++) {
- str_truncate(str, 0);
+ /* Some of the base64_decode() callers use fixed size buffers.
+ Use a fixed size buffer here as well to test that
+ base64_decode() can't allocate any extra space even
+ temporarily. */
+ size_t max_decoded_size =
+ MAX_BASE64_DECODED_SIZE(strlen(tests[i].input));
+ buffer_create_from_data(&buf, t_malloc0(max_decoded_size),
+ max_decoded_size);
+ str = &buf;
src_pos = 0;
ret = base64url_decode(tests[i].input, strlen(tests[i].input),
&src_pos, str);
test_assert_idx(tests[i].ret == ret, i);
- test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i);
+ test_assert_idx(strlen(tests[i].output) == str_len(str) &&
+ memcmp(tests[i].output, str_data(str),
+ str_len(str)) == 0, i);
test_assert_idx(src_pos == tests[i].src_pos ||
(tests[i].src_pos == UINT_MAX &&
src_pos == strlen(tests[i].input)), i);
#include "istream-private.h"
#include "istream-base64.h"
-static const struct {
+struct base64_istream_test {
const char *input;
const char *output;
int stream_errno;
-} tests[] = {
+};
+
+static const struct base64_istream_test base64_tests[] = {
{ "aGVsbG8gd29ybGQ=", "hello world", 0 },
{ "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world", 0 },
- { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n", "hello world", 0 },
+ { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n",
+ "hello world", 0 },
+ { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC+INC60YPRgCDQtNC+0Y/MgdGCLg==",
+ "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
+ "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2e", 0 },
+ { "\r", "", 0 },
+ { "\n", "", 0 },
+ { "\r\n", "", 0 },
+ { " ", "", 0 },
+ { "foo", "", EPIPE },
+ { "foo ", "", EINVAL },
+ { "Zm9vC", "foo", EPIPE },
+ { "Zm9v!", "foo", EINVAL },
+ { "Zm9!v", "", EINVAL },
+ { "Zm9 v", "", EINVAL },
+ { "Zm 9v", "", EINVAL },
+ { "Z m9v", "", EINVAL },
+};
+
+static const struct base64_istream_test base64url_tests[] = {
+ { "aGVsbG8gd29ybGQ=", "hello world", 0 },
+ { "\naGVs\nbG8g\nd29y\nbGQ=\n", "hello world", 0 },
+ { " aGVs \r\n bG8g \r\n d29y \t \r\n bGQ= \r\n\r\n",
+ "hello world", 0 },
+ { "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC-INC60YPRgCDQtNC-0Y_MgdGCLg==",
+ "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
+ "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2e", 0 },
{ "\r", "", 0 },
{ "\n", "", 0 },
{ "\r\n", "", 0 },
};
static void
-decode_test(const char *base64_input, const char *output, int stream_errno)
+decode_test(unsigned int base64_input_len,
+ struct istream *input_data, struct istream *input,
+ const char *output, int stream_errno)
{
- 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) ;
test_assert(size == strlen(output));
if (size > 0)
test_assert(memcmp(data, output, size) == 0);
+}
+
+static void
+decode_base64_test(const char *base64_input, const char *output,
+ int stream_errno)
+{
+ unsigned int base64_input_len = strlen(base64_input);
+ struct istream *input_data, *input;
+
+ 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);
+
+ decode_test(base64_input_len, input_data, input, output, stream_errno);
+
+ i_stream_unref(&input);
+ i_stream_unref(&input_data);
+}
+
+static void
+decode_base64url_test(const char *base64_input, const char *output,
+ int stream_errno)
+{
+ unsigned int base64_input_len = strlen(base64_input);
+ struct istream *input_data, *input;
+
+ input_data = test_istream_create_data(base64_input, base64_input_len);
+ test_istream_set_allow_eof(input_data, FALSE);
+ input = i_stream_create_base64url_decoder(input_data);
+
+ decode_test(base64_input_len, input_data, input, output, stream_errno);
+
i_stream_unref(&input);
i_stream_unref(&input_data);
}
{
unsigned int i;
- for (i = 0; i < N_ELEMENTS(tests); i++) {
+ for (i = 0; i < N_ELEMENTS(base64_tests); i++) {
+ const struct base64_istream_test *test = &base64_tests[i];
+
test_begin(t_strdup_printf("istream base64 decoder %u", i+1));
- decode_test(tests[i].input, tests[i].output, tests[i].stream_errno);
+ decode_base64_test(test->input, test->output,
+ test->stream_errno);
+ test_end();
+ }
+
+ for (i = 0; i < N_ELEMENTS(base64url_tests); i++) {
+ const struct base64_istream_test *test = &base64url_tests[i];
+
+ test_begin(t_strdup_printf("istream base64url decoder %u",
+ i+1));
+ decode_base64url_test(test->input, test->output,
+ test->stream_errno);
test_end();
}
}
#include "istream-private.h"
#include "istream-base64.h"
-static const struct test {
+struct base64_istream_test {
const char *input;
unsigned int chars_per_line;
bool crlf;
const char *output;
-} tests[] = {
+};
+
+static const struct base64_istream_test base64_tests[] = {
{ "", 80, FALSE, "" },
{ "1", 80, FALSE, "MQ==" },
{ "12", 80, FALSE, "MTI=" },
"aGVsbG8g\ndG8gdGhl\nIHdvcmxk\nISE=" },
{ "hello to the world!!", 8, TRUE,
"aGVsbG8g\r\ndG8gdGhl\r\nIHdvcmxk\r\nISE=" },
+ { "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
+ "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2e", 80, FALSE,
+ "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC+INC60YPRgCDQtNC+0Y/MgdGCLg==" },
};
+static const struct base64_istream_test base64url_tests[] = {
+ { "", 80, FALSE, "" },
+ { "1", 80, FALSE, "MQ==" },
+ { "12", 80, FALSE, "MTI=" },
+ { "123", 80, FALSE, "MTIz" },
+ { "1234", 80, FALSE, "MTIzNA==" },
+ { "12345", 80, FALSE, "MTIzNDU=" },
+ { "hello world", 80, FALSE, "aGVsbG8gd29ybGQ=" },
+ { "hello world", 4, FALSE, "aGVs\nbG8g\nd29y\nbGQ=" },
+ { "hello world", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=" },
+ { "hello worlds", 80, FALSE, "aGVsbG8gd29ybGRz" },
+ { "hello worlds", 4, FALSE, "aGVs\nbG8g\nd29y\nbGRz" },
+ { "hello worlds", 4, TRUE, "aGVs\r\nbG8g\r\nd29y\r\nbGRz" },
+ { "hell world", 80, FALSE, "aGVsbCB3b3JsZA==" },
+ { "hell world", 4, FALSE, "aGVs\nbCB3\nb3Js\nZA==" },
+ { "hell world", 4, TRUE, "aGVs\r\nbCB3\r\nb3Js\r\nZA==" },
+ { "hello to the world!!", 80, FALSE,
+ "aGVsbG8gdG8gdGhlIHdvcmxkISE=" },
+ { "hello to the world!!", 8, FALSE,
+ "aGVsbG8g\ndG8gdGhl\nIHdvcmxk\nISE=" },
+ { "hello to the world!!", 8, TRUE,
+ "aGVsbG8g\r\ndG8gdGhl\r\nIHdvcmxk\r\nISE=" },
+ { "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
+ "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
+ "\x81\xd1\x82\x2e", 80, FALSE,
+ "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgtC-INC60YPRgCDQtNC-0Y_MgdGCLg==" },
+};
static const char *hello = "hello world";
-static void encode_test(const char *text, unsigned int chars_per_line,
- bool crlf, const char *output)
+static void encode_test(unsigned int text_len,
+ struct istream *input, struct istream *input_data,
+ const char *output)
{
- unsigned int i, text_len = strlen(text);
- struct istream *input, *input_data;
+ unsigned int i;
const unsigned char *data;
uoff_t stream_size;
size_t size;
ssize_t ret;
- input_data = test_istream_create_data(text, text_len);
- test_istream_set_allow_eof(input_data, FALSE);
- input = i_stream_create_base64_encoder(input_data, chars_per_line, crlf);
-
for (i = 1; i <= text_len; i++) {
test_istream_set_size(input_data, i);
while ((ret = i_stream_read(input)) > 0) ;
ret = i_stream_get_size(input, TRUE, &stream_size);
test_assert(ret > 0);
test_assert(size == stream_size);
+}
+
+static void
+encode_base64_test(const char *text, unsigned int chars_per_line,
+ bool crlf, const char *output)
+{
+ unsigned int text_len = strlen(text);
+ struct istream *input, *input_data;
+
+ input_data = test_istream_create_data(text, text_len);
+ test_istream_set_allow_eof(input_data, FALSE);
+ input = i_stream_create_base64_encoder(input_data, chars_per_line,
+ crlf);
+
+ encode_test(text_len, input, input_data, output);
i_stream_unref(&input);
i_stream_unref(&input_data);
}
static void
-test_istream_base64_encoder_seek(const char *textin, const char *textout)
+encode_base64url_test(const char *text, unsigned int chars_per_line,
+ bool crlf, const char *output)
{
- unsigned int offset, len = strlen(textout);
+ unsigned int text_len = strlen(text);
struct istream *input, *input_data;
+
+ input_data = test_istream_create_data(text, text_len);
+ test_istream_set_allow_eof(input_data, FALSE);
+ input = i_stream_create_base64url_encoder(input_data, chars_per_line,
+ crlf);
+
+ encode_test(text_len, input, input_data, output);
+
+ i_stream_unref(&input);
+ i_stream_unref(&input_data);
+}
+
+static void
+test_encoder_seek(struct istream *input, const char *textout)
+{
+ unsigned int offset, len = strlen(textout);
const unsigned char *data;
size_t size;
ssize_t ret;
- input_data = i_stream_create_from_data(textin, strlen(textin));
- input = i_stream_create_base64_encoder(input_data, 4, TRUE);
-
while (i_stream_read(input) > 0) ;
i_stream_skip(input, i_stream_get_data_size(input));
test_assert(memcmp(data, textout+offset, size) == 0);
i_stream_skip(input, size);
}
+}
+
+static void
+test_istream_base64_encoder_seek(const char *textin, const char *textout)
+{
+ struct istream *input, *input_data;
+
+ input_data = i_stream_create_from_data(textin, strlen(textin));
+ input = i_stream_create_base64_encoder(input_data, 4, TRUE);
+
+ test_encoder_seek(input, textout);
+
+ i_stream_unref(&input);
+ i_stream_unref(&input_data);
+}
+
+static void
+test_istream_base64url_encoder_seek(const char *textin, const char *textout)
+{
+ struct istream *input, *input_data;
+
+ input_data = i_stream_create_from_data(textin, strlen(textin));
+ input = i_stream_create_base64url_encoder(input_data, 4, TRUE);
+
+ test_encoder_seek(input, textout);
i_stream_unref(&input);
i_stream_unref(&input_data);
{
unsigned int i;
- for (i = 0; i < N_ELEMENTS(tests); i++) {
- test_begin(t_strdup_printf("istream base64 decoder %u", i+1));
- encode_test(tests[i].input, tests[i].chars_per_line,
- tests[i].crlf, tests[i].output);
+ for (i = 0; i < N_ELEMENTS(base64_tests); i++) {
+ const struct base64_istream_test *test = &base64_tests[i];
+
+ test_begin(t_strdup_printf(
+ "istream base64 decoder %u", i+1));
+ encode_base64_test(test->input, test->chars_per_line,
+ test->crlf, test->output);
+ test_end();
+ }
+
+ for (i = 0; i < N_ELEMENTS(base64url_tests); i++) {
+ const struct base64_istream_test *test = &base64url_tests[i];
+
+ test_begin(t_strdup_printf(
+ "istream base64url decoder %u", i+1));
+ encode_base64url_test(test->input, test->chars_per_line,
+ test->crlf, test->output);
test_end();
}
+
test_begin("istream base64 encoder seek");
- test_istream_base64_encoder_seek(hello, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=");
+ test_istream_base64_encoder_seek(
+ hello, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=");
+ test_end();
+
+ test_begin("istream base64url encoder seek");
+ test_istream_base64url_encoder_seek(
+ hello, "aGVs\r\nbG8g\r\nd29y\r\nbGQ=");
test_end();
}