Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-stream-filter.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /*
3  *  Copyright (C) 2000 Ximian Inc.
4  *
5  *  Authors: Michael Zucchi <notzed@ximian.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "camel-stream-filter.h"
31
32 #define d(x) 
33
34 /* use my malloc debugger? */
35 /*extern void g_check(void *mp);*/
36 #define g_check(x)
37
38 struct _filter {
39         struct _filter *next;
40         int id;
41         CamelMimeFilter *filter;
42 };
43
44 struct _CamelStreamFilterPrivate {
45         struct _filter *filters;
46         int filterid;           /* next filter id */
47         
48         char *realbuffer;       /* buffer - READ_PAD */
49         char *buffer;           /* READ_SIZE bytes */
50
51         char *filtered;         /* the filtered data */
52         size_t filteredlen;
53
54         guint last_was_read:1;  /* was the last op read or write? */
55         guint flushed:1;        /* were the filters flushed? */
56 };
57
58 #define READ_PAD (128)          /* bytes padded before buffer */
59 #define READ_SIZE (4096)
60
61 #define _PRIVATE(o) (((CamelStreamFilter *)(o))->priv)
62
63 static void camel_stream_filter_class_init (CamelStreamFilterClass *klass);
64 static void camel_stream_filter_init       (CamelStreamFilter *obj);
65
66 static  ssize_t   do_read       (CamelStream *stream, char *buffer, size_t n);
67 static  ssize_t   do_write      (CamelStream *stream, const char *buffer, size_t n);
68 static  int       do_flush      (CamelStream *stream);
69 static  int       do_close      (CamelStream *stream);
70 static  gboolean  do_eos        (CamelStream *stream);
71 static  int       do_reset      (CamelStream *stream);
72
73 static CamelStreamClass *camel_stream_filter_parent;
74
75 static void
76 camel_stream_filter_class_init (CamelStreamFilterClass *klass)
77 {
78         CamelStreamClass *camel_stream_class = (CamelStreamClass *) klass;
79
80         camel_stream_filter_parent = CAMEL_STREAM_CLASS (camel_type_get_global_classfuncs (camel_stream_get_type ()));
81
82         camel_stream_class->read = do_read;
83         camel_stream_class->write = do_write;
84         camel_stream_class->flush = do_flush;
85         camel_stream_class->close = do_close;
86         camel_stream_class->eos = do_eos; 
87         camel_stream_class->reset = do_reset;
88
89 }
90
91 static void
92 camel_stream_filter_init (CamelStreamFilter *obj)
93 {
94         struct _CamelStreamFilterPrivate *p;
95         
96         _PRIVATE(obj) = p = g_malloc0(sizeof(*p));
97         p->realbuffer = g_malloc(READ_SIZE + READ_PAD);
98         p->buffer = p->realbuffer + READ_PAD;
99         p->last_was_read = TRUE;
100         p->flushed = FALSE;
101 }
102
103 static void
104 camel_stream_filter_finalize(CamelObject *o)
105 {
106         CamelStreamFilter *filter = (CamelStreamFilter *)o;
107         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
108         struct _filter *fn, *f;
109
110         f = p->filters;
111         while (f) {
112                 fn = f->next;
113                 camel_object_unref((CamelObject *)f->filter);
114                 g_free(f);
115                 f = fn;
116         }
117         g_free(p->realbuffer);
118         g_free(p);
119         camel_object_unref((CamelObject *)filter->source);
120 }
121
122 CamelType
123 camel_stream_filter_get_type (void)
124 {
125         static CamelType type = CAMEL_INVALID_TYPE;
126         
127         if (type == CAMEL_INVALID_TYPE) {
128                 type = camel_type_register (CAMEL_STREAM_TYPE, "CamelStreamFilter",
129                                             sizeof (CamelStreamFilter),
130                                             sizeof (CamelStreamFilterClass),
131                                             (CamelObjectClassInitFunc) camel_stream_filter_class_init,
132                                             NULL,
133                                             (CamelObjectInitFunc) camel_stream_filter_init,
134                                             (CamelObjectFinalizeFunc) camel_stream_filter_finalize);
135         }
136         
137         return type;
138 }
139
140 /**
141  * camel_stream_filter_new:
142  *
143  * Create a new #CamelStreamFilter object.
144  * 
145  * Returns a new #CamelStreamFilter object.
146  **/
147 CamelStreamFilter *
148 camel_stream_filter_new_with_stream(CamelStream *stream)
149 {
150         CamelStreamFilter *new = CAMEL_STREAM_FILTER ( camel_object_new (camel_stream_filter_get_type ()));
151
152         new->source = stream;
153         camel_object_ref ((CamelObject *)stream);
154
155         return new;
156 }
157
158 /**
159  * camel_stream_filter_add:
160  * @stream: a #CamelStreamFilter object
161  * @filter: a #CamelMimeFilter object
162  * 
163  * Add a new #CamelMimeFilter to execute during the processing of this
164  * stream.  Each filter added is processed after the previous one.
165  *
166  * Note that a filter should only be added to a single stream
167  * at a time, otherwise unpredictable results may occur.
168  * 
169  * Returns a filter id for the added @filter.
170  **/
171 int
172 camel_stream_filter_add (CamelStreamFilter *stream, CamelMimeFilter *filter)
173 {
174         struct _CamelStreamFilterPrivate *p = _PRIVATE(stream);
175         struct _filter *fn, *f;
176
177         fn = g_malloc(sizeof(*fn));
178         fn->id = p->filterid++;
179         fn->filter = filter;
180         camel_object_ref (filter);
181
182         /* sure, we could use a GList, but we wouldn't save much */
183         f = (struct _filter *)&p->filters;
184         while (f->next)
185                 f = f->next;
186         f->next = fn;
187         fn->next = NULL;
188         return fn->id;
189 }
190
191 /**
192  * camel_stream_filter_remove:
193  * @stream: a #CamelStreamFilter object
194  * @id: Filter id, as returned from #camel_stream_filter_add
195  * 
196  * Remove a processing filter from the stream by id.
197  **/
198 void
199 camel_stream_filter_remove(CamelStreamFilter *stream, int id)
200 {
201         struct _CamelStreamFilterPrivate *p = _PRIVATE(stream);
202         struct _filter *fn, *f;
203
204         f = (struct _filter *)&p->filters;
205         while (f && f->next) {
206                 fn = f->next;
207                 if (fn->id == id) {
208                         f->next = fn->next;
209                         camel_object_unref(fn->filter);
210                         g_free(fn);
211                 }
212                 f = f->next;
213         }
214 }
215
216 static ssize_t
217 do_read (CamelStream *stream, char *buffer, size_t n)
218 {
219         CamelStreamFilter *filter = (CamelStreamFilter *)stream;
220         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
221         ssize_t size;
222         struct _filter *f;
223
224         p->last_was_read = TRUE;
225
226         g_check(p->realbuffer);
227
228         if (p->filteredlen<=0) {
229                 size_t presize = READ_PAD;
230
231                 size = camel_stream_read(filter->source, p->buffer, READ_SIZE);
232                 if (size <= 0) {
233                         /* this is somewhat untested */
234                         if (camel_stream_eos(filter->source)) {
235                                 f = p->filters;
236                                 p->filtered = p->buffer;
237                                 p->filteredlen = 0;
238                                 while (f) {
239                                         camel_mime_filter_complete(f->filter, p->filtered, p->filteredlen,
240                                                                    presize, &p->filtered, &p->filteredlen, &presize);
241                                         g_check(p->realbuffer);
242                                         f = f->next;
243                                 }
244                                 size = p->filteredlen;
245                                 p->flushed = TRUE;
246                         }
247                         if (size <= 0)
248                                 return size;
249                 } else {
250                         f = p->filters;
251                         p->filtered = p->buffer;
252                         p->filteredlen = size;
253
254                         d(printf ("\n\nOriginal content (%s): '", ((CamelObject *)filter->source)->klass->name));
255                         d(fwrite(p->filtered, sizeof(char), p->filteredlen, stdout));
256                         d(printf("'\n"));
257
258                         while (f) {
259                                 camel_mime_filter_filter(f->filter, p->filtered, p->filteredlen, presize,
260                                                          &p->filtered, &p->filteredlen, &presize);
261                                 g_check(p->realbuffer);
262
263                                 d(printf ("Filtered content (%s): '", ((CamelObject *)f->filter)->klass->name));
264                                 d(fwrite(p->filtered, sizeof(char), p->filteredlen, stdout));
265                                 d(printf("'\n"));
266
267                                 f = f->next;
268                         }
269                 }
270         }
271
272         size = MIN(n, p->filteredlen);
273         memcpy(buffer, p->filtered, size);
274         p->filteredlen -= size;
275         p->filtered += size;
276
277         g_check(p->realbuffer);
278
279         return size;
280 }
281
282 /* Note: Since the caller expects to write out as much as they asked us to
283    write (for 'success'), we return what they asked us to write (for 'success')
284    rather than the true number of written bytes */
285 static ssize_t
286 do_write (CamelStream *stream, const char *buf, size_t n)
287 {
288         CamelStreamFilter *filter = (CamelStreamFilter *)stream;
289         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
290         struct _filter *f;
291         size_t presize, len, left = n;
292         char *buffer, realbuffer[READ_SIZE+READ_PAD];
293
294         p->last_was_read = FALSE;
295
296         d(printf ("\n\nWriting: Original content (%s): '", ((CamelObject *)filter->source)->klass->name));
297         d(fwrite(buf, sizeof(char), n, stdout));
298         d(printf("'\n"));
299
300         g_check(p->realbuffer);
301
302         while (left) {
303                 /* Sigh, since filters expect non const args, copy the input first, we do this in handy sized chunks */
304                 len = MIN(READ_SIZE, left);
305                 buffer = realbuffer + READ_PAD;
306                 memcpy(buffer, buf, len);
307                 buf += len;
308                 left -= len;
309
310                 f = p->filters;
311                 presize = READ_PAD;
312                 while (f) {
313                         camel_mime_filter_filter(f->filter, buffer, len, presize, &buffer, &len, &presize);
314
315                         g_check(p->realbuffer);
316
317                         d(printf ("Filtered content (%s): '", ((CamelObject *)f->filter)->klass->name));
318                         d(fwrite(buffer, sizeof(char), len, stdout));
319                         d(printf("'\n"));
320
321                         f = f->next;
322                 }
323
324                 if (camel_stream_write(filter->source, buffer, len) != len)
325                         return -1;
326         }
327
328         g_check(p->realbuffer);
329
330         return n;
331 }
332
333 static int
334 do_flush (CamelStream *stream)
335 {
336         CamelStreamFilter *filter = (CamelStreamFilter *)stream;
337         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
338         struct _filter *f;
339         char *buffer;
340         size_t presize;
341         size_t len;
342         
343         if (p->last_was_read)
344                 return 0;
345         
346         buffer = "";
347         len = 0;
348         presize = 0;
349         f = p->filters;
350         
351         d(printf ("\n\nFlushing: Original content (%s): '", ((CamelObject *)filter->source)->klass->name));
352         d(fwrite(buffer, sizeof(char), len, stdout));
353         d(printf("'\n"));
354
355         while (f) {
356                 camel_mime_filter_complete(f->filter, buffer, len, presize, &buffer, &len, &presize);
357
358                 d(printf ("Filtered content (%s): '", ((CamelObject *)f->filter)->klass->name));
359                 d(fwrite(buffer, sizeof(char), len, stdout));
360                 d(printf("'\n"));
361
362                 f = f->next;
363         }
364         if (len > 0 && camel_stream_write(filter->source, buffer, len) == -1)
365                 return -1;
366         return camel_stream_flush(filter->source);
367 }
368
369 static int
370 do_close (CamelStream *stream)
371 {
372         CamelStreamFilter *filter = (CamelStreamFilter *)stream;
373         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
374
375         if (!p->last_was_read) {
376                 do_flush(stream);
377         }
378         return camel_stream_close(filter->source);
379 }
380
381 static gboolean
382 do_eos (CamelStream *stream)
383 {
384         CamelStreamFilter *filter = (CamelStreamFilter *)stream;
385         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
386         
387         if (p->filteredlen > 0)
388                 return FALSE;
389         
390         if (!p->flushed)
391                 return FALSE;
392         
393         return camel_stream_eos(filter->source);
394 }
395
396 static int
397 do_reset (CamelStream *stream)
398 {
399         CamelStreamFilter *filter = (CamelStreamFilter *)stream;
400         struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
401         struct _filter *f;
402
403         p->filteredlen = 0;
404         p->flushed = FALSE;
405         
406         /* and reset filters */
407         f = p->filters;
408         while (f) {
409                 camel_mime_filter_reset(f->filter);
410                 f = f->next;
411         }
412
413         return camel_stream_reset(filter->source);
414 }
415