Change priority from extra to optional in debian/control.
[debian/iodine.git] / src / base64.c
index 803d938a6c85e157235ebf913c05b727df046db1..71bbcf130e896fee92c3c29eb17a290dab59995b 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * Copyright (c) 2006-2009 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"
 
 #define BLKSIZE_RAW 3
 #define BLKSIZE_ENC 4
 
-static const char cb64[] = 
+/* 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[128];
+static unsigned char rev64[256];
 static int reverse_init = 0;
 
 static int base64_encode(char *, size_t *, const void *, size_t);
@@ -36,8 +39,6 @@ static int base64_handles_dots();
 static int base64_blksize_raw();
 static int base64_blksize_enc();
 
-#define REV64(x) rev64[(int) (x)]
-
 static struct encoder base64_encoder =
 {
        "Base64",
@@ -55,145 +56,151 @@ struct encoder
        return &base64_encoder;
 }
 
-static int 
+static int
 base64_handles_dots()
 {
        return 0;
 }
 
-static int 
+static int
 base64_blksize_raw()
 {
        return BLKSIZE_RAW;
 }
 
-static int 
+static int
 base64_blksize_enc()
 {
        return BLKSIZE_ENC;
 }
 
-static int 
-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 = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC - 1) - 1;
-       /* how big will the encoded data be */
-       newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW + 1) + 1;
-       /* if the buffer is too small, eat some of the data */
-       if (*buflen < newsize) {
-               size = maxsize;
-       }
-
-       p = s = (unsigned char *) buf;
-       q = (unsigned char *)data;
-
-       for(i=0;i<size;i+=BLKSIZE_RAW) {
-               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 += BLKSIZE_RAW;
-               p += BLKSIZE_ENC;
-       }       
-       *p = 0;
-
-       /* store number of bytes from data that was used */
-       *buflen = size;
-
-       return strlen(buf) - 1;
 }
 
-#define DECODE_ERROR 0xffffffff
-
 static int
-decode_token(const unsigned char *t, unsigned char *data, size_t len) 
+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
+ */
 {
-       if (len < 2)
-               return 0;
+       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 */
 
-       data[0] = ((REV64(t[0]) & 0x3f) << 2) | 
-                         ((REV64(t[1]) & 0x30) >> 4);
+       /* Note: Don't bother to optimize manually. GCC optimizes
+          better(!) when using simplistic array indexing. */
 
-       if (len < 3)
-               return 1;
+       while (1) {
+               if (iout >= *buflen || iin >= size)
+                       break;
+               buf[iout] = cb64[((udata[iin] & 0xfc) >> 2)];
+               iout++;
 
-       data[1] = ((REV64(t[1]) & 0x0f) << 4) | 
-                         ((REV64(t[2]) & 0x3c) >> 2);
+               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 (len < 4)
-               return 2;
+               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++;
+       }
 
-       data[2] = ((REV64(t[2]) & 0x03) << 6) |
-                         (REV64(t[3]) & 0x3f);
+       buf[iout] = '\0';
 
-       return 3;
+       /* store number of bytes from data that was used */
+       *buflen = iin;
+
+       return iout;
 }
 
+#define REV64(x) rev64[(int) (x)]
+
 static int
 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)
+ */
 {
-       unsigned char *q;
-       size_t newsize;
-       size_t maxsize;
-       const char *p;
-       unsigned char c;
-       unsigned char block[BLKSIZE_ENC];
-       int len;
-       int i;
+       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 (!reverse_init) {
-               for (i = 0; i < 64; i++) {
-                       c = cb64[i];
-                       rev64[(int) c] = i;
-               }
-               reverse_init = 1;
-       }
-       
-       /* chars needed to decode slen */
-       newsize = BLKSIZE_RAW * (slen / BLKSIZE_ENC + 1) + 1;
-       /* encoded chars that fit in buf */
-       maxsize = BLKSIZE_ENC * (*buflen / BLKSIZE_RAW + 1) + 1;
-       /* if the buffer is too small, eat some of the data */
-       if (*buflen < newsize) {
-               slen = maxsize;
-       }
-       
+       base64_reverse_init ();
 
-       q = buf;
-       for (p = str; *p; p += BLKSIZE_ENC) {
-               /* since the str is const, we unescape in another buf */
-               for (i = 0; i < BLKSIZE_ENC; i++) {
-                       block[i] = p[i];
-               }
-               len = decode_token(block, (unsigned char *) q, slen);   
-               q += len;
-               slen -= BLKSIZE_ENC;
-               
-               if (len < BLKSIZE_RAW)
+       /* Note: Don't bother to optimize manually. GCC optimizes
+          better(!) when using simplistic array indexing. */
+
+       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 (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++;
+
+               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;
+}