Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-mime-filter-yenc.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29
30 #include "camel-mime-filter-yenc.h"
31
32 static void camel_mime_filter_yenc_class_init (CamelMimeFilterYencClass *klass);
33 static void camel_mime_filter_yenc_init (CamelMimeFilterYenc *filter, CamelMimeFilterYencClass *klass);
34
35 static void filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
36                            char **out, size_t *outlen, size_t *outprespace);
37 static void filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
38                              char **out, size_t *outlen, size_t *outprespace);
39 static void filter_reset (CamelMimeFilter *filter);
40
41
42 static CamelMimeFilterClass *parent_class = NULL;
43
44
45 CamelType
46 camel_mime_filter_yenc_get_type (void)
47 {
48         static CamelType type = CAMEL_INVALID_TYPE;
49         
50         if (type == CAMEL_INVALID_TYPE) {
51                 type = camel_type_register (camel_mime_filter_get_type (),
52                                             "CamelMimeFilterYenc",
53                                             sizeof (CamelMimeFilterYenc),
54                                             sizeof (CamelMimeFilterYencClass),
55                                             (CamelObjectClassInitFunc) camel_mime_filter_yenc_class_init,
56                                             NULL,
57                                             (CamelObjectInitFunc) camel_mime_filter_yenc_init,
58                                             NULL);
59         }
60         
61         return type;
62 }
63
64
65 static void
66 camel_mime_filter_yenc_class_init (CamelMimeFilterYencClass *klass)
67 {
68         CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
69         
70         parent_class = CAMEL_MIME_FILTER_CLASS (camel_type_get_global_classfuncs (camel_mime_filter_get_type ()));
71         
72         filter_class->reset = filter_reset;
73         filter_class->filter = filter_filter;
74         filter_class->complete = filter_complete;
75 }
76
77 static void
78 camel_mime_filter_yenc_init (CamelMimeFilterYenc *filter, CamelMimeFilterYencClass *klass)
79 {
80         filter->part = 0;
81         filter->pcrc = CAMEL_MIME_YENCODE_CRC_INIT;
82         filter->crc = CAMEL_MIME_YENCODE_CRC_INIT;
83 }
84
85
86 /* here we do all of the basic yEnc filtering */
87 static void
88 filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
89                char **out, size_t *outlen, size_t *outprespace)
90 {
91         CamelMimeFilterYenc *yenc = (CamelMimeFilterYenc *) filter;
92         size_t newlen = 0;
93         
94         switch (yenc->direction) {
95         case CAMEL_MIME_FILTER_YENC_DIRECTION_ENCODE:
96                 /* won't go to more than 2 * (x + 2) + 62 */
97                 camel_mime_filter_set_size (filter, (len + 2) * 2 + 62, FALSE);
98                 newlen = camel_yencode_step (in, len, filter->outbuf, &yenc->state,
99                                              &yenc->pcrc, &yenc->crc);
100                 g_assert (newlen <= (len + 2) * 2 + 62);
101                 break;
102         case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
103                 if (!(yenc->state & CAMEL_MIME_YDECODE_STATE_DECODE)) {
104                         register char *inptr, *inend;
105                         size_t left;
106                         
107                         inptr = in;
108                         inend = inptr + len;
109                         
110                         /* we cannot start decoding until we have found an =ybegin line */
111                         if (!(yenc->state & CAMEL_MIME_YDECODE_STATE_BEGIN)) {
112                                 while (inptr < inend) {
113                                         left = inend - inptr;
114                                         if (left < 8) {
115                                                 if (!strncmp (inptr, "=ybegin ", left))
116                                                         camel_mime_filter_backup (filter, inptr, left);
117                                                 break;
118                                         } else if (!strncmp (inptr, "=ybegin ", 8)) {
119                                                 for (in = inptr; inptr < inend && *inptr != '\n'; inptr++);
120                                                 if (inptr < inend) {
121                                                         inptr++;
122                                                         yenc->state |= CAMEL_MIME_YDECODE_STATE_BEGIN;
123                                                         /* we can start ydecoding if the next line isn't
124                                                            a ypart... */
125                                                         in = inptr;
126                                                         len = inend - in;
127                                                 } else {
128                                                         /* we don't have enough... */
129                                                         camel_mime_filter_backup (filter, in, left);
130                                                 }
131                                                 break;
132                                         }
133                                         
134                                         /* go to the next line */
135                                         while (inptr < inend && *inptr != '\n')
136                                                 inptr++;
137                                         
138                                         if (inptr < inend)
139                                                 inptr++;
140                                 }
141                         }
142                         
143                         left = inend - inptr;
144                         if ((yenc->state & CAMEL_MIME_YDECODE_STATE_BEGIN) && left > 0) {
145                                 /* we have found an '=ybegin' line but we may yet have an "=ypart" line to
146                                    yield before decoding the content */
147                                 if (left < 7 && !strncmp (inptr, "=ypart ", left)) {
148                                         camel_mime_filter_backup (filter, inptr, left);
149                                 } else if (!strncmp (inptr, "=ypart ", 7)) {
150                                         for (in = inptr; inptr < inend && *inptr != '\n'; inptr++);
151                                         if (inptr < inend) {
152                                                 inptr++;
153                                                 yenc->state |= CAMEL_MIME_YDECODE_STATE_PART | CAMEL_MIME_YDECODE_STATE_DECODE;
154                                                 in = inptr;
155                                                 len = inend - in;
156                                         } else {
157                                                 camel_mime_filter_backup (filter, in, left);
158                                         }
159                                 } else {
160                                         /* guess it doesn't have a =ypart line */
161                                         yenc->state |= CAMEL_MIME_YDECODE_STATE_DECODE;
162                                 }
163                         }
164                 }
165                 
166                 if ((yenc->state & CAMEL_MIME_YDECODE_STATE_DECODE) && !(yenc->state & CAMEL_MIME_YDECODE_STATE_END)) {
167                         /* all yEnc headers have been found so we can now start decoding */
168                         camel_mime_filter_set_size (filter, len + 3, FALSE);
169                         newlen = camel_ydecode_step (in, len, filter->outbuf, &yenc->state, &yenc->pcrc, &yenc->crc);
170                         g_assert (newlen <= len + 3);
171                 } else {
172                         newlen = 0;
173                 }
174                 break;
175         }
176         
177         *out = filter->outbuf;
178         *outlen = newlen;
179         *outprespace = filter->outpre;
180 }
181
182 static void
183 filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
184                  char **out, size_t *outlen, size_t *outprespace)
185 {
186         CamelMimeFilterYenc *yenc = (CamelMimeFilterYenc *) filter;
187         size_t newlen = 0;
188         
189         switch (yenc->direction) {
190         case CAMEL_MIME_FILTER_YENC_DIRECTION_ENCODE:
191                 /* won't go to more than 2 * (x + 2) + 62 */
192                 camel_mime_filter_set_size (filter, (len + 2) * 2 + 62, FALSE);
193                 newlen = camel_yencode_close (in, len, filter->outbuf, &yenc->state,
194                                                &yenc->pcrc, &yenc->crc);
195                 g_assert (newlen <= (len + 2) * 2 + 62);
196                 break;
197         case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
198                 if ((yenc->state & CAMEL_MIME_YDECODE_STATE_DECODE) && !(yenc->state & CAMEL_MIME_YDECODE_STATE_END)) {
199                         /* all yEnc headers have been found so we can now start decoding */
200                         camel_mime_filter_set_size (filter, len + 3, FALSE);
201                         newlen = camel_ydecode_step (in, len, filter->outbuf, &yenc->state,
202                                                       &yenc->pcrc, &yenc->crc);
203                         g_assert (newlen <= len + 3);
204                 } else {
205                         newlen = 0;
206                 }
207                 break;
208         }
209         
210         *out = filter->outbuf;
211         *outlen = newlen;
212         *outprespace = filter->outpre;
213 }
214
215 /* should this 'flush' outstanding state/data bytes? */
216 static void
217 filter_reset (CamelMimeFilter *filter)
218 {
219         CamelMimeFilterYenc *yenc = (CamelMimeFilterYenc *) filter;
220         
221         switch (yenc->direction) {
222         case CAMEL_MIME_FILTER_YENC_DIRECTION_ENCODE:
223                 yenc->state = CAMEL_MIME_YENCODE_STATE_INIT;
224                 break;
225         case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
226                 yenc->state = CAMEL_MIME_YDECODE_STATE_INIT;
227                 break;
228         }
229         yenc->pcrc = CAMEL_MIME_YENCODE_CRC_INIT;
230         yenc->crc = CAMEL_MIME_YENCODE_CRC_INIT;
231 }
232
233
234 /**
235  * camel_mime_filter_yenc_new:
236  * @direction: encode direction
237  *
238  * Create a new #CamelMimeFilterYenc filter object.
239  *
240  * Returns a new #CamelMimeFilterYenc object
241  **/
242 CamelMimeFilter *
243 camel_mime_filter_yenc_new (CamelMimeFilterYencDirection direction)
244 {
245         CamelMimeFilterYenc *new;
246         
247         new = (CamelMimeFilterYenc *) camel_object_new (CAMEL_TYPE_MIME_FILTER_YENC);
248         new->direction = direction;
249         
250         switch (direction) {
251         case CAMEL_MIME_FILTER_YENC_DIRECTION_ENCODE:
252                 new->state = CAMEL_MIME_YENCODE_STATE_INIT;
253                 break;
254         case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
255                 new->state = CAMEL_MIME_YDECODE_STATE_INIT;
256                 break;
257         default:
258                 g_assert_not_reached ();
259         }
260         
261         return (CamelMimeFilter *) new;
262 }
263
264
265 /**
266  * camel_mime_filter_yenc_set_state:
267  * @yenc: a #CamelMimeFilterYenc object
268  * @state: encode/decode state
269  *
270  * Sets the current state of the yencoder/ydecoder
271  **/
272 void
273 camel_mime_filter_yenc_set_state (CamelMimeFilterYenc *yenc, int state)
274 {
275         g_return_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc));
276         
277         yenc->state = state;
278 }
279
280
281 /**
282  * camel_mime_filter_yenc_set_crc:
283  * @yenc: a #CamelMimeFilterYenc object
284  * @crc: crc32 value
285  *
286  * Sets the current crc32 value on the yEnc filter @yenc to @crc.
287  **/
288 void
289 camel_mime_filter_yenc_set_crc (CamelMimeFilterYenc *yenc, guint32 crc)
290 {
291         g_return_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc));
292         
293         yenc->crc = crc;
294 }
295
296
297 #if 0
298 /* FIXME: once we parse out the yenc part id, we can re-enable this interface */
299 /**
300  * camel_mime_filter_yenc_get_part:
301  * @yenc: a #CamelMimeFilterYenc object
302  *
303  * Gets the part id of the current decoded yEnc stream or %-1 on fail.
304  *
305  * Returns the part id of the current decoded yEnc stream or %-1 on
306  * fail.
307  **/
308 int
309 camel_mime_filter_yenc_get_part (CamelMimeFilterYenc *yenc)
310 {
311         g_return_val_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc), -1);
312         
313         if (yenc->state & CAMEL_MIME_YDECODE_STATE_PART)
314                 return yenc->part;
315         
316         return -1;
317 }
318 #endif
319
320 /**
321  * camel_mime_filter_yenc_get_pcrc:
322  * @yenc: a #CamelMimeFilterYenc object
323  *
324  * Get the computed part crc or (#guint32) %-1 on fail.
325  *
326  * Returns the computed part crc or (#guint32) %-1 on fail.
327  **/
328 guint32
329 camel_mime_filter_yenc_get_pcrc (CamelMimeFilterYenc *yenc)
330 {
331         g_return_val_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc), -1);
332         
333         return CAMEL_MIME_YENCODE_CRC_FINAL (yenc->pcrc);
334 }
335
336
337 /**
338  * camel_mime_filter_yenc_get_crc:
339  * @yenc: a #CamelMimeFiletrYenc object
340  *
341  * Get the computed crc or (#guint32) -1 on fail.
342  *
343  * Returns the computed crc or (#guint32) -1 on fail.
344  **/
345 guint32
346 camel_mime_filter_yenc_get_crc (CamelMimeFilterYenc *yenc)
347 {
348         g_return_val_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc), -1);
349         
350         return CAMEL_MIME_YENCODE_CRC_FINAL (yenc->crc);
351 }
352
353
354 static const int yenc_crc_table[256] = {
355         0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
356         0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
357         0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
358         0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
359         0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
360         0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
361         0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
362         0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
363         0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
364         0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
365         0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
366         0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
367         0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
368         0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
369         0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
370         0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
371         0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
372         0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
373         0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
374         0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
375         0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
376         0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
377         0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
378         0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
379         0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
380         0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
381         0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
382         0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
383         0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
384         0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
385         0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
386         0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
387 };
388
389 #define yenc_crc_add(crc, c) (yenc_crc_table[(((int) (crc)) ^ ((unsigned char) (c))) & 0xff] ^ ((((int) (crc)) >> 8) & 0x00ffffff))
390
391 #define YENC_NEWLINE_ESCAPE (CAMEL_MIME_YDECODE_STATE_EOLN | CAMEL_MIME_YDECODE_STATE_ESCAPE)
392
393
394 /**
395  * camel_ydecode_step:
396  * @in: input buffer
397  * @inlen: input buffer length
398  * @out: output buffer
399  * @state: ydecode state
400  * @pcrc: part crc state
401  * @crc: crc state
402  *
403  * Performs a 'decode step' on a chunk of yEncoded data of length
404  * @inlen pointed to by @in and writes to @out. Assumes the =ybegin
405  * and =ypart lines have already been stripped off.
406  *
407  * To get the crc32 value of the part, use #CAMEL_MIME_YENCODE_CRC_FINAL
408  * (@pcrc). If there are more parts, you should reuse @crc without
409  * re-initializing. Once all parts have been decoded, you may get the
410  * combined crc32 value of all the parts using #CAMEL_MIME_YENCODE_CRC_FINAL
411  * (@crc).
412  *
413  * Returns the number of bytes decoded
414  **/
415 size_t
416 camel_ydecode_step (const unsigned char *in, size_t inlen, unsigned char *out,
417                     int *state, guint32 *pcrc, guint32 *crc)
418 {
419         register const unsigned char *inptr;
420         register unsigned char *outptr;
421         const unsigned char *inend;
422         unsigned char ch;
423         int ystate;
424         
425         if (*state & CAMEL_MIME_YDECODE_STATE_END)
426                 return 0;
427         
428         ystate = *state;
429         
430         inend = in + inlen;
431         outptr = out;
432         
433         inptr = in;
434         while (inptr < inend) {
435                 ch = *inptr++;
436                 
437                 if ((ystate & YENC_NEWLINE_ESCAPE) == YENC_NEWLINE_ESCAPE) {
438                         ystate &= ~CAMEL_MIME_YDECODE_STATE_EOLN;
439                         
440                         if (ch == 'y') {
441                                 /* we probably have a =yend here */
442                                 ystate |= CAMEL_MIME_YDECODE_STATE_END;
443                                 break;
444                         }
445                 }
446                 
447                 if (ch == '\n') {
448                         ystate |= CAMEL_MIME_YDECODE_STATE_EOLN;
449                         continue;
450                 }
451                 
452                 if (ystate & CAMEL_MIME_YDECODE_STATE_ESCAPE) {
453                         ystate &= ~CAMEL_MIME_YDECODE_STATE_ESCAPE;
454                         ch -= 64;
455                 } else if (ch == '=') {
456                         ystate |= CAMEL_MIME_YDECODE_STATE_ESCAPE;
457                         continue;
458                 }
459                 
460                 ystate &= ~CAMEL_MIME_YDECODE_STATE_EOLN;
461                 
462                 *outptr++ = ch -= 42;
463                 
464                 *pcrc = yenc_crc_add (*pcrc, ch);
465                 *crc = yenc_crc_add (*crc, ch);
466         }
467         
468         *state = ystate;
469         
470         return outptr - out;
471 }
472
473
474 /**
475  * camel_yencode_step:
476  * @in: input buffer
477  * @inlen: input buffer length
478  * @out: output buffer
479  * @state: yencode state
480  * @pcrc: part crc state
481  * @crc: crc state
482  *
483  * Performs an yEncode 'encode step' on a chunk of raw data of length
484  * @inlen pointed to by @in and writes to @out.
485  *
486  * @state should be initialized to #CAMEL_MIME_YENCODE_STATE_INIT before
487  * beginning making the first call to this function. Subsequent calls
488  * should reuse @state.
489  *
490  * Along the same lines, @pcrc and @crc should be initialized to
491  * #CAMEL_MIME_YENCODE_CRC_INIT before using.
492  *
493  * Returns the number of bytes encoded
494  **/
495 size_t
496 camel_yencode_step (const unsigned char *in, size_t inlen, unsigned char *out,
497                     int *state, guint32 *pcrc, guint32 *crc)
498 {
499         register const unsigned char *inptr;
500         register unsigned char *outptr;
501         const unsigned char *inend;
502         register int already;
503         unsigned char ch;
504         
505         inend = in + inlen;
506         outptr = out;
507         
508         already = *state;
509         
510         inptr = in;
511         while (inptr < inend) {
512                 ch = (*inptr++);
513                 
514                 *pcrc = yenc_crc_add (*pcrc, ch);
515                 *crc = yenc_crc_add (*crc, ch);
516                 
517                 ch += 42;
518                 
519                 if (ch == '\0' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '=') {
520                         *outptr++ = '=';
521                         *outptr++ = ch + 64;
522                         already += 2;
523                 } else {
524                         *outptr++ = ch;
525                         already++;
526                 }
527                 
528                 if (already >= 128) {
529                         *outptr++ = '\n';
530                         already = 0;
531                 }
532         }
533         
534         *state = already;
535         
536         return outptr - out;
537 }
538
539
540 /**
541  * camel_yencode_close:
542  * @in: input buffer
543  * @inlen: input buffer length
544  * @out: output buffer
545  * @state: yencode state
546  * @pcrc: part crc state
547  * @crc: crc state
548  *
549  * Call this function when finished encoding data with
550  * #camel_yencode_step to flush off the remaining state.
551  *
552  * #CAMEL_MIME_YENCODE_CRC_FINAL (@pcrc) will give you the crc32 of the
553  * encoded "part". If there are more "parts" to encode, you should
554  * re-use @crc when encoding the next "parts" and then use
555  * #CAMEL_MIME_YENCODE_CRC_FINAL (@crc) to get the combined crc32 value of
556  * all the parts.
557  *
558  * Returns the number of bytes encoded.
559  **/
560 size_t
561 camel_yencode_close (const unsigned char *in, size_t inlen, unsigned char *out,
562                      int *state, guint32 *pcrc, guint32 *crc)
563 {
564         register unsigned char *outptr;
565         
566         outptr = out;
567         
568         if (inlen)
569                 outptr += camel_yencode_step (in, inlen, out, state, pcrc, crc);
570         
571         if (*state)
572                 *outptr++ = '\n';
573         
574         *state = CAMEL_MIME_YENCODE_STATE_INIT;
575         
576         return outptr - out;
577 }