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