]> diplodocus.org Git - nmh/blob - sbr/base64.c
mhbuildsbr.c: Flip logic, moving goto to then-block; no need for else.
[nmh] / sbr / base64.c
1 /* base64.c -- routines for converting to base64
2 *
3 * This code is Copyright (c) 2012, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <h/mh.h>
9 #include <h/mime.h>
10 #include <h/md5.h>
11 #include <inttypes.h>
12
13 static const char nib2b64[0x40+1] =
14 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
15
16 int
17 writeBase64aux (FILE *in, FILE *out, int crlf)
18 {
19 unsigned int cc, n;
20 unsigned char inbuf[3];
21 int skipnl = 0;
22
23 n = BPERLIN;
24 while ((cc = fread (inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
25 unsigned long bits;
26 char *bp;
27 char outbuf[4];
28
29 if (cc < sizeof(inbuf)) {
30 inbuf[2] = 0;
31 if (cc < sizeof(inbuf) - 1)
32 inbuf[1] = 0;
33 }
34
35 /*
36 * Convert a LF to a CRLF if desired. That means we need to push
37 * data back into the input stream.
38 */
39
40 if (crlf) {
41 unsigned int i;
42
43 for (i = 0; i < cc; i++) {
44 if (inbuf[i] == '\n' && !skipnl) {
45 inbuf[i] = '\r';
46 /*
47 * If it's the last character in the buffer, we can just
48 * substitute a \r and push a \n back. Otherwise shuffle
49 * everything down and push the last character back.
50 */
51 if (i == cc - 1) {
52 /*
53 * If we're at the end of the input, there might be
54 * more room in inbuf; if so, add it there. Otherwise
55 * push it back to the input.
56 */
57 if (cc < sizeof(inbuf))
58 inbuf[cc++] = '\n';
59 else
60 ungetc('\n', in);
61 skipnl = 1;
62 } else {
63 /* This only works as long as sizeof(inbuf) == 3 */
64 ungetc(inbuf[cc - 1], in);
65 if (cc == 3 && i == 0)
66 inbuf[2] = inbuf[1];
67 inbuf[++i] = '\n';
68 }
69 } else {
70 skipnl = 0;
71 }
72 }
73 }
74
75 bits = (inbuf[0] & 0xff) << 16;
76 bits |= (inbuf[1] & 0xff) << 8;
77 bits |= inbuf[2] & 0xff;
78
79 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
80 *--bp = nib2b64[bits & 0x3f];
81 if (cc < sizeof(inbuf)) {
82 outbuf[3] = '=';
83 if (cc < sizeof inbuf - 1)
84 outbuf[2] = '=';
85 }
86
87 if (fwrite (outbuf, sizeof(*outbuf), sizeof(outbuf), out) <
88 sizeof outbuf) {
89 advise ("writeBase64aux", "fwrite");
90 }
91
92 if (cc < sizeof(inbuf)) {
93 putc ('\n', out);
94 return OK;
95 }
96
97 if (--n <= 0) {
98 n = BPERLIN;
99 putc ('\n', out);
100 }
101 }
102 if (n != BPERLIN)
103 putc ('\n', out);
104
105 return OK;
106 }
107
108
109 /* Caller is responsible for ensuring that the out array is long
110 enough. Given length is that of in, out should be have at
111 least this capacity:
112 4 * [length/3] + length/57 + 2
113 But double the length will certainly be sufficient. */
114 int
115 writeBase64 (const unsigned char *in, size_t length, unsigned char *out)
116 {
117 unsigned int n = BPERLIN;
118
119 while (1) {
120 unsigned long bits;
121 unsigned char *bp;
122 unsigned int cc;
123 for (cc = 0; length > 0 && cc < 3; ++cc, --length)
124 /* empty */ ;
125
126 if (cc == 0)
127 break;
128
129 bits = (in[0] & 0xff) << 16;
130 if (cc > 1) {
131 bits |= (in[1] & 0xff) << 8;
132 if (cc > 2) {
133 bits |= in[2] & 0xff;
134 }
135 }
136
137 for (bp = out + 4; bp > out; bits >>= 6)
138 *--bp = nib2b64[bits & 0x3f];
139 if (cc < 3) {
140 out[3] = '=';
141 if (cc < 2)
142 out[2] = '=';
143 out += 4;
144 n = 0;
145 break;
146 }
147
148 in += 3;
149 out += 4;
150 if (--n <= 0) {
151 n = BPERLIN;
152 *out++ = '\n';
153 }
154 }
155 if (n != BPERLIN)
156 *out++ = '\n';
157
158 *out = '\0';
159
160 return OK;
161 }
162
163 /*
164 * Essentially a duplicate of writeBase64, but without line wrapping or
165 * newline termination (note: string IS NUL terminated)
166 */
167
168 int
169 writeBase64raw (const unsigned char *in, size_t length, unsigned char *out)
170 {
171 while (1) {
172 unsigned long bits;
173 unsigned char *bp;
174 unsigned int cc;
175 for (cc = 0; length > 0 && cc < 3; ++cc, --length)
176 /* empty */ ;
177
178 if (cc == 0)
179 break;
180
181 bits = (in[0] & 0xff) << 16;
182 if (cc > 1) {
183 bits |= (in[1] & 0xff) << 8;
184 if (cc > 2) {
185 bits |= in[2] & 0xff;
186 }
187 }
188
189 for (bp = out + 4; bp > out; bits >>= 6)
190 *--bp = nib2b64[bits & 0x3f];
191 if (cc < 3) {
192 out[3] = '=';
193 if (cc < 2)
194 out[2] = '=';
195 out += 4;
196 break;
197 }
198
199 in += 3;
200 out += 4;
201 }
202
203 *out = '\0';
204
205 return OK;
206 }
207
208
209 static const unsigned char b642nib[0x80] = {
210 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
211 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
212 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
213 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
214 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
215 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
216 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
217 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
218 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
219 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
220 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
221 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
222 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
223 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
224 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
225 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
226 };
227
228 /*
229 * Decode a base64 string. The result, decoded, must be freed by the caller.
230 * See description of arguments with declaration in h/prototypes.h.
231 */
232 int
233 decodeBase64 (const char *encoded, unsigned char **decoded, size_t *len,
234 int skip_crs, unsigned char *digest) {
235 const char *cp = encoded;
236 int self_delimiting = 0;
237 int bitno, skip;
238 uint32_t bits;
239 /* Size the decoded string very conservatively. */
240 charstring_t decoded_c = charstring_create (strlen (encoded));
241 MD5_CTX mdContext;
242
243 if (digest)
244 MD5Init (&mdContext);
245
246 bitno = 18;
247 bits = 0L;
248 skip = 0;
249
250 for (; *cp; ++cp) {
251 switch (*cp) {
252 unsigned char value;
253
254 default:
255 if (isspace ((unsigned char) *cp)) {
256 break;
257 }
258 if (skip || (((unsigned char) *cp) & 0x80) ||
259 (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) {
260 inform("invalid BASE64 encoding in %s", cp);
261 charstring_free (decoded_c);
262 *decoded = NULL;
263
264 return NOTOK;
265 }
266
267 bits |= value << bitno;
268 test_end:
269 if ((bitno -= 6) < 0) {
270 char b = (char) ((bits >> 16) & 0xff);
271
272 if (! skip_crs || b != '\r') {
273 charstring_push_back (decoded_c, b);
274 }
275 if (digest)
276 MD5Update (&mdContext, (unsigned char *) &b, 1);
277 if (skip < 2) {
278 b = (bits >> 8) & 0xff;
279 if (! skip_crs || b != '\r') {
280 charstring_push_back (decoded_c, b);
281 }
282 if (digest)
283 MD5Update (&mdContext, (unsigned char *) &b, 1);
284 if (skip < 1) {
285 b = bits & 0xff;
286 if (! skip_crs || b != '\r') {
287 charstring_push_back (decoded_c, b);
288 }
289 if (digest)
290 MD5Update (&mdContext, (unsigned char *) &b, 1);
291 }
292 }
293
294 bitno = 18;
295 bits = 0L;
296 skip = 0;
297 }
298 break;
299
300 case '=':
301 if (++skip <= 3)
302 goto test_end;
303 self_delimiting = 1;
304 break;
305 }
306 }
307
308 if (! self_delimiting && bitno != 18) {
309 int i;
310
311 /* Show some context for the error. */
312 for (i = 0; i < 20 && cp > encoded; ++i, --cp) {}
313 inform("premature ending (bitno %d) near %s", bitno,
314 cp ? cp : encoded);
315 charstring_free (decoded_c);
316 *decoded = NULL;
317
318 return NOTOK;
319 }
320
321 *decoded = (unsigned char *) charstring_buffer_copy (decoded_c);
322 *len = charstring_bytes (decoded_c);
323 charstring_free (decoded_c);
324
325 if (digest) {
326 MD5Final (digest, &mdContext);
327 }
328
329 return OK;
330 }
331
332
333 /*
334 * Prepare an unsigned char array for display by replacing non-printable
335 * ASCII bytes with their hex representation. Assumes ASCII input. output
336 * is allocated by the function and must be freed by the caller.
337 */
338 void
339 hexify (const unsigned char *input, size_t len, char **output) {
340 /* Start with a charstring capacity that's arbitrarily larger than len. */
341 const charstring_t tmp = charstring_create (2 * len);
342 const unsigned char *cp = input;
343 size_t i;
344
345 for (i = 0; i < len; ++i, ++cp) {
346 if (isascii(*cp) && isprint(*cp)) {
347 charstring_push_back (tmp, (const char) *cp);
348 } else {
349 char s[16];
350 const int num = snprintf(s, sizeof s, "[0x%02x]", *cp);
351
352 if (num <= 0 || (unsigned int) num >= sizeof s) {
353 inform("hexify failed to write nonprintable character, needed %d bytes", num + 1);
354 } else {
355 charstring_append_cstring (tmp, s);
356 }
357 }
358 }
359
360 *output = charstring_buffer_copy (tmp);
361 charstring_free (tmp);
362 }