]> diplodocus.org Git - nmh/blob - sbr/base64.c
uip/sortm.c: Remove unused variable and dlocaltimenow() call.
[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 } else {
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
138 for (bp = out + 4; bp > out; bits >>= 6)
139 *--bp = nib2b64[bits & 0x3f];
140 if (cc < 3) {
141 out[3] = '=';
142 if (cc < 2)
143 out[2] = '=';
144 out += 4;
145 n = 0;
146 break;
147 }
148
149 in += 3;
150 out += 4;
151 if (--n <= 0) {
152 n = BPERLIN;
153 *out++ = '\n';
154 }
155 }
156 if (n != BPERLIN)
157 *out++ = '\n';
158
159 *out = '\0';
160
161 return OK;
162 }
163
164 /*
165 * Essentially a duplicate of writeBase64, but without line wrapping or
166 * newline termination (note: string IS NUL terminated)
167 */
168
169 int
170 writeBase64raw (const unsigned char *in, size_t length, unsigned char *out)
171 {
172 while (1) {
173 unsigned long bits;
174 unsigned char *bp;
175 unsigned int cc;
176 for (cc = 0; length > 0 && cc < 3; ++cc, --length)
177 /* empty */ ;
178
179 if (cc == 0) {
180 break;
181 } else {
182 bits = (in[0] & 0xff) << 16;
183 if (cc > 1) {
184 bits |= (in[1] & 0xff) << 8;
185 if (cc > 2) {
186 bits |= in[2] & 0xff;
187 }
188 }
189 }
190
191 for (bp = out + 4; bp > out; bits >>= 6)
192 *--bp = nib2b64[bits & 0x3f];
193 if (cc < 3) {
194 out[3] = '=';
195 if (cc < 2)
196 out[2] = '=';
197 out += 4;
198 break;
199 }
200
201 in += 3;
202 out += 4;
203 }
204
205 *out = '\0';
206
207 return OK;
208 }
209
210
211 static const unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
216 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
217 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
218 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
219 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
220 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
221 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
222 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
223 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
224 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
225 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
226 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
227 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
228 };
229
230 /*
231 * Decode a base64 string. The result, decoded, must be freed by the caller.
232 * See description of arguments with declaration in h/prototypes.h.
233 */
234 int
235 decodeBase64 (const char *encoded, unsigned char **decoded, size_t *len,
236 int skip_crs, unsigned char *digest) {
237 const char *cp = encoded;
238 int self_delimiting = 0;
239 int bitno, skip;
240 uint32_t bits;
241 /* Size the decoded string very conservatively. */
242 charstring_t decoded_c = charstring_create (strlen (encoded));
243 MD5_CTX mdContext;
244
245 if (digest)
246 MD5Init (&mdContext);
247
248 bitno = 18;
249 bits = 0L;
250 skip = 0;
251
252 for (; *cp; ++cp) {
253 switch (*cp) {
254 unsigned char value;
255
256 default:
257 if (isspace ((unsigned char) *cp)) {
258 break;
259 }
260 if (skip || (((unsigned char) *cp) & 0x80) ||
261 (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) {
262 inform("invalid BASE64 encoding in %s", cp);
263 charstring_free (decoded_c);
264 *decoded = NULL;
265
266 return NOTOK;
267 }
268
269 bits |= value << bitno;
270 test_end:
271 if ((bitno -= 6) < 0) {
272 char b = (char) ((bits >> 16) & 0xff);
273
274 if (! skip_crs || b != '\r') {
275 charstring_push_back (decoded_c, b);
276 }
277 if (digest)
278 MD5Update (&mdContext, (unsigned char *) &b, 1);
279 if (skip < 2) {
280 b = (bits >> 8) & 0xff;
281 if (! skip_crs || b != '\r') {
282 charstring_push_back (decoded_c, b);
283 }
284 if (digest)
285 MD5Update (&mdContext, (unsigned char *) &b, 1);
286 if (skip < 1) {
287 b = bits & 0xff;
288 if (! skip_crs || b != '\r') {
289 charstring_push_back (decoded_c, b);
290 }
291 if (digest)
292 MD5Update (&mdContext, (unsigned char *) &b, 1);
293 }
294 }
295
296 bitno = 18;
297 bits = 0L;
298 skip = 0;
299 }
300 break;
301
302 case '=':
303 if (++skip > 3) {
304 self_delimiting = 1;
305 break;
306 } else {
307 goto test_end;
308 }
309 }
310 }
311
312 if (! self_delimiting && bitno != 18) {
313 int i;
314
315 /* Show some context for the error. */
316 for (i = 0; i < 20 && cp > encoded; ++i, --cp) {}
317 inform("premature ending (bitno %d) near %s", bitno,
318 cp ? cp : encoded);
319 charstring_free (decoded_c);
320 *decoded = NULL;
321
322 return NOTOK;
323 }
324
325 *decoded = (unsigned char *) charstring_buffer_copy (decoded_c);
326 *len = charstring_bytes (decoded_c);
327 charstring_free (decoded_c);
328
329 if (digest) {
330 MD5Final (digest, &mdContext);
331 }
332
333 return OK;
334 }
335
336
337 /*
338 * Prepare an unsigned char array for display by replacing non-printable
339 * ASCII bytes with their hex representation. Assumes ASCII input. output
340 * is allocated by the function and must be freed by the caller.
341 */
342 void
343 hexify (const unsigned char *input, size_t len, char **output) {
344 /* Start with a charstring capacity that's arbitrarily larger than len. */
345 const charstring_t tmp = charstring_create (2 * len);
346 const unsigned char *cp = input;
347 size_t i;
348
349 for (i = 0; i < len; ++i, ++cp) {
350 if (isascii(*cp) && isprint(*cp)) {
351 charstring_push_back (tmp, (const char) *cp);
352 } else {
353 char s[16];
354 const int num = snprintf(s, sizeof s, "[0x%02x]", *cp);
355
356 if (num <= 0 || (unsigned int) num >= sizeof s) {
357 inform("hexify failed to write nonprintable character, needed %d bytes", num + 1);
358 } else {
359 charstring_append_cstring (tmp, s);
360 }
361 }
362 }
363
364 *output = charstring_buffer_copy (tmp);
365 charstring_free (tmp);
366 }