X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=base64.c;h=122465f8feea79cf06d09bfd759d2446ee0f3f12;hp=e9892244d62a0e00a320003bb3fe740e5e89f866;hb=e7f035b3ad7ecc64ec87d9ce890f89365dec1411;hpb=3ea5399aeec092dcdb9f039288f1e146aadbb2f7 diff --git a/base64.c b/base64.c index e9892244..122465f8 100644 --- a/base64.c +++ b/base64.c @@ -13,117 +13,146 @@ #include "base64.h" #include "string.h" -static const char Base64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - +static const unsigned char base64_tab[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, /* 00-07 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 08-0f */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 10-17 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 18-1f */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 20-2f */ + 255, 255, 255, 62, 255, 255, 255, 63, /* 28-2f */ + 52 , 53, 54, 55, 56, 57, 58, 59, /* 30-37 */ + 60 , 61, 255, 255, 255, 255, 255, 255, /* 38-3f */ + 255, 0, 1, 2, 3, 4, 5, 6, /* 40-47 */ + 7 , 8, 9, 10, 11, 12, 13, 14, /* 48-4f */ + 15 , 16, 17, 18, 19, 20, 21, 22, /* 50-57 */ + 23 , 24, 25, 255, 255, 255, 255, 255, /* 58-5f */ + 255, 26, 27, 28, 29, 30, 31, 32, /* 60-6f */ + 33 , 34, 35, 36, 37, 38, 39, 40, /* 68-6f */ + 41 , 42, 43, 44, 45, 46, 47, 48, /* 70-77 */ + 49 , 50, 51, 255, 255, 255, 255, 255, /* 78-7f */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 80-87 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 88-8f */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 90-97 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 98-9f */ + 255, 255, 255, 255, 255, 255, 255, 255, /* a0-a7 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* a8-af */ + 255, 255, 255, 255, 255, 255, 255, 255, /* b0-b7 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* b8-bf */ + 255, 255, 255, 255, 255, 255, 255, 255, /* c0-c7 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* c8-cf */ + 255, 255, 255, 255, 255, 255, 255, 255, /* d0-d7 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* d8-df */ + 255, 255, 255, 255, 255, 255, 255, 255, /* e0-e7 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* e8-ef */ + 255, 255, 255, 255, 255, 255, 255, 255, /* f0-f7 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* f8-ff */ +}; + +/** Maximal possible size of the decoded data. */ +#define BASE64_MAX_DECODED_SIZE(_encoded_size) ((_encoded_size) / 4 * 3) + +#define PAD64 '=' /** * 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; - - state = 0; - tarindex = 0; - - while ((ch = *src++) != '\0') { - if (para_isspace(ch)) /* Skip whitespace anywhere. */ + size_t i, j, state; /* source/target indices */ + const char *end = src + encoded_size, *p; + unsigned char *target, uch; + + if (encoded_size == (size_t)-1) + encoded_size = strlen(src); + target = para_malloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1); + + for ( + i = 0, j = 0, state = 0; + i < encoded_size && (uch = src[i]) != '\0'; + i++ + ) { + if (para_isspace(uch)) /* Skip whitespace anywhere. */ continue; - - if (ch == Pad64) + if (uch == PAD64) break; - - pos = strchr(Base64, ch); - if (pos == NULL) /* A non-base64 character. */ - return -E_BASE64; - + if (base64_tab[uch] == 255) /* A non-base64 character. */ + goto fail; + uch = base64_tab[uch]; switch (state) { case 0: - if (tarindex >= targsize) - return -E_BASE64; - target[tarindex] = (pos - Base64) << 2; - state = 1; + target[j] = uch << 2; break; case 1: - if (tarindex + 1 >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64) >> 4; - target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4; - tarindex++; - state = 2; + target[j] |= uch >> 4; + j++; + target[j] = (uch & 0x0f) << 4; break; case 2: - if (tarindex + 1 >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64) >> 2; - target[tarindex + 1] = ((pos - Base64) & 0x03) << 6; - tarindex++; - state = 3; + target[j] |= uch >> 2; + j++; + target[j] = (uch & 0x03) << 6; break; case 3: - if (tarindex >= targsize) - return -E_BASE64; - target[tarindex] |= pos - Base64; - tarindex++; - state = 0; + target[j] |= uch; + j++; break; } + state = (state + 1) % 4; } - + p = (i < encoded_size)? src + i : NULL; /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ + if (p && *p == PAD64) { /* We got a pad char. Skip it, get next. */ + p++; switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return -E_BASE64; + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + goto fail; - case 2: /* Valid, means one byte of info */ + case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ - for (; ch != '\0'; ch = *src++) - if (!isspace(ch)) + for (; p < end && *p != '\0'; p++) + if (!para_isspace(*p)) break; /* Make sure there is another trailing = sign. */ - if (ch != Pad64) - return -E_BASE64; - ch = *src++; /* Skip the = */ + if (*p != PAD64) + goto fail; /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ + p++; - case 3: /* Valid, means two bytes of info */ + case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ - for (; ch != '\0'; ch = *src++) - if (!isspace(ch)) - return -E_BASE64; - + for (; p < end && *p != '\0'; p++) + if (!para_isspace(*p)) + goto fail; /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ - if (target[tarindex] != 0) - return -E_BASE64; + if (target[j] != 0) + goto fail; } } else { /* @@ -131,37 +160,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[j] = '\0'; /* just to be sure */ + if (decoded_size) + *decoded_size = j; + *result = (char *)target; + 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); }