Change priority from extra to optional in debian/control.
[debian/iodine.git] / src / base64.c
index 512a1997e713dc1a01c1fad7fe6a6af70ba5d563..71bbcf130e896fee92c3c29eb17a290dab59995b 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
+ * 2006-2009 Bjorn Andersson <flex@kryo.se>
+ * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include <string.h>
 
 #include "encoding.h"
-#include "common.h"
 #include "base64.h"
 
-static const char cb64[] = 
-       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789.";
-static unsigned char rev64[128];
+#define BLKSIZE_RAW 3
+#define BLKSIZE_ENC 4
+
+/* Note: the "unofficial" char is last here, which means that the \377 pattern
+   in DOWNCODECCHECK1 ('Y' request) will properly test it. */
+static const char cb64[] =
+       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+";
+static unsigned char rev64[256];
 static int reverse_init = 0;
 
-#define REV64(x) rev64[(int) (x)]
-#define MODE   (cb64[62])
-#define P62    (cb64[62])
-#define P63    (cb64[63])
+static int base64_encode(char *, size_t *, const void *, size_t);
+static int base64_decode(void *, size_t *, const char *, size_t);
+static int base64_handles_dots();
+static int base64_blksize_raw();
+static int base64_blksize_enc();
 
 static struct encoder base64_encoder =
 {
-       "BASE64",
+       "Base64",
        base64_encode,
        base64_decode,
        base64_handles_dots,
-       base64_handles_dots
+       base64_handles_dots,
+       base64_blksize_raw,
+       base64_blksize_enc
 };
 
 struct encoder
@@ -47,292 +56,151 @@ struct encoder
        return &base64_encoder;
 }
 
-int 
+static int
 base64_handles_dots()
 {
        return 0;
 }
 
-static void
-findesc(int *count, unsigned char *esc, char c1, char c2, char c3, char c4)
+static int
+base64_blksize_raw()
 {
-       int min1 = 0;
-       int min2 = 0;
-
-       int num1 = 0xFF; /* a very big number */
-       int num2 = 0xFE; /* a nearly as big number */
-
-       int i;
-
-       /* check if no more escapes needed */
-       if (count[62] == 0 && count[63] == 0) {
-               esc[0] = MODE;
-               esc[1] = MODE;
-               return;
-       }
-
-       for (i = 0; i < 62; i++) {
-               if (i == c1 || i == c2 || i == c3 || i == c4) {
-                       continue;
-               }
-
-               if (count[i] < num1) {
-                       min2 = min1;
-                       num2 = num1;
-                       min1 = i;
-                       num1 = count[i];
-               } else if (count[i] < num2) {
-                       min2 = i;
-                       num2 = count[i];
-               }
-       }
-
-       esc[0] = cb64[min1];
-       esc[1] = cb64[min2];
+       return BLKSIZE_RAW;
 }
-       
-static void
-escape_chars(char *buf, size_t buflen)
-{
-       int counter[64];
-       int escapes;
-       int reset;
-       int i;
-       unsigned char temp[4096];
-       unsigned char *r;
-       unsigned char *w;
-       unsigned char *e;
-       unsigned char esc[2];
-
-       memset(counter, 0, sizeof(counter));
-       esc[0] = P62;
-       esc[1] = P63;
-
-       /* first, find the number of times each token is used */
-       r = (unsigned char *) buf;
-       w = temp;
-       while (*r) {
-               counter[REV64(*r)]++;
-               *w++ = *r++;
-       }
 
-       /* check if work needed */
-       if (counter[62] == 0 && counter[63] == 0)
-               return;
-       
-       r = temp;
-       w = (unsigned char *) buf;
-       reset = 1;
-       escapes = 0;
-       /* check a block for esc chars */
-       while (*r) {
-               if (reset == 0 && escapes == 0 && (
-                   r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] ||
-                   r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1])) {
-                       /* last set of escape chars were unused.
-                        * if we reset last escape switch then maybe we dont have to switch now */
-
-                       /* change the latest escape switch to 999 (RESET) */
-                       e[1] = MODE;
-                       e[2] = MODE;
-                        
-                       /* store default esc chars */
-                       esc[0] = P62;
-                       esc[1] = P63;
-
-                       reset = 1;
-               }
-               /* these two if blocks can not be combined because a block can contain both
-                * char 9 and/or . and the current escape chars. */
-               if (r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] ||
-                   r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1]) {
-                       /* switch escape chars */
-                       escapes = 0;
-                       reset = 0;
-
-                       /* find 2 suitable escape chars */
-                       findesc(counter, esc, REV64(r[0]), REV64(r[1]), REV64(r[2]), REV64(r[3]));
-
-                       /* store escape switch position */
-                       e = w;
-
-                       /* write new escape chars */
-                       *w++ = MODE;
-                       *w++ = esc[0];
-                       *w++ = esc[1];
-               }
-               
-               /* update counter on remaining chars */
-               for (i = 0; i < 4; i++) {
-                       if (r[i])
-                               counter[REV64(r[i])]--;
-               }
-
-               /* do the escaping */
-               for (i = 0; i < 4; i++) {
-                       if (r[i] == P62) {
-                               r[i] = esc[0];
-                               escapes++;
-                       } else if (r[i] == P63) {
-                               r[i] = esc[1];
-                               escapes++;
-                       }
-               }       
-               
-               /* copy back to buf */
-               *w++ = *r++;
-               *w++ = *r++;
-               *w++ = *r++;
-               *w++ = *r++;
-       }
+static int
+base64_blksize_enc()
+{
+       return BLKSIZE_ENC;
 }
 
