]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - base64.c
base64: Saner semantics for base64_decode() and uudecode().
[paraslash.git] / base64.c
index e9892244d62a0e00a320003bb3fe740e5e89f866..4809a2f285f63dd83132f693be23f68945fce21d 100644 (file)
--- a/base64.c
+++ b/base64.c
@@ -17,29 +17,47 @@ static const char Base64[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 static const char Pad64 = '=';
 
+/** Maximal possible size of the decoded data. */
+#define BASE64_MAX_DECODED_SIZE(_encoded_size) ((_encoded_size) / 4 * 3)
+
 /**
  * base64-decode a buffer.
  *
  * \param src The buffer to decode.
- * \param target Result is stored here.
- * \param targsize Number of bytes of \a target.
+ * \param encoded_size The special value -1 means: look for terminating zero byte.
+ * \param result Points to dynamically allocated target buffer on success.
+ * \param decoded_size Number of bytes written to \a result.
  *
  * Skips all whitespace anywhere. Converts characters, four at a time, starting
  * at (or after) src from base - 64 numbers into three 8 bit bytes in the
  * target area.
  *
- * \return The number of data bytes stored at the target, -E_BASE64 on errors.
+ * It is OK to pass a \p NULL pointer as \a decoded_size. The result is
+ * terminated with a zero byte.
+ *
+ * \return Standard. The contents of result \a and \a decoded_size are
+ * undefined on failure.
  */
-int base64_decode(char const *src, unsigned char *target, size_t targsize)
+int base64_decode(char const *src, size_t encoded_size, char **result,
+               size_t *decoded_size)
 {
        unsigned int tarindex, state;
        int ch;
        char *pos;
+       const char *end = src + encoded_size;
+       unsigned char *target;
+       size_t targsize;
+
+       if (encoded_size == (size_t)-1)
+               encoded_size = strlen(src);
+       targsize = BASE64_MAX_DECODED_SIZE(encoded_size);
+       target = para_malloc(targsize + 1);
 
        state = 0;
        tarindex = 0;
 
-       while ((ch = *src++) != '\0') {
+       while (src < end) {
+               ch = *src++;
                if (para_isspace(ch)) /* Skip whitespace anywhere. */
                        continue;
 
@@ -48,18 +66,18 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
 
                pos = strchr(Base64, ch);
                if (pos == NULL) /* A non-base64 character. */
-                       return -E_BASE64;
+                       goto fail;
 
                switch (state) {
                case 0:
                        if (tarindex >= targsize)
-                               return -E_BASE64;
+                               goto fail;
                        target[tarindex] = (pos - Base64) << 2;
                        state = 1;
                        break;
                case 1:
                        if (tarindex + 1 >= targsize)
-                               return -E_BASE64;
+                               goto fail;
                        target[tarindex] |= (pos - Base64) >> 4;
                        target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
                        tarindex++;
@@ -67,7 +85,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                        break;
                case 2:
                        if (tarindex + 1 >= targsize)
-                               return -E_BASE64;
+                               goto fail;
                        target[tarindex] |= (pos - Base64) >> 2;
                        target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
                        tarindex++;
@@ -75,7 +93,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                        break;
                case 3:
                        if (tarindex >= targsize)
-                               return -E_BASE64;
+                               goto fail;
                        target[tarindex] |= pos - Base64;
                        tarindex++;
                        state = 0;
@@ -88,12 +106,12 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
         * on a byte boundary, and/or with erroneous trailing characters.
         */
 
-       if (ch == Pad64) {              /* We got a pad char. */
+       if (*src == Pad64) {            /* We got a pad char. */
                ch = *src++;            /* Skip it, get next. */
                switch (state) {
                case 0:         /* Invalid = in first position */
                case 1:         /* Invalid = in second position */
-                       return -E_BASE64;
+                       goto fail;
 
                case 2:         /* Valid, means one byte of info */
                        /* Skip any number of spaces. */
@@ -102,7 +120,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                                        break;
                        /* Make sure there is another trailing = sign. */
                        if (ch != Pad64)
-                               return -E_BASE64;
+                               goto fail;
                        ch = *src++;            /* Skip the = */
                        /* Fall through to "single trailing =" case. */
                        /* FALLTHROUGH */
@@ -114,7 +132,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                         */
                        for (; ch != '\0'; ch = *src++)
                                if (!isspace(ch))
-                                       return -E_BASE64;
+                                       goto fail;
 
                        /*
                         * Now make sure for cases 2 and 3 that the "extra"
@@ -123,7 +141,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                         * subliminal channel.
                         */
                        if (target[tarindex] != 0)
-                               return -E_BASE64;
+                               goto fail;
                }
        } else {
                /*
@@ -131,37 +149,44 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize)
                 * have no partial bytes lying around.
                 */
                if (state != 0)
-                       return -E_BASE64;
+                       goto fail;
        }
-       return tarindex;
+       /* success */
+       target[tarindex] = '\0'; /* just to be sure */
+       *result = (char *)target;
+       if (decoded_size)
+               *decoded_size = tarindex;
+       return 1;
+fail:
+       free(target);
+       return -E_BASE64;
 }
 
 /**
- * uudecode a buffer.
+ * Decode a buffer using the uuencode Base64 algorithm.
  *
  * \param src The buffer to decode.
- * \param target Result buffer.
- * \param targsize The length of \a target in bytes.
+ * \param encoded_size Number of input bytes in the source buffer.
+ * \param result Contains the decoded data on success.
+ * \param decoded_size Number of output bytes on success.
+ *
+ * This is just a simple wrapper for \ref base64_decode() which strips
+ * whitespace.
  *
- * This is just a simple wrapper for base64_decode() which strips whitespace.
+ * \return The return value of the underlying call to \ref base64_decode().
  *
- * \return The return value of the underlying call to base64_decode().
+ * \sa uuencode(1), uudecode(1).
  */
-int uudecode(const char *src, unsigned char *target, size_t targsize)
+int uudecode(char const *src, size_t encoded_size, char **result,
+               size_t *decoded_size)
 {
-       int len;
-       char *encoded, *p;
+       const char *end = src + encoded_size, *p;
 
-       /* copy the 'readonly' source */
-       encoded = para_strdup(src);
        /* skip whitespace and data */
-       for (p = encoded; *p == ' ' || *p == '\t'; p++)
+       for (p = src; p < end && (*p == ' ' || *p == '\t'); p++)
                ;
-       for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
+       for (; p < end && *p != '\0' && *p != ' ' && *p != '\t'; p++)
                ;
        /* and remove trailing whitespace because base64_decode needs this */
-       *p = '\0';
-       len = base64_decode(encoded, target, targsize);
-       free(encoded);
-       return len;
+       return base64_decode(src, p - src, result, decoded_size);
 }