File: base64.c

package info (click to toggle)
fetchmail 6.5.6-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 7,596 kB
  • sloc: ansic: 19,190; sh: 7,108; python: 2,395; perl: 564; yacc: 447; lex: 286; makefile: 260; awk: 124; lisp: 84; exp: 43; sed: 17
file content (190 lines) | stat: -rw-r--r-- 5,641 bytes parent folder | download
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 */