/*
* 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;
}
}