-in
-base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
+inline static void
+base64_reverse_init()
 {
-       size_t newsize;
-       size_t maxsize;
-       unsigned char c;
-       unsigned char *s;
-       unsigned char *p;
-       unsigned char *q;
        int i;
+       unsigned char c;
 
-       memset(buf, 0, *buflen);
-       
        if (!reverse_init) {
+               memset (rev64, 0, 256);
                for (i = 0; i < 64; i++) {
                        c = cb64[i];
                        rev64[(int) c] = i;
                }
                reverse_init = 1;
        }
+}
 
-       /* how many chars can we encode within the buf */
-       maxsize = 3 * (*buflen / 4 - 1) - 1;
-       /* how big will the encoded data be */
-       newsize = 4 * (size / 3 + 1) + 1;
-       /* if the buffer is too small, eat some of the data */
-       if (*buflen < newsize) {
-               size = maxsize;
-       }
+static int
+base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
+/*
+ * Fills *buf with max. *buflen characters, encoding size bytes of *data.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold the trailing '\0'.
+ *
+ * return value    : #bytes filled in buf   (excluding \0)
+ * sets *buflen to : #bytes encoded from data
+ */
+{
+       unsigned char *udata = (unsigned char *) data;
+       int iout = 0;   /* to-be-filled output char */
+       int iin = 0;    /* one more than last input byte that can be
+                          successfully decoded */
 
-       p = s = (unsigned char *) buf;
-       q = (unsigned char *)data;
+       /* Note: Don't bother to optimize manually. GCC optimizes
+          better(!) when using simplistic array indexing. */
 
-       for(i=0;i<size;i+=3) {
-               p[0] = cb64[((q[0] & 0xfc) >> 2)];
-               p[1] = cb64[(((q[0] & 0x03) << 4) | ((q[1] & 0xf0) >> 4))];
-               p[2] = (i+1 < size) ? cb64[((q[1] & 0x0f) << 2 ) | ((q[2] & 0xc0) >> 6)] : '\0';
-               p[3] = (i+2 < size) ? cb64[(q[2] & 0x3f)] : '\0';
-               
-               q += 3;
-               p += 4;
-       }       
-       *p = 0;
+       while (1) {
+               if (iout >= *buflen || iin >= size)
+                       break;
+               buf[iout] = cb64[((udata[iin] & 0xfc) >> 2)];
+               iout++;
 
-       escape_chars(buf, *buflen);
+               if (iout >= *buflen || iin >= size) {
+                       iout--;         /* previous char is useless */
+                       break;
+               }
+               buf[iout] = cb64[((udata[iin] & 0x03) << 4) |
+                                 ((iin + 1 < size) ?
+                                  ((udata[iin + 1] & 0xf0) >> 4) : 0)];
+               iin++;                  /* 0 complete, iin=1 */
+               iout++;
+
+               if (iout >= *buflen || iin >= size)
+                       break;
+               buf[iout] = cb64[((udata[iin] & 0x0f) << 2 ) |
+                                 ((iin + 1 < size) ?
+                                  ((udata[iin + 1] & 0xc0) >> 6) : 0)];
+               iin++;                  /* 1 complete, iin=2 */
+               iout++;
+
+               if (iout >= *buflen || iin >= size)
+                       break;
+               buf[iout] = cb64[(udata[iin] & 0x3f)];
+               iin++;                  /* 2 complete, iin=3 */
+               iout++;
+       }
+
+       buf[iout] = '\0';
 
        /* store number of bytes from data that was used */
-       *buflen = size;
+       *buflen = iin;
 
-       return strlen(buf) - 1;
+       return iout;
 }
 
