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