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