/* * 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 * Jeffrey Stedfast */ #include #include "camel-mime-filter-basic.h" #include "camel-mime-utils.h" #define CAMEL_MIME_FILTER_BASIC_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_MIME_FILTER_BASIC, CamelMimeFilterBasicPrivate)) struct _CamelMimeFilterBasicPrivate { CamelMimeFilterBasicType type; guchar uubuf[60]; gint state; gint save; }; G_DEFINE_TYPE (CamelMimeFilterBasic, camel_mime_filter_basic, CAMEL_TYPE_MIME_FILTER) /* here we do all of the basic mime filtering */ static void mime_filter_basic_filter (CamelMimeFilter *mime_filter, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace) { CamelMimeFilterBasicPrivate *priv; gsize newlen; priv = CAMEL_MIME_FILTER_BASIC_GET_PRIVATE (mime_filter); switch (priv->type) { case CAMEL_MIME_FILTER_BASIC_BASE64_ENC: /* wont go to more than 2x size (overly conservative) */ camel_mime_filter_set_size ( mime_filter, len * 2 + 6, FALSE); newlen = g_base64_encode_step ( (const guchar *) in, len, TRUE, mime_filter->outbuf, &priv->state, &priv->save); g_return_if_fail (newlen <= len * 2 + 6); break; case CAMEL_MIME_FILTER_BASIC_QP_ENC: /* *4 is overly conservative, but will do */ camel_mime_filter_set_size ( mime_filter, len * 4 + 4, FALSE); newlen = camel_quoted_encode_step ( (guchar *) in, len, (guchar *) mime_filter->outbuf, &priv->state, (gint *) &priv->save); g_return_if_fail (newlen <= len * 4 + 4); break; case CAMEL_MIME_FILTER_BASIC_UU_ENC: /* won't go to more than 2 * (x + 2) + 62 */ camel_mime_filter_set_size ( mime_filter, (len + 2) * 2 + 62, FALSE); newlen = camel_uuencode_step ( (guchar *) in, len, (guchar *) mime_filter->outbuf, priv->uubuf, &priv->state, (guint32 *) &priv->save); g_return_if_fail (newlen <= (len + 2) * 2 + 62); break; case CAMEL_MIME_FILTER_BASIC_BASE64_DEC: /* output can't possibly exceed the input size */ camel_mime_filter_set_size (mime_filter, len + 3, FALSE); newlen = g_base64_decode_step ( in, len, (guchar *) mime_filter->outbuf, &priv->state, (guint *) &priv->save); g_return_if_fail (newlen <= len + 3); break; case CAMEL_MIME_FILTER_BASIC_QP_DEC: /* output can't possibly exceed the input size */ camel_mime_filter_set_size (mime_filter, len + 2, FALSE); newlen = camel_quoted_decode_step ( (guchar *) in, len, (guchar *) mime_filter->outbuf, &priv->state, (gint *) &priv->save); g_return_if_fail (newlen <= len + 2); break; case CAMEL_MIME_FILTER_BASIC_UU_DEC: if (!(priv->state & CAMEL_UUDECODE_STATE_BEGIN)) { const gchar *inptr, *inend; gsize left; inptr = in; inend = inptr + len; while (inptr < inend) { left = inend - inptr; if (left < 6) { if (!strncmp (inptr, "begin ", left)) camel_mime_filter_backup (mime_filter, inptr, left); break; } else if (!strncmp (inptr, "begin ", 6)) { for (in = inptr; inptr < inend && *inptr != '\n'; inptr++); if (inptr < inend) { inptr++; priv->state |= CAMEL_UUDECODE_STATE_BEGIN; /* we can start uudecoding... */ in = inptr; len = inend - in; } else { camel_mime_filter_backup (mime_filter, in, left); } break; } /* go to the next line */ for (; inptr < inend && *inptr != '\n'; inptr++); if (inptr < inend) inptr++; } } if ((priv->state & CAMEL_UUDECODE_STATE_BEGIN) && !(priv->state & CAMEL_UUDECODE_STATE_END)) { /* "begin \n" has been * found, so we can now start decoding */ camel_mime_filter_set_size ( mime_filter, len + 3, FALSE); newlen = camel_uudecode_step ( (guchar *) in, len, (guchar *) mime_filter->outbuf, &priv->state, (guint32 *) &priv->save); } else { newlen = 0; } break; default: g_warning ("unknown type %u in CamelMimeFilterBasic", priv->type); goto donothing; } *out = mime_filter->outbuf; *outlen = newlen; *outprespace = mime_filter->outpre; return; donothing: *out = (gchar *) in; *outlen = len; *outprespace = prespace; } static void mime_filter_basic_complete (CamelMimeFilter *mime_filter, const gchar *in, gsize len, gsize prespace, gchar **out, gsize *outlen, gsize *outprespace) { CamelMimeFilterBasicPrivate *priv; gsize newlen = 0; priv = CAMEL_MIME_FILTER_BASIC_GET_PRIVATE (mime_filter); switch (priv->type) { case CAMEL_MIME_FILTER_BASIC_BASE64_ENC: /* wont go to more than 2x size (overly conservative) */ camel_mime_filter_set_size ( mime_filter, len * 2 + 6, FALSE); if (len > 0) newlen += g_base64_encode_step ( (const guchar *) in, len, TRUE, mime_filter->outbuf, &priv->state, &priv->save); newlen += g_base64_encode_close ( TRUE, mime_filter->outbuf, &priv->state, &priv->save); g_return_if_fail (newlen <= len * 2 + 6); break; case CAMEL_MIME_FILTER_BASIC_QP_ENC: /* *4 is definetly more than needed ... */ camel_mime_filter_set_size ( mime_filter, len * 4 + 4, FALSE); newlen = camel_quoted_encode_close ( (guchar *) in, len, (guchar *) mime_filter->outbuf, &priv->state, &priv->save); g_return_if_fail (newlen <= len * 4 + 4); break; case CAMEL_MIME_FILTER_BASIC_UU_ENC: /* won't go to more than 2 * (x + 2) + 62 */ camel_mime_filter_set_size ( mime_filter, (len + 2) * 2 + 62, FALSE); newlen = camel_uuencode_close ( (guchar *) in, len, (guchar *) mime_filter->outbuf, priv->uubuf, &priv->state, (guint32 *) &priv->save); g_return_if_fail (newlen <= (len + 2) * 2 + 62); break; case CAMEL_MIME_FILTER_BASIC_BASE64_DEC: /* output can't possibly exceed the input size */ camel_mime_filter_set_size (mime_filter, len, FALSE); newlen = g_base64_decode_step ( in, len, (guchar *) mime_filter->outbuf, &priv->state, (guint *) &priv->save); g_return_if_fail (newlen <= len); break; case CAMEL_MIME_FILTER_BASIC_QP_DEC: /* output can't possibly exceed the input size, * well unless its not really qp, then +2 max */ camel_mime_filter_set_size (mime_filter, len + 2, FALSE); newlen = camel_quoted_decode_step ( (guchar *) in, len, (guchar *) mime_filter->outbuf, &priv->state, (gint *) &priv->save); g_return_if_fail (newlen <= len + 2); break; case CAMEL_MIME_FILTER_BASIC_UU_DEC: if ((priv->state & CAMEL_UUDECODE_STATE_BEGIN) && !(priv->state & CAMEL_UUDECODE_STATE_END)) { /* "begin \n" has been * found, so we can now start decoding */ camel_mime_filter_set_size ( mime_filter, len + 3, FALSE); newlen = camel_uudecode_step ( (guchar *) in, len, (guchar *) mime_filter->outbuf, &priv->state, (guint32 *) &priv->save); } else { newlen = 0; } break; default: g_warning ("unknown type %u in CamelMimeFilterBasic", priv->type); goto donothing; } *out = mime_filter->outbuf; *outlen = newlen; *outprespace = mime_filter->outpre; return; donothing: *out = (gchar *) in; *outlen = len; *outprespace = prespace; } /* should this 'flush' outstanding state/data bytes? */ static void mime_filter_basic_reset (CamelMimeFilter *mime_filter) { CamelMimeFilterBasicPrivate *priv; priv = CAMEL_MIME_FILTER_BASIC_GET_PRIVATE (mime_filter); switch (priv->type) { case CAMEL_MIME_FILTER_BASIC_QP_ENC: priv->state = -1; break; default: priv->state = 0; } priv->save = 0; } static void camel_mime_filter_basic_class_init (CamelMimeFilterBasicClass *class) { CamelMimeFilterClass *mime_filter_class; g_type_class_add_private (class, sizeof (CamelMimeFilterBasicPrivate)); mime_filter_class = CAMEL_MIME_FILTER_CLASS (class); mime_filter_class->filter = mime_filter_basic_filter; mime_filter_class->complete = mime_filter_basic_complete; mime_filter_class->reset = mime_filter_basic_reset; } static void camel_mime_filter_basic_init (CamelMimeFilterBasic *filter) { filter->priv = CAMEL_MIME_FILTER_BASIC_GET_PRIVATE (filter); } /** * camel_mime_filter_basic_new: * @type: a #CamelMimeFilterBasicType type * * Create a new #CamelMimeFilterBasic object of type @type. * * Returns: a new #CamelMimeFilterBasic object **/ CamelMimeFilter * camel_mime_filter_basic_new (CamelMimeFilterBasicType type) { CamelMimeFilter *new; switch (type) { case CAMEL_MIME_FILTER_BASIC_BASE64_ENC: case CAMEL_MIME_FILTER_BASIC_QP_ENC: case CAMEL_MIME_FILTER_BASIC_BASE64_DEC: case CAMEL_MIME_FILTER_BASIC_QP_DEC: case CAMEL_MIME_FILTER_BASIC_UU_ENC: case CAMEL_MIME_FILTER_BASIC_UU_DEC: new = g_object_new (CAMEL_TYPE_MIME_FILTER_BASIC, NULL); CAMEL_MIME_FILTER_BASIC (new)->priv->type = type; break; default: g_warning ("Invalid type of CamelMimeFilterBasic requested: %u", type); new = NULL; break; } camel_mime_filter_reset (new); return new; }