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