summaryrefslogtreecommitdiffstats
path: root/src/lib/base64.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/base64.c')
-rw-r--r--src/lib/base64.c968
1 files changed, 968 insertions, 0 deletions
diff --git a/src/lib/base64.c b/src/lib/base64.c
new file mode 100644
index 0000000..5e4f96a
--- /dev/null
+++ b/src/lib/base64.c
@@ -0,0 +1,968 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "base64.h"
+#include "buffer.h"
+
+/*
+ * Low-level Base64 encoder
+ */
+
+uoff_t base64_get_full_encoded_size(struct base64_encoder *enc, uoff_t src_size)
+{
+ bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
+ bool no_padding = HAS_ALL_BITS(enc->flags,
+ BASE64_ENCODE_FLAG_NO_PADDING);
+ uoff_t out_size;
+ uoff_t newlines;
+
+ if (src_size == 0)
+ return 0;
+
+ /* calculate size of encoded data */
+ out_size = MAX_BASE64_ENCODED_SIZE(src_size);
+ if (no_padding) {
+ switch (src_size % 3) {
+ case 0:
+ break;
+ case 1:
+ i_assert(out_size > 2);
+ out_size -= 2;
+ break;
+ case 2:
+ i_assert(out_size > 1);
+ out_size -= 1;
+ break;
+ }
+ }
+
+ if (out_size > enc->max_line_len) {
+ /* newline between each full line */
+ i_assert(enc->max_line_len > 0);
+ newlines = (out_size / enc->max_line_len) - 1;
+ /* an extra newline to separate the partial last line from the
+ previous full line */
+ if ((out_size % enc->max_line_len) != 0)
+ newlines++;
+
+ /* update size with added newlines */
+ out_size += newlines * (crlf ? 2 : 1);
+ }
+
+ return out_size;
+}
+
+static size_t
+base64_encode_get_out_size(struct base64_encoder *enc, size_t src_size)
+{
+ size_t res_size = enc->w_buf_len;
+
+ i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
+
+ if (src_size == 0)
+ return res_size;
+
+ /* Handle sub-position */
+ switch (enc->sub_pos) {
+ case 0:
+ break;
+ case 1:
+ res_size++;
+ src_size--;
+ if (src_size == 0)
+ return res_size;
+ /* fall through */
+ case 2:
+ res_size += 2;
+ src_size--;
+ break;
+ default:
+ i_unreached();
+ }
+
+ /* We're now at a 3-byte boundary */
+ if (src_size == 0)
+ return res_size;
+
+ /* Calculate size we can append to the output from remaining input */
+ res_size += ((src_size) / 3) * 4;
+ switch (src_size % 3) {
+ case 0:
+ break;
+ case 1:
+ res_size += 1;
+ break;
+ case 2:
+ res_size += 2;
+ break;
+ }
+ return res_size;
+}
+
+size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size)
+{
+ bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
+ size_t out_size = base64_encode_get_out_size(enc, src_size);
+
+ if (src_size == 0) {
+ /* last block */
+ switch (enc->sub_pos) {
+ case 0:
+ break;
+ case 1:
+ out_size += 3;
+ break;
+ case 2:
+ out_size += 2;
+ break;
+ default:
+ i_unreached();
+ }
+ }
+
+ if (enc->max_line_len < SIZE_MAX) {
+ size_t line_part, lines;
+
+ /* Calculate how many line endings must be added */
+ i_assert(enc->max_line_len > 0);
+ lines = out_size / enc->max_line_len;
+ line_part = out_size % enc->max_line_len;
+ if (enc->cur_line_len > (enc->max_line_len - line_part))
+ lines++;
+
+ out_size += lines * (crlf ? 2 : 1);
+ }
+
+ if (enc->pending_lf)
+ out_size++;
+
+ return out_size;
+}
+
+size_t base64_encode_get_full_space(struct base64_encoder *enc,
+ size_t dst_space)
+{
+ bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
+ bool no_padding = HAS_ALL_BITS(enc->flags,
+ BASE64_ENCODE_FLAG_NO_PADDING);
+ size_t src_space = 0;
+
+ i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
+
+ if (enc->max_line_len < SIZE_MAX) {
+ size_t max_line_space, lines, nl_space;
+
+ /* Calculate how many line endings must be added if all space
+ were used. */
+ i_assert(enc->max_line_len < SIZE_MAX-2);
+ max_line_space = enc->max_line_len + (crlf ? 2 : 1);
+ lines = dst_space / max_line_space;
+
+ /* Calculate how much space is used by newline characters and
+ subtract this from the available space. */
+ nl_space = lines * (crlf ? 2 : 1);
+ if (dst_space <= nl_space)
+ return 0;
+ dst_space -= nl_space;
+ }
+
+ if (dst_space <= enc->w_buf_len)
+ return 0;
+ dst_space -= enc->w_buf_len;
+
+ if (enc->pending_lf)
+ dst_space--;
+ if (dst_space == 0)
+ return 0;
+
+ /* Handle sub-position */
+ switch (enc->sub_pos) {
+ case 0:
+ break;
+ case 1:
+ dst_space--;
+ src_space++;
+ /* fall through */
+ case 2:
+ if (dst_space < 2)
+ return src_space;
+ dst_space -= 2;
+ src_space++;
+ break;
+ default:
+ i_unreached();
+ }
+
+ if (dst_space == 0)
+ return src_space;
+
+ src_space += dst_space / 4 * 3;
+ if (no_padding) {
+ switch (dst_space % 4) {
+ case 0:
+ case 1:
+ break;
+ case 2:
+ src_space += 1;
+ break;
+ case 3:
+ src_space += 2;
+ break;
+ }
+ }
+
+ return src_space;
+}
+
+static void
+base64_encode_more_data(struct base64_encoder *enc,
+ const unsigned char *src_c, size_t src_size,
+ size_t *src_pos_r, size_t dst_avail, buffer_t *dest)
+{
+ const struct base64_scheme *b64 = enc->b64;
+ const char *b64enc = b64->encmap;
+ size_t res_size;
+ unsigned char *start, *ptr, *end;
+ size_t src_pos;
+
+ i_assert(!enc->pending_lf);
+
+ /* determine how much we can write in destination buffer */
+ if (dst_avail == 0) {
+ *src_pos_r = 0;
+ return;
+ }
+
+ /* pre-allocate space in the destination buffer */
+ res_size = base64_encode_get_out_size(enc, src_size);
+ if (res_size > dst_avail)
+ res_size = dst_avail;
+
+ start = buffer_append_space_unsafe(dest, res_size);
+ end = start + res_size;
+ ptr = start;
+
+ /* write bytes not written in previous call */
+ i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
+ if (enc->w_buf_len > res_size) {
+ memcpy(ptr, enc->w_buf, res_size);
+ ptr += res_size;
+ enc->w_buf_len -= res_size;
+ memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len);
+ } else if (enc->w_buf_len > 0) {
+ memcpy(ptr, enc->w_buf, enc->w_buf_len);
+ ptr += enc->w_buf_len;
+ enc->w_buf_len = 0;
+ }
+ if (ptr == end) {
+ *src_pos_r = 0;
+ return;
+ }
+ i_assert(enc->w_buf_len == 0);
+ i_assert(src_size != 0);
+
+ /* Handle sub-position */
+ src_pos = 0;
+ switch (enc->sub_pos) {
+ case 0:
+ break;
+ case 1:
+ i_assert(ptr < end);
+ ptr[0] = b64enc[enc->buf | (src_c[src_pos] >> 4)];
+ ptr++;
+ enc->buf = (src_c[src_pos] & 0x0f) << 2;
+ src_pos++;
+ if (src_pos == src_size || ptr == end) {
+ enc->sub_pos = 2;
+ *src_pos_r = src_pos;
+ return;
+ }
+ /* fall through */
+ case 2:
+ ptr[0] = b64enc[enc->buf | ((src_c[src_pos] & 0xc0) >> 6)];
+ enc->w_buf[0] = b64enc[src_c[src_pos] & 0x3f];
+ ptr++;
+ src_pos++;
+ if (ptr < end) {
+ ptr[0] = enc->w_buf[0];
+ ptr++;
+ enc->w_buf_len = 0;
+ } else {
+ enc->sub_pos = 0;
+ enc->w_buf_len = 1;
+ *src_pos_r = src_pos;
+ return;
+ }
+ break;
+ default:
+ i_unreached();
+ }
+ enc->sub_pos = 0;
+
+ /* We're now at a 3-byte boundary */
+ if (src_pos == src_size) {
+ i_assert(ptr == end);
+ *src_pos_r = src_pos;
+ return;
+ }
+
+ /* Convert the bulk */
+ for (; src_size - src_pos > 2 && &ptr[3] < end;
+ src_pos += 3, ptr += 4) {
+ ptr[0] = b64enc[src_c[src_pos] >> 2];
+ ptr[1] = b64enc[((src_c[src_pos] & 0x03) << 4) |
+ (src_c[src_pos+1] >> 4)];
+ ptr[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) |
+ ((src_c[src_pos+2] & 0xc0) >> 6)];
+ ptr[3] = b64enc[src_c[src_pos+2] & 0x3f];
+ }
+
+ /* Convert the bytes beyond the last 3-byte boundary and update state
+ for next call */
+ switch (src_size - src_pos) {
+ case 0:
+ enc->sub_pos = 0;
+ enc->buf = 0;
+ break;
+ case 1:
+ enc->sub_pos = 1;
+ enc->w_buf[0] = b64enc[src_c[src_pos] >> 2];
+ enc->w_buf_len = 1;
+ enc->buf = (src_c[src_pos] & 0x03) << 4;
+ src_pos++;
+ break;
+ case 2:
+ enc->sub_pos = 2;
+ enc->w_buf[0] = b64enc[src_c[src_pos] >> 2];
+ enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) |
+ (src_c[src_pos+1] >> 4)];
+ enc->w_buf_len = 2;
+ enc->buf = (src_c[src_pos+1] & 0x0f) << 2;
+ src_pos += 2;
+ break;
+ default:
+ /* hit the end of the destination buffer */
+ enc->sub_pos = 0;
+ enc->w_buf[0] = b64enc[src_c[src_pos] >> 2];
+ enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) |
+ (src_c[src_pos+1] >> 4)];
+ enc->w_buf[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) |
+ ((src_c[src_pos+2] & 0xc0) >> 6)];
+ enc->w_buf[3] = b64enc[src_c[src_pos+2] & 0x3f];
+ enc->w_buf_len = 4;
+ enc->buf = 0;
+ src_pos += 3;
+ }
+
+ /* fill the remaining allocated space */
+ i_assert(ptr <= end);
+ res_size = end - ptr;
+ i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
+ if (enc->w_buf_len > res_size) {
+ memcpy(ptr, enc->w_buf, res_size);
+ ptr += res_size;
+ enc->w_buf_len -= res_size;
+ memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len);
+ } else if (enc->w_buf_len > 0) {
+ memcpy(ptr, enc->w_buf, enc->w_buf_len);
+ ptr += enc->w_buf_len;
+ enc->w_buf_len = 0;
+ }
+
+ i_assert(ptr == end);
+ *src_pos_r = src_pos;
+}
+
+bool base64_encode_more(struct base64_encoder *enc,
+ const void *src, size_t src_size, size_t *src_pos_r,
+ buffer_t *dest)
+{
+ bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
+ const unsigned char *src_c, *src_p;
+ size_t src_pos, src_left;
+
+ i_assert(!enc->finishing);
+ i_assert(!enc->finished);
+
+ src_p = src_c = src;
+ src_left = src_size;
+ while (src_left > 0) {
+ size_t dst_avail, dst_pos, line_avail, written;
+
+ /* determine how much we can write in destination buffer */
+ dst_avail = buffer_get_avail_size(dest);
+ if (dst_avail == 0)
+ break;
+
+ /* Emit pending newline immediately */
+ if (enc->pending_lf) {
+ i_assert(crlf);
+ buffer_append_c(dest, '\n');
+ enc->pending_lf = FALSE;
+ dst_avail--;
+ if (dst_avail == 0)
+ break;
+ }
+
+ i_assert(enc->max_line_len > 0);
+ i_assert(enc->cur_line_len <= enc->max_line_len);
+ line_avail = I_MIN(enc->max_line_len - enc->cur_line_len,
+ dst_avail);
+
+ if (line_avail > 0) {
+ dst_pos = dest->used;
+ base64_encode_more_data(enc, src_p, src_left, &src_pos,
+ line_avail, dest);
+ i_assert(src_pos <= src_left);
+ src_p += src_pos;
+ src_left -= src_pos;
+ i_assert(dest->used >= dst_pos);
+ written = dest->used - dst_pos;
+
+ i_assert(written <= line_avail);
+ i_assert(written <= enc->max_line_len);
+ i_assert(enc->cur_line_len <=
+ (enc->max_line_len - written));
+ enc->cur_line_len += written;
+ dst_avail -= written;
+ }
+
+ if (dst_avail == 0)
+ break;
+
+ if (src_left > 0 && enc->cur_line_len == enc->max_line_len) {
+ if (crlf) {
+ if (dst_avail >= 2) {
+ /* emit the full CRLF sequence */
+ buffer_append(dest, "\r\n", 2);
+ } else {
+ /* emit CR */
+ buffer_append_c(dest, '\r');
+ /* remember the LF */
+ enc->pending_lf = TRUE;
+ }
+ } else {
+ buffer_append_c(dest, '\n');
+ }
+ enc->cur_line_len = 0;
+ }
+ }
+
+ i_assert(src_p >= src_c);
+ src_pos = (src_p - src_c);
+ if (src_pos_r != NULL)
+ *src_pos_r = src_pos;
+ return (src_pos == src_size);
+}
+
+bool base64_encode_finish(struct base64_encoder *enc, buffer_t *dest)
+{
+ const struct base64_scheme *b64 = enc->b64;
+ const char *b64enc = b64->encmap;
+ bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
+ bool padding = HAS_NO_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING);
+ unsigned char *ptr, *end;
+ size_t dst_avail, line_avail, write_full, write;
+ unsigned int w_buf_pos = 0;
+
+ i_assert(!enc->finished);
+ enc->finishing = TRUE;
+
+ dst_avail = 0;
+ if (dest != NULL)
+ dst_avail = buffer_get_avail_size(dest);
+
+ if (enc->w_buf_len > 0 || enc->pending_lf) {
+ if (dst_avail == 0)
+ return FALSE;
+ i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
+ }
+
+ i_assert(enc->max_line_len > 0);
+ i_assert(enc->cur_line_len <= enc->max_line_len);
+ line_avail = enc->max_line_len - enc->cur_line_len;
+
+ switch (enc->sub_pos) {
+ case 0:
+ break;
+ case 1:
+ i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 3));
+ enc->w_buf[enc->w_buf_len] = b64enc[enc->buf];
+ enc->w_buf_len++;
+ if (padding) {
+ enc->w_buf[enc->w_buf_len + 0] = '=';
+ enc->w_buf[enc->w_buf_len + 1] = '=';
+ enc->w_buf_len += 2;
+ }
+ break;
+ case 2:
+ i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 2));
+ enc->w_buf[enc->w_buf_len] = b64enc[enc->buf];
+ enc->w_buf_len++;
+ if (padding) {
+ enc->w_buf[enc->w_buf_len + 0] = '=';
+ enc->w_buf_len++;
+ }
+ break;
+ default:
+ i_unreached();
+ }
+ enc->sub_pos = 0;
+
+ write_full = write = enc->w_buf_len;
+ if (enc->pending_lf)
+ write_full++;
+ if (enc->max_line_len < SIZE_MAX && line_avail < write) {
+ unsigned int lines;
+
+ lines = I_MAX((write - line_avail) / enc->max_line_len, 1);
+ write_full += lines * (crlf ? 2 : 1);
+ } else {
+ line_avail = write;
+ }
+
+ if (write_full == 0) {
+ enc->finished = TRUE;
+ return TRUE;
+ }
+
+ i_assert(dest != NULL);
+ if (write_full > dst_avail)
+ write_full = dst_avail;
+
+ ptr = buffer_append_space_unsafe(dest, write_full);
+ end = ptr + write_full;
+ if (enc->pending_lf) {
+ ptr[0] = '\n';
+ dst_avail--;
+ ptr++;
+ enc->pending_lf = FALSE;
+ }
+ if (line_avail > dst_avail)
+ line_avail = dst_avail;
+ if (line_avail > 0) {
+ memcpy(ptr, enc->w_buf, line_avail);
+ ptr += line_avail;
+ w_buf_pos += line_avail;
+ }
+ while (ptr < end && w_buf_pos < enc->w_buf_len) {
+ enc->cur_line_len = 0;
+ if (crlf) {
+ ptr[0] = '\r';
+ ptr++;
+ if (ptr == end) {
+ enc->pending_lf = TRUE;
+ break;
+ }
+ }
+ ptr[0] = '\n';
+ ptr++;
+ if (ptr == end)
+ break;
+
+ write = I_MIN(enc->w_buf_len - w_buf_pos, enc->max_line_len);
+ write = I_MIN(write, (size_t)(end - ptr));
+ memcpy(ptr, &enc->w_buf[w_buf_pos], write);
+ ptr += write;
+ w_buf_pos += write;
+ enc->cur_line_len += write;
+ i_assert(ptr <= end);
+ }
+ i_assert(ptr == end);
+ if (w_buf_pos < enc->w_buf_len) {
+ enc->w_buf_len -= w_buf_pos;
+ memmove(enc->w_buf, enc->w_buf + w_buf_pos, enc->w_buf_len);
+ return FALSE;
+ }
+ if (enc->pending_lf)
+ return FALSE;
+ enc->finished = TRUE;
+ return TRUE;
+}
+
+/*
+ * Low-level Base64 decoder
+ */
+
+#define IS_EMPTY(c) \
+ ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t')
+
+static inline void
+base64_skip_whitespace(struct base64_decoder *dec, const unsigned char *src_c,
+ size_t src_size, size_t *src_pos)
+{
+ if (HAS_ALL_BITS(dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE))
+ return;
+
+ /* skip any whitespace in the padding */
+ while ((*src_pos) < src_size && IS_EMPTY(src_c[(*src_pos)]))
+ (*src_pos)++;
+}
+
+int base64_decode_more(struct base64_decoder *dec,
+ const void *src, size_t src_size, size_t *src_pos_r,
+ buffer_t *dest)
+{
+ const struct base64_scheme *b64 = dec->b64;
+ const unsigned char *src_c = src;
+ bool expect_boundary = HAS_ALL_BITS(
+ dec->flags, BASE64_DECODE_FLAG_EXPECT_BOUNDARY);
+ bool no_whitespace = HAS_ALL_BITS(
+ dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE);
+ bool no_padding = HAS_ALL_BITS(
+ dec->flags, BASE64_DECODE_FLAG_NO_PADDING);
+ size_t src_pos, dst_avail;
+ int ret = 1;
+
+ i_assert(!dec->finished);
+ i_assert(!dec->failed);
+
+ if (dec->seen_boundary) {
+ /* already seen the boundary/end of base64 data */
+ if (src_pos_r != NULL)
+ *src_pos_r = 0;
+ dec->failed = TRUE;
+ return -1;
+ }
+
+ src_pos = 0;
+ if (dec->seen_end) {
+ /* skip any whitespace at the end */
+ base64_skip_whitespace(dec, src_c, src_size, &src_pos);
+ if (src_pos_r != NULL)
+ *src_pos_r = src_pos;
+ if (src_pos < src_size) {
+ if (!expect_boundary) {
+ dec->failed = TRUE;
+ return -1;
+ }
+ dec->seen_boundary = TRUE;
+ return 0;
+ }
+ if (no_whitespace) {
+ dec->seen_boundary = TRUE;
+ return 0;
+ }
+ /* more whitespace may follow */
+ return 1;
+ }
+
+ if (src_size == 0) {
+ if (src_pos_r != NULL)
+ *src_pos_r = 0;
+ return 1;
+ }
+
+ dst_avail = buffer_get_avail_size(dest);
+ if (dst_avail == 0) {
+ i_assert(src_pos_r != NULL);
+ *src_pos_r = 0;
+ return 1;
+ }
+
+ for (; !dec->seen_padding && src_pos < src_size; src_pos++) {
+ unsigned char in = src_c[src_pos];
+ unsigned char dm = b64->decmap[in];
+
+ if (dm == 0xff) {
+ if (no_whitespace) {
+ ret = -1;
+ break;
+ }
+ if (unlikely(!IS_EMPTY(in))) {
+ ret = -1;
+ break;
+ }
+ continue;
+ }
+
+ if (dst_avail == 0) {
+ i_assert(src_pos_r != NULL);
+ *src_pos_r = src_pos;
+ return 1;
+ }
+
+ switch (dec->sub_pos) {
+ case 0:
+ dec->buf = dm;
+ dec->sub_pos++;
+ break;
+ case 1:
+ dec->buf = (dec->buf << 2) | (dm >> 4);
+ buffer_append_c(dest, dec->buf);
+ dst_avail--;
+ dec->buf = dm;
+ dec->sub_pos++;
+ break;
+ case 2:
+ dec->buf = ((dec->buf << 4) & 0xff) | (dm >> 2);
+ buffer_append_c(dest, dec->buf);
+ dst_avail--;
+ dec->buf = dm;
+ dec->sub_pos++;
+ break;
+ case 3:
+ dec->buf = ((dec->buf << 6) & 0xc0) | dm;
+ buffer_append_c(dest, dec->buf);
+ dst_avail--;
+ dec->buf = 0;
+ dec->sub_pos = 0;
+ break;
+ default:
+ i_unreached();
+ }
+ }
+
+ if (dec->seen_padding) {
+ /* skip any whitespace in or after the padding */
+ base64_skip_whitespace(dec, src_c, src_size, &src_pos);
+ if (src_pos == src_size) {
+ if (src_pos_r != NULL)
+ *src_pos_r = src_pos;
+ return 1;
+ }
+ }
+
+ if (dec->seen_padding || ret < 0) {
+ /* try to parse the end (padding) of the base64 input */
+ i_assert(src_pos < src_size);
+
+ if (no_padding) {
+ /* no padding allowed */
+ i_assert(!dec->seen_padding);
+ ret = -1;
+ } else {
+ switch (dec->sub_pos) {
+ case 0:
+ case 1:
+ /* no padding expected */
+ ret = -1;
+ break;
+ case 2:
+ if (unlikely(src_c[src_pos] != '=')) {
+ /* invalid character */
+ ret = -1;
+ break;
+ }
+ dec->seen_padding = TRUE;
+ dec->sub_pos++;
+ src_pos++;
+ if (src_pos == src_size) {
+ ret = 1;
+ break;
+ }
+ /* skip any whitespace in the padding */
+ base64_skip_whitespace(dec, src_c, src_size,
+ &src_pos);
+ if (src_pos == src_size) {
+ ret = 1;
+ break;
+ }
+ /* fall through */
+ case 3:
+ if (unlikely(src_c[src_pos] != '=')) {
+ /* invalid character */
+ ret = -1;
+ break;
+ }
+ dec->seen_padding = TRUE;
+ dec->seen_end = TRUE;
+ dec->sub_pos = 0;
+ src_pos++;
+ /* skip any trailing whitespace */
+ base64_skip_whitespace(dec, src_c, src_size,
+ &src_pos);
+ if (src_pos < src_size) {
+ ret = -1;
+ break;
+ }
+ if (no_whitespace) {
+ dec->seen_boundary = TRUE;
+ ret = 0;
+ } else {
+ /* more whitespace may follow */
+ ret = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ if (ret < 0) {
+ if (!expect_boundary) {
+ dec->failed = TRUE;
+ } else {
+ dec->seen_boundary = TRUE;
+ ret = 0;
+ }
+ }
+ if (src_pos_r != NULL)
+ *src_pos_r = src_pos;
+ return ret;
+}
+
+int base64_decode_finish(struct base64_decoder *dec)
+{
+ i_assert(!dec->finished);
+ dec->finished = TRUE;
+
+ if (dec->failed)
+ return -1;
+
+ if (HAS_ALL_BITS(dec->flags,
+ BASE64_DECODE_FLAG_NO_PADDING)) {
+ i_assert(!dec->seen_padding);
+ return 0;
+ }
+ if (HAS_ALL_BITS(dec->flags,
+ BASE64_DECODE_FLAG_IGNORE_PADDING))
+ return 0;
+ return (dec->sub_pos == 0 ? 0 : -1);
+}
+
+/*
+ * Generic Base64 API
+ */
+
+buffer_t *t_base64_scheme_encode(const struct base64_scheme *b64,
+ enum base64_encode_flags flags,
+ size_t max_line_len,
+ const void *src, size_t src_size)
+{
+ buffer_t *buf;
+
+ buf = t_buffer_create(MAX_BASE64_ENCODED_SIZE(src_size));
+ base64_scheme_encode(b64, flags, max_line_len, src, src_size, buf);
+ return buf;
+}
+
+
+int base64_scheme_decode(const struct base64_scheme *b64,
+ enum base64_decode_flags flags,
+ const void *src, size_t src_size, buffer_t *dest)
+{
+ struct base64_decoder dec;
+ int ret;
+
+ base64_decode_init(&dec, b64, flags);
+ ret = base64_decode_more(&dec, src, src_size, NULL, dest);
+ if (ret >= 0)
+ ret = base64_decode_finish(&dec);
+
+ return ret;
+}
+
+buffer_t *t_base64_scheme_decode(const struct base64_scheme *b64,
+ enum base64_decode_flags flags,
+ const void *src, size_t src_size)
+{
+ buffer_t *buf;
+
+ buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(src_size));
+ (void)base64_scheme_decode(b64, flags, src, src_size, buf);
+ return buf;
+}
+
+/*
+ * "base64" encoding scheme (RFC 4648, Section 4)
+ */
+
+struct base64_scheme base64_scheme = {
+ .encmap = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+ },
+ .decmap = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, /* 40-47 */
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */
+
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+};
+
+/*
+ * "base64url" encoding scheme (RFC 4648, Section 5)
+ */
+
+struct base64_scheme base64url_scheme = {
+ .encmap = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '-', '_',
+ },
+ .decmap = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, /* 40-47 */
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f, /* 88-95 */
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */
+
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+};