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