1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
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.
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.
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
28 #include "gmime-stream-filter.h"
32 * SECTION: gmime-stream-filter
33 * @title: GMimeStreamFilter
34 * @short_description: A filtering stream
37 * A #GMimeStream meant for filtering data passing through it.
39 * This stream class is useful for converting data of one type to
40 * another using #GMimeFilter objects.
42 * When data passes through a #GMimeStreamFilter, it will pass through
43 * #GMimeFilter filters in the order they were added.
47 #define READ_PAD (64) /* bytes padded before buffer */
48 #define READ_SIZE (4096)
50 #define _PRIVATE(o) (((GMimeStreamFilter *)(o))->priv)
58 struct _GMimeStreamFilterPrivate {
59 struct _filter *filters;
60 int filterid; /* next filter id */
62 char *realbuffer; /* buffer - READ_PAD */
63 char *buffer; /* READ_SIZE bytes */
65 char *filtered; /* the filtered data */
68 int last_was_read:1; /* was the last op read or write? */
69 int flushed:1; /* have the filters been flushed? */
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);
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);
88 static GMimeStreamClass *parent_class = NULL;
92 g_mime_stream_filter_get_type (void)
94 static GType type = 0;
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),
106 (GInstanceInitFunc) g_mime_stream_filter_init,
109 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamFilter", &info, 0);
117 g_mime_stream_filter_class_init (GMimeStreamFilterClass *klass)
119 GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
120 GObjectClass *object_class = G_OBJECT_CLASS (klass);
122 parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
124 object_class->finalize = g_mime_stream_filter_finalize;
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;
139 g_mime_stream_filter_init (GMimeStreamFilter *stream, GMimeStreamFilterClass *klass)
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;
153 g_mime_stream_filter_finalize (GObject *object)
155 GMimeStreamFilter *filter = (GMimeStreamFilter *) object;
156 struct _GMimeStreamFilterPrivate *p = filter->priv;
157 struct _filter *fn, *f;
162 g_object_unref (f->filter);
167 g_free (p->realbuffer);
171 g_object_unref (filter->source);
173 G_OBJECT_CLASS (parent_class)->finalize (object);
178 stream_read (GMimeStream *stream, char *buf, size_t n)
180 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
181 struct _GMimeStreamFilterPrivate *priv = filter->priv;
185 priv->last_was_read = TRUE;
187 if (priv->filteredlen <= 0) {
188 size_t presize = READ_PAD;
190 nread = g_mime_stream_read (filter->source, priv->buffer, READ_SIZE);
192 /* this is somewhat untested */
193 if (g_mime_stream_eos (filter->source) && !priv->flushed) {
194 priv->filtered = priv->buffer;
195 priv->filteredlen = 0;
199 g_mime_filter_complete (f->filter, priv->filtered, priv->filteredlen,
200 presize, &priv->filtered, &priv->filteredlen,
205 nread = priv->filteredlen;
206 priv->flushed = TRUE;
212 priv->filtered = priv->buffer;
213 priv->filteredlen = nread;
214 priv->flushed = FALSE;
218 g_mime_filter_filter (f->filter, priv->filtered, priv->filteredlen, presize,
219 &priv->filtered, &priv->filteredlen, &presize);
226 nread = MIN (n, priv->filteredlen);
227 memcpy (buf, priv->filtered, nread);
228 priv->filteredlen -= nread;
229 priv->filtered += nread;
235 stream_write (GMimeStream *stream, const char *buf, size_t n)
237 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
238 struct _GMimeStreamFilterPrivate *priv = filter->priv;
239 char *buffer = (char *) buf;
240 ssize_t nwritten = n;
244 priv->last_was_read = FALSE;
245 priv->flushed = FALSE;
250 g_mime_filter_filter (f->filter, buffer, n, presize, &buffer, &n, &presize);
255 if (g_mime_stream_write (filter->source, buffer, n) == -1)
258 /* return original input len because that's what our caller expects */
263 stream_flush (GMimeStream *stream)
265 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
266 struct _GMimeStreamFilterPrivate *priv = filter->priv;
271 if (priv->last_was_read) {
282 g_mime_filter_complete (f->filter, buffer, len, presize, &buffer, &len, &presize);
287 if (len > 0 && g_mime_stream_write (filter->source, buffer, len) == -1)
290 return g_mime_stream_flush (filter->source);
294 stream_close (GMimeStream *stream)
296 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
297 struct _GMimeStreamFilterPrivate *priv = filter->priv;
299 if (!priv->last_was_read)
300 stream_flush (stream);
302 return g_mime_stream_close (filter->source);
306 stream_eos (GMimeStream *stream)
308 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
309 struct _GMimeStreamFilterPrivate *priv = filter->priv;
311 if (priv->filteredlen > 0)
317 return g_mime_stream_eos (filter->source);
321 stream_reset (GMimeStream *stream)
323 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
324 struct _GMimeStreamFilterPrivate *priv = filter->priv;
327 if (g_mime_stream_reset (filter->source) == -1)
330 priv->filteredlen = 0;
331 priv->flushed = FALSE;
333 /* and reset filters */
336 g_mime_filter_reset (f->filter);
344 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
350 stream_tell (GMimeStream *stream)
356 stream_length (GMimeStream *stream)
358 return stream->bound_end - stream->bound_start;
362 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
364 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
365 GMimeStreamFilter *sub;
367 sub = g_object_newv (GMIME_TYPE_STREAM_FILTER, 0, NULL);
368 sub->source = filter->source;
369 g_object_ref (sub->source);
371 if (filter->priv->filters) {
372 struct _filter *f, *sn, *s = NULL;
374 f = filter->priv->filters;
376 sn = g_new (struct _filter, 1);
377 sn->filter = g_mime_filter_copy (f->filter);
384 s = sub->priv->filters = sn;
392 sub->priv->filterid = filter->priv->filterid;
395 g_mime_stream_construct (GMIME_STREAM (filter), start, end);
397 return GMIME_STREAM (sub);
402 * g_mime_stream_filter_new:
403 * @stream: source stream
405 * Creates a new #GMimeStreamFilter object using @stream as the source
408 * Returns: a new filter stream with @stream as its source.
411 g_mime_stream_filter_new (GMimeStream *stream)
413 GMimeStreamFilter *filter;
415 g_return_val_if_fail (GMIME_IS_STREAM (stream), NULL);
417 filter = g_object_newv (GMIME_TYPE_STREAM_FILTER, 0, NULL);
418 filter->source = stream;
419 g_object_ref (stream);
421 g_mime_stream_construct (GMIME_STREAM (filter),
425 return GMIME_STREAM (filter);
430 * g_mime_stream_filter_add:
431 * @stream: a #GMimeStreamFilter
432 * @filter: a #GMimeFilter
434 * Adds @filter to @stream. Filters are applied in the same order in
435 * which they are added.
437 * Returns: an id for the filter.
440 g_mime_stream_filter_add (GMimeStreamFilter *stream, GMimeFilter *filter)
442 struct _GMimeStreamFilterPrivate *priv;
443 struct _filter *f, *fn;
445 g_return_val_if_fail (GMIME_IS_STREAM_FILTER (stream), -1);
446 g_return_val_if_fail (GMIME_IS_FILTER (filter), -1);
448 g_object_ref (filter);
452 fn = g_new (struct _filter, 1);
455 fn->id = priv->filterid++;
457 f = (struct _filter *) &priv->filters;
469 * g_mime_stream_filter_remove:
470 * @stream: a #GMimeStreamFilter
473 * Removed a filter from the stream based on the id (as returned from
477 g_mime_stream_filter_remove (GMimeStreamFilter *stream, int id)
479 struct _GMimeStreamFilterPrivate *priv;
480 struct _filter *f, *fn;
482 g_return_if_fail (GMIME_IS_STREAM_FILTER (stream));
489 f = (struct _filter *) &priv->filters;
490 while (f && f->next) {
494 g_object_unref (fn->filter);