1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
33 #include "camel-mime-filter-gzip.h"
39 GZIP_FLAG_FTEXT = (1 << 0),
40 GZIP_FLAG_FHCRC = (1 << 1),
41 GZIP_FLAG_FEXTRA = (1 << 2),
42 GZIP_FLAG_FNAME = (1 << 3),
43 GZIP_FLAG_FCOMMENT = (1 << 4),
44 GZIP_FLAG_RESERVED0 = (1 << 5),
45 GZIP_FLAG_RESERVED1 = (1 << 6),
46 GZIP_FLAG_RESERVED2 = (1 << 7),
49 #define GZIP_FLAG_RESERVED (GZIP_FLAG_RESERVED0 | GZIP_FLAG_RESERVED1 | GZIP_FLAG_RESERVED2)
52 unsigned char buf[10];
74 guint8 got_fcomment:1;
82 struct _CamelMimeFilterGZipPrivate {
92 static void camel_mime_filter_gzip_class_init (CamelMimeFilterGZipClass *klass);
93 static void camel_mime_filter_gzip_init (CamelMimeFilterGZip *filter, CamelMimeFilterGZipClass *klass);
94 static void camel_mime_filter_gzip_finalize (CamelObject *object);
96 static void filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
97 char **out, size_t *outlen, size_t *outprespace);
98 static void filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
99 char **out, size_t *outlen, size_t *outprespace);
100 static void filter_reset (CamelMimeFilter *filter);
103 static CamelMimeFilterClass *parent_class = NULL;
107 camel_mime_filter_gzip_get_type (void)
109 static CamelType type = CAMEL_INVALID_TYPE;
111 if (type == CAMEL_INVALID_TYPE) {
112 type = camel_type_register (camel_mime_filter_get_type (),
113 "CamelMimeFilterGZip",
114 sizeof (CamelMimeFilterGZip),
115 sizeof (CamelMimeFilterGZipClass),
116 (CamelObjectClassInitFunc) camel_mime_filter_gzip_class_init,
118 (CamelObjectInitFunc) camel_mime_filter_gzip_init,
119 (CamelObjectFinalizeFunc) camel_mime_filter_gzip_finalize);
127 camel_mime_filter_gzip_class_init (CamelMimeFilterGZipClass *klass)
129 CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
131 parent_class = CAMEL_MIME_FILTER_CLASS (camel_type_get_global_classfuncs (camel_mime_filter_get_type ()));
133 filter_class->reset = filter_reset;
134 filter_class->filter = filter_filter;
135 filter_class->complete = filter_complete;
139 camel_mime_filter_gzip_init (CamelMimeFilterGZip *filter, CamelMimeFilterGZipClass *klass)
141 filter->priv = g_new0 (struct _CamelMimeFilterGZipPrivate, 1);
142 filter->priv->stream = g_new0 (z_stream, 1);
143 filter->priv->crc32 = crc32 (0, Z_NULL, 0);
147 camel_mime_filter_gzip_finalize (CamelObject *object)
149 CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) object;
150 struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
152 if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
153 deflateEnd (priv->stream);
155 inflateEnd (priv->stream);
157 g_free (priv->stream);
163 gzip_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
164 char **out, size_t *outlen, size_t *outprespace, int flush)
166 CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
167 struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
170 if (!priv->state.zip.wrote_hdr) {
171 priv->hdr.v.id1 = 31;
172 priv->hdr.v.id2 = 139;
173 priv->hdr.v.cm = Z_DEFLATED;
174 priv->hdr.v.mtime = 0;
176 if (gzip->level == Z_BEST_COMPRESSION)
178 else if (gzip->level == Z_BEST_SPEED)
182 priv->hdr.v.os = 255;
184 camel_mime_filter_set_size (filter, (len * 2) + 22, FALSE);
186 memcpy (filter->outbuf, priv->hdr.buf, 10);
188 priv->stream->next_out = filter->outbuf + 10;
189 priv->stream->avail_out = filter->outsize - 10;
191 priv->state.zip.wrote_hdr = TRUE;
193 camel_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
195 priv->stream->next_out = filter->outbuf;
196 priv->stream->avail_out = filter->outsize;
199 priv->stream->next_in = in;
200 priv->stream->avail_in = len;
203 /* FIXME: handle error cases? */
204 if ((retval = deflate (priv->stream, flush)) != Z_OK)
205 fprintf (stderr, "gzip: %d: %s\n", retval, priv->stream->msg);
207 if (flush == Z_FULL_FLUSH) {
210 n = filter->outsize - priv->stream->avail_out;
211 camel_mime_filter_set_size (filter, n + (priv->stream->avail_in * 2) + 12, TRUE);
212 priv->stream->avail_out = filter->outsize - n;
213 priv->stream->next_out = filter->outbuf + n;
215 if (priv->stream->avail_in == 0) {
218 val = GUINT32_TO_LE (priv->crc32);
219 memcpy (priv->stream->next_out, &val, 4);
220 priv->stream->avail_out -= 4;
221 priv->stream->next_out += 4;
223 val = GUINT32_TO_LE (priv->isize);
224 memcpy (priv->stream->next_out, &val, 4);
225 priv->stream->avail_out -= 4;
226 priv->stream->next_out += 4;
231 if (priv->stream->avail_in > 0)
232 camel_mime_filter_backup (filter, priv->stream->next_in, priv->stream->avail_in);
238 priv->crc32 = crc32 (priv->crc32, in, len - priv->stream->avail_in);
239 priv->isize += len - priv->stream->avail_in;
241 *out = filter->outbuf;
242 *outlen = filter->outsize - priv->stream->avail_out;
243 *outprespace = filter->outpre;
247 gunzip_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
248 char **out, size_t *outlen, size_t *outprespace, int flush)
250 CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
251 struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
255 if (!priv->state.unzip.got_hdr) {
257 camel_mime_filter_backup (filter, in, len);
261 memcpy (priv->hdr.buf, in, 10);
262 priv->state.unzip.got_hdr = TRUE;
266 priv->state.unzip.is_valid = (priv->hdr.v.id1 == 31 &&
267 priv->hdr.v.id2 == 139 &&
268 priv->hdr.v.cm == Z_DEFLATED);
271 if (!priv->state.unzip.is_valid)
274 if (priv->hdr.v.flg & GZIP_FLAG_FEXTRA) {
275 if (!priv->state.unzip.got_xlen) {
277 camel_mime_filter_backup (filter, in, len);
281 memcpy (&val, in, 2);
282 priv->state.unzip.xlen = GUINT16_FROM_LE (val);
283 priv->state.unzip.got_xlen = TRUE;
288 if (priv->state.unzip.xlen_nread < priv->state.unzip.xlen) {
289 need = priv->state.unzip.xlen - priv->state.unzip.xlen_nread;
292 priv->state.unzip.xlen_nread += need;
296 priv->state.unzip.xlen_nread += len;
302 if ((priv->hdr.v.flg & GZIP_FLAG_FNAME) && !priv->state.unzip.got_fname) {
303 while (*in && len > 0) {
308 if (*in == '\0' && len > 0) {
309 priv->state.unzip.got_fname = TRUE;
317 if ((priv->hdr.v.flg & GZIP_FLAG_FCOMMENT) && !priv->state.unzip.got_fcomment) {
318 while (*in && len > 0) {
323 if (*in == '\0' && len > 0) {
324 priv->state.unzip.got_fcomment = TRUE;
332 if ((priv->hdr.v.flg & GZIP_FLAG_FHCRC) && !priv->state.unzip.got_crc16) {
334 camel_mime_filter_backup (filter, in, len);
338 memcpy (&val, in, 2);
339 priv->state.unzip.crc16 = GUINT16_FROM_LE (val);
347 camel_mime_filter_set_size (filter, (len * 2) + 12, FALSE);
349 priv->stream->next_in = in;
350 priv->stream->avail_in = len - 8;
352 priv->stream->next_out = filter->outbuf;
353 priv->stream->avail_out = filter->outsize;
356 /* FIXME: handle error cases? */
357 if ((retval = inflate (priv->stream, flush)) != Z_OK)
358 fprintf (stderr, "gunzip: %d: %s\n", retval, priv->stream->msg);
360 if (flush == Z_FULL_FLUSH) {
363 if (priv->stream->avail_in == 0) {
364 /* FIXME: extract & compare calculated crc32 and isize values? */
368 n = filter->outsize - priv->stream->avail_out;
369 camel_mime_filter_set_size (filter, n + (priv->stream->avail_in * 2) + 12, TRUE);
370 priv->stream->avail_out = filter->outsize - n;
371 priv->stream->next_out = filter->outbuf + n;
373 priv->stream->avail_in += 8;
375 if (priv->stream->avail_in > 0)
376 camel_mime_filter_backup (filter, priv->stream->next_in, priv->stream->avail_in);
382 /* FIXME: if we keep this, we could check that the gzip'd
383 * stream is sane, but how would we tell our consumer if it
385 /*priv->crc32 = crc32 (priv->crc32, in, len - priv->stream->avail_in - 8);
386 priv->isize += len - priv->stream->avail_in - 8;*/
388 *out = filter->outbuf;
389 *outlen = filter->outsize - priv->stream->avail_out;
390 *outprespace = filter->outpre;
394 filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
395 char **out, size_t *outlen, size_t *outprespace)
397 CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
399 if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
400 gzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_SYNC_FLUSH);
402 gunzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_SYNC_FLUSH);
406 filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
407 char **out, size_t *outlen, size_t *outprespace)
409 CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
411 if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
412 gzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_FULL_FLUSH);
414 gunzip_filter (filter, in, len, prespace, out, outlen, outprespace, Z_FULL_FLUSH);
417 /* should this 'flush' outstanding state/data bytes? */
419 filter_reset (CamelMimeFilter *filter)
421 CamelMimeFilterGZip *gzip = (CamelMimeFilterGZip *) filter;
422 struct _CamelMimeFilterGZipPrivate *priv = gzip->priv;
424 memset (&priv->state, 0, sizeof (priv->state));
426 if (gzip->mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
427 deflateReset (priv->stream);
429 inflateReset (priv->stream);
431 priv->crc32 = crc32 (0, Z_NULL, 0);
437 * camel_mime_filter_gzip_new:
438 * @mode: zip or unzip
439 * @level: compression level
441 * Creates a new gzip (or gunzip) filter.
443 * Returns a new gzip (or gunzip) filter.
446 camel_mime_filter_gzip_new (CamelMimeFilterGZipMode mode, int level)
448 CamelMimeFilterGZip *new;
451 new = (CamelMimeFilterGZip *) camel_object_new (CAMEL_TYPE_MIME_FILTER_GZIP);
455 if (mode == CAMEL_MIME_FILTER_GZIP_MODE_ZIP)
456 retval = deflateInit2 (new->priv->stream, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
458 retval = inflateInit2 (new->priv->stream, -MAX_WBITS);
460 if (retval != Z_OK) {
461 camel_object_unref (new);
465 return (CamelMimeFilter *) new;