-#define DECODE_ERROR 0xffffffff
+#define REV64(x) rev64[(int) (x)]
 
 static int
-decode_token(const unsigned char *t, unsigned char *data, size_t len) 
+base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
+/*
+ * Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
+ * Decoding stops early when *str contains \0.
+ * Illegal encoded chars are assumed to decode to zero.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold a trailing '\0' that is added (though *buf will usually
+ * contain full-binary data).
+ *
+ * return value    : #bytes filled in buf   (excluding \0)
+ */
 {
-       if (len < 2)
-               return 0;
-
-       data[0] = ((REV64(t[0]) & 0x3f) << 2) | 
-                         ((REV64(t[1]) & 0x30) >> 4);
-
-       if (len < 3)
-               return 1;
-
-       data[1] = ((REV64(t[1]) & 0x0f) << 4) | 
-                         ((REV64(t[2]) & 0x3c) >> 2);
+       unsigned char *ubuf = (unsigned char *) buf;
+       int iout = 0;   /* to-be-filled output byte */
+       int iin = 0;    /* next input char to use in decoding */
 
-       if (len < 4)
-               return 2;
+       base64_reverse_init ();
 
-       data[2] = ((REV64(t[2]) & 0x03) << 6) |
-                         (REV64(t[3]) & 0x3f);
+       /* Note: Don't bother to optimize manually. GCC optimizes
+          better(!) when using simplistic array indexing. */
 
-       return 3;
-}
-
-int
-base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
-{
-       unsigned char *q;
-       size_t newsize;
-       size_t maxsize;
-       const char *p;
-       unsigned char c;
-       unsigned char block[4];
-       unsigned char prot62;
-       unsigned char prot63;
-       int len;
-       int i;
+       while (1) {
+               if (iout >= *buflen || iin + 1 >= slen ||
+                   str[iin] == '\0' || str[iin + 1] == '\0')
+                       break;
+               ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) |
+                            ((REV64(str[iin + 1]) & 0x30) >> 4);
+               iin++;                  /* 0 used up, iin=1 */
+               iout++;
 
-       if (!reverse_init) {
-               for (i = 0; i < 64; i++) {
-                       c = cb64[i];
-                       rev64[(int) c] = i;
-               }
-               reverse_init = 1;
-       }
-       
-       /* chars needed to decode slen */
-       newsize = 3 * (slen / 4 + 1) + 1;
-       /* encoded chars that fit in buf */
-       maxsize = 4 * (*buflen / 3 + 1) + 1;
-       /* if the buffer is too small, eat some of the data */
-       if (*buflen < newsize) {
-               slen = maxsize;
-       }
-       
-       prot62 = P62;
-       prot63 = P63;
+               if (iout >= *buflen || iin + 1 >= slen ||
+                   str[iin] == '\0' || str[iin + 1] == '\0')
+                       break;
+               ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) |
+                            ((REV64(str[iin + 1]) & 0x3c) >> 2);
+               iin++;                  /* 1 used up, iin=2 */
+               iout++;
 
-       q = buf;
-       for (p = str; *p; p += 4) {
-               /* handle escape instructions */
-               if (*p == MODE) {
-                       p++;
-                       if (p[0] == MODE && p[1] == MODE) {
-                               /* reset escape chars */
-                               prot62 = P62;
-                               prot63 = P63;
-                               
-                               p += 2;
-                       } else {
-                               prot62 = *p++;
-                               prot63 = *p++;
-                       }
-               }
-               /* since the str is const, we unescape in another buf */
-               for (i = 0; i < 4; i++) {
-                       block[i] = p[i];
-                       if (prot62 == block[i]) {
-                               block[i] = P62;
-                       } else if (prot63 == block[i]) {
-                               block[i] = P63;
-                       }
-               }
-               len = decode_token(block, (unsigned char *) q, slen);   
-               q += len;
-               slen -= 4;
-               
-               if (len < 3)
+               if (iout >= *buflen || iin + 1 >= slen ||
+                   str[iin] == '\0' || str[iin + 1] == '\0')
                        break;
+               ubuf[iout] = ((REV64(str[iin]) & 0x03) << 6) |
+                            (REV64(str[iin + 1]) & 0x3f);
+               iin += 2;               /* 2,3 used up, iin=4 */
+               iout++;
        }
-       *q = '\0';
-       
-       return q - (unsigned char *) buf;
-}
 
+       ubuf[iout] = '\0';
+
+       return iout;
+}