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