1 /*
2  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
3  *
4  * This library is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authors: Michael Zucchi <notzed@ximian.com>
17  *          Jeffrey Stedfast <fejj@ximian.com>
18  */
19 
20 #include <string.h>
21 
22 #include "camel-mime-filter-basic.h"
23 #include "camel-mime-utils.h"
24 
25 struct _CamelMimeFilterBasicPrivate {
26 	CamelMimeFilterBasicType type;
27 	guchar uubuf[60];
28 	gint state;
29 	gint save;
30 };
31 
G_DEFINE_TYPE_WITH_PRIVATE(CamelMimeFilterBasic,camel_mime_filter_basic,CAMEL_TYPE_MIME_FILTER)32 G_DEFINE_TYPE_WITH_PRIVATE (CamelMimeFilterBasic, camel_mime_filter_basic, CAMEL_TYPE_MIME_FILTER)
33 
34 /* here we do all of the basic mime filtering */
35 static void
36 mime_filter_basic_filter (CamelMimeFilter *mime_filter,
37                           const gchar *in,
38                           gsize len,
39                           gsize prespace,
40                           gchar **out,
41                           gsize *outlen,
42                           gsize *outprespace)
43 {
44 	CamelMimeFilterBasicPrivate *priv;
45 	gsize newlen;
46 
47 	priv = CAMEL_MIME_FILTER_BASIC (mime_filter)->priv;
48 
49 	switch (priv->type) {
50 	case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
51 		/* wont go to more than 2x size (overly conservative) */
52 		camel_mime_filter_set_size (
53 			mime_filter, len * 2 + 6, FALSE);
54 		newlen = g_base64_encode_step (
55 			(const guchar *) in, len,
56 			TRUE,
57 			mime_filter->outbuf,
58 			&priv->state,
59 			&priv->save);
60 		g_return_if_fail (newlen <= len * 2 + 6);
61 		break;
62 	case CAMEL_MIME_FILTER_BASIC_QP_ENC:
63 		/* *4 is overly conservative, but will do */
64 		camel_mime_filter_set_size (
65 			mime_filter, len * 4 + 4, FALSE);
66 		newlen = camel_quoted_encode_step (
67 			(guchar *) in, len,
68 			(guchar *) mime_filter->outbuf,
69 			&priv->state,
70 			(gint *) &priv->save);
71 		g_return_if_fail (newlen <= len * 4 + 4);
72 		break;
73 	case CAMEL_MIME_FILTER_BASIC_UU_ENC:
74 		/* won't go to more than 2 * (x + 2) + 62 */
75 		camel_mime_filter_set_size (
76 			mime_filter, (len + 2) * 2 + 62, FALSE);
77 		newlen = camel_uuencode_step (
78 			(guchar *) in, len,
79 			(guchar *) mime_filter->outbuf,
80 			priv->uubuf,
81 			&priv->state,
82 			(guint32 *) &priv->save);
83 		g_return_if_fail (newlen <= (len + 2) * 2 + 62);
84 		break;
85 	case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
86 		camel_mime_filter_set_size (mime_filter, (len * 3 / 4) + 3, FALSE);
87 		newlen = g_base64_decode_step (
88 			in, len,
89 			(guchar *) mime_filter->outbuf,
90 			&priv->state,
91 			(guint *) &priv->save);
92 		g_return_if_fail (newlen <= len + 3);
93 		break;
94 	case CAMEL_MIME_FILTER_BASIC_QP_DEC:
95 		/* output can't possibly exceed the input size */
96 		camel_mime_filter_set_size (mime_filter, len + 2, FALSE);
97 		newlen = camel_quoted_decode_step (
98 			(guchar *) in, len,
99 			(guchar *) mime_filter->outbuf,
100 			&priv->state,
101 			(gint *) &priv->save);
102 		g_return_if_fail (newlen <= len + 2);
103 		break;
104 	case CAMEL_MIME_FILTER_BASIC_UU_DEC:
105 		if (!(priv->state & CAMEL_UUDECODE_STATE_BEGIN)) {
106 			const gchar *inptr, *inend;
107 			gsize left;
108 
109 			inptr = in;
110 			inend = inptr + len;
111 
112 			while (inptr < inend) {
113 				left = inend - inptr;
114 				if (left < 6) {
115 					if (!strncmp (inptr, "begin ", left))
116 						camel_mime_filter_backup (mime_filter, inptr, left);
117 					break;
118 				} else if (!strncmp (inptr, "begin ", 6)) {
119 					for (in = inptr; inptr < inend && *inptr != '\n'; inptr++);
120 					if (inptr < inend) {
121 						inptr++;
122 						priv->state |= CAMEL_UUDECODE_STATE_BEGIN;
123 						/* we can start uudecoding... */
124 						in = inptr;
125 						len = inend - in;
126 					} else {
127 						camel_mime_filter_backup (mime_filter, in, left);
128 					}
129 					break;
130 				}
131 
132 				/* go to the next line */
133 				for (; inptr < inend && *inptr != '\n'; inptr++);
134 
135 				if (inptr < inend)
136 					inptr++;
137 			}
138 		}
139 
140 		if ((priv->state & CAMEL_UUDECODE_STATE_BEGIN) && !(priv->state & CAMEL_UUDECODE_STATE_END)) {
141 			/* "begin <mode> <filename>\n" has been
142 			 * found, so we can now start decoding */
143 			camel_mime_filter_set_size (
144 				mime_filter, len + 3, FALSE);
145 			newlen = camel_uudecode_step (
146 				(guchar *) in, len,
147 				(guchar *) mime_filter->outbuf,
148 				&priv->state,
149 				(guint32 *) &priv->save);
150 		} else {
151 			newlen = 0;
152 		}
153 		break;
154 	default:
155 		g_warning ("unknown type %u in CamelMimeFilterBasic", priv->type);
156 		goto donothing;
157 	}
158 
159 	*out = mime_filter->outbuf;
160 	*outlen = newlen;
161 	*outprespace = mime_filter->outpre;
162 
163 	return;
164 donothing:
165 	*out = (gchar *) in;
166 	*outlen = len;
167 	*outprespace = prespace;
168 }
169 
170 static void
mime_filter_basic_complete(CamelMimeFilter * mime_filter,const gchar * in,gsize len,gsize prespace,gchar ** out,gsize * outlen,gsize * outprespace)171 mime_filter_basic_complete (CamelMimeFilter *mime_filter,
172                             const gchar *in,
173                             gsize len,
174                             gsize prespace,
175                             gchar **out,
176                             gsize *outlen,
177                             gsize *outprespace)
178 {
179 	CamelMimeFilterBasicPrivate *priv;
180 	gsize newlen = 0;
181 
182 	priv = CAMEL_MIME_FILTER_BASIC (mime_filter)->priv;
183 
184 	switch (priv->type) {
185 	case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
186 		/* wont go to more than 2x size (overly conservative) */
187 		camel_mime_filter_set_size (
188 			mime_filter, len * 2 + 6, FALSE);
189 		if (len > 0)
190 			newlen += g_base64_encode_step (
191 				(const guchar *) in, len,
192 				TRUE,
193 				mime_filter->outbuf,
194 				&priv->state,
195 				&priv->save);
196 		newlen += g_base64_encode_close (
197 			TRUE,
198 			mime_filter->outbuf,
199 			&priv->state,
200 			&priv->save);
201 		g_return_if_fail (newlen <= len * 2 + 6);
202 		break;
203 	case CAMEL_MIME_FILTER_BASIC_QP_ENC:
204 		/* *4 is definetly more than needed ... */
205 		camel_mime_filter_set_size (
206 			mime_filter, len * 4 + 4, FALSE);
207 		newlen = camel_quoted_encode_close (
208 			(guchar *) in, len,
209 			(guchar *) mime_filter->outbuf,
210 			&priv->state,
211 			&priv->save);
212 		g_return_if_fail (newlen <= len * 4 + 4);
213 		break;
214 	case CAMEL_MIME_FILTER_BASIC_UU_ENC:
215 		/* won't go to more than 2 * (x + 2) + 62 */
216 		camel_mime_filter_set_size (
217 			mime_filter, (len + 2) * 2 + 62, FALSE);
218 		newlen = camel_uuencode_close (
219 			(guchar *) in, len,
220 			(guchar *) mime_filter->outbuf,
221 			priv->uubuf,
222 			&priv->state,
223 			(guint32 *) &priv->save);
224 		g_return_if_fail (newlen <= (len + 2) * 2 + 62);
225 		break;
226 	case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
227 		camel_mime_filter_set_size (mime_filter, (len * 3 / 4) + 3, FALSE);
228 		newlen = g_base64_decode_step (
229 			in, len,
230 			(guchar *) mime_filter->outbuf,
231 			&priv->state,
232 			(guint *) &priv->save);
233 		g_return_if_fail (newlen <= len);
234 		break;
235 	case CAMEL_MIME_FILTER_BASIC_QP_DEC:
236 		/* output can't possibly exceed the input size,
237 		 * well unless its not really qp, then +2 max */
238 		camel_mime_filter_set_size (mime_filter, len + 2, FALSE);
239 		newlen = camel_quoted_decode_step (
240 			(guchar *) in, len,
241 			(guchar *) mime_filter->outbuf,
242 			&priv->state,
243 			(gint *) &priv->save);
244 		g_return_if_fail (newlen <= len + 2);
245 		break;
246 	case CAMEL_MIME_FILTER_BASIC_UU_DEC:
247 		if ((priv->state & CAMEL_UUDECODE_STATE_BEGIN) && !(priv->state & CAMEL_UUDECODE_STATE_END)) {
248 			/* "begin <mode> <filename>\n" has been
249 			 * found, so we can now start decoding */
250 			camel_mime_filter_set_size (
251 				mime_filter, len + 3, FALSE);
252 			newlen = camel_uudecode_step (
253 				(guchar *) in, len,
254 				(guchar *) mime_filter->outbuf,
255 				&priv->state,
256 				(guint32 *) &priv->save);
257 		} else {
258 			newlen = 0;
259 		}
260 		break;
261 	default:
262 		g_warning ("unknown type %u in CamelMimeFilterBasic", priv->type);
263 		goto donothing;
264 	}
265 
266 	*out = mime_filter->outbuf;
267 	*outlen = newlen;
268 	*outprespace = mime_filter->outpre;
269 
270 	return;
271 donothing:
272 	*out = (gchar *) in;
273 	*outlen = len;
274 	*outprespace = prespace;
275 }
276 
277 /* should this 'flush' outstanding state/data bytes? */
278 static void
mime_filter_basic_reset(CamelMimeFilter * mime_filter)279 mime_filter_basic_reset (CamelMimeFilter *mime_filter)
280 {
281 	CamelMimeFilterBasicPrivate *priv;
282 
283 	priv = CAMEL_MIME_FILTER_BASIC (mime_filter)->priv;
284 
285 	switch (priv->type) {
286 	case CAMEL_MIME_FILTER_BASIC_QP_ENC:
287 		priv->state = -1;
288 		break;
289 	default:
290 		priv->state = 0;
291 	}
292 	priv->save = 0;
293 }
294 
295 static void
camel_mime_filter_basic_class_init(CamelMimeFilterBasicClass * class)296 camel_mime_filter_basic_class_init (CamelMimeFilterBasicClass *class)
297 {
298 	CamelMimeFilterClass *mime_filter_class;
299 
300 	mime_filter_class = CAMEL_MIME_FILTER_CLASS (class);
301 	mime_filter_class->filter = mime_filter_basic_filter;
302 	mime_filter_class->complete = mime_filter_basic_complete;
303 	mime_filter_class->reset = mime_filter_basic_reset;
304 }
305 
306 static void
camel_mime_filter_basic_init(CamelMimeFilterBasic * filter)307 camel_mime_filter_basic_init (CamelMimeFilterBasic *filter)
308 {
309 	filter->priv = camel_mime_filter_basic_get_instance_private (filter);
310 }
311 
312 /**
313  * camel_mime_filter_basic_new:
314  * @type: a #CamelMimeFilterBasicType type
315  *
316  * Create a new #CamelMimeFilterBasic object of type @type.
317  *
318  * Returns: a new #CamelMimeFilterBasic object
319  **/
320 CamelMimeFilter *
camel_mime_filter_basic_new(CamelMimeFilterBasicType type)321 camel_mime_filter_basic_new (CamelMimeFilterBasicType type)
322 {
323 	CamelMimeFilter *new;
324 
325 	switch (type) {
326 	case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
327 	case CAMEL_MIME_FILTER_BASIC_QP_ENC:
328 	case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
329 	case CAMEL_MIME_FILTER_BASIC_QP_DEC:
330 	case CAMEL_MIME_FILTER_BASIC_UU_ENC:
331 	case CAMEL_MIME_FILTER_BASIC_UU_DEC:
332 		new = g_object_new (CAMEL_TYPE_MIME_FILTER_BASIC, NULL);
333 		CAMEL_MIME_FILTER_BASIC (new)->priv->type = type;
334 		break;
335 	default:
336 		g_warning ("Invalid type of CamelMimeFilterBasic requested: %u", type);
337 		new = NULL;
338 		break;
339 	}
340 	camel_mime_filter_reset (new);
341 
342 	return new;
343 }
344 
345