Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-encodings.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 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
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 *
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
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
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
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
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
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
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
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
422 g_mime_encoding_base64_encode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
423 {
424         const register 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
508 g_mime_encoding_base64_decode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
509 {
510         const register 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
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
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         const register 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
785 g_mime_encoding_uudecode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
786 {
787         const register 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
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
941 g_mime_encoding_quoted_encode_step (const unsigned char *inbuf, size_t inlen, unsigned char *outbuf, int *state, guint32 *save)
942 {
943         const register 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
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 }