/* -*- 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: Dan Winship
* Jeffrey Stedfast
*/
#include "camel-mime-filter-crlf.h"
#define CAMEL_MIME_FILTER_CRLF_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_MIME_FILTER_CRLF, CamelMimeFilterCRLFPrivate))
struct _CamelMimeFilterCRLFPrivate {
CamelMimeFilterCRLFDirection direction;
CamelMimeFilterCRLFMode mode;
gboolean saw_cr;
gboolean saw_lf;
gboolean saw_dot;
};
G_DEFINE_TYPE (CamelMimeFilterCRLF, camel_mime_filter_crlf, CAMEL_TYPE_MIME_FILTER)
static void
mime_filter_crlf_filter (CamelMimeFilter *mime_filter,
const gchar *in,
gsize len,
gsize prespace,
gchar **out,
gsize *outlen,
gsize *outprespace)
{
CamelMimeFilterCRLFPrivate *priv;
register const gchar *inptr;
const gchar *inend;
gboolean do_dots;
gchar *outptr;
priv = CAMEL_MIME_FILTER_CRLF_GET_PRIVATE (mime_filter);
do_dots = priv->mode == CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS;
inptr = in;
inend = in + len;
if (priv->direction == CAMEL_MIME_FILTER_CRLF_ENCODE) {
camel_mime_filter_set_size (mime_filter, 3 * len, FALSE);
outptr = mime_filter->outbuf;
while (inptr < inend) {
if (*inptr == '\r') {
priv->saw_cr = TRUE;
} else if (*inptr == '\n') {
priv->saw_lf = TRUE;
if (!priv->saw_cr)
*outptr++ = '\r';
priv->saw_cr = FALSE;
} else {
if (do_dots && *inptr == '.' && priv->saw_lf)
*outptr++ = '.';
priv->saw_cr = FALSE;
priv->saw_lf = FALSE;
}
*outptr++ = *inptr++;
}
} else {
/* Output can "grow" by one byte if priv->saw_cr was set as
* a carry-over from the previous invocation. This will happen
* in practice, as the input is processed in arbitrarily-sized
* blocks. */
camel_mime_filter_set_size (mime_filter, len + 1, FALSE);
outptr = mime_filter->outbuf;
while (inptr < inend) {
if (*inptr == '\r') {
priv->saw_cr = TRUE;
} else {
if (priv->saw_cr) {
priv->saw_cr = FALSE;
if (*inptr == '\n') {
priv->saw_lf = TRUE;
*outptr++ = *inptr++;
continue;
} else
*outptr++ = '\r';
}
*outptr++ = *inptr;
}
if (do_dots && *inptr == '.') {
if (priv->saw_lf) {
priv->saw_dot = TRUE;
priv->saw_lf = FALSE;
inptr++;
} else if (priv->saw_dot) {
priv->saw_dot = FALSE;
}
}
priv->saw_lf = FALSE;
inptr++;
}
}
*out = mime_filter->outbuf;
*outlen = outptr - mime_filter->outbuf;
*outprespace = mime_filter->outpre;
}
static void
mime_filter_crlf_complete (CamelMimeFilter *mime_filter,
const gchar *in,
gsize len,
gsize prespace,
gchar **out,
gsize *outlen,
gsize *outprespace)
{
if (len)
mime_filter_crlf_filter (
mime_filter, in, len, prespace,
out, outlen, outprespace);
}
static void
mime_filter_crlf_reset (CamelMimeFilter *mime_filter)
{
CamelMimeFilterCRLFPrivate *priv;
priv = CAMEL_MIME_FILTER_CRLF_GET_PRIVATE (mime_filter);
priv->saw_cr = FALSE;
priv->saw_lf = TRUE;
priv->saw_dot = FALSE;
}
static void
camel_mime_filter_crlf_class_init (CamelMimeFilterCRLFClass *class)
{
CamelMimeFilterClass *mime_filter_class;
g_type_class_add_private (class, sizeof (CamelMimeFilterCRLFPrivate));
mime_filter_class = CAMEL_MIME_FILTER_CLASS (class);
mime_filter_class->filter = mime_filter_crlf_filter;
mime_filter_class->complete = mime_filter_crlf_complete;
mime_filter_class->reset = mime_filter_crlf_reset;
}
static void
camel_mime_filter_crlf_init (CamelMimeFilterCRLF *filter)
{
filter->priv = CAMEL_MIME_FILTER_CRLF_GET_PRIVATE (filter);
filter->priv->saw_cr = FALSE;
filter->priv->saw_lf = TRUE;
filter->priv->saw_dot = FALSE;
}
/**
* camel_mime_filter_crlf_new:
* @direction: encode vs decode
* @mode: whether or not to perform SMTP dot-escaping
*
* Create a new #CamelMimeFiletrCRLF object.
*
* Returns: a new #CamelMimeFilterCRLF object
**/
CamelMimeFilter *
camel_mime_filter_crlf_new (CamelMimeFilterCRLFDirection direction,
CamelMimeFilterCRLFMode mode)
{
CamelMimeFilter *filter;
CamelMimeFilterCRLFPrivate *priv;
filter = g_object_new (CAMEL_TYPE_MIME_FILTER_CRLF, NULL);
priv = CAMEL_MIME_FILTER_CRLF_GET_PRIVATE (filter);
priv->direction = direction;
priv->mode = mode;
return filter;
}