/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* 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: Jeffrey Stedfast
* Michael Zucchi
*/
/* canonicalisation filter, used for secure mime incoming and outgoing */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "camel-mime-filter-canon.h"
#define CAMEL_MIME_FILTER_CANON_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_MIME_FILTER_CANON, CamelMimeFilterCanonPrivate))
struct _CamelMimeFilterCanonPrivate {
guint32 flags;
};
G_DEFINE_TYPE (
CamelMimeFilterCanon,
camel_mime_filter_canon,
CAMEL_TYPE_MIME_FILTER)
static void
mime_filter_canon_run (CamelMimeFilter *mime_filter,
const gchar *in,
gsize len,
gsize prespace,
gchar **out,
gsize *outlen,
gsize *outprespace,
gint last)
{
CamelMimeFilterCanonPrivate *priv;
register guchar *inptr, c;
const guchar *inend, *start;
gchar *starto;
register gchar *o;
gint lf = 0;
priv = CAMEL_MIME_FILTER_CANON_GET_PRIVATE (mime_filter);
/* first, work out how much space we need */
inptr = (guchar *) in;
inend = (const guchar *) (in + len);
while (inptr < inend)
if (*inptr++ == '\n')
lf++;
/* worst case, extra 3 chars per line
* "From \n" -> "=46rom \r\n"
* We add 1 extra incase we're called from complete, when we didn't end in \n */
camel_mime_filter_set_size (mime_filter, len + lf * 3 + 4, FALSE);
o = mime_filter->outbuf;
inptr = (guchar *) in;
start = inptr;
starto = o;
while (inptr < inend) {
/* first, check start of line, we always start at the start of the line */
c = *inptr;
if (priv->flags & CAMEL_MIME_FILTER_CANON_FROM && c == 'F') {
inptr++;
if (inptr < inend - 4) {
if (strncmp ((gchar *) inptr, "rom ", 4) == 0) {
strcpy (o, "=46rom ");
inptr+=4;
o+= 7;
} else
*o++ = 'F';
} else if (last)
*o++ = 'F';
else
break;
}
/* now scan for end of line */
while (inptr < inend) {
c = *inptr++;
if (c == '\n') {
/* check to strip trailing space */
if (priv->flags & CAMEL_MIME_FILTER_CANON_STRIP) {
while (o > starto && (o[-1] == ' ' || o[-1] == '\t' || o[-1]=='\r'))
o--;
}
/* check end of line canonicalisation */
if (o > starto) {
if (priv->flags & CAMEL_MIME_FILTER_CANON_CRLF) {
if (o[-1] != '\r')
*o++ = '\r';
} else {
if (o[-1] == '\r')
o--;
}
} else if (priv->flags & CAMEL_MIME_FILTER_CANON_CRLF) {
/* empty line */
*o++ = '\r';
}
*o++ = c;
start = inptr;
starto = o;
break;
} else
*o++ = c;
}
}
/* TODO: We should probably track if we end somewhere in the middle of a line,
* otherwise we potentially backup a full line, which could be large */
/* we got to the end of the data without finding anything, backup to start and re-process next time around */
if (last) {
*outlen = o - mime_filter->outbuf;
} else {
camel_mime_filter_backup (
mime_filter, (const gchar *) start, inend - start);
*outlen = starto - mime_filter->outbuf;
}
*out = mime_filter->outbuf;
*outprespace = mime_filter->outpre;
}
static void
mime_filter_canon_filter (CamelMimeFilter *mime_filter,
const gchar *in,
gsize len,
gsize prespace,
gchar **out,
gsize *outlen,
gsize *outprespace)
{
mime_filter_canon_run (
mime_filter, in, len, prespace,
out, outlen, outprespace, FALSE);
}
static void
mime_filter_canon_complete (CamelMimeFilter *mime_filter,
const gchar *in,
gsize len,
gsize prespace,
gchar **out,
gsize *outlen,
gsize *outprespace)
{
mime_filter_canon_run (
mime_filter, in, len, prespace,
out, outlen, outprespace, TRUE);
}
static void
mime_filter_canon_reset (CamelMimeFilter *mime_filter)
{
/* no-op */
}
static void
camel_mime_filter_canon_class_init (CamelMimeFilterCanonClass *class)
{
CamelMimeFilterClass *mime_filter_class;
g_type_class_add_private (class, sizeof (CamelMimeFilterCanonPrivate));
mime_filter_class = CAMEL_MIME_FILTER_CLASS (class);
mime_filter_class->filter = mime_filter_canon_filter;
mime_filter_class->complete = mime_filter_canon_complete;
mime_filter_class->reset = mime_filter_canon_reset;
}
static void
camel_mime_filter_canon_init (CamelMimeFilterCanon *filter)
{
filter->priv = CAMEL_MIME_FILTER_CANON_GET_PRIVATE (filter);
}
/**
* camel_mime_filter_canon_new:
* @flags: bitwise flags defining the behaviour of the filter
*
* Create a new filter to canonicalise an input stream.
*
* Returns: a new #CamelMimeFilterCanon
**/
CamelMimeFilter *
camel_mime_filter_canon_new (guint32 flags)
{
CamelMimeFilter *filter;
filter = g_object_new (CAMEL_TYPE_MIME_FILTER_CANON, NULL);
CAMEL_MIME_FILTER_CANON (filter)->priv->flags = flags;
return filter;
}