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