Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-mime-filter.c
1 /*
2  *  Copyright (C) 2000 Ximian Inc.
3  *
4  *  Authors: Michael Zucchi <notzed@ximian.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program 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  * 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 program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include <string.h>
22
23 #include "camel-mime-filter.h"
24
25 /*#define MALLOC_CHECK */ /* for some malloc checking, requires mcheck enabled */
26
27 /* only suitable for glibc */
28 #ifdef MALLOC_CHECK
29 #include <mcheck.h>
30 #endif
31
32 struct _CamelMimeFilterPrivate {
33         char *inbuf;
34         size_t inlen;
35 };
36
37 #define PRE_HEAD (64)
38 #define BACK_HEAD (64)
39 #define _PRIVATE(o) (((CamelMimeFilter *)(o))->priv)
40 #define FCLASS(o) ((CamelMimeFilterClass *)(CAMEL_OBJECT_GET_CLASS(o)))
41
42 static CamelObjectClass *camel_mime_filter_parent;
43
44 static void complete (CamelMimeFilter *mf, char *in, size_t len, 
45                       size_t prespace, char **out, size_t *outlen, 
46                       size_t *outprespace);
47
48 static void
49 camel_mime_filter_class_init (CamelMimeFilterClass *klass)
50 {
51         camel_mime_filter_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
52
53         klass->complete = complete;
54 }
55
56 static void
57 camel_mime_filter_init (CamelMimeFilter *obj)
58 {
59         obj->outreal = NULL;
60         obj->outbuf = NULL;
61         obj->outsize = 0;
62
63         obj->backbuf = NULL;
64         obj->backsize = 0;
65         obj->backlen = 0;
66
67         _PRIVATE(obj) = g_malloc0(sizeof(*obj->priv));
68 }
69
70 static void
71 camel_mime_filter_finalize(CamelObject *o)
72 {
73         CamelMimeFilter *f = (CamelMimeFilter *)o;
74         struct _CamelMimeFilterPrivate *p = _PRIVATE(f);
75
76         g_free(f->outreal);
77         g_free(f->backbuf);
78         g_free(p->inbuf);
79         g_free(p);
80 }
81
82 CamelType
83 camel_mime_filter_get_type (void)
84 {
85         static CamelType camel_mime_filter_type = CAMEL_INVALID_TYPE;
86         
87         if (camel_mime_filter_type == CAMEL_INVALID_TYPE) {
88                 camel_mime_filter_type = camel_type_register (CAMEL_OBJECT_TYPE, "CamelMimeFilter",
89                                                               sizeof (CamelMimeFilter),
90                                                               sizeof (CamelMimeFilterClass),
91                                                               (CamelObjectClassInitFunc) camel_mime_filter_class_init,
92                                                               NULL,
93                                                               (CamelObjectInitFunc) camel_mime_filter_init,
94                                                               (CamelObjectFinalizeFunc) camel_mime_filter_finalize);
95         }
96         
97         return camel_mime_filter_type;
98 }
99
100 static void
101 complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
102 {
103         /* default - do nothing */
104 }
105
106
107 /**
108  * camel_mime_filter_new:
109  *
110  * Create a new #CamelMimeFilter object.
111  * 
112  * Returns a new #CamelMimeFilter
113  **/
114 CamelMimeFilter *
115 camel_mime_filter_new (void)
116 {
117         CamelMimeFilter *new = CAMEL_MIME_FILTER ( camel_object_new (camel_mime_filter_get_type ()));
118         return new;
119 }
120
121 #ifdef MALLOC_CHECK
122 static void
123 checkmem(void *p)
124 {
125         if (p) {
126                 int status = mprobe(p);
127
128                 switch (status) {
129                 case MCHECK_HEAD:
130                         printf("Memory underrun at %p\n", p);
131                         abort();
132                 case MCHECK_TAIL:
133                         printf("Memory overrun at %p\n", p);
134                         abort();
135                 case MCHECK_FREE:
136                         printf("Double free %p\n", p);
137                         abort();
138                 }
139         }
140 }
141 #endif
142
143 static void filter_run(CamelMimeFilter *f,
144                        char *in, size_t len, size_t prespace,
145                        char **out, size_t *outlen, size_t *outprespace,
146                        void (*filterfunc)(CamelMimeFilter *f,
147                                           char *in, size_t len, size_t prespace,
148                                           char **out, size_t *outlen, size_t *outprespace))
149 {
150         struct _CamelMimeFilterPrivate *p;
151
152 #ifdef MALLOC_CHECK
153         checkmem(f->outreal);
154         checkmem(f->backbuf);
155 #endif
156         /*
157           here we take a performance hit, if the input buffer doesn't
158           have the pre-space required.  We make a buffer that does ...
159         */
160         if (prespace < f->backlen) {
161                 int newlen = len+prespace+f->backlen;
162                 p = _PRIVATE(f);
163                 if (p->inlen < newlen) {
164                         /* NOTE: g_realloc copies data, we dont need that (slower) */
165                         g_free(p->inbuf);
166                         p->inbuf = g_malloc(newlen+PRE_HEAD);
167                         p->inlen = newlen+PRE_HEAD;
168                 }
169                 /* copy to end of structure */
170                 memcpy(p->inbuf+p->inlen - len, in, len);
171                 in = p->inbuf+p->inlen - len;
172                 prespace = p->inlen - len;
173         }
174
175 #ifdef MALLOC_CHECK
176         checkmem(f->outreal);
177         checkmem(f->backbuf);
178 #endif
179
180         /* preload any backed up data */
181         if (f->backlen > 0) {
182                 memcpy(in-f->backlen, f->backbuf, f->backlen);
183                 in -= f->backlen;
184                 len += f->backlen;
185                 prespace -= f->backlen;
186                 f->backlen = 0;
187         }
188         
189         filterfunc(f, in, len, prespace, out, outlen, outprespace);
190
191 #ifdef MALLOC_CHECK
192         checkmem(f->outreal);
193         checkmem(f->backbuf);
194 #endif
195
196 }
197
198
199 /**
200  * camel_mime_filter_filter:
201  * @filter: a #CamelMimeFilter object
202  * @in: input buffer
203  * @len: length of @in
204  * @prespace: amount of prespace
205  * @out: pointer to the output buffer (to be set)
206  * @outlen: pointer to the length of the output buffer (to be set)
207  * @outprespace: pointer to the output prespace length (to be set)
208  *
209  * Passes the input buffer, @in, through @filter and generates an
210  * output buffer, @out.
211  **/
212 void
213 camel_mime_filter_filter (CamelMimeFilter *filter,
214                           char *in, size_t len, size_t prespace,
215                           char **out, size_t *outlen, size_t *outprespace)
216 {
217         if (FCLASS(filter)->filter)
218                 filter_run(filter, in, len, prespace, out, outlen, outprespace, FCLASS(filter)->filter);
219         else
220                 g_error("Filter function unplmenented in class");
221 }
222
223
224 /**
225  * camel_mime_filter_complete:
226  * @filter: a #CamelMimeFilter object
227  * @in: input buffer
228  * @len: length of @in
229  * @prespace: amount of prespace
230  * @out: pointer to the output buffer (to be set)
231  * @outlen: pointer to the length of the output buffer (to be set)
232  * @outprespace: pointer to the output prespace length (to be set)
233  *
234  * Passes the input buffer, @in, through @filter and generates an
235  * output buffer, @out and makes sure that all data is flushed to the
236  * output buffer. This must be the last filtering call made, no
237  * further calls to #camel_mime_filter_filter may be called on @filter
238  * until @filter has been reset using #camel_mime_filter_reset.
239  **/
240 void
241 camel_mime_filter_complete (CamelMimeFilter *filter,
242                             char *in, size_t len, size_t prespace,
243                             char **out, size_t *outlen, size_t *outprespace)
244 {
245         if (FCLASS(filter)->complete)
246                 filter_run(filter, in, len, prespace, out, outlen, outprespace, FCLASS(filter)->complete);
247 }
248
249
250 /**
251  * camel_mime_filter_reset:
252  * @filter: a #CamelMimeFilter object
253  *
254  * Resets the state on @filter so that it may be used again.
255  **/
256 void
257 camel_mime_filter_reset(CamelMimeFilter *filter)
258 {
259         if (FCLASS(filter)->reset) {
260                 FCLASS(filter)->reset(filter);
261         }
262
263         /* could free some buffers, if they are really big? */
264         filter->backlen = 0;
265 }
266
267
268 /**
269  * camel_mime_filter_backup:
270  * @filter: a #camelMimeFilter object
271  * @data: data buffer to backup
272  * @length: length of @data
273  *
274  * Saves @data to be used as prespace input data to the next call to
275  * #camel_mime_filter_filter or #camel_mime_filter_complete.
276  *
277  * Note: New calls replace old data.
278  **/
279 void
280 camel_mime_filter_backup(CamelMimeFilter *filter, const char *data, size_t length)
281 {
282         if (filter->backsize < length) {
283                 /* g_realloc copies data, unnecessary overhead */
284                 g_free(filter->backbuf);
285                 filter->backbuf = g_malloc(length+BACK_HEAD);
286                 filter->backsize = length+BACK_HEAD;
287         }
288         filter->backlen = length;
289         memcpy(filter->backbuf, data, length);
290 }
291
292
293 /**
294  * camel_mime_filter_set_size:
295  * @filter: a #camelMimeFilter object
296  * @size: requested amount of storage space
297  * @keep: %TRUE to keep existing buffered data or %FALSE otherwise
298  *
299  * Ensure that @filter has enough storage space to store @size bytes
300  * for filter output.
301  **/
302 void
303 camel_mime_filter_set_size(CamelMimeFilter *filter, size_t size, int keep)
304 {
305         if (filter->outsize < size) {
306                 int offset = filter->outptr - filter->outreal;
307                 if (keep) {
308                         filter->outreal = g_realloc(filter->outreal, size + PRE_HEAD*4);
309                 } else {
310                         g_free(filter->outreal);
311                         filter->outreal = g_malloc(size + PRE_HEAD*4);
312                 }
313                 filter->outptr = filter->outreal + offset;
314                 filter->outbuf = filter->outreal + PRE_HEAD*4;
315                 filter->outsize = size;
316                 /* this could be offset from the end of the structure, but 
317                    this should be good enough */
318                 filter->outpre = PRE_HEAD*4;
319         }
320 }