/* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: Michael Zucchi */ #include #include "camel-mime-filter.h" /*#define MALLOC_CHECK */ /* for some malloc checking, requires mcheck enabled */ /* only suitable for glibc */ #ifdef MALLOC_CHECK #include #endif #define CAMEL_MIME_FILTER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_MIME_FILTER, CamelMimeFilterPrivate)) struct _CamelMimeFilterPrivate { gchar *inbuf; gsize inlen; }; /* Compatible with filter() and complete() methods. */ typedef void (*FilterMethod) (CamelMimeFilter *filter, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace); #define PRE_HEAD (64) #define BACK_HEAD (64) G_DEFINE_ABSTRACT_TYPE (CamelMimeFilter, camel_mime_filter, G_TYPE_OBJECT) static void mime_filter_finalize (GObject *object) { CamelMimeFilter *mime_filter; mime_filter = CAMEL_MIME_FILTER (object); g_free (mime_filter->outreal); g_free (mime_filter->backbuf); g_free (mime_filter->priv->inbuf); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (camel_mime_filter_parent_class)->finalize (object); } static void mime_filter_complete (CamelMimeFilter *mime_filter, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace) { /* default - do nothing */ } static void camel_mime_filter_class_init (CamelMimeFilterClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (CamelMimeFilterPrivate)); object_class = G_OBJECT_CLASS (class); object_class->finalize = mime_filter_finalize; class->complete = mime_filter_complete; } static void camel_mime_filter_init (CamelMimeFilter *mime_filter) { mime_filter->priv = CAMEL_MIME_FILTER_GET_PRIVATE (mime_filter); mime_filter->outreal = NULL; mime_filter->outbuf = NULL; mime_filter->outsize = 0; mime_filter->backbuf = NULL; mime_filter->backsize = 0; mime_filter->backlen = 0; } /** * camel_mime_filter_new: * * Create a new #CamelMimeFilter object. * * Returns: a new #CamelMimeFilter **/ CamelMimeFilter * camel_mime_filter_new (void) { return g_object_new (CAMEL_TYPE_MIME_FILTER, NULL); } #ifdef MALLOC_CHECK static void checkmem (gpointer p) { if (p) { gint status = mprobe (p); switch (status) { case MCHECK_HEAD: printf ("Memory underrun at %p\n", p); abort (); case MCHECK_TAIL: printf ("Memory overrun at %p\n", p); abort (); case MCHECK_FREE: printf ("Double free %p\n", p); abort (); } } } #endif static void filter_run (CamelMimeFilter *f, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace, FilterMethod method) { #ifdef MALLOC_CHECK checkmem (f->outreal); checkmem (f->backbuf); #endif /* * here we take a performance hit, if the input buffer doesn't * have the pre-space required. We make a buffer that does ... */ if (f->backlen > 0) { struct _CamelMimeFilterPrivate *p; gint newlen; p = CAMEL_MIME_FILTER_GET_PRIVATE (f); newlen = len + prespace + f->backlen; if (p->inlen < newlen) { /* NOTE: g_realloc copies data, we dont need that (slower) */ g_free (p->inbuf); p->inbuf = g_malloc (newlen + PRE_HEAD); p->inlen = newlen + PRE_HEAD; } /* copy to end of structure */ memcpy (p->inbuf + p->inlen - len, in, len); in = p->inbuf + p->inlen - len; prespace = p->inlen - len; /* preload any backed up data */ memcpy ((gchar *) in - f->backlen, f->backbuf, f->backlen); in -= f->backlen; len += f->backlen; prespace -= f->backlen; f->backlen = 0; } #ifdef MALLOC_CHECK checkmem (f->outreal); checkmem (f->backbuf); #endif method (f, in, len, prespace, out, outlen, outprespace); #ifdef MALLOC_CHECK checkmem (f->outreal); checkmem (f->backbuf); #endif } /** * camel_mime_filter_filter: * @filter: a #CamelMimeFilter object * @in: input buffer * @len: length of @in * @prespace: amount of prespace * @out: pointer to the output buffer (to be set) * @outlen: pointer to the length of the output buffer (to be set) * @outprespace: pointer to the output prespace length (to be set) * * Passes the input buffer, @in, through @filter and generates an * output buffer, @out. **/ void camel_mime_filter_filter (CamelMimeFilter *filter, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace) { CamelMimeFilterClass *class; g_return_if_fail (CAMEL_IS_MIME_FILTER (filter)); g_return_if_fail (in != NULL); class = CAMEL_MIME_FILTER_GET_CLASS (filter); g_return_if_fail (class->filter != NULL); filter_run ( filter, in, len, prespace, out, outlen, outprespace, class->filter); } /** * camel_mime_filter_complete: * @filter: a #CamelMimeFilter object * @in: input buffer * @len: length of @in * @prespace: amount of prespace * @out: pointer to the output buffer (to be set) * @outlen: pointer to the length of the output buffer (to be set) * @outprespace: pointer to the output prespace length (to be set) * * Passes the input buffer, @in, through @filter and generates an * output buffer, @out and makes sure that all data is flushed to the * output buffer. This must be the last filtering call made, no * further calls to camel_mime_filter_filter() may be called on @filter * until @filter has been reset using camel_mime_filter_reset(). **/ void camel_mime_filter_complete (CamelMimeFilter *filter, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace) { CamelMimeFilterClass *class; g_return_if_fail (CAMEL_IS_MIME_FILTER (filter)); g_return_if_fail (in != NULL); class = CAMEL_MIME_FILTER_GET_CLASS (filter); g_return_if_fail (class->complete != NULL); filter_run ( filter, in, len, prespace, out, outlen, outprespace, class->complete); } /** * camel_mime_filter_reset: * @filter: a #CamelMimeFilter object * * Resets the state on @filter so that it may be used again. **/ void camel_mime_filter_reset (CamelMimeFilter *filter) { CamelMimeFilterClass *class; g_return_if_fail (CAMEL_IS_MIME_FILTER (filter)); class = CAMEL_MIME_FILTER_GET_CLASS (filter); if (class->reset != NULL) class->reset (filter); /* could free some buffers, if they are really big? */ filter->backlen = 0; } /** * camel_mime_filter_backup: * @filter: a #CamelMimeFilter object * @data: data buffer to backup * @length: length of @data * * Saves @data to be used as prespace input data to the next call to * camel_mime_filter_filter() or camel_mime_filter_complete(). * * Note: New calls replace old data. **/ void camel_mime_filter_backup (CamelMimeFilter *filter, const gchar *data, gsize length) { if (filter->backsize < length) { /* g_realloc copies data, unnecessary overhead */ g_free (filter->backbuf); filter->backbuf = g_malloc (length + BACK_HEAD); filter->backsize = length + BACK_HEAD; } filter->backlen = length; memcpy (filter->backbuf, data, length); } /** * camel_mime_filter_set_size: * @filter: a #CamelMimeFilter object * @size: requested amount of storage space * @keep: %TRUE to keep existing buffered data or %FALSE otherwise * * Ensure that @filter has enough storage space to store @size bytes * for filter output. **/ void camel_mime_filter_set_size (CamelMimeFilter *filter, gsize size, gint keep) { if (filter->outsize < size) { gint offset = filter->outptr - filter->outreal; if (keep) { filter->outreal = g_realloc (filter->outreal, size + PRE_HEAD * 4); } else { g_free (filter->outreal); filter->outreal = g_malloc (size + PRE_HEAD * 4); } filter->outptr = filter->outreal + offset; filter->outbuf = filter->outreal + PRE_HEAD * 4; filter->outsize = size; /* this could be offset from the end of the structure, but * this should be good enough */ filter->outpre = PRE_HEAD * 4; } }