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  */
18 
19 #include <string.h>
20 
21 #include "camel-mime-filter.h"
22 
23 /*#define MALLOC_CHECK */ /* for some malloc checking, requires mcheck enabled */
24 
25 /* only suitable for glibc */
26 #ifdef MALLOC_CHECK
27 #include <mcheck.h>
28 #endif
29 
30 struct _CamelMimeFilterPrivate {
31 	gchar *inbuf;
32 	gsize inlen;
33 };
34 
35 /* Compatible with filter() and complete() methods. */
36 typedef void	(*FilterMethod)			(CamelMimeFilter *filter,
37 						 const gchar *in,
38 						 gsize len,
39 						 gsize prespace,
40 						 gchar **out,
41 						 gsize *outlen,
42 						 gsize *outprespace);
43 
44 #define PRE_HEAD (64)
45 #define BACK_HEAD (64)
46 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(CamelMimeFilter,camel_mime_filter,G_TYPE_OBJECT)47 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (CamelMimeFilter, camel_mime_filter, G_TYPE_OBJECT)
48 
49 static void
50 mime_filter_finalize (GObject *object)
51 {
52 	CamelMimeFilter *mime_filter;
53 
54 	mime_filter = CAMEL_MIME_FILTER (object);
55 
56 	g_free (mime_filter->outreal);
57 	g_free (mime_filter->backbuf);
58 	g_free (mime_filter->priv->inbuf);
59 
60 	/* Chain up to parent's finalize() method. */
61 	G_OBJECT_CLASS (camel_mime_filter_parent_class)->finalize (object);
62 }
63 
64 static void
mime_filter_complete(CamelMimeFilter * mime_filter,const gchar * in,gsize len,gsize prespace,gchar ** out,gsize * outlen,gsize * outprespace)65 mime_filter_complete (CamelMimeFilter *mime_filter,
66                       const gchar *in,
67                       gsize len,
68                       gsize prespace,
69                       gchar **out,
70                       gsize *outlen,
71                       gsize *outprespace)
72 {
73 	/* default - do nothing */
74 }
75 
76 static void
camel_mime_filter_class_init(CamelMimeFilterClass * class)77 camel_mime_filter_class_init (CamelMimeFilterClass *class)
78 {
79 	GObjectClass *object_class;
80 
81 	object_class = G_OBJECT_CLASS (class);
82 	object_class->finalize = mime_filter_finalize;
83 
84 	class->complete = mime_filter_complete;
85 }
86 
87 static void
camel_mime_filter_init(CamelMimeFilter * mime_filter)88 camel_mime_filter_init (CamelMimeFilter *mime_filter)
89 {
90 	mime_filter->priv = camel_mime_filter_get_instance_private (mime_filter);
91 
92 	mime_filter->outreal = NULL;
93 	mime_filter->outbuf = NULL;
94 	mime_filter->outsize = 0;
95 
96 	mime_filter->backbuf = NULL;
97 	mime_filter->backsize = 0;
98 	mime_filter->backlen = 0;
99 }
100 
101 /**
102  * camel_mime_filter_new:
103  *
104  * Create a new #CamelMimeFilter object.
105  *
106  * Returns: a new #CamelMimeFilter
107  **/
108 CamelMimeFilter *
camel_mime_filter_new(void)109 camel_mime_filter_new (void)
110 {
111 	return g_object_new (CAMEL_TYPE_MIME_FILTER, NULL);
112 }
113 
114 #ifdef MALLOC_CHECK
115 static void
checkmem(gpointer p)116 checkmem (gpointer p)
117 {
118 	if (p) {
119 		gint status = mprobe (p);
120 
121 		switch (status) {
122 		case MCHECK_HEAD:
123 			printf ("Memory underrun at %p\n", p);
124 			abort ();
125 		case MCHECK_TAIL:
126 			printf ("Memory overrun at %p\n", p);
127 			abort ();
128 		case MCHECK_FREE:
129 			printf ("Double free %p\n", p);
130 			abort ();
131 		}
132 	}
133 }
134 #endif
135 
136 static void
filter_run(CamelMimeFilter * f,const gchar * in,gsize len,gsize prespace,gchar ** out,gsize * outlen,gsize * outprespace,FilterMethod method)137 filter_run (CamelMimeFilter *f,
138             const gchar *in,
139             gsize len,
140             gsize prespace,
141             gchar **out,
142             gsize *outlen,
143             gsize *outprespace,
144             FilterMethod method)
145 {
146 #ifdef MALLOC_CHECK
147 	checkmem (f->outreal);
148 	checkmem (f->backbuf);
149 #endif
150 	/*
151 	 * here we take a performance hit, if the input buffer doesn't
152 	 * have the pre-space required.  We make a buffer that does ...
153 	*/
154 	if (f->backlen > 0) {
155 		struct _CamelMimeFilterPrivate *p;
156 		gint newlen;
157 
158 		p = CAMEL_MIME_FILTER (f)->priv;
159 
160 		newlen = len + prespace + f->backlen;
161 		if (p->inlen < newlen) {
162 			/* NOTE: g_realloc copies data, we dont need that (slower) */
163 			g_free (p->inbuf);
164 			p->inbuf = g_malloc (newlen + PRE_HEAD);
165 			p->inlen = newlen + PRE_HEAD;
166 		}
167 
168 		/* copy to end of structure */
169 		memcpy (p->inbuf + p->inlen - len, in, len);
170 		in = p->inbuf + p->inlen - len;
171 		prespace = p->inlen - len;
172 
173 		/* preload any backed up data */
174 		memcpy ((gchar *) in - f->backlen, f->backbuf, f->backlen);
175 		in -= f->backlen;
176 		len += f->backlen;
177 		prespace -= f->backlen;
178 		f->backlen = 0;
179 	}
180 
181 #ifdef MALLOC_CHECK
182 	checkmem (f->outreal);
183 	checkmem (f->backbuf);
184 #endif
185 
186 	method (f, in, len, prespace, out, outlen, outprespace);
187 
188 #ifdef MALLOC_CHECK
189 	checkmem (f->outreal);
190 	checkmem (f->backbuf);
191 #endif
192 
193 }
194 
195 /**
196  * camel_mime_filter_filter:
197  * @filter: a #CamelMimeFilter object
198  * @in: (array length=len) (element-type guint8): input buffer
199  * @len: length of @in
200  * @prespace: amount of prespace
201  * @out: (out) (array length=outlen) (element-type guint8): pointer to the output buffer (to be set)
202  * @outlen: (out): pointer to the length of the output buffer (to be set)
203  * @outprespace: (out): pointer to the output prespace length (to be set)
204  *
205  * Passes the input buffer, @in, through @filter and generates an
206  * output buffer, @out.
207  **/
208 void
camel_mime_filter_filter(CamelMimeFilter * filter,const gchar * in,gsize len,gsize prespace,gchar ** out,gsize * outlen,gsize * outprespace)209 camel_mime_filter_filter (CamelMimeFilter *filter,
210                           const gchar *in,
211                           gsize len,
212                           gsize prespace,
213                           gchar **out,
214                           gsize *outlen,
215                           gsize *outprespace)
216 {
217 	CamelMimeFilterClass *class;
218 
219 	g_return_if_fail (CAMEL_IS_MIME_FILTER (filter));
220 	g_return_if_fail (in != NULL);
221 
222 	class = CAMEL_MIME_FILTER_GET_CLASS (filter);
223 	g_return_if_fail (class != NULL);
224 	g_return_if_fail (class->filter != NULL);
225 
226 	filter_run (
227 		filter, in, len, prespace, out,
228 		outlen, outprespace, class->filter);
229 }
230 
231 /**
232  * camel_mime_filter_complete:
233  * @filter: a #CamelMimeFilter object
234  * @in: (array length=len) (element-type guint8): input buffer
235  * @len: length of @in
236  * @prespace: amount of prespace
237  * @out: (out) (array length=outlen) (element-type guint8): pointer to the output buffer (to be set)
238  * @outlen: (out): pointer to the length of the output buffer (to be set)
239  * @outprespace: (out): pointer to the output prespace length (to be set)
240  *
241  * Passes the input buffer, @in, through @filter and generates an
242  * output buffer, @out and makes sure that all data is flushed to the
243  * output buffer. This must be the last filtering call made, no
244  * further calls to camel_mime_filter_filter() may be called on @filter
245  * until @filter has been reset using camel_mime_filter_reset().
246  **/
247 void
camel_mime_filter_complete(CamelMimeFilter * filter,const gchar * in,gsize len,gsize prespace,gchar ** out,gsize * outlen,gsize * outprespace)248 camel_mime_filter_complete (CamelMimeFilter *filter,
249                             const gchar *in,
250                             gsize len,
251                             gsize prespace,
252                             gchar **out,
253                             gsize *outlen,
254                             gsize *outprespace)
255 {
256 	CamelMimeFilterClass *class;
257 
258 	g_return_if_fail (CAMEL_IS_MIME_FILTER (filter));
259 	g_return_if_fail (in != NULL);
260 
261 	class = CAMEL_MIME_FILTER_GET_CLASS (filter);
262 	g_return_if_fail (class != NULL);
263 	g_return_if_fail (class->complete != NULL);
264 
265 	filter_run (
266 		filter, in, len, prespace, out,
267 		outlen, outprespace, class->complete);
268 }
269 
270 /**
271  * camel_mime_filter_reset:
272  * @filter: a #CamelMimeFilter object
273  *
274  * Resets the state on @filter so that it may be used again.
275  **/
276 void
camel_mime_filter_reset(CamelMimeFilter * filter)277 camel_mime_filter_reset (CamelMimeFilter *filter)
278 {
279 	CamelMimeFilterClass *class;
280 
281 	g_return_if_fail (CAMEL_IS_MIME_FILTER (filter));
282 
283 	class = CAMEL_MIME_FILTER_GET_CLASS (filter);
284 	g_return_if_fail (class != NULL);
285 
286 	if (class->reset != NULL)
287 		class->reset (filter);
288 
289 	/* could free some buffers, if they are really big? */
290 	filter->backlen = 0;
291 }
292 
293 /**
294  * camel_mime_filter_backup:
295  * @filter: a #CamelMimeFilter object
296  * @data: (array length=length) (element-type guint8): data buffer to backup
297  * @length: length of @data
298  *
299  * Saves @data to be used as prespace input data to the next call to
300  * camel_mime_filter_filter() or camel_mime_filter_complete().
301  *
302  * Note: New calls replace old data.
303  **/
304 void
camel_mime_filter_backup(CamelMimeFilter * filter,const gchar * data,gsize length)305 camel_mime_filter_backup (CamelMimeFilter *filter,
306                           const gchar *data,
307                           gsize length)
308 {
309 	if (filter->backsize < length) {
310 		/* g_realloc copies data, unnecessary overhead */
311 		g_free (filter->backbuf);
312 		filter->backbuf = g_malloc (length + BACK_HEAD);
313 		filter->backsize = length + BACK_HEAD;
314 	}
315 
316 	filter->backlen = length;
317 
318 	if (length || filter->backbuf)
319 		memcpy (filter->backbuf, data, length);
320 }
321 
322 /**
323  * camel_mime_filter_set_size:
324  * @filter: a #CamelMimeFilter object
325  * @size: requested amount of storage space
326  * @keep: %TRUE to keep existing buffered data or %FALSE otherwise
327  *
328  * Ensure that @filter has enough storage space to store @size bytes
329  * for filter output.
330  **/
331 void
camel_mime_filter_set_size(CamelMimeFilter * filter,gsize size,gint keep)332 camel_mime_filter_set_size (CamelMimeFilter *filter,
333                             gsize size,
334                             gint keep)
335 {
336 	if (filter->outsize < size) {
337 		gint offset = filter->outptr - filter->outreal;
338 		if (keep) {
339 			filter->outreal = g_realloc (filter->outreal, size + PRE_HEAD * 4);
340 		} else {
341 			g_free (filter->outreal);
342 			filter->outreal = g_malloc (size + PRE_HEAD * 4);
343 		}
344 		filter->outptr = filter->outreal + offset;
345 		filter->outbuf = filter->outreal + PRE_HEAD * 4;
346 		filter->outsize = size;
347 		/* this could be offset from the end of the structure, but
348 		 * this should be good enough */
349 		filter->outpre = PRE_HEAD * 4;
350 	}
351 }
352