chiark / gitweb /
base64 encoder
authorRichard Kettlewell <rjk@greenend.org.uk>
Tue, 18 Dec 2007 11:53:54 +0000 (11:53 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Tue, 18 Dec 2007 11:53:54 +0000 (11:53 +0000)
lib/mime.c
lib/mime.h
lib/test.c

index 5562c7e..f7de533 100644 (file)
@@ -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
index 3300989..b6d21ce 100644 (file)
@@ -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 */
index 399c7f1..c3e4c34 100644 (file)
@@ -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) {