aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-mime-filter-gzip.c
diff options
context:
space:
mode:
authorJeffrey Stedfast <fejj@novell.com>2004-05-11 03:29:57 +0800
committerJeffrey Stedfast <fejj@src.gnome.org>2004-05-11 03:29:57 +0800
commita4cd02dd230934271f925da4d3ef99de668cff6a (patch)
treebc41ed0d6d9307a11f3756c94b893a25219eac28 /camel/camel-mime-filter-gzip.c
parentee8a9bc3e88aaf0956591b744beafb0d6cb9a3cf (diff)
downloadgsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.tar
gsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.tar.gz
gsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.tar.bz2
gsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.tar.lz
gsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.tar.xz
gsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.tar.zst
gsoc2013-evolution-a4cd02dd230934271f925da4d3ef99de668cff6a.zip
New class for zipping/unzipping gzip streams.
2004-05-10 Jeffrey Stedfast <fejj@novell.com> * camel-mime-filter-gzip.[c,h]: New class for zipping/unzipping gzip streams. * camel-mime-filter-yenc.[c,h]: New class for encoding/decoding the crack known as YEncode. svn path=/trunk/; revision=25847
Diffstat (limited to 'camel/camel-mime-filter-gzip.c')
-rw-r--r--camel/camel-mime-filter-gzip.c461
1 files changed, 461 insertions, 0 deletions
diff --git a/camel/camel-mime-filter-gzip.c b/camel/camel-mime-filter-gzip.c
new file mode 100644
index 0000000000..f3c640240b
--- /dev/null
+++ b/camel/camel-mime-filter-gzip.c
@@ -0,0 +1,461 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <zlib.h>
+
+#include "camel-mime-filter-gzip.h"
+
+
+/* rfc1952 */
+
+enum {
+ GZIP_FLAG_FTEXT = (1 << 0),
+ GZIP_FLAG_FHCRC = (1 << 1),
+ GZIP_FLAG_FEXTRA = (1 << 2),
+ GZIP_FLAG_FNAME = (1 << 3),
+ GZIP_FLAG_FCOMMENT = (1 << 4),
+ GZIP_FLAG_RESERVED0 = (1 << 5),
+ GZIP_FLAG_RESERVED1 = (1 << 6),
+ GZIP_FLAG_RESERVED2 = (1 << 7),
+};
+
+#define GZIP_FLAG_RESERVED (GZIP_FLAG_RESERVED0 | GZIP_FLAG_RESERVED1 | GZIP_FLAG_RESERVED2)
+
+typedef union {
+ unsigned char buf[10];
+ struct {
+ guint8 id1;
+ guint8 id2;
+ guint8 cm;
+ guint8 flg;
+ guint32 mtime;
+ guint8 xfl;
+ guint8 os;
+ } v;
+} gzip_hdr_t;
+
+typedef union {
+ struct {
+ guint16 xlen;
+ guint16 xlen_nread;
+ guint16 crc16;
+
+ guint8 got_hdr:1;
+ guint8 is_valid:1;
+ guint8 got_xlen:1;
+ guint8 got_fname:1;
+ guint8 got_fcomment:1;
+ guint8 got_crc16:1;
+ } unzip;
+ struct {
+ guint32 wrote_hdr:1;
+ } zip;
+} gzip_state_t;
+
+struct _CamelMimeFilterGZipPrivate {
+ z_stream *stream;
+
+ gzip_state_t state;
+ gzip_hdr_t hdr;
+
+ guint32 crc32;
+ guint32 isize;
+};
+
+static void camel_mime_filter_gzip_class_init (CamelMimeFilterGZipClass *klass);
+static void camel_mime_filter_gzip_init (CamelMimeFilterGZip *filter, CamelMimeFilterGZipClass *klass);
+static void camel_mime_filter_gzip_finalize (CamelObject *object);
+
+static void filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+static void filter_reset (CamelMimeFilter *filter);
+
+
+static CamelMimeFilterClass *parent_class = NULL;
+
+
+CamelType
+camel_mime_filter_gzip_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (camel_mime_filter_get_type (),
+ "CamelMimeFilterGZip",
+ sizeof (CamelMimeFilterGZip),
+ sizeof (CamelMimeFilterGZipClass),
+ (CamelObjectClassInitFunc) camel_mime_filter_gzip_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mime_filter_gzip_init,
+ (CamelObjectFinalizeFunc) camel_mime_filter_gzip_finalize);
+ }
+
+ return type;
+}
+
+
+static void
+camel_mime_filter_gzip_class_init (CamelMimeFilterGZipClass *klass)
+{
+ CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
+
+ parent_class = CAMEL_MIME_FILTER_CLASS (camel_type_get_global_classfuncs (camel_mime_filter_get_type ()));
+
+ filter_class->reset = filter_reset;
+ filter_class->filter = filter_filter;
+ filter_class->complete = filter_complete;
+}
+
+static void
+camel_mime_filter_gzip_init (CamelMimeFilterGZip *filter, CamelMimeFilterGZipClass *klass)
+{
+ filter->priv = g_new0 (struct _CamelMimeFilterGZipPrivate, 1);
+ filter->priv->stream = g_new0 (z_stream, 1);
+ filter->priv->crc32 = crc32 (0, Z_NULL, 0);
+}
+
+static void
+camel_mime_filter_gzip_finalize (CamelObject *object)
+{
+ CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) object;
+ struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
+
+ g_free (priv->stream);
+ g_free (priv);
+}
+
+
+static void
+gzip_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace, int flush)
+{
+ CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
+ struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
+ int retval;
+
+ if (!priv->state.zip.wrote_hdr) {
+ priv->hdr.v.id1 = 31;
+ priv->hdr.v.id2 = 139;
+ priv->hdr.v.cm = Z_DEFLATED;
+ priv->hdr.v.mtime = 0;
+ priv->hdr.v.flg = 0;
+ if (gzip->level == Z_BEST_COMPRESSION)
+ priv->hdr.v.xfl = 2;
+ else if (gzip->level == Z_BEST_SPEED)
+ priv->hdr.v.xfl = 4;
+ else
+ priv->hdr.v.xfl = 0;
+ priv->hdr.v.os = 255;
+
+ camel_mime_filter_set_size (filter, (len * 2) + 22, FALSE);
+
+ memcpy (filter->outbuf, priv->hdr.buf, 10);
+
+ priv->stream->next_out = filter->outbuf + 10;
+ priv->stream->avail_out = filter->outsize - 10;
+
+ priv->state.zip.wrote_hdr = TRUE;
+ } else {
+ camel_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
+
+ priv->stream->next_out = filter->outbuf;
+ priv->stream->avail_out = filter->outsize;
+ }
+
+ priv->stream->next_in = in;
+ priv->stream->avail_in = len;
+
+ do {
+ /* FIXME: handle error cases? */
+ if ((retval = deflate (priv->stream, flush)) != Z_OK)
+ fprintf (stderr, "gzip: %d: %s\n", retval, priv->stream->msg);
+
+ if (flush == Z_FULL_FLUSH) {
+ size_t outlen;
+
+ outlen = filter->outsize - priv->stream->avail_out;
+ camel_mime_filter_set_size (filter, outlen + (priv->stream->avail_in * 2) + 12, TRUE);
+ priv->stream->avail_out = filter->outsize - outlen;
+ priv->stream->next_out = filter->outbuf + outlen;
+
+ if (priv->stream->avail_in == 0) {
+ guint32 val;
+
+ val = GUINT32_TO_LE (priv->crc32);
+ memcpy (priv->stream->next_out, &val, 4);
+ priv->stream->avail_out -= 4;
+ priv->stream->next_out += 4;
+
+ val = GUINT32_TO_LE (priv->isize);
+ memcpy (priv->stream->next_out, &val, 4);
+ priv->stream->avail_out -= 4;
+ priv->stream->next_out += 4;
+
+ break;
+ }
+ } else {
+ if (priv->stream->avail_in > 0)
+ camel_mime_filter_backup (filter, priv->stream->next_in, priv->stream->avail_in);
+
+ break;
+ }
+ } while (1);
+
+ priv->crc32 = crc32 (priv->crc32, in, len - priv->stream->avail_in);
+ priv->isize += len - priv->stream->avail_in;
+
+ *out = filter->outbuf;
+ *outlen = filter->outsize - priv->stream->avail_out;
+ *outprespace = filter->outpre;
+}
+
+static void
+gunzip_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace, int flush)
+{
+ CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
+ struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
+ guint16 need, val;
+ int retval;
+
+ if (!priv->state.unzip.got_hdr) {
+ if (len < 10) {
+ camel_mime_filter_backup (filter, in, len);
+ return;
+ }
+
+ memcpy (priv->hdr.buf, in, 10);
+ priv->state.unzip.got_hdr = TRUE;
+ len -= 10;
+ in += 10;
+
+ priv->state.unzip.is_valid = (priv->hdr.v.id1 == 31 &&
+ priv->hdr.v.id2 == 139 &&
+ priv->hdr.v.cm == Z_DEFLATED);
+ }
+
+ if (!priv->state.unzip.is_valid)
+ return;
+
+ if (priv->hdr.v.flg & GZIP_FLAG_FEXTRA) {
+ if (!priv->state.unzip.got_xlen) {
+ if (len < 2) {
+ camel_mime_filter_backup (filter, in, len);
+ return;
+ }
+
+ memcpy (&val, in, 2);
+ priv->state.unzip.xlen = GUINT16_FROM_LE (val);
+ priv->state.unzip.got_xlen = TRUE;
+ len -= 2;
+ in += 2;
+ }
+
+ if (priv->state.unzip.xlen_nread < priv->state.unzip.xlen) {
+ need = priv->state.unzip.xlen - priv->state.unzip.xlen_nread;
+
+ if (need < len) {
+ priv->state.unzip.xlen_nread += need;
+ len -= need;
+ in += need;
+ } else {
+ priv->state.unzip.xlen_nread += len;
+ return;
+ }
+ }
+ }
+
+ if ((priv->hdr.v.flg & GZIP_FLAG_FNAME) && !priv->state.unzip.got_fname) {
+ while (*in && len > 0) {
+ len--;
+ in++;
+ }
+
+ if (*in == '\0' && len > 0) {
+ priv->state.unzip.got_fname = TRUE;
+ len--;
+ in++;
+ } else {
+ return;
+ }
+ }
+
+ if ((priv->hdr.v.flg & GZIP_FLAG_FCOMMENT) && !priv->state.unzip.got_fcomment) {
+ while (*in && len > 0) {
+ len--;
+ in++;
+ }
+
+ if (*in == '\0' && len > 0) {
+ priv->state.unzip.got_fcomment = TRUE;
+ len--;
+ in++;
+ } else {
+ return;
+ }
+ }
+
+ if ((priv->hdr.v.flg & GZIP_FLAG_FHCRC) && !priv->state.unzip.got_crc16) {
+ if (len < 2) {
+ camel_mime_filter_backup (filter, in, len);
+ return;
+ }
+
+ memcpy (&val, in, 2);
+ priv->state.unzip.crc16 = GUINT16_FROM_LE (val);
+ len -= 2;
+ in += 2;
+ }
+
+ if (len == 0)
+ return;
+
+ camel_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
+
+ priv->stream->next_in = in;
+ priv->stream->avail_in = len - 8;
+
+ priv->stream->next_out = filter->outbuf;
+ priv->stream->avail_out = filter->outsize;
+
+ do {
+ /* FIXME: handle error cases? */
+ if ((retval = inflate (priv->stream, flush)) != Z_OK)
+ fprintf (stderr, "gunzip: %d: %s\n", retval, priv->stream->msg);
+
+ if (flush == Z_FULL_FLUSH) {
+ size_t outlen;
+
+ if (priv->stream->avail_in == 0) {
+ /* FIXME: extract & compare calculated crc32 and isize values? */
+ break;
+ }
+
+ outlen = filter->outsize - priv->stream->avail_out;
+ camel_mime_filter_set_size (filter, outlen + (priv->stream->avail_in * 2) + 12, TRUE);
+ priv->stream->avail_out = filter->outsize - outlen;
+ priv->stream->next_out = filter->outbuf + outlen;
+ } else {
+ priv->stream->avail_in += 8;
+
+ if (priv->stream->avail_in > 0)
+ camel_mime_filter_backup (filter, priv->stream->next_in, priv->stream->avail_in);
+
+ break;
+ }
+ } while (1);
+
+ /* FIXME: if we keep this, we could check that the gzip'd
+ * stream is sane, but how would we tell our consumer if it
+ * was/wasn't? */
+ /*priv->crc32 = crc32 (priv->crc32, in, len - priv->stream->avail_in - 8);
+ priv->isize += len - priv->stream->avail_in - 8;*/
+
+ *out = filter->outbuf;
+ *outlen = filter->outsize - priv->stream->avail_out;
+ *outprespace = filter->outpre;
+}
+
+static void
+filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace)
+{
+ CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
+
+ if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
+ gzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_SYNC_FLUSH);
+ else
+ gunzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_SYNC_FLUSH);
+}
+
+static void
+filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace)
+{
+ CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
+
+ if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
+ gzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_FULL_FLUSH);
+ else
+ gunzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_FULL_FLUSH);
+}
+
+/* should this 'flush' outstanding state/data bytes? */
+static void
+filter_reset (CamelMimeFilter *filter)
+{
+ CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
+ struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
+
+ memset (&priv->state, 0, sizeof (priv->state));
+
+ if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
+ deflateReset (priv->stream);
+ else
+ inflateReset (priv->stream);
+
+ priv->crc32 = crc32 (0, Z_NULL, 0);
+ priv->isize = 0;
+}
+
+
+/**
+ * camel_mime_filter_gzip_new:
+ * @mode: zip or unzip
+ * @level: compression level
+ *
+ * Creates a new gzip (or gunzip) filter.
+ *
+ * Returns a new gzip (or gunzip) filter.
+ **/
+CamelMimeFilter *
+camel_mime_filter_gzip_new (CamelMimeFilterGZipMode mode, int level)
+{
+ CamelMimeFilterGZip *new;
+ int retval;
+
+ new = (CamelMimeFilterGZip *) camel_object_new (CAMEL_TYPE_MIME_FILTER_GZIP);
+ new->mode = mode;
+ new->level = level;
+
+ if (mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
+ retval = deflateInit2 (new->priv->stream, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ else
+ retval = inflateInit2 (new->priv->stream, -MAX_WBITS);
+
+ if (retval != Z_OK) {
+ camel_object_unref (new);
+ return NULL;
+ }
+
+ return (CamelMimeFilter *) new;
+}