1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2014 Jeffrey Stedfast and Michael Zucchi
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20 
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29 
30 #include "gmime-table-private.h"
31 #include "gmime-encodings.h"
32 
33 
34 #ifdef ENABLE_WARNINGS
35 #define w(x) x
36 #else
37 #define w(x)
38 #endif /* ENABLE_WARNINGS */
39 
40 #define d(x)
41 
42 
43 /**
44  * SECTION: gmime-encodings
45  * @title: gmime-encodings
46  * @short_description: MIME encoding functions
47  * @see_also:
48  *
49  * Utility functions to encode or decode MIME
50  * Content-Transfer-Encodings.
51  **/
52 
53 
54 #define GMIME_UUENCODE_CHAR(c) ((c) ? (c) + ' ' : '`')
55 #define	GMIME_UUDECODE_CHAR(c) (((c) - ' ') & 077)
56 
57 static char base64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
58 
59 static unsigned char gmime_base64_rank[256] = {
60 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
61 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
62 	255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
63 	 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
64 	255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
65 	 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
66 	255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
67 	 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
68 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
69 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
70 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
71 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
72 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
73 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
74 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
75 	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
76 };
77 
78 static unsigned char gmime_uu_rank[256] = {
79 	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
80 	 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
81 	  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
82 	 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
83 	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
84 	 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
85 	  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
86 	 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
87 	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
88 	 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
89 	  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
90 	 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
91 	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
92 	 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
93 	  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
94 	 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
95 };
96 
97 static unsigned char tohex[16] = {
98 	'0', '1', '2', '3', '4', '5', '6', '7',
99 	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
100 };
101 
102 
103 /**
104  * g_mime_content_encoding_from_string:
105  * @str: a string representing a Content-Transfer-Encoding value
106  *
107  * Gets the appropriate #GMimeContentEncoding enumeration value based
108  * on the input string.
109  *
110  * Returns: the #GMimeContentEncoding specified by @str or
111  * #GMIME_CONTENT_ENCODING_DEFAULT on error.
112  **/
113 GMimeContentEncoding
g_mime_content_encoding_from_string(const char * str)114 g_mime_content_encoding_from_string (const char *str)
115 {
116 	if (!g_ascii_strcasecmp (str, "7bit"))
117 		return GMIME_CONTENT_ENCODING_7BIT;
118 	else if (!g_ascii_strcasecmp (str, "8bit"))
119 		return GMIME_CONTENT_ENCODING_8BIT;
120 	else if (!g_ascii_strcasecmp (str, "7-bit"))
121 		return GMIME_CONTENT_ENCODING_7BIT;
122 	else if (!g_ascii_strcasecmp (str, "8-bit"))
123 		return GMIME_CONTENT_ENCODING_8BIT;
124 	else if (!g_ascii_strcasecmp (str, "binary"))
125 		return GMIME_CONTENT_ENCODING_BINARY;
126 	else if (!g_ascii_strcasecmp (str, "base64"))
127 		return GMIME_CONTENT_ENCODING_BASE64;
128 	else if (!g_ascii_strcasecmp (str, "quoted-printable"))
129 		return GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE;
130 	else if (!g_ascii_strcasecmp (str, "uuencode"))
131 		return GMIME_CONTENT_ENCODING_UUENCODE;
132 	else if (!g_ascii_strcasecmp (str, "x-uuencode"))
133 		return GMIME_CONTENT_ENCODING_UUENCODE;
134 	else if (!g_ascii_strcasecmp (str, "x-uue"))
135 		return GMIME_CONTENT_ENCODING_UUENCODE;
136 	else
137 		return GMIME_CONTENT_ENCODING_DEFAULT;
138 }
139 
140 
141 /**
142  * g_mime_content_encoding_to_string:
143  * @encoding: a #GMimeContentEncoding
144  *
145  * Gets the string value of the content encoding.
146  *
147  * Returns: the encoding type as a string or %NULL on error. Available
148  * values for the encoding are: #GMIME_CONTENT_ENCODING_DEFAULT,
149  * #GMIME_CONTENT_ENCODING_7BIT, #GMIME_CONTENT_ENCODING_8BIT,
150  * #GMIME_CONTENT_ENCODING_BINARY, #GMIME_CONTENT_ENCODING_BASE64,
151  * #GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE and
152  * #GMIME_CONTENT_ENCODING_UUENCODE.
153  **/
154 const char *
g_mime_content_encoding_to_string(GMimeContentEncoding encoding)155 g_mime_content_encoding_to_string (GMimeContentEncoding encoding)
156 {
157 	switch (encoding) {
158         case GMIME_CONTENT_ENCODING_7BIT:
159 		return "7bit";
160         case GMIME_CONTENT_ENCODING_8BIT:
161 		return "8bit";
162 	case GMIME_CONTENT_ENCODING_BINARY:
163 		return "binary";
164         case GMIME_CONTENT_ENCODING_BASE64:
165 		return "base64";
166         case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
167 		return "quoted-printable";
168 	case GMIME_CONTENT_ENCODING_UUENCODE:
169 		return "x-uuencode";
170 	default:
171 		/* I guess this is a good default... */
172 		return NULL;
173 	}
174 }
175 
176 
177 /**
178  * g_mime_encoding_init_encode:
179  * @state: a #GMimeEncoding to initialize
180  * @encoding: a #GMimeContentEncoding to use
181  *
182  * Initializes a #GMimeEncoding state machine for encoding to
183  * @encoding.
184  **/
185 void
g_mime_encoding_init_encode(GMimeEncoding * state,GMimeContentEncoding encoding)186 g_mime_encoding_init_encode (GMimeEncoding *state, GMimeContentEncoding encoding)
187 {
188 	state->encoding = encoding;
189 	state->encode = TRUE;
190 
191 	g_mime_encoding_reset (state);
192 }
193 
194 
195 /**
196  * g_mime_encoding_init_decode:
197  * @state: a #GMimeEncoding to initialize
198  * @encoding: a #GMimeContentEncoding to use
199  *
200  * Initializes a #GMimeEncoding state machine for decoding from
201  * @encoding.
202  **/
203 void
g_mime_encoding_init_decode(GMimeEncoding * state,GMimeContentEncoding encoding)204 g_mime_encoding_init_decode (GMimeEncoding *state, GMimeContentEncoding encoding)
205 {
206 	state->encoding = encoding;
207 	state->encode = FALSE;
208 
209 	g_mime_encoding_reset (state);
210 }
211 
212 
213 /**
214  * g_mime_encoding_reset:
215  * @state: a #GMimeEncoding to reset
216  *
217  * Resets the state of the #GMimeEncoding.
218  **/
219 void
g_mime_encoding_reset(GMimeEncoding * state)220 g_mime_encoding_reset (GMimeEncoding *state)
221 {
222 	if (state->encode) {
223 		if (state->encoding == GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE)
224 			state->state = -1;
225 		else
226 			state->state = 0;
227 	} else {
228 		state->state = 0;
229 	}
230 
231 	state->save = 0;
232 }
233 
234 
235 /**
236  * g_mime_encoding_outlen:
237  * @state: a #GMimeEncoding
238  * @inlen: an input length
239  *
240  * Given the input length, @inlen, calculate the needed output length
241  * to perform an encoding or decoding step.
242  *
243  * Returns: the maximum number of bytes needed to encode or decode a
244  * buffer of @inlen bytes.
245  **/
246 size_t
g_mime_encoding_outlen(GMimeEncoding * state,size_t inlen)247 g_mime_encoding_outlen (GMimeEncoding *state, size_t inlen)
248 {
249 	switch (state->encoding) {
250 	case GMIME_CONTENT_ENCODING_BASE64:
251 		if (state->encode)
252 			return GMIME_BASE64_ENCODE_LEN (inlen);
253 		else
254 			return inlen + 3;
255 	case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
256 		if (state->encode)
257 			return GMIME_QP_ENCODE_LEN (inlen);
258 		else
259 			return inlen + 2;
260 	case GMIME_CONTENT_ENCODING_UUENCODE:
261 		if (state->encode)
262 			return GMIME_UUENCODE_LEN (inlen);
263 		else
264 			return inlen + 3;
265 	default:
266 		return inlen;
267 	}
268 }
269 
270 
271 /**
272  * g_mime_encoding_step:
273  * @state: a #GMimeEncoding
274  * @inbuf: an input buffer to encode or decode
275  * @inlen: input buffer length
276  * @outbuf: an output buffer
277  *
278  * Incrementally encodes or decodes (depending on @state) an input
279  * stream by 'stepping' through a block of input at a time.
280  *
281  * You should make sure @outbuf is large enough by calling
282  * g_mime_encoding_outlen() to find out how large @outbuf might need
283  * to be.
284  *
285  * Returns: the number of bytes written to @outbuf.
286  **/
287 size_t
g_mime_encoding_step(GMimeEncoding * state,const char * inbuf,size_t inlen,char * outbuf)288 g_mime_encoding_step (GMimeEncoding *state, const char *inbuf, size_t inlen, char *outbuf)
289 {
290 	const unsigned char *inptr = (const unsigned char *) inbuf;
291 	unsigned char *outptr = (unsigned char *) outbuf;
292 
293 	switch (state->encoding) {
294 	case GMIME_CONTENT_ENCODING_BASE64:
295 		if (state->encode)
296 			return g_mime_encoding_base64_encode_step (inptr, inlen, outptr, &state->state, &state->save);
297 		else
298 			return g_mime_encoding_base64_decode_step (inptr, inlen, outptr, &state->state, &state->save);
299 	case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
300 		if (state->encode)
301 			return g_mime_encoding_quoted_encode_step (inptr, inlen, outptr, &state->state, &state->save);
302 		else
303 			return g_mime_encoding_quoted_decode_step (inptr, inlen, outptr, &state->state, &state->save);
304 	case GMIME_CONTENT_ENCODING_UUENCODE:
305 		if (state->encode)
306 			return g_mime_encoding_uuencode_step (inptr, inlen, outptr, state->uubuf, &state->state, &state->save);
307 		else
308 			return g_mime_encoding_uudecode_step (inptr, inlen, outptr, &state->state, &state->save);
309 	default:
310 		memcpy (outbuf, inbuf, inlen);
311 		return inlen;
312 	}
313 }
314 
315 
316 /**
317  * g_mime_encoding_flush:
318  * @state: a #GMimeEncoding
319  * @inbuf: an input buffer to encode or decode
320  * @inlen: input buffer length
321  * @outbuf: an output buffer
322  *
323  * Completes the incremental encode or decode of the input stream (see
324  * g_mime_encoding_step() for details).
325  *
326  * Returns: the number of bytes written to @outbuf.
327  **/
328 size_t
g_mime_encoding_flush(GMimeEncoding * state,const char * inbuf,size_t inlen,char * outbuf)329 g_mime_encoding_flush (GMimeEncoding *state, const char *inbuf, size_t inlen, char *outbuf)
330 {
331 	const unsigned char *inptr = (const unsigned char *) inbuf;
332 	unsigned char *outptr = (unsigned char *) outbuf;
333 
334 	switch (state->encoding) {
335 	case GMIME_CONTENT_ENCODING_BASE64:
336 		if (state->encode)
337 			return g_mime_encoding_base64_encode_close (inptr, inlen, outptr, &state->state, &state->save);
338 		else
339 			return g_mime_encoding_base64_decode_step (inptr, inlen, outptr, &state->state, &state->save);
340 	case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
341 		if (state->encode)
342 			return g_mime_encoding_quoted_encode_close (inptr, inlen, outptr, &state->state, &state->save);
343 		else
344 			return g_mime_encoding_quoted_decode_step (inptr, inlen, outptr, &state->state, &state->save);
345 	case GMIME_CONTENT_ENCODING_UUENCODE:
346 		if (state->encode)
347 			return g_mime_encoding_uuencode_close (inptr, inlen, outptr, state->uubuf, &state->state, &state->save);
348 		else
349 			return g_mime_encoding_uudecode_step (inptr, inlen, outptr, &state->state, &state->save);
350 	default:
351 		memcpy (outbuf, inbuf, inlen);
352 		return inlen;
353 	}
354 }
355 
356 
357 /**
358  * g_mime_encoding_base64_encode_close:
359  * @inbuf: input buffer
360  * @inlen: input buffer length
361  * @outbuf: output buffer
362  * @state: holds the number of bits that are stored in @save
363  * @save: leftover bits that have not yet been encoded
364  *
365  * Base64 encodes the input stream to the output stream. Call this
366  * when finished encoding data with g_mime_encoding_base64_encode_step()
367  * to flush off the last little bit.
368  *
369  * Returns: the number of bytes encoded.
370  **/
371 size_t
g_mime_encoding_base64_encode_close(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)372 g_mime_encoding_base64_encode_close (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
373 {
374 	unsigned char *outptr = outbuf;
375 	int c1, c2;
376 
377 	if (inlen > 0)
378 		outptr += g_mime_encoding_base64_encode_step (inbuf, inlen, outptr, state, save);
379 
380 	c1 = ((unsigned char *)save)[1];
381 	c2 = ((unsigned char *)save)[2];
382 
383 	switch (((unsigned char *)save)[0]) {
384 	case 2:
385 		outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
386 		goto skip;
387 	case 1:
388 		outptr[2] = '=';
389 	skip:
390 		outptr[0] = base64_alphabet [c1 >> 2];
391 		outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
392 		outptr[3] = '=';
393 		outptr += 4;
394 		break;
395 	}
396 
397 	*outptr++ = '\n';
398 
399 	*save = 0;
400 	*state = 0;
401 
402 	return (outptr - outbuf);
403 }
404 
405 
406 /**
407  * g_mime_encoding_base64_encode_step:
408  * @inbuf: input buffer
409  * @inlen: input buffer length
410  * @outbuf: output buffer
411  * @state: holds the number of bits that are stored in @save
412  * @save: leftover bits that have not yet been encoded
413  *
414  * Base64 encodes a chunk of data. Performs an 'encode step', only
415  * encodes blocks of 3 characters to the output at a time, saves
416  * left-over state in state and save (initialise to 0 on first
417  * invocation).
418  *
419  * Returns: the number of bytes encoded.
420  **/
421 size_t
g_mime_encoding_base64_encode_step(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)422 g_mime_encoding_base64_encode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
423 {
424 	register const unsigned char *inptr;
425 	register unsigned char *outptr;
426 
427 	if (inlen == 0)
428 		return 0;
429 
430 	outptr = outbuf;
431 	inptr = inbuf;
432 
433 	if (inlen + ((unsigned char *)save)[0] > 2) {
434 		const unsigned char *inend = inbuf + inlen - 2;
435 		register int c1 = 0, c2 = 0, c3 = 0;
436 		register int already;
437 
438 		already = *state;
439 
440 		switch (((char *)save)[0]) {
441 		case 1:	c1 = ((unsigned char *)save)[1]; goto skip1;
442 		case 2:	c1 = ((unsigned char *)save)[1];
443 			c2 = ((unsigned char *)save)[2]; goto skip2;
444 		}
445 
446 		/* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
447 		while (inptr < inend) {
448 			c1 = *inptr++;
449 		skip1:
450 			c2 = *inptr++;
451 		skip2:
452 			c3 = *inptr++;
453 			*outptr++ = base64_alphabet [c1 >> 2];
454 			*outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
455 			*outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
456 			*outptr++ = base64_alphabet [c3 & 0x3f];
457 			/* this is a bit ugly ... */
458 			if ((++already) >= 19) {
459 				*outptr++ = '\n';
460 				already = 0;
461 			}
462 		}
463 
464 		((unsigned char *)save)[0] = 0;
465 		inlen = 2 - (inptr - inend);
466 		*state = already;
467 	}
468 
469 	d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
470 
471 	if (inlen > 0) {
472 		register char *saveout;
473 
474 		/* points to the slot for the next char to save */
475 		saveout = & (((char *)save)[1]) + ((char *)save)[0];
476 
477 		/* inlen can only be 0, 1 or 2 */
478 		switch (inlen) {
479 		case 2:	*saveout++ = *inptr++;
480 		case 1:	*saveout++ = *inptr++;
481 		}
482 		((char *)save)[0] += (char) inlen;
483 	}
484 
485 	d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
486 		  (int)((char *)save)[0],
487 		  (int)((char *)save)[1],
488 		  (int)((char *)save)[2]));
489 
490 	return (outptr - outbuf);
491 }
492 
493 
494 /**
495  * g_mime_encoding_base64_decode_step:
496  * @inbuf: input buffer
497  * @inlen: input buffer length
498  * @outbuf: output buffer
499  * @state: holds the number of bits that are stored in @save
500  * @save: leftover bits that have not yet been decoded
501  *
502  * Decodes a chunk of base64 encoded data.
503  *
504  * Returns: the number of bytes decoded (which have been dumped in
505  * @outbuf).
506  **/
507 size_t
g_mime_encoding_base64_decode_step(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)508 g_mime_encoding_base64_decode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
509 {
510 	register const unsigned char *inptr;
511 	register unsigned char *outptr;
512 	const unsigned char *inend;
513 	register guint32 saved;
514 	unsigned char c;
515 	int npad, n, i;
516 
517 	inend = inbuf + inlen;
518 	outptr = outbuf;
519 	inptr = inbuf;
520 
521 	npad = (*state >> 8) & 0xff;
522 	n = *state & 0xff;
523 	saved = *save;
524 
525 	/* convert 4 base64 bytes to 3 normal bytes */
526 	while (inptr < inend) {
527 		c = gmime_base64_rank[*inptr++];
528 		if (c != 0xff) {
529 			saved = (saved << 6) | c;
530 			n++;
531 			if (n == 4) {
532 				*outptr++ = saved >> 16;
533 				*outptr++ = saved >> 8;
534 				*outptr++ = saved;
535 				n = 0;
536 
537 				if (npad > 0) {
538 					outptr -= npad;
539 					npad = 0;
540 				}
541 			}
542 		}
543 	}
544 
545 	/* quickly scan back for '=' on the end somewhere */
546 	/* fortunately we can drop 1 output char for each trailing '=' (up to 2) */
547 	for (i = 2; inptr > inbuf && i; ) {
548 		inptr--;
549 		if (gmime_base64_rank[*inptr] != 0xff) {
550 			if (*inptr == '=' && outptr > outbuf) {
551 				if (n == 0) {
552 					/* we've got a complete quartet so it's
553 					   safe to drop an output character. */
554 					outptr--;
555 				} else if (npad < 2) {
556 					/* keep a record of the number of ='s at
557 					   the end of the input stream, up to 2 */
558 					npad++;
559 				}
560 			}
561 
562 			i--;
563 		}
564 	}
565 
566 	*state = (npad << 8) | n;
567 	*save = n ? saved : 0;
568 
569 	return (outptr - outbuf);
570 }
571 
572 
573 /**
574  * g_mime_encoding_uuencode_close:
575  * @inbuf: input buffer
576  * @inlen: input buffer length
577  * @outbuf: output buffer
578  * @uubuf: temporary buffer of 60 bytes
579  * @state: holds the number of bits that are stored in @save
580  * @save: leftover bits that have not yet been encoded
581  *
582  * Uuencodes a chunk of data. Call this when finished encoding data
583  * with g_mime_encoding_uuencode_step() to flush off the last little bit.
584  *
585  * Returns: the number of bytes encoded.
586  **/
587 size_t
g_mime_encoding_uuencode_close(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,unsigned char * uubuf,int * state,guint32 * save)588 g_mime_encoding_uuencode_close (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, unsigned char *uubuf, int *state, guint32 *save)
589 {
590 	register unsigned char *outptr, *bufptr;
591 	register guint32 saved;
592 	int uulen, uufill, i;
593 
594 	outptr = outbuf;
595 
596 	if (inlen > 0)
597 		outptr += g_mime_encoding_uuencode_step (inbuf, inlen, outbuf, uubuf, state, save);
598 
599 	uufill = 0;
600 
601 	saved = *save;
602 	i = *state & 0xff;
603 	uulen = (*state >> 8) & 0xff;
604 
605 	bufptr = uubuf + ((uulen / 3) * 4);
606 
607 	if (i > 0) {
608 		while (i < 3) {
609 			saved <<= 8;
610 			uufill++;
611 			i++;
612 		}
613 
614 		if (i == 3) {
615 			/* convert 3 normal bytes into 4 uuencoded bytes */
616 			unsigned char b0, b1, b2;
617 
618 			b0 = (saved >> 16) & 0xff;
619 			b1 = (saved >> 8) & 0xff;
620 			b2 = saved & 0xff;
621 
622 			*bufptr++ = GMIME_UUENCODE_CHAR ((b0 >> 2) & 0x3f);
623 			*bufptr++ = GMIME_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f);
624 			*bufptr++ = GMIME_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f);
625 			*bufptr++ = GMIME_UUENCODE_CHAR (b2 & 0x3f);
626 
627 			uulen += 3;
628 			saved = 0;
629 			i = 0;
630 		}
631 	}
632 
633 	if (uulen > 0) {
634 		int cplen = ((uulen / 3) * 4);
635 
636 		*outptr++ = GMIME_UUENCODE_CHAR ((uulen - uufill) & 0xff);
637 		memcpy (outptr, uubuf, cplen);
638 		outptr += cplen;
639 		*outptr++ = '\n';
640 		uulen = 0;
641 	}
642 
643 	*outptr++ = GMIME_UUENCODE_CHAR (uulen & 0xff);
644 	*outptr++ = '\n';
645 
646 	*save = 0;
647 	*state = 0;
648 
649 	return (outptr - outbuf);
650 }
651 
652 
653 /**
654  * g_mime_encoding_uuencode_step:
655  * @inbuf: input buffer
656  * @inlen: input buffer length
657  * @outbuf: output stream
658  * @uubuf: temporary buffer of 60 bytes
659  * @state: holds the number of bits that are stored in @save
660  * @save: leftover bits that have not yet been encoded
661  *
662  * Uuencodes a chunk of data. Performs an 'encode step', only encodes
663  * blocks of 45 characters to the output at a time, saves left-over
664  * state in @uubuf, @state and @save (initialize to 0 on first
665  * invocation).
666  *
667  * Returns: the number of bytes encoded.
668  **/
669 size_t
g_mime_encoding_uuencode_step(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,unsigned char * uubuf,int * state,guint32 * save)670 g_mime_encoding_uuencode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, unsigned char *uubuf, int *state, guint32 *save)
671 {
672 	register unsigned char *outptr, *bufptr;
673 	register const unsigned char *inptr;
674 	const unsigned char *inend;
675 	unsigned char b0, b1, b2;
676 	guint32 saved;
677 	int uulen, i;
678 
679 	if (inlen == 0)
680 		return 0;
681 
682 	inend = inbuf + inlen;
683 	outptr = outbuf;
684 	inptr = inbuf;
685 
686 	saved = *save;
687 	i = *state & 0xff;
688 	uulen = (*state >> 8) & 0xff;
689 
690 	if ((inlen + uulen) < 45) {
691 		/* not enough input to write a full uuencoded line */
692 		bufptr = uubuf + ((uulen / 3) * 4);
693 	} else {
694 		bufptr = outptr + 1;
695 
696 		if (uulen > 0) {
697 			/* copy the previous call's tmpbuf to outbuf */
698 			memcpy (bufptr, uubuf, ((uulen / 3) * 4));
699 			bufptr += ((uulen / 3) * 4);
700 		}
701 	}
702 
703 	if (i == 2) {
704 		b0 = (saved >> 8) & 0xff;
705 		b1 = saved & 0xff;
706 		saved = 0;
707 		i = 0;
708 
709 		goto skip2;
710 	} else if (i == 1) {
711 		if ((inptr + 2) < inend) {
712 			b0 = saved & 0xff;
713 			saved = 0;
714 			i = 0;
715 
716 			goto skip1;
717 		}
718 
719 		while (inptr < inend) {
720 			saved = (saved << 8) | *inptr++;
721 			i++;
722 		}
723 	}
724 
725 	while (inptr < inend) {
726 		while (uulen < 45 && (inptr + 3) <= inend) {
727 			b0 = *inptr++;
728 		skip1:
729 			b1 = *inptr++;
730 		skip2:
731 			b2 = *inptr++;
732 
733 			/* convert 3 normal bytes into 4 uuencoded bytes */
734 			*bufptr++ = GMIME_UUENCODE_CHAR ((b0 >> 2) & 0x3f);
735 			*bufptr++ = GMIME_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f);
736 			*bufptr++ = GMIME_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f);
737 			*bufptr++ = GMIME_UUENCODE_CHAR (b2 & 0x3f);
738 
739 			uulen += 3;
740 		}
741 
742 		if (uulen >= 45) {
743 			/* output the uu line length */
744 			*outptr = GMIME_UUENCODE_CHAR (uulen & 0xff);
745 			outptr += ((45 / 3) * 4) + 1;
746 
747 			*outptr++ = '\n';
748 			uulen = 0;
749 
750 			if ((inptr + 45) <= inend) {
751 				/* we have enough input to output another full line */
752 				bufptr = outptr + 1;
753 			} else {
754 				bufptr = uubuf;
755 			}
756 		} else {
757 			/* not enough input to continue... */
758 			for (i = 0, saved = 0; inptr < inend; i++)
759 				saved = (saved << 8) | *inptr++;
760 		}
761 	}
762 
763 	*save = saved;
764 	*state = ((uulen & 0xff) << 8) | (i & 0xff);
765 
766 	return (outptr - outbuf);
767 }
768 
769 
770 /**
771  * g_mime_encoding_uudecode_step:
772  * @inbuf: input buffer
773  * @inlen: input buffer length
774  * @outbuf: output buffer
775  * @state: holds the number of bits that are stored in @save
776  * @save: leftover bits that have not yet been decoded
777  *
778  * Uudecodes a chunk of data. Performs a 'decode step' on a chunk of
779  * uuencoded data. Assumes the "begin mode filename" line has
780  * been stripped off.
781  *
782  * Returns: the number of bytes decoded.
783  **/
784 size_t
g_mime_encoding_uudecode_step(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)785 g_mime_encoding_uudecode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
786 {
787 	register const unsigned char *inptr;
788 	register unsigned char *outptr;
789 	const unsigned char *inend;
790 	unsigned char ch;
791 	register guint32 saved;
792 	gboolean last_was_eoln;
793 	int uulen, i;
794 
795 	if (*state & GMIME_UUDECODE_STATE_END)
796 		return 0;
797 
798 	saved = *save;
799 	i = *state & 0xff;
800 	uulen = (*state >> 8) & 0xff;
801 	if (uulen == 0)
802 		last_was_eoln = TRUE;
803 	else
804 		last_was_eoln = FALSE;
805 
806 	inend = inbuf + inlen;
807 	outptr = outbuf;
808 	inptr = inbuf;
809 
810 	while (inptr < inend) {
811 		if (*inptr == '\n') {
812 			last_was_eoln = TRUE;
813 
814 			inptr++;
815 			continue;
816 		} else if (!uulen || last_was_eoln) {
817 			/* first octet on a line is the uulen octet */
818 			uulen = gmime_uu_rank[*inptr];
819 			last_was_eoln = FALSE;
820 			if (uulen == 0) {
821 				*state |= GMIME_UUDECODE_STATE_END;
822 				break;
823 			}
824 
825 			inptr++;
826 			continue;
827 		}
828 
829 		ch = *inptr++;
830 
831 		if (uulen > 0) {
832 			/* save the byte */
833 			saved = (saved << 8) | ch;
834 			i++;
835 			if (i == 4) {
836 				/* convert 4 uuencoded bytes to 3 normal bytes */
837 				unsigned char b0, b1, b2, b3;
838 
839 				b0 = saved >> 24;
840 				b1 = saved >> 16 & 0xff;
841 				b2 = saved >> 8 & 0xff;
842 				b3 = saved & 0xff;
843 
844 				if (uulen >= 3) {
845 					*outptr++ = gmime_uu_rank[b0] << 2 | gmime_uu_rank[b1] >> 4;
846 					*outptr++ = gmime_uu_rank[b1] << 4 | gmime_uu_rank[b2] >> 2;
847 				        *outptr++ = gmime_uu_rank[b2] << 6 | gmime_uu_rank[b3];
848 					uulen -= 3;
849 				} else {
850 					if (uulen >= 1) {
851 						*outptr++ = gmime_uu_rank[b0] << 2 | gmime_uu_rank[b1] >> 4;
852 						uulen--;
853 					}
854 
855 					if (uulen >= 1) {
856 						*outptr++ = gmime_uu_rank[b1] << 4 | gmime_uu_rank[b2] >> 2;
857 						uulen--;
858 					}
859 				}
860 
861 				i = 0;
862 				saved = 0;
863 			}
864 		} else {
865 			break;
866 		}
867 	}
868 
869 	*save = saved;
870 	*state = (*state & GMIME_UUDECODE_STATE_MASK) | ((uulen & 0xff) << 8) | (i & 0xff);
871 
872 	return (outptr - outbuf);
873 }
874 
875 
876 /**
877  * g_mime_encoding_quoted_encode_close:
878  * @inbuf: input buffer
879  * @inlen: input buffer length
880  * @outbuf: output buffer
881  * @state: holds the number of bits that are stored in @save
882  * @save: leftover bits that have not yet been encoded
883  *
884  * Quoted-printable encodes a block of text. Call this when finished
885  * encoding data with g_mime_encoding_quoted_encode_step() to flush off
886  * the last little bit.
887  *
888  * Returns: the number of bytes encoded.
889  **/
890 size_t
g_mime_encoding_quoted_encode_close(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)891 g_mime_encoding_quoted_encode_close (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
892 {
893 	register unsigned char *outptr = outbuf;
894 	int last;
895 
896 	if (inlen > 0)
897 		outptr += g_mime_encoding_quoted_encode_step (inbuf, inlen, outptr, state, save);
898 
899 	last = *state;
900 	if (last != -1) {
901 		/* space/tab must be encoded if its the last character on
902 		   the line */
903 		if (is_qpsafe (last) && !is_blank (last)) {
904 			*outptr++ = last;
905 		} else {
906 			*outptr++ = '=';
907 			*outptr++ = tohex[(last >> 4) & 0xf];
908 			*outptr++ = tohex[last & 0xf];
909 		}
910 	}
911 
912 	if (last != '\n') {
913 		/* we end with =\n so that the \n isn't interpreted as a real
914 		   \n when it gets decoded later */
915 		*outptr++ = '=';
916 		*outptr++ = '\n';
917 	}
918 
919 	*save = 0;
920 	*state = -1;
921 
922 	return (outptr - outbuf);
923 }
924 
925 
926 /**
927  * g_mime_encoding_quoted_encode_step:
928  * @inbuf: input buffer
929  * @inlen: input buffer length
930  * @outbuf: output buffer
931  * @state: holds the number of bits that are stored in @save
932  * @save: leftover bits that have not yet been encoded
933  *
934  * Quoted-printable encodes a block of text. Performs an 'encode
935  * step', saves left-over state in state and save (initialise to -1 on
936  * first invocation).
937  *
938  * Returns: the number of bytes encoded.
939  **/
940 size_t
g_mime_encoding_quoted_encode_step(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)941 g_mime_encoding_quoted_encode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
942 {
943 	register const unsigned char *inptr = inbuf;
944 	const unsigned char *inend = inbuf + inlen;
945 	register unsigned char *outptr = outbuf;
946 	register guint32 sofar = *save;  /* keeps track of how many chars on a line */
947 	register int last = *state;  /* keeps track if last char to end was a space cr etc */
948 	unsigned char c;
949 
950 	while (inptr < inend) {
951 		c = *inptr++;
952 		if (c == '\r') {
953 			if (last != -1) {
954 				*outptr++ = '=';
955 				*outptr++ = tohex[(last >> 4) & 0xf];
956 				*outptr++ = tohex[last & 0xf];
957 				sofar += 3;
958 			}
959 			last = c;
960 		} else if (c == '\n') {
961 			if (last != -1 && last != '\r') {
962 				*outptr++ = '=';
963 				*outptr++ = tohex[(last >> 4) & 0xf];
964 				*outptr++ = tohex[last & 0xf];
965 			}
966 			*outptr++ = '\n';
967 			sofar = 0;
968 			last = -1;
969 		} else {
970 			if (last != -1) {
971 				if (is_qpsafe (last)) {
972 					*outptr++ = last;
973 					sofar++;
974 				} else {
975 					*outptr++ = '=';
976 					*outptr++ = tohex[(last >> 4) & 0xf];
977 					*outptr++ = tohex[last & 0xf];
978 					sofar += 3;
979 				}
980 			}
981 
982 			if (is_qpsafe (c)) {
983 				if (sofar > 74) {
984 					*outptr++ = '=';
985 					*outptr++ = '\n';
986 					sofar = 0;
987 				}
988 
989 				/* delay output of space char */
990 				if (is_blank (c)) {
991 					last = c;
992 				} else {
993 					*outptr++ = c;
994 					sofar++;
995 					last = -1;
996 				}
997 			} else {
998 				if (sofar > 72) {
999 					*outptr++ = '=';
1000 					*outptr++ = '\n';
1001 					sofar = 3;
1002 				} else
1003 					sofar += 3;
1004 
1005 				*outptr++ = '=';
1006 				*outptr++ = tohex[(c >> 4) & 0xf];
1007 				*outptr++ = tohex[c & 0xf];
1008 				last = -1;
1009 			}
1010 		}
1011 	}
1012 
1013 	*save = sofar;
1014 	*state = last;
1015 
1016 	return (outptr - outbuf);
1017 }
1018 
1019 
1020 /**
1021  * g_mime_encoding_quoted_decode_step:
1022  * @inbuf: input buffer
1023  * @inlen: input buffer length
1024  * @outbuf: output buffer
1025  * @state: holds the number of bits that are stored in @save
1026  * @save: leftover bits that have not yet been decoded
1027  *
1028  * Decodes a block of quoted-printable encoded data. Performs a
1029  * 'decode step' on a chunk of QP encoded data.
1030  *
1031  * Returns: the number of bytes decoded.
1032  **/
1033 size_t
g_mime_encoding_quoted_decode_step(const unsigned char * inbuf,size_t inlen,unsigned char * outbuf,int * state,guint32 * save)1034 g_mime_encoding_quoted_decode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
1035 {
1036 	/* FIXME: this does not strip trailing spaces from lines (as
1037 	 * it should, rfc 2045, section 6.7) Should it also
1038 	 * canonicalise the end of line to CR LF??
1039 	 *
1040 	 * Note: Trailing rubbish (at the end of input), like = or =x
1041 	 * or =\r will be lost.
1042 	 */
1043 	const register unsigned char *inptr = inbuf;
1044 	const unsigned char *inend = inbuf + inlen;
1045 	register unsigned char *outptr = outbuf;
1046 	guint32 isave = *save;
1047 	int istate = *state;
1048 	unsigned char c;
1049 
1050 	d(printf ("quoted-printable, decoding text '%.*s'\n", inlen, inbuf));
1051 
1052 	while (inptr < inend) {
1053 		switch (istate) {
1054 		case 0:
1055 			while (inptr < inend) {
1056 				c = *inptr++;
1057 				/* FIXME: use a specials table to avoid 3 comparisons for the common case */
1058 				if (c == '=') {
1059 					istate = 1;
1060 					break;
1061 				}
1062 #ifdef CANONICALISE_EOL
1063 				/*else if (c=='\r') {
1064 					state = 3;
1065 				} else if (c=='\n') {
1066 					*outptr++ = '\r';
1067 					*outptr++ = c;
1068 					} */
1069 #endif
1070 				else {
1071 					*outptr++ = c;
1072 				}
1073 			}
1074 			break;
1075 		case 1:
1076 			c = *inptr++;
1077 			if (c == '\n') {
1078 				/* soft break ... unix end of line */
1079 				istate = 0;
1080 			} else {
1081 				isave = c;
1082 				istate = 2;
1083 			}
1084 			break;
1085 		case 2:
1086 			c = *inptr++;
1087 			if (isxdigit (c) && isxdigit (isave)) {
1088 				c = toupper ((int) c);
1089 				isave = toupper ((int) isave);
1090 				*outptr++ = (((isave >= 'A' ? isave - 'A' + 10 : isave - '0') & 0x0f) << 4)
1091 					| ((c >= 'A' ? c - 'A' + 10 : c - '0') & 0x0f);
1092 			} else if (c == '\n' && isave == '\r') {
1093 				/* soft break ... canonical end of line */
1094 			} else {
1095 				/* just output the data */
1096 				*outptr++ = '=';
1097 				*outptr++ = isave;
1098 				*outptr++ = c;
1099 			}
1100 			istate = 0;
1101 			break;
1102 #ifdef CANONICALISE_EOL
1103 		case 3:
1104 			/* convert \n -> to \r\n, leaves \r\n alone */
1105 			c = *inptr++;
1106 			if (c == '\n') {
1107 				*outptr++ = '\r';
1108 				*outptr++ = c;
1109 			} else {
1110 				*outptr++ = '\r';
1111 				*outptr++ = '\n';
1112 				*outptr++ = c;
1113 			}
1114 			istate = 0;
1115 			break;
1116 #endif
1117 		}
1118 	}
1119 
1120 	*state = istate;
1121 	*save = isave;
1122 
1123 	return (outptr - outbuf);
1124 }
1125