1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
31 #include "gmime-filter-gzip.h"
33 #ifdef ENABLE_WARNINGS
37 #endif /* ENABLE_WARNINGS */
41 * SECTION: gmime-filter-gzip
42 * @title: GMimeFilterGZip
43 * @short_description: GNU Zip compression/decompression
44 * @see_also: #GMimeFilter
46 * A #GMimeFilter used for compressing or decompressing a stream using
54 GZIP_FLAG_FTEXT = (1 << 0),
55 GZIP_FLAG_FHCRC = (1 << 1),
56 GZIP_FLAG_FEXTRA = (1 << 2),
57 GZIP_FLAG_FNAME = (1 << 3),
58 GZIP_FLAG_FCOMMENT = (1 << 4),
59 GZIP_FLAG_RESERVED0 = (1 << 5),
60 GZIP_FLAG_RESERVED1 = (1 << 6),
61 GZIP_FLAG_RESERVED2 = (1 << 7),
82 #define GZIP_FLAG_RESERVED (GZIP_FLAG_RESERVED0 | GZIP_FLAG_RESERVED1 | GZIP_FLAG_RESERVED2)
84 /* http://www.gzip.org/zlib/rfc-gzip.html */
86 unsigned char buf[10];
108 guint8 got_fcomment:1;
116 struct _GMimeFilterGZipPrivate {
126 static void g_mime_filter_gzip_class_init (GMimeFilterGZipClass *klass);
127 static void g_mime_filter_gzip_init (GMimeFilterGZip *filter, GMimeFilterGZipClass *klass);
128 static void g_mime_filter_gzip_finalize (GObject *object);
130 static GMimeFilter *filter_copy (GMimeFilter *filter);
131 static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
132 char **out, size_t *outlen, size_t *outprespace);
133 static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
134 char **out, size_t *outlen, size_t *outprespace);
135 static void filter_reset (GMimeFilter *filter);
138 static GMimeFilterClass *parent_class = NULL;
142 g_mime_filter_gzip_get_type (void)
144 static GType type = 0;
147 static const GTypeInfo info = {
148 sizeof (GMimeFilterGZipClass),
149 NULL, /* base_class_init */
150 NULL, /* base_class_finalize */
151 (GClassInitFunc) g_mime_filter_gzip_class_init,
152 NULL, /* class_finalize */
153 NULL, /* class_data */
154 sizeof (GMimeFilterGZip),
156 (GInstanceInitFunc) g_mime_filter_gzip_init,
159 type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterGZip", &info, 0);
167 g_mime_filter_gzip_class_init (GMimeFilterGZipClass *klass)
169 GObjectClass *object_class = G_OBJECT_CLASS (klass);
170 GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
172 parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
174 object_class->finalize = g_mime_filter_gzip_finalize;
176 filter_class->copy = filter_copy;
177 filter_class->filter = filter_filter;
178 filter_class->complete = filter_complete;
179 filter_class->reset = filter_reset;
183 g_mime_filter_gzip_init (GMimeFilterGZip *filter, GMimeFilterGZipClass *klass)
185 filter->priv = g_new0 (struct _GMimeFilterGZipPrivate, 1);
186 filter->priv->stream = g_new0 (z_stream, 1);
187 filter->priv->crc32 = crc32 (0, Z_NULL, 0);
191 g_mime_filter_gzip_finalize (GObject *object)
193 GMimeFilterGZip *gzip = (GMimeFilterGZip *) object;
194 struct _GMimeFilterGZipPrivate *priv = gzip->priv;
196 if (gzip->mode == GMIME_FILTER_GZIP_MODE_ZIP)
197 deflateEnd (priv->stream);
199 inflateEnd (priv->stream);
201 g_free (priv->stream);
204 G_OBJECT_CLASS (parent_class)->finalize (object);
209 filter_copy (GMimeFilter *filter)
211 GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
213 return g_mime_filter_gzip_new (gzip->mode, gzip->level);
217 gzip_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
218 char **out, size_t *outlen, size_t *outprespace, int flush)
220 GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
221 struct _GMimeFilterGZipPrivate *priv = gzip->priv;
224 if (!priv->state.zip.wrote_hdr) {
225 priv->hdr.v.id1 = 31;
226 priv->hdr.v.id2 = 139;
227 priv->hdr.v.cm = Z_DEFLATED;
228 priv->hdr.v.mtime = 0;
230 if (gzip->level == Z_BEST_COMPRESSION)
232 else if (gzip->level == Z_BEST_SPEED)
236 priv->hdr.v.os = GZIP_OS_UNKNOWN;
238 g_mime_filter_set_size (filter, (len * 2) + 22, FALSE);
240 memcpy (filter->outbuf, priv->hdr.buf, 10);
242 priv->stream->next_out = (unsigned char *) filter->outbuf + 10;
243 priv->stream->avail_out = filter->outsize - 10;
245 priv->state.zip.wrote_hdr = TRUE;
247 g_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
249 priv->stream->next_out = (unsigned char *) filter->outbuf;
250 priv->stream->avail_out = filter->outsize;
253 priv->stream->next_in = (unsigned char *) in;
254 priv->stream->avail_in = len;
257 /* FIXME: handle error cases? */
258 if ((retval = deflate (priv->stream, flush)) != Z_OK)
259 w(fprintf (stderr, "gzip: %d: %s\n", retval, priv->stream->msg));
261 if (flush == Z_FULL_FLUSH) {
264 outlen = filter->outsize - priv->stream->avail_out;
265 g_mime_filter_set_size (filter, outlen + (priv->stream->avail_in * 2) + 12, TRUE);
266 priv->stream->next_out = (unsigned char *) filter->outbuf + outlen;
267 priv->stream->avail_out = filter->outsize - outlen;
269 if (priv->stream->avail_in == 0) {
272 val = GUINT32_TO_LE (priv->crc32);
273 memcpy (priv->stream->next_out, &val, 4);
274 priv->stream->avail_out -= 4;
275 priv->stream->next_out += 4;
277 val = GUINT32_TO_LE (priv->isize);
278 memcpy (priv->stream->next_out, &val, 4);
279 priv->stream->avail_out -= 4;
280 priv->stream->next_out += 4;
285 if (priv->stream->avail_in > 0)
286 g_mime_filter_backup (filter, (char *) priv->stream->next_in,
287 priv->stream->avail_in);
293 priv->crc32 = crc32 (priv->crc32, (unsigned char *) in, len - priv->stream->avail_in);
294 priv->isize += len - priv->stream->avail_in;
296 *out = filter->outbuf;
297 *outlen = filter->outsize - priv->stream->avail_out;
298 *outprespace = filter->outpre;
302 gunzip_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
303 char **out, size_t *outlen, size_t *outprespace, int flush)
305 GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
306 struct _GMimeFilterGZipPrivate *priv = gzip->priv;
310 if (!priv->state.unzip.got_hdr) {
312 g_mime_filter_backup (filter, in, len);
316 memcpy (priv->hdr.buf, in, 10);
317 priv->state.unzip.got_hdr = TRUE;
321 priv->state.unzip.is_valid = (priv->hdr.v.id1 == 31 &&
322 priv->hdr.v.id2 == 139 &&
323 priv->hdr.v.cm == Z_DEFLATED);
326 if (!priv->state.unzip.is_valid)
329 if (priv->hdr.v.flg & GZIP_FLAG_FEXTRA) {
330 if (!priv->state.unzip.got_xlen) {
332 g_mime_filter_backup (filter, in, len);
336 memcpy (&val, in, 2);
337 priv->state.unzip.xlen = GUINT16_FROM_LE (val);
338 priv->state.unzip.got_xlen = TRUE;
343 if (priv->state.unzip.xlen_nread < priv->state.unzip.xlen) {
344 need = priv->state.unzip.xlen - priv->state.unzip.xlen_nread;
347 priv->state.unzip.xlen_nread += need;
351 priv->state.unzip.xlen_nread += len;
357 if ((priv->hdr.v.flg & GZIP_FLAG_FNAME) && !priv->state.unzip.got_fname) {
358 while (*in && len > 0) {
363 if (*in == '\0' && len > 0) {
364 priv->state.unzip.got_fname = TRUE;
372 if ((priv->hdr.v.flg & GZIP_FLAG_FCOMMENT) && !priv->state.unzip.got_fcomment) {
373 while (*in && len > 0) {
378 if (*in == '\0' && len > 0) {
379 priv->state.unzip.got_fcomment = TRUE;
387 if ((priv->hdr.v.flg & GZIP_FLAG_FHCRC) && !priv->state.unzip.got_crc16) {
389 g_mime_filter_backup (filter, in, len);
393 memcpy (&val, in, 2);
394 priv->state.unzip.crc16 = GUINT16_FROM_LE (val);
402 g_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
404 priv->stream->next_in = (unsigned char *) in;
405 priv->stream->avail_in = len - 8;
407 priv->stream->next_out = (unsigned char *) filter->outbuf;
408 priv->stream->avail_out = filter->outsize;
411 /* FIXME: handle error cases? */
412 /* Note: Z_BUF_ERROR is not really an error unless there is input available */
413 if ((retval = inflate (priv->stream, flush)) != Z_OK &&
414 !(retval == Z_BUF_ERROR && !priv->stream->avail_in))
415 w(fprintf (stderr, "gunzip: %d: %s\n", retval, priv->stream->msg));
417 if (flush == Z_FULL_FLUSH) {
420 if (priv->stream->avail_in == 0) {
421 /* FIXME: extract & compare calculated crc32 and isize values? */
425 outlen = filter->outsize - priv->stream->avail_out;
426 g_mime_filter_set_size (filter, outlen + (priv->stream->avail_in * 2) + 12, TRUE);
427 priv->stream->next_out = (unsigned char *) filter->outbuf + outlen;
428 priv->stream->avail_out = filter->outsize - outlen;
430 priv->stream->avail_in += 8;
432 if (priv->stream->avail_in > 0)
433 g_mime_filter_backup (filter, (char *) priv->stream->next_in,
434 priv->stream->avail_in);
440 /* FIXME: if we keep this, we could check that the gzip'd
441 * stream is sane, but how would we tell our consumer if it
443 /*priv->crc32 = crc32 (priv->crc32, in, len - priv->stream->avail_in - 8);
444 priv->isize += len - priv->stream->avail_in - 8;*/
446 *out = filter->outbuf;
447 *outlen = filter->outsize - priv->stream->avail_out;
448 *outprespace = filter->outpre;
452 filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
453 char **out, size_t *outlen, size_t *outprespace)
455 GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
457 if (gzip->mode == GMIME_FILTER_GZIP_MODE_ZIP)
458 gzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_SYNC_FLUSH);
460 gunzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_SYNC_FLUSH);
464 filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
465 char **out, size_t *outlen, size_t *outprespace)
467 GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
469 if (gzip->mode == GMIME_FILTER_GZIP_MODE_ZIP)
470 gzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_FULL_FLUSH);
472 gunzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_FULL_FLUSH);
475 /* should this 'flush' outstanding state/data bytes? */
477 filter_reset (GMimeFilter *filter)
479 GMimeFilterGZip *gzip = (GMimeFilterGZip *) filter;
480 struct _GMimeFilterGZipPrivate *priv = gzip->priv;
482 memset (&priv->state, 0, sizeof (priv->state));
484 if (gzip->mode == GMIME_FILTER_GZIP_MODE_ZIP)
485 deflateReset (priv->stream);
487 inflateReset (priv->stream);
489 priv->crc32 = crc32 (0, Z_NULL, 0);
495 * g_mime_filter_gzip_new:
496 * @mode: zip or unzip
497 * @level: compression level
499 * Creates a new gzip (or gunzip) filter.
501 * Returns: a new gzip (or gunzip) filter.
504 g_mime_filter_gzip_new (GMimeFilterGZipMode mode, int level)
506 GMimeFilterGZip *new;
509 new = g_object_newv (GMIME_TYPE_FILTER_GZIP, 0, NULL);
513 if (mode == GMIME_FILTER_GZIP_MODE_ZIP)
514 retval = deflateInit2 (new->priv->stream, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
516 retval = inflateInit2 (new->priv->stream, -MAX_WBITS);
518 if (retval != Z_OK) {
519 g_object_unref (new);
523 return (GMimeFilter *) new;