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