1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
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.
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.
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.
30 #include "camel-mime-filter-yenc.h"
32 static void camel_mime_filter_yenc_class_init (CamelMimeFilterYencClass *klass);
33 static void camel_mime_filter_yenc_init (CamelMimeFilterYenc *filter, CamelMimeFilterYencClass *klass);
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);
42 static CamelMimeFilterClass *parent_class = NULL;
46 camel_mime_filter_yenc_get_type (void)
48 static CamelType type = CAMEL_INVALID_TYPE;
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,
57 (CamelObjectInitFunc) camel_mime_filter_yenc_init,
66 camel_mime_filter_yenc_class_init (CamelMimeFilterYencClass *klass)
68 CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
70 parent_class = CAMEL_MIME_FILTER_CLASS (camel_type_get_global_classfuncs (camel_mime_filter_get_type ()));
72 filter_class->reset = filter_reset;
73 filter_class->filter = filter_filter;
74 filter_class->complete = filter_complete;
78 camel_mime_filter_yenc_init (CamelMimeFilterYenc *filter, CamelMimeFilterYencClass *klass)
81 filter->pcrc = CAMEL_MIME_YENCODE_CRC_INIT;
82 filter->crc = CAMEL_MIME_YENCODE_CRC_INIT;
86 /* here we do all of the basic yEnc filtering */
88 filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
89 char **out, size_t *outlen, size_t *outprespace)
91 CamelMimeFilterYenc *yenc = (CamelMimeFilterYenc *) filter;
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, ¥c->state,
99 ¥c->pcrc, ¥c->crc);
100 g_assert (newlen <= (len + 2) * 2 + 62);
102 case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
103 if (!(yenc->state & CAMEL_MIME_YDECODE_STATE_DECODE)) {
104 register char *inptr, *inend;
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;
115 if (!strncmp (inptr, "=ybegin ", left))
116 camel_mime_filter_backup (filter, inptr, left);
118 } else if (!strncmp (inptr, "=ybegin ", 8)) {
119 for (in = inptr; inptr < inend && *inptr != '\n'; inptr++);
122 yenc->state |= CAMEL_MIME_YDECODE_STATE_BEGIN;
123 /* we can start ydecoding if the next line isn't
128 /* we don't have enough... */
129 camel_mime_filter_backup (filter, in, left);
134 /* go to the next line */
135 while (inptr < inend && *inptr != '\n')
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++);
153 yenc->state |= CAMEL_MIME_YDECODE_STATE_PART | CAMEL_MIME_YDECODE_STATE_DECODE;
157 camel_mime_filter_backup (filter, in, left);
160 /* guess it doesn't have a =ypart line */
161 yenc->state |= CAMEL_MIME_YDECODE_STATE_DECODE;
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, ¥c->state, ¥c->pcrc, ¥c->crc);
170 g_assert (newlen <= len + 3);
177 *out = filter->outbuf;
179 *outprespace = filter->outpre;
183 filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
184 char **out, size_t *outlen, size_t *outprespace)
186 CamelMimeFilterYenc *yenc = (CamelMimeFilterYenc *) filter;
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, ¥c->state,
194 ¥c->pcrc, ¥c->crc);
195 g_assert (newlen <= (len + 2) * 2 + 62);
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, ¥c->state,
202 ¥c->pcrc, ¥c->crc);
203 g_assert (newlen <= len + 3);
210 *out = filter->outbuf;
212 *outprespace = filter->outpre;
215 /* should this 'flush' outstanding state/data bytes? */
217 filter_reset (CamelMimeFilter *filter)
219 CamelMimeFilterYenc *yenc = (CamelMimeFilterYenc *) filter;
221 switch (yenc->direction) {
222 case CAMEL_MIME_FILTER_YENC_DIRECTION_ENCODE:
223 yenc->state = CAMEL_MIME_YENCODE_STATE_INIT;
225 case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
226 yenc->state = CAMEL_MIME_YDECODE_STATE_INIT;
229 yenc->pcrc = CAMEL_MIME_YENCODE_CRC_INIT;
230 yenc->crc = CAMEL_MIME_YENCODE_CRC_INIT;
235 * camel_mime_filter_yenc_new:
236 * @direction: encode direction
238 * Create a new #CamelMimeFilterYenc filter object.
240 * Returns a new #CamelMimeFilterYenc object
243 camel_mime_filter_yenc_new (CamelMimeFilterYencDirection direction)
245 CamelMimeFilterYenc *new;
247 new = (CamelMimeFilterYenc *) camel_object_new (CAMEL_TYPE_MIME_FILTER_YENC);
248 new->direction = direction;
251 case CAMEL_MIME_FILTER_YENC_DIRECTION_ENCODE:
252 new->state = CAMEL_MIME_YENCODE_STATE_INIT;
254 case CAMEL_MIME_FILTER_YENC_DIRECTION_DECODE:
255 new->state = CAMEL_MIME_YDECODE_STATE_INIT;
258 g_assert_not_reached ();
261 return (CamelMimeFilter *) new;
266 * camel_mime_filter_yenc_set_state:
267 * @yenc: a #CamelMimeFilterYenc object
268 * @state: encode/decode state
270 * Sets the current state of the yencoder/ydecoder
273 camel_mime_filter_yenc_set_state (CamelMimeFilterYenc *yenc, int state)
275 g_return_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc));
282 * camel_mime_filter_yenc_set_crc:
283 * @yenc: a #CamelMimeFilterYenc object
286 * Sets the current crc32 value on the yEnc filter @yenc to @crc.
289 camel_mime_filter_yenc_set_crc (CamelMimeFilterYenc *yenc, guint32 crc)
291 g_return_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc));
298 /* FIXME: once we parse out the yenc part id, we can re-enable this interface */
300 * camel_mime_filter_yenc_get_part:
301 * @yenc: a #CamelMimeFilterYenc object
303 * Gets the part id of the current decoded yEnc stream or %-1 on fail.
305 * Returns the part id of the current decoded yEnc stream or %-1 on
309 camel_mime_filter_yenc_get_part (CamelMimeFilterYenc *yenc)
311 g_return_val_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc), -1);
313 if (yenc->state & CAMEL_MIME_YDECODE_STATE_PART)
321 * camel_mime_filter_yenc_get_pcrc:
322 * @yenc: a #CamelMimeFilterYenc object
324 * Get the computed part crc or (#guint32) %-1 on fail.
326 * Returns the computed part crc or (#guint32) %-1 on fail.
329 camel_mime_filter_yenc_get_pcrc (CamelMimeFilterYenc *yenc)
331 g_return_val_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc), -1);
333 return CAMEL_MIME_YENCODE_CRC_FINAL (yenc->pcrc);
338 * camel_mime_filter_yenc_get_crc:
339 * @yenc: a #CamelMimeFiletrYenc object
341 * Get the computed crc or (#guint32) -1 on fail.
343 * Returns the computed crc or (#guint32) -1 on fail.
346 camel_mime_filter_yenc_get_crc (CamelMimeFilterYenc *yenc)
348 g_return_val_if_fail (CAMEL_IS_MIME_FILTER_YENC (yenc), -1);
350 return CAMEL_MIME_YENCODE_CRC_FINAL (yenc->crc);
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
389 #define yenc_crc_add(crc, c) (yenc_crc_table[(((int) (crc)) ^ ((unsigned char) (c))) & 0xff] ^ ((((int) (crc)) >> 8) & 0x00ffffff))
391 #define YENC_NEWLINE_ESCAPE (CAMEL_MIME_YDECODE_STATE_EOLN | CAMEL_MIME_YDECODE_STATE_ESCAPE)
395 * camel_ydecode_step:
397 * @inlen: input buffer length
398 * @out: output buffer
399 * @state: ydecode state
400 * @pcrc: part crc state
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.
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
413 * Returns the number of bytes decoded
416 camel_ydecode_step (const unsigned char *in, size_t inlen, unsigned char *out,
417 int *state, guint32 *pcrc, guint32 *crc)
419 register const unsigned char *inptr;
420 register unsigned char *outptr;
421 const unsigned char *inend;
425 if (*state & CAMEL_MIME_YDECODE_STATE_END)
434 while (inptr < inend) {
437 if ((ystate & YENC_NEWLINE_ESCAPE) == YENC_NEWLINE_ESCAPE) {
438 ystate &= ~CAMEL_MIME_YDECODE_STATE_EOLN;
441 /* we probably have a =yend here */
442 ystate |= CAMEL_MIME_YDECODE_STATE_END;
448 ystate |= CAMEL_MIME_YDECODE_STATE_EOLN;
452 if (ystate & CAMEL_MIME_YDECODE_STATE_ESCAPE) {
453 ystate &= ~CAMEL_MIME_YDECODE_STATE_ESCAPE;
455 } else if (ch == '=') {
456 ystate |= CAMEL_MIME_YDECODE_STATE_ESCAPE;
460 ystate &= ~CAMEL_MIME_YDECODE_STATE_EOLN;
462 *outptr++ = ch -= 42;
464 *pcrc = yenc_crc_add (*pcrc, ch);
465 *crc = yenc_crc_add (*crc, ch);
475 * camel_yencode_step:
477 * @inlen: input buffer length
478 * @out: output buffer
479 * @state: yencode state
480 * @pcrc: part crc state
483 * Performs an yEncode 'encode step' on a chunk of raw data of length
484 * @inlen pointed to by @in and writes to @out.
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.
490 * Along the same lines, @pcrc and @crc should be initialized to
491 * #CAMEL_MIME_YENCODE_CRC_INIT before using.
493 * Returns the number of bytes encoded
496 camel_yencode_step (const unsigned char *in, size_t inlen, unsigned char *out,
497 int *state, guint32 *pcrc, guint32 *crc)
499 register const unsigned char *inptr;
500 register unsigned char *outptr;
501 const unsigned char *inend;
502 register int already;
511 while (inptr < inend) {
514 *pcrc = yenc_crc_add (*pcrc, ch);
515 *crc = yenc_crc_add (*crc, ch);
519 if (ch == '\0' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '=') {
528 if (already >= 128) {
541 * camel_yencode_close:
543 * @inlen: input buffer length
544 * @out: output buffer
545 * @state: yencode state
546 * @pcrc: part crc state
549 * Call this function when finished encoding data with
550 * #camel_yencode_step to flush off the remaining state.
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
558 * Returns the number of bytes encoded.
561 camel_yencode_close (const unsigned char *in, size_t inlen, unsigned char *out,
562 int *state, guint32 *pcrc, guint32 *crc)
564 register unsigned char *outptr;
569 outptr += camel_yencode_step (in, inlen, out, state, pcrc, crc);
574 *state = CAMEL_MIME_YENCODE_STATE_INIT;