1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
/*
* base64.c -- base-64 conversion routines.
*
* For license terms, see the file COPYING in this directory.
*
* This base 64 encoding is defined in RFC2045 section 6.8,
* "Base64 Content-Transfer-Encoding", but lines must not be broken in the
* scheme used here.
*/
#include "config.h"
#include "fetchmail.h"
static const char base64digits[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define BAD (-1)
static int8_t base64val[128];
static bool inited = false;
static inline int DECODE64(int c)
{
if (c < 0 || c > 0x7F) return BAD;
return base64val[c];
}
static void init_decode_table(void)
{
if (inited) return;
memset(base64val, BAD, sizeof(base64val));
for (uint8_t idx = 0; idx < strlen(base64digits); idx++)
{
const unsigned int val = base64digits[idx];
ASSERT(("ensure that our character set fits 7 bits" && val <= 0x7F));
base64val[val] = idx;
}
inited = true;
}
unsigned len64frombits(unsigned inlen)
{
return (inlen + 2)/3*4;
}
int to64frombits(char *out, const void *in_, int inlen, size_t outlen)
/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
{
int rc = 0;
const unsigned char *in = (const unsigned char *)in_;
ASSERT(inlen >= 0);
for (; inlen >= 3; inlen -= 3)
{
if (outlen < 5) { rc = -1; goto fail; } /* buffer too small */
*out++ = base64digits[in[0] >> 2];
*out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
*out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
*out++ = base64digits[in[2] & 0x3f];
in += 3;
outlen -= 4;
}
if (inlen > 0)
{
unsigned char fragment;
if (outlen < 5) { rc = -1; goto fail; } /* buffer too small */
*out++ = base64digits[in[0] >> 2];
fragment = (in[0] << 4) & 0x30;
if (inlen > 1)
fragment |= in[1] >> 4;
*out++ = base64digits[fragment];
*out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c];
*out++ = '=';
}
fail:
*out = '\0';
return rc;
}
int from64tobits(void *out_, const char *in, int maxlen)
/** base 64 to raw bytes in quasi-big-endian order, \return count of bytes, or
* -1 on error (invalid input characters, or input not properly padded with '='
* \a maxlen limits output buffer size, set to zero to ignore (for in-place
* decoding)
* This function will silently skip over a "+ " combination at the beginning
* of the input). It will stop reading at a CR or if the 4th byte of a base64
* input group was a '='
*/
{
if (!inited) init_decode_table();
int len = 0;
unsigned char digit1, digit2, digit3, digit4;
unsigned char *out = (unsigned char *)out_;
ASSERT(maxlen >= 0);
if (in[0] == '+' && in[1] == ' ')
in += 2;
if (*in == '\r')
return(0);
do {
digit1 = in[0];
if (DECODE64(digit1) == BAD)
return(-1);
digit2 = in[1];
if (DECODE64(digit2) == BAD)
return(-1);
digit3 = in[2];
if (digit3 != '=' && DECODE64(digit3) == BAD)
return(-1);
digit4 = in[3];
if (digit4 != '=' && DECODE64(digit4) == BAD)
return(-1);
in += 4;
++len;
if (maxlen && len > maxlen)
return(-1);
*out++ = (DECODE64(digit1) << 2) | (DECODE64(digit2) >> 4);
if (digit3 != '=')
{
++len;
if (maxlen && len > maxlen)
return(-1);
*out++ = ((DECODE64(digit2) << 4) & 0xf0) | (DECODE64(digit3) >> 2);
if (digit4 != '=')
{
++len;
if (maxlen && len > maxlen)
return(-1);
*out++ = ((DECODE64(digit3) << 6) & 0xc0) | DECODE64(digit4);
}
}
} while
(*in && *in != '\r' && digit4 != '=');
return len;
}
#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
#include "sdump.h"
const char *program_name = "base64(test)";
int main(int argc, char **argv) {
int rc = EXIT_SUCCESS;
if (argc <= 1) {
fprintf(stderr, "Usage: %s string [...]\n", argv[0]);
exit(EXIT_FAILURE);
}
for (int i = 1 ; i < argc ; i++) {
char *x;
char *tmp;
int res;
int len;
printf("-- TESTING %s --\n", (tmp = sdump_c(argv[i])));
xfree(tmp);
x = xstrdup(argv[i]);
res = from64tobits(x, x, 0); // does not NUL terminate!
if (res >= 0) x[res] = '\0';
// do not set rc to failure here - input might be anything, that's not a malfunction
printf("from64tobits = %d, \"%s\"\n", res, res >= 0 ? (tmp = sdump_c(x)) : "(invalid input)");
xfree(tmp);
xfree(x);
len = strlen(argv[i]);
int alloclen = len64frombits(len) + 1 /* room for '\0' */;
printf("input len %d, allocating %d...\n", len, alloclen);
x = (char *)xmalloc(alloclen);
res = to64frombits(x, argv[i], len, alloclen);
if (res < 0) rc = EXIT_FAILURE;
printf("to64frombits = %d, \"%s\"\n", res, res >= 0 ? x : "(error)");
res = from64tobits(x, x, 0);
if (res >= 0) x[res] = '\0'; else rc = EXIT_FAILURE;
printf("from64tobits = %d, \"%s\"\n", res, res >= 0 ? (tmp = sdump_c(x)) : "(invalid encoding)");
xfree(tmp);
res = strcmp(x, argv[i]);
printf("strcmp(orig, decode(encode(orig))) = %d\n", res);
if (res) rc = EXIT_FAILURE;
xfree(x);
}
if (rc) puts("\nFAILED");
else puts("\nsuccess");
return rc;
}
#endif
/* base64.c ends here */
|