Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-stream-filter.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library 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  *  Lesser 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 library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27
28 #include "gmime-stream-filter.h"
29
30
31 /**
32  * SECTION: gmime-stream-filter
33  * @title: GMimeStreamFilter
34  * @short_description: A filtering stream
35  * @see_also: #Filters
36  *
37  * A #GMimeStream meant for filtering data passing through it.
38  *
39  * This stream class is useful for converting data of one type to
40  * another using #GMimeFilter objects.
41  *
42  * When data passes through a #GMimeStreamFilter, it will pass through
43  * #GMimeFilter filters in the order they were added.
44  **/
45
46
47 #define READ_PAD (64)           /* bytes padded before buffer */
48 #define READ_SIZE (4096)
49
50 #define _PRIVATE(o) (((GMimeStreamFilter *)(o))->priv)
51
52 struct _filter {
53         struct _filter *next;
54         GMimeFilter *filter;
55         int id;
56 };
57
58 struct _GMimeStreamFilterPrivate {
59         struct _filter *filters;
60         int filterid;           /* next filter id */
61         
62         char *realbuffer;       /* buffer - READ_PAD */
63         char *buffer;           /* READ_SIZE bytes */
64         
65         char *filtered;         /* the filtered data */
66         size_t filteredlen;
67         
68         int last_was_read:1;    /* was the last op read or write? */
69         int flushed:1;          /* have the filters been flushed? */
70 };
71
72 static void g_mime_stream_filter_class_init (GMimeStreamFilterClass *klass);
73 static void g_mime_stream_filter_init (GMimeStreamFilter *stream, GMimeStreamFilterClass *klass);
74 static void g_mime_stream_filter_finalize (GObject *object);
75
76 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t n);
77 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t n);
78 static int stream_flush (GMimeStream *stream);
79 static int stream_close (GMimeStream *stream);
80 static gboolean stream_eos (GMimeStream *stream);
81 static int stream_reset (GMimeStream *stream);
82 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
83 static gint64 stream_tell (GMimeStream *stream);
84 static gint64 stream_length (GMimeStream *stream);
85 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
86
87
88 static GMimeStreamClass *parent_class = NULL;
89
90
91 GType
92 g_mime_stream_filter_get_type (void)
93 {
94         static GType type = 0;
95         
96         if (!type) {
97                 static const GTypeInfo info = {
98                         sizeof (GMimeStreamFilterClass),
99                         NULL, /* base_class_init */
100                         NULL, /* base_class_finalize */
101                         (GClassInitFunc) g_mime_stream_filter_class_init,
102                         NULL, /* class_finalize */
103                         NULL, /* class_data */
104                         sizeof (GMimeStreamFilter),
105                         0,    /* n_preallocs */
106                         (GInstanceInitFunc) g_mime_stream_filter_init,
107                 };
108                 
109                 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamFilter", &info, 0);
110         }
111         
112         return type;
113 }
114
115
116 static void
117 g_mime_stream_filter_class_init (GMimeStreamFilterClass *klass)
118 {
119         GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
120         GObjectClass *object_class = G_OBJECT_CLASS (klass);
121         
122         parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
123         
124         object_class->finalize = g_mime_stream_filter_finalize;
125         
126         stream_class->read = stream_read;
127         stream_class->write = stream_write;
128         stream_class->flush = stream_flush;
129         stream_class->close = stream_close;
130         stream_class->eos = stream_eos;
131         stream_class->reset = stream_reset;
132         stream_class->seek = stream_seek;
133         stream_class->tell = stream_tell;
134         stream_class->length = stream_length;
135         stream_class->substream = stream_substream;
136 }
137
138 static void
139 g_mime_stream_filter_init (GMimeStreamFilter *stream, GMimeStreamFilterClass *klass)
140 {
141         stream->source = NULL;
142         stream->priv = g_new (struct _GMimeStreamFilterPrivate, 1);
143         stream->priv->filters = NULL;
144         stream->priv->filterid = 0;
145         stream->priv->realbuffer = g_malloc (READ_SIZE + READ_PAD);
146         stream->priv->buffer = stream->priv->realbuffer + READ_PAD;
147         stream->priv->last_was_read = TRUE;
148         stream->priv->filteredlen = 0;
149         stream->priv->flushed = FALSE;
150 }
151
152 static void
153 g_mime_stream_filter_finalize (GObject *object)
154 {
155         GMimeStreamFilter *filter = (GMimeStreamFilter *) object;
156         struct _GMimeStreamFilterPrivate *p = filter->priv;
157         struct _filter *fn, *f;
158         
159         f = p->filters;
160         while (f) {
161                 fn = f->next;
162                 g_object_unref (f->filter);
163                 g_free (f);
164                 f = fn;
165         }
166         
167         g_free (p->realbuffer);
168         g_free (p);
169         
170         if (filter->source)
171                 g_object_unref (filter->source);
172         
173         G_OBJECT_CLASS (parent_class)->finalize (object);
174 }
175
176
177 static ssize_t
178 stream_read (GMimeStream *stream, char *buf, size_t n)
179 {
180         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
181         struct _GMimeStreamFilterPrivate *priv = filter->priv;
182         struct _filter *f;
183         ssize_t nread;
184         
185         priv->last_was_read = TRUE;
186         
187         if (priv->filteredlen <= 0) {
188                 size_t presize = READ_PAD;
189                 
190                 nread = g_mime_stream_read (filter->source, priv->buffer, READ_SIZE);
191                 if (nread <= 0) {
192                         /* this is somewhat untested */
193                         if (g_mime_stream_eos (filter->source) && !priv->flushed) {
194                                 priv->filtered = priv->buffer;
195                                 priv->filteredlen = 0;
196                                 f = priv->filters;
197                                 
198                                 while (f != NULL) {
199                                         g_mime_filter_complete (f->filter, priv->filtered, priv->filteredlen,
200                                                                 presize, &priv->filtered, &priv->filteredlen,
201                                                                 &presize);
202                                         f = f->next;
203                                 }
204                                 
205                                 nread = priv->filteredlen;
206                                 priv->flushed = TRUE;
207                         }
208                         
209                         if (nread <= 0)
210                                 return nread;
211                 } else {
212                         priv->filtered = priv->buffer;
213                         priv->filteredlen = nread;
214                         priv->flushed = FALSE;
215                         f = priv->filters;
216                         
217                         while (f != NULL) {
218                                 g_mime_filter_filter (f->filter, priv->filtered, priv->filteredlen, presize,
219                                                       &priv->filtered, &priv->filteredlen, &presize);
220                                 
221                                 f = f->next;
222                         }
223                 }
224         }
225         
226         nread = MIN (n, priv->filteredlen);
227         memcpy (buf, priv->filtered, nread);
228         priv->filteredlen -= nread;
229         priv->filtered += nread;
230         
231         return nread;
232 }
233
234 static ssize_t
235 stream_write (GMimeStream *stream, const char *buf, size_t n)
236 {
237         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
238         struct _GMimeStreamFilterPrivate *priv = filter->priv;
239         char *buffer = (char *) buf;
240         ssize_t nwritten = n;
241         struct _filter *f;
242         size_t presize;
243         
244         priv->last_was_read = FALSE;
245         priv->flushed = FALSE;
246         
247         f = priv->filters;
248         presize = 0;
249         while (f != NULL) {
250                 g_mime_filter_filter (f->filter, buffer, n, presize, &buffer, &n, &presize);
251                 
252                 f = f->next;
253         }
254         
255         if (g_mime_stream_write (filter->source, buffer, n) == -1)
256                 return -1;
257         
258         /* return original input len because that's what our caller expects */
259         return nwritten;
260 }
261
262 static int
263 stream_flush (GMimeStream *stream)
264 {
265         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
266         struct _GMimeStreamFilterPrivate *priv = filter->priv;
267         size_t presize, len;
268         struct _filter *f;
269         char *buffer;
270         
271         if (priv->last_was_read) {
272                 /* no-op */
273                 return 0;
274         }
275         
276         buffer = "";
277         len = 0;
278         presize = 0;
279         f = priv->filters;
280         
281         while (f != NULL) {
282                 g_mime_filter_complete (f->filter, buffer, len, presize, &buffer, &len, &presize);
283                 
284                 f = f->next;
285         }
286         
287         if (len > 0 && g_mime_stream_write (filter->source, buffer, len) == -1)
288                 return -1;
289         
290         return g_mime_stream_flush (filter->source);
291 }
292
293 static int
294 stream_close (GMimeStream *stream)
295 {
296         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
297         struct _GMimeStreamFilterPrivate *priv = filter->priv;
298         
299         if (!priv->last_was_read)
300                 stream_flush (stream);
301         
302         return g_mime_stream_close (filter->source);
303 }
304
305 static gboolean
306 stream_eos (GMimeStream *stream)
307 {
308         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
309         struct _GMimeStreamFilterPrivate *priv = filter->priv;
310         
311         if (priv->filteredlen > 0)
312                 return FALSE;
313         
314         if (!priv->flushed)
315                 return FALSE;
316         
317         return g_mime_stream_eos (filter->source);
318 }
319
320 static int
321 stream_reset (GMimeStream *stream)
322 {
323         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
324         struct _GMimeStreamFilterPrivate *priv = filter->priv;
325         struct _filter *f;
326         
327         if (g_mime_stream_reset (filter->source) == -1)
328                 return -1;
329         
330         priv->filteredlen = 0;
331         priv->flushed = FALSE;
332         
333         /* and reset filters */
334         f = priv->filters;
335         while (f != NULL) {
336                 g_mime_filter_reset (f->filter);
337                 f = f->next;
338         }
339         
340         return 0;
341 }
342
343 static gint64
344 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
345 {
346         return -1;
347 }
348
349 static gint64
350 stream_tell (GMimeStream *stream)
351 {
352         return -1;
353 }
354
355 static gint64
356 stream_length (GMimeStream *stream)
357 {
358         return stream->bound_end - stream->bound_start;
359 }
360
361 static GMimeStream *
362 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
363 {
364         GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
365         GMimeStreamFilter *sub;
366         
367         sub = g_object_newv (GMIME_TYPE_STREAM_FILTER, 0, NULL);
368         sub->source = filter->source;
369         g_object_ref (sub->source);
370         
371         if (filter->priv->filters) {
372                 struct _filter *f, *sn, *s = NULL;
373                 
374                 f = filter->priv->filters;
375                 while (f) {
376                         sn = g_new (struct _filter, 1);
377                         sn->filter = g_mime_filter_copy (f->filter);
378                         sn->id = f->id;
379                         
380                         if (s) {
381                                 s->next = sn;
382                                 s = sn;
383                         } else {
384                                 s = sub->priv->filters = sn;
385                         }
386                         
387                         f = f->next;
388                 }
389                 
390                 s->next = NULL;
391                 
392                 sub->priv->filterid = filter->priv->filterid;
393         }
394         
395         g_mime_stream_construct (GMIME_STREAM (filter), start, end);
396         
397         return GMIME_STREAM (sub);
398 }
399
400
401 /**
402  * g_mime_stream_filter_new:
403  * @stream: source stream
404  *
405  * Creates a new #GMimeStreamFilter object using @stream as the source
406  * stream.
407  *
408  * Returns: a new filter stream with @stream as its source.
409  **/
410 GMimeStream *
411 g_mime_stream_filter_new (GMimeStream *stream)
412 {
413         GMimeStreamFilter *filter;
414         
415         g_return_val_if_fail (GMIME_IS_STREAM (stream), NULL);
416         
417         filter = g_object_newv (GMIME_TYPE_STREAM_FILTER, 0, NULL);
418         filter->source = stream;
419         g_object_ref (stream);
420         
421         g_mime_stream_construct (GMIME_STREAM (filter),
422                                  stream->bound_start,
423                                  stream->bound_end);
424         
425         return GMIME_STREAM (filter);
426 }
427
428
429 /**
430  * g_mime_stream_filter_add:
431  * @stream: a #GMimeStreamFilter
432  * @filter: a #GMimeFilter
433  *
434  * Adds @filter to @stream. Filters are applied in the same order in
435  * which they are added.
436  *
437  * Returns: an id for the filter.
438  **/
439 int
440 g_mime_stream_filter_add (GMimeStreamFilter *stream, GMimeFilter *filter)
441 {
442         struct _GMimeStreamFilterPrivate *priv;
443         struct _filter *f, *fn;
444         
445         g_return_val_if_fail (GMIME_IS_STREAM_FILTER (stream), -1);
446         g_return_val_if_fail (GMIME_IS_FILTER (filter), -1);
447         
448         g_object_ref (filter);
449         
450         priv = stream->priv;
451         
452         fn = g_new (struct _filter, 1);
453         fn->next = NULL;
454         fn->filter = filter;
455         fn->id = priv->filterid++;
456         
457         f = (struct _filter *) &priv->filters;
458         while (f->next)
459                 f = f->next;
460         
461         f->next = fn;
462         fn->next = NULL;
463         
464         return fn->id;
465 }
466
467
468 /**
469  * g_mime_stream_filter_remove:
470  * @stream: a #GMimeStreamFilter
471  * @id: filter id
472  *
473  * Removed a filter from the stream based on the id (as returned from
474  * filter_add).
475  **/
476 void
477 g_mime_stream_filter_remove (GMimeStreamFilter *stream, int id)
478 {
479         struct _GMimeStreamFilterPrivate *priv;
480         struct _filter *f, *fn;
481         
482         g_return_if_fail (GMIME_IS_STREAM_FILTER (stream));
483         
484         priv = stream->priv;
485         
486         if (id == -1)
487                 return;
488         
489         f = (struct _filter *) &priv->filters;
490         while (f && f->next) {
491                 fn = f->next;
492                 if (fn->id == id) {
493                         f->next = fn->next;
494                         g_object_unref (fn->filter);
495                         g_free (fn);
496                 }
497                 f = f->next;
498         }
499 }