1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* GMime
3 * Copyright (C) 2000-2020 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
g_mime_stream_filter_get_type(void)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
g_mime_stream_filter_class_init(GMimeStreamFilterClass * klass)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
g_mime_stream_filter_init(GMimeStreamFilter * stream,GMimeStreamFilterClass * klass)139 g_mime_stream_filter_init (GMimeStreamFilter *stream, GMimeStreamFilterClass *klass)
140 {
141 stream->source = NULL;
142 stream->owner = FALSE;
143 stream->priv = g_new (struct _GMimeStreamFilterPrivate, 1);
144 stream->priv->filters = NULL;
145 stream->priv->filterid = 0;
146 stream->priv->realbuffer = g_malloc (READ_SIZE + READ_PAD);
147 stream->priv->buffer = stream->priv->realbuffer + READ_PAD;
148 stream->priv->last_was_read = TRUE;
149 stream->priv->filteredlen = 0;
150 stream->priv->flushed = FALSE;
151 }
152
153 static void
g_mime_stream_filter_finalize(GObject * object)154 g_mime_stream_filter_finalize (GObject *object)
155 {
156 GMimeStreamFilter *filter = (GMimeStreamFilter *) object;
157 struct _GMimeStreamFilterPrivate *p = filter->priv;
158 struct _filter *fn, *f;
159
160 f = p->filters;
161 while (f) {
162 fn = f->next;
163 g_object_unref (f->filter);
164 g_free (f);
165 f = fn;
166 }
167
168 g_free (p->realbuffer);
169 g_free (p);
170
171 if (filter->source)
172 g_object_unref (filter->source);
173
174 G_OBJECT_CLASS (parent_class)->finalize (object);
175 }
176
177
178 static ssize_t
stream_read(GMimeStream * stream,char * buf,size_t n)179 stream_read (GMimeStream *stream, char *buf, size_t n)
180 {
181 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
182 struct _GMimeStreamFilterPrivate *priv = filter->priv;
183 struct _filter *f;
184 ssize_t nread;
185
186 priv->last_was_read = TRUE;
187
188 if (priv->filteredlen <= 0) {
189 size_t presize = READ_PAD;
190
191 nread = g_mime_stream_read (filter->source, priv->buffer, READ_SIZE);
192 if (nread <= 0) {
193 /* this is somewhat untested */
194 if (g_mime_stream_eos (filter->source) && !priv->flushed) {
195 priv->filtered = priv->buffer;
196 priv->filteredlen = 0;
197 f = priv->filters;
198
199 while (f != NULL) {
200 g_mime_filter_complete (f->filter, priv->filtered, priv->filteredlen,
201 presize, &priv->filtered, &priv->filteredlen,
202 &presize);
203 f = f->next;
204 }
205
206 nread = priv->filteredlen;
207 priv->flushed = TRUE;
208 }
209
210 if (nread <= 0)
211 return nread;
212 } else {
213 priv->filtered = priv->buffer;
214 priv->filteredlen = nread;
215 priv->flushed = FALSE;
216 f = priv->filters;
217
218 while (f != NULL) {
219 g_mime_filter_filter (f->filter, priv->filtered, priv->filteredlen, presize,
220 &priv->filtered, &priv->filteredlen, &presize);
221
222 f = f->next;
223 }
224 }
225 }
226
227 nread = MIN (n, priv->filteredlen);
228 memcpy (buf, priv->filtered, nread);
229 priv->filteredlen -= nread;
230 priv->filtered += nread;
231
232 return nread;
233 }
234
235 static ssize_t
stream_write(GMimeStream * stream,const char * buf,size_t n)236 stream_write (GMimeStream *stream, const char *buf, size_t n)
237 {
238 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
239 struct _GMimeStreamFilterPrivate *priv = filter->priv;
240 char *buffer = (char *) buf;
241 ssize_t nwritten = n;
242 struct _filter *f;
243 size_t presize;
244
245 priv->last_was_read = FALSE;
246 priv->flushed = FALSE;
247
248 f = priv->filters;
249 presize = 0;
250 while (f != NULL) {
251 g_mime_filter_filter (f->filter, buffer, n, presize, &buffer, &n, &presize);
252
253 f = f->next;
254 }
255
256 if (g_mime_stream_write (filter->source, buffer, n) == -1)
257 return -1;
258
259 /* return original input len because that's what our caller expects */
260 return nwritten;
261 }
262
263 static int
stream_flush(GMimeStream * stream)264 stream_flush (GMimeStream *stream)
265 {
266 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
267 struct _GMimeStreamFilterPrivate *priv = filter->priv;
268 size_t presize, len;
269 struct _filter *f;
270 char *buffer;
271
272 if (priv->last_was_read) {
273 /* no-op */
274 return 0;
275 }
276
277 buffer = "";
278 len = 0;
279 presize = 0;
280 f = priv->filters;
281
282 while (f != NULL) {
283 g_mime_filter_complete (f->filter, buffer, len, presize, &buffer, &len, &presize);
284
285 f = f->next;
286 }
287
288 if (len > 0 && g_mime_stream_write (filter->source, buffer, len) == -1)
289 return -1;
290
291 return 0;
292 }
293
294 static int
stream_close(GMimeStream * stream)295 stream_close (GMimeStream *stream)
296 {
297 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
298 struct _GMimeStreamFilterPrivate *priv = filter->priv;
299
300 if (!priv->last_was_read)
301 stream_flush (stream);
302
303 if (filter->owner)
304 return g_mime_stream_close (filter->source);
305
306 return 0;
307 }
308
309 static gboolean
stream_eos(GMimeStream * stream)310 stream_eos (GMimeStream *stream)
311 {
312 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
313 struct _GMimeStreamFilterPrivate *priv = filter->priv;
314
315 if (priv->filteredlen > 0)
316 return FALSE;
317
318 if (!priv->flushed)
319 return FALSE;
320
321 return g_mime_stream_eos (filter->source);
322 }
323
324 static int
stream_reset(GMimeStream * stream)325 stream_reset (GMimeStream *stream)
326 {
327 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
328 struct _GMimeStreamFilterPrivate *priv = filter->priv;
329 struct _filter *f;
330
331 if (g_mime_stream_reset (filter->source) == -1)
332 return -1;
333
334 priv->filteredlen = 0;
335 priv->flushed = FALSE;
336
337 /* and reset filters */
338 f = priv->filters;
339 while (f != NULL) {
340 g_mime_filter_reset (f->filter);
341 f = f->next;
342 }
343
344 return 0;
345 }
346
347 static gint64
stream_seek(GMimeStream * stream,gint64 offset,GMimeSeekWhence whence)348 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
349 {
350 return -1;
351 }
352
353 static gint64
stream_tell(GMimeStream * stream)354 stream_tell (GMimeStream *stream)
355 {
356 return -1;
357 }
358
359 static gint64
stream_length(GMimeStream * stream)360 stream_length (GMimeStream *stream)
361 {
362 return stream->bound_end - stream->bound_start;
363 }
364
365 static GMimeStream *
stream_substream(GMimeStream * stream,gint64 start,gint64 end)366 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
367 {
368 GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
369 GMimeStreamFilter *sub;
370
371 sub = g_object_new (GMIME_TYPE_STREAM_FILTER, NULL);
372 sub->source = filter->source;
373 g_object_ref (sub->source);
374
375 if (filter->priv->filters) {
376 struct _filter *f, *sn, *s = NULL;
377
378 f = filter->priv->filters;
379 while (f) {
380 sn = g_new (struct _filter, 1);
381 sn->filter = g_mime_filter_copy (f->filter);
382 sn->id = f->id;
383
384 if (s) {
385 s->next = sn;
386 s = sn;
387 } else {
388 s = sub->priv->filters = sn;
389 }
390
391 f = f->next;
392 }
393
394 s->next = NULL;
395
396 sub->priv->filterid = filter->priv->filterid;
397 }
398
399 g_mime_stream_construct ((GMimeStream *) filter, start, end);
400
401 return (GMimeStream *) sub;
402 }
403
404
405 /**
406 * g_mime_stream_filter_new:
407 * @stream: source stream
408 *
409 * Creates a new #GMimeStreamFilter object using @stream as the source
410 * stream.
411 *
412 * Returns: a new filter stream with @stream as its source.
413 **/
414 GMimeStream *
g_mime_stream_filter_new(GMimeStream * stream)415 g_mime_stream_filter_new (GMimeStream *stream)
416 {
417 GMimeStreamFilter *filter;
418
419 g_return_val_if_fail (GMIME_IS_STREAM (stream), NULL);
420
421 filter = g_object_new (GMIME_TYPE_STREAM_FILTER, NULL);
422 filter->source = stream;
423 g_object_ref (stream);
424
425 g_mime_stream_construct ((GMimeStream *) filter,
426 stream->bound_start,
427 stream->bound_end);
428
429 return (GMimeStream *) filter;
430 }
431
432
433 /**
434 * g_mime_stream_filter_add:
435 * @stream: a #GMimeStreamFilter
436 * @filter: a #GMimeFilter
437 *
438 * Adds @filter to @stream. Filters are applied in the same order in
439 * which they are added.
440 *
441 * Returns: an id for the filter.
442 **/
443 int
g_mime_stream_filter_add(GMimeStreamFilter * stream,GMimeFilter * filter)444 g_mime_stream_filter_add (GMimeStreamFilter *stream, GMimeFilter *filter)
445 {
446 struct _GMimeStreamFilterPrivate *priv;
447 struct _filter *f, *fn;
448
449 g_return_val_if_fail (GMIME_IS_STREAM_FILTER (stream), -1);
450 g_return_val_if_fail (GMIME_IS_FILTER (filter), -1);
451
452 g_object_ref (filter);
453
454 priv = stream->priv;
455
456 fn = g_new (struct _filter, 1);
457 fn->next = NULL;
458 fn->filter = filter;
459 fn->id = priv->filterid++;
460
461 f = (struct _filter *) &priv->filters;
462 while (f->next)
463 f = f->next;
464
465 f->next = fn;
466 fn->next = NULL;
467
468 return fn->id;
469 }
470
471
472 /**
473 * g_mime_stream_filter_remove:
474 * @stream: a #GMimeStreamFilter
475 * @id: filter id
476 *
477 * Removed a filter from the stream based on the id (as returned from
478 * filter_add).
479 **/
480 void
g_mime_stream_filter_remove(GMimeStreamFilter * stream,int id)481 g_mime_stream_filter_remove (GMimeStreamFilter *stream, int id)
482 {
483 struct _GMimeStreamFilterPrivate *priv;
484 struct _filter *f, *fn;
485
486 g_return_if_fail (GMIME_IS_STREAM_FILTER (stream));
487
488 priv = stream->priv;
489
490 if (id == -1)
491 return;
492
493 f = (struct _filter *) &priv->filters;
494 while (f && f->next) {
495 fn = f->next;
496 if (fn->id == id) {
497 f->next = fn->next;
498 g_object_unref (fn->filter);
499 g_free (fn);
500 }
501 f = f->next;
502 }
503 }
504
505
506 /**
507 * g_mime_stream_filter_set_owner:
508 * @stream: a #GMimeStreamFilter
509 * @owner: %TRUE if this stream should own the source stream or %FALSE otherwise
510 *
511 * Sets whether or not @stream owns the source stream..
512 *
513 * Note: @owner should be %TRUE if the stream should close() the
514 * backend source stream when destroyed or %FALSE otherwise.
515 **/
516 void
g_mime_stream_filter_set_owner(GMimeStreamFilter * stream,gboolean owner)517 g_mime_stream_filter_set_owner (GMimeStreamFilter *stream, gboolean owner)
518 {
519 g_return_if_fail (GMIME_IS_STREAM_FILTER (stream));
520
521 stream->owner = owner;
522 }
523
524
525 /**
526 * g_mime_stream_filter_get_owner:
527 * @stream: a #GMimeStreamFilter
528 *
529 * Gets whether or not @stream owns the source stream.
530 *
531 * Returns: %TRUE if @stream owns the source stream or %FALSE
532 * otherwise.
533 **/
534 gboolean
g_mime_stream_filter_get_owner(GMimeStreamFilter * stream)535 g_mime_stream_filter_get_owner (GMimeStreamFilter *stream)
536 {
537 g_return_val_if_fail (GMIME_IS_STREAM_FILTER (stream), FALSE);
538
539 return stream->owner;
540 }
541