From: Richard Kettlewell Date: Tue, 18 Dec 2007 11:53:54 +0000 (+0000) Subject: base64 encoder X-Git-Tag: 3.0~204 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/8a7ccdfe933e3c22b38c029c07450a6c6f3b25ec base64 encoder --- diff --git a/lib/mime.c b/lib/mime.c index 5562c7e..f7de533 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -284,7 +284,7 @@ const char *mime_parse(const char *s, } if(*s) s += 2; if(cte) { - if(!strcmp(cte, "base64")) return mime_base64(s); + if(!strcmp(cte, "base64")) return mime_base64(s, 0); if(!strcmp(cte, "quoted-printable")) return mime_qp(s); } return s; @@ -431,22 +431,23 @@ char *mime_qp(const char *s) { return d.vec; } +static const char mime_base64_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + /** @brief Convert MIME base64 * @param s base64 data * @return Decoded data */ -char *mime_base64(const char *s) { +char *mime_base64(const char *s, size_t *nsp) { struct dynstr d; const char *t; int b[4], n, c; - static const char table[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; dynstr_init(&d); n = 0; while((c = (unsigned char)*s++)) { - if((t = strchr(table, c))) { - b[n++] = t - table; + if((t = strchr(mime_base64_table, c))) { + b[n++] = t - mime_base64_table; if(n == 4) { dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); @@ -462,10 +463,55 @@ char *mime_base64(const char *s) { break; } } + if(nsp) + *nsp = d.nvec; dynstr_terminate(&d); return d.vec; } +/** @brief Convert a binary string to base64 + * @param s Bytes to convert + * @param ns Number of bytes to convert + * @return Encoded data + * + * This function does not attempt to split up lines. + */ +char *mime_to_base64(const uint8_t *s, size_t ns) { + struct dynstr d[1]; + + dynstr_init(d); + while(ns >= 3) { + /* Input bytes with output bits: AAAAAABB BBBBCCCC CCDDDDDD */ + /* Output bytes with input bits: 000000 001111 111122 222222 */ + dynstr_append(d, mime_base64_table[s[0] >> 2]); + dynstr_append(d, mime_base64_table[((s[0] & 3) << 4) + + (s[1] >> 4)]); + dynstr_append(d, mime_base64_table[((s[1] & 15) << 2) + + (s[2] >> 6)]); + dynstr_append(d, mime_base64_table[s[2] & 63]); + ns -= 3; + s += 3; + } + if(ns > 0) { + dynstr_append(d, mime_base64_table[s[0] >> 2]); + switch(ns) { + case 1: + dynstr_append(d, mime_base64_table[(s[0] & 3) << 4]); + dynstr_append(d, '='); + dynstr_append(d, '='); + break; + case 2: + dynstr_append(d, mime_base64_table[((s[0] & 3) << 4) + + (s[1] >> 4)]); + dynstr_append(d, mime_base64_table[(s[1] & 15) << 2]); + dynstr_append(d, '='); + break; + } + } + dynstr_terminate(d); + return d->vec; +} + /** @brief Parse a RFC2109 Cookie: header * @param s Header field value * @param cd Where to store result diff --git a/lib/mime.h b/lib/mime.h index 3300989..b6d21ce 100644 --- a/lib/mime.h +++ b/lib/mime.h @@ -54,7 +54,8 @@ int mime_rfc2388_content_disposition(const char *s, /* Parse an RFC2388-style content-disposition field */ char *mime_qp(const char *s); -char *mime_base64(const char *s); +char *mime_base64(const char *s, size_t *nsp); +char *mime_to_base64(const uint8_t *s, size_t ns); /* convert quoted-printable or base64 data */ /** @brief Parsed form of an HTTP Cookie: header field */ diff --git a/lib/test.c b/lib/test.c index 399c7f1..c3e4c34 100644 --- a/lib/test.c +++ b/lib/test.c @@ -458,24 +458,47 @@ static void test_mime(void) { " to the aid of their country."), "Now's the time for all folk to come to the aid of their country."); - check_string(mime_base64(""), ""); - check_string(mime_base64("BBBB"), "\x04\x10\x41"); - check_string(mime_base64("////"), "\xFF\xFF\xFF"); - check_string(mime_base64("//BB"), "\xFF\xF0\x41"); - check_string(mime_base64("BBBB//BB////"), - "\x04\x10\x41" "\xFF\xF0\x41" "\xFF\xFF\xFF"); - check_string(mime_base64("B B B B / / B B / / / /"), - "\x04\x10\x41" "\xFF\xF0\x41" "\xFF\xFF\xFF"); - check_string(mime_base64("B\r\nBBB.// B-B//~//"), +#define check_base64(encoded, decoded) do { \ + check_string(mime_base64(encoded, 0), decoded); \ + check_string(mime_to_base64((const uint8_t *)decoded, \ + (sizeof decoded) - 1), \ + encoded); \ + } while(0) + + + check_base64("", ""); + check_base64("BBBB", "\x04\x10\x41"); + check_base64("////", "\xFF\xFF\xFF"); + check_base64("//BB", "\xFF\xF0\x41"); + check_base64("BBBB//BB////", + "\x04\x10\x41" "\xFF\xF0\x41" "\xFF\xFF\xFF"); + check_base64("BBBBBA==", + "\x04\x10\x41" "\x04"); + check_base64("BBBBBBA=", + "\x04\x10\x41" "\x04\x10"); + + /* Check that decoding handles various kinds of rubbish OK */ + check_string(mime_base64("B B B B / / B B / / / /", 0), + "\x04\x10\x41" "\xFF\xF0\x41" "\xFF\xFF\xFF"); + check_string(mime_base64("B\r\nBBB.// B-B//~//", 0), "\x04\x10\x41" "\xFF\xF0\x41" "\xFF\xFF\xFF"); - check_string(mime_base64("BBBB="), - "\x04\x10\x41"); - check_string(mime_base64("BBBBx="), /* not actually valid base64 */ + check_string(mime_base64("BBBB BB==", 0), + "\x04\x10\x41" "\x04"); + check_string(mime_base64("BBBB BB = =", 0), + "\x04\x10\x41" "\x04"); + check_string(mime_base64("BBBB BBB=", 0), + "\x04\x10\x41" "\x04\x10"); + check_string(mime_base64("BBBB BBB = ", 0), + "\x04\x10\x41" "\x04\x10"); + check_string(mime_base64("BBBB=", 0), "\x04\x10\x41"); - check_string(mime_base64("BBBB BB=="), + check_string(mime_base64("BBBBBB==", 0), "\x04\x10\x41" "\x04"); - check_string(mime_base64("BBBB BBB="), + check_string(mime_base64("BBBBBBB=", 0), "\x04\x10\x41" "\x04\x10"); + /* Not actually valid base64 */ + check_string(mime_base64("BBBBx=", 0), + "\x04\x10\x41"); } static void test_cookies(void) {