ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / gzip-method.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gzip-method.c - GZIP access method for the GNOME Virtual File
3    System.
4
5    Copyright (C) 1999 Free Software Foundation
6
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11
12    The Gnome Library 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 GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public
18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.
21
22    Author: Ettore Perazzoli <ettore@comm2000.it> */
23
24 #include <config.h>
25
26 #include <glib/galloca.h>
27 #include <glib/gmessages.h>
28 #include <glib/gstrfuncs.h>
29 #include <libgnomevfs/gnome-vfs-mime.h>
30 #include <libgnomevfs/gnome-vfs-module.h>
31 #include <libgnomevfs/gnome-vfs-ops.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 #include <zlib.h>
36
37 struct _GZipMethodHandle {
38         GnomeVFSURI *uri;
39         GnomeVFSHandle *parent_handle;
40         GnomeVFSOpenMode open_mode;
41         time_t modification_time;
42
43         GnomeVFSResult last_vfs_result;
44         gint last_z_result;
45         z_stream zstream;
46         guchar *buffer;
47         guint32 crc;
48 };
49 typedef struct _GZipMethodHandle GZipMethodHandle;
50
51 \f
52 #define GZIP_MAGIC_1 0x1f
53 #define GZIP_MAGIC_2 0x8b
54
55 #define GZIP_FLAG_ASCII        0x01 /* bit 0 set: file probably ascii text */
56 #define GZIP_FLAG_HEAD_CRC     0x02 /* bit 1 set: header CRC present */
57 #define GZIP_FLAG_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
58 #define GZIP_FLAG_ORIG_NAME    0x08 /* bit 3 set: original file name present */
59 #define GZIP_FLAG_COMMENT      0x10 /* bit 4 set: file comment present */
60 #define GZIP_FLAG_RESERVED     0xE0 /* bits 5..7: reserved */
61
62 #define GZIP_HEADER_SIZE 10
63 #define GZIP_FOOTER_SIZE 8
64
65 #define Z_BUFSIZE 16384
66
67 \f
68 static GnomeVFSResult   do_open         (GnomeVFSMethod *method,
69                                          GnomeVFSMethodHandle **method_handle,
70                                          GnomeVFSURI *uri,
71                                          GnomeVFSOpenMode mode,
72                                          GnomeVFSContext *context);
73
74 static GnomeVFSResult   do_create       (GnomeVFSMethod *method,
75                                          GnomeVFSMethodHandle **method_handle,
76                                          GnomeVFSURI *uri,
77                                          GnomeVFSOpenMode mode,
78                                          gboolean exclusive,
79                                          guint perm,
80                                          GnomeVFSContext *context);
81
82 static GnomeVFSResult   do_close        (GnomeVFSMethod *method,
83                                          GnomeVFSMethodHandle *method_handle,
84                                          GnomeVFSContext *context);
85
86 static GnomeVFSResult   do_read         (GnomeVFSMethod *method,
87                                          GnomeVFSMethodHandle *method_handle,
88                                          gpointer buffer,
89                                          GnomeVFSFileSize num_bytes,
90                                          GnomeVFSFileSize *bytes_read,
91                                          GnomeVFSContext *context);
92
93 static GnomeVFSResult   do_write        (GnomeVFSMethod *method,
94                                          GnomeVFSMethodHandle *method_handle,
95                                          gconstpointer buffer,
96                                          GnomeVFSFileSize num_bytes,
97                                          GnomeVFSFileSize *bytes_written,
98                                          GnomeVFSContext *context);
99
100 static GnomeVFSResult   do_get_file_info(GnomeVFSMethod *method,
101                                          GnomeVFSURI *uri,
102                                          GnomeVFSFileInfo *file_info,
103                                          GnomeVFSFileInfoOptions options,
104                                          GnomeVFSContext *context);
105
106 static gboolean         do_is_local     (GnomeVFSMethod *method,
107                                          const GnomeVFSURI *uri);
108
109 static GnomeVFSMethod method = {
110         sizeof (GnomeVFSMethod),
111         do_open,
112         do_create,
113         do_close,
114         do_read,
115         do_write,
116         NULL,                   /* seek */
117         NULL,                   /* tell */
118         NULL,                   /* truncate_handle FIXME bugzilla.eazel.com 1175 */
119         NULL,                   /* open_directory */
120         NULL,                   /* close_directory */
121         NULL,                   /* read_directory */
122         do_get_file_info,
123         NULL,                   /* get_file_info_from_handle */
124         do_is_local,
125         NULL,                   /* make_directory */
126         NULL,                   /* remove_directory */
127         NULL,                   /* move */
128         NULL,                   /* unlink */
129         NULL,                   /* check_same_fs */
130         NULL,                   /* set_file_info */
131         NULL,                   /* truncate */
132         NULL,                   /* find_directory */
133         NULL                    /* create_symbolic_link */
134 };
135
136 #define RETURN_IF_FAIL(action)                  \
137 G_STMT_START{                                   \
138         GnomeVFSResult __tmp_result;            \
139                                                 \
140         __tmp_result = (action);                \
141         if (__tmp_result != GNOME_VFS_OK)       \
142                 return __tmp_result;            \
143 }G_STMT_END
144
145 #define VALID_URI(u) ((u)->parent!=NULL&&(((u)->text==NULL)||((u)->text[0]=='\0')||(((u)->text[0]=='/')&&((u)->text[1]=='\0'))))
146
147 \f
148 /* GZip handle creation/destruction.  */
149
150 static GZipMethodHandle *
151 gzip_method_handle_new (GnomeVFSHandle *parent_handle,
152                         time_t modification_time,
153                         GnomeVFSURI *uri,
154                         GnomeVFSOpenMode open_mode)
155 {
156         GZipMethodHandle *new;
157
158         new = g_new (GZipMethodHandle, 1);
159
160         new->parent_handle = parent_handle;
161         new->modification_time = modification_time;
162         new->uri = gnome_vfs_uri_ref (uri);
163         new->open_mode = open_mode;
164
165         new->buffer = NULL;
166         new->crc = crc32 (0, Z_NULL, 0);
167
168         return new;
169 }
170
171 static void
172 gzip_method_handle_destroy (GZipMethodHandle *handle)
173 {
174         gnome_vfs_uri_unref (handle->uri);
175         g_free (handle->buffer);
176         g_free (handle);
177 }
178
179 \f
180 /* GZip method initialization for compression/decompression.  */
181
182 static gboolean
183 gzip_method_handle_init_for_inflate (GZipMethodHandle *handle)
184 {
185         handle->zstream.zalloc = NULL;
186         handle->zstream.zfree = NULL;
187         handle->zstream.opaque = NULL;
188
189         g_free (handle->buffer);
190
191         handle->buffer = g_malloc (Z_BUFSIZE);
192         handle->zstream.next_in = handle->buffer;
193         handle->zstream.avail_in = 0;
194
195         if (inflateInit2 (&handle->zstream, -MAX_WBITS) != Z_OK) {
196                 g_free (handle->buffer);
197                 return FALSE;
198         }
199
200         handle->last_z_result = Z_OK;
201         handle->last_vfs_result = GNOME_VFS_OK;
202
203         return TRUE;
204 }
205
206 static gboolean
207 gzip_method_handle_init_for_deflate (GZipMethodHandle *handle)
208 {
209         handle->zstream.zalloc = NULL;
210         handle->zstream.zfree = NULL;
211         handle->zstream.opaque = NULL;
212
213         g_free (handle->buffer);
214
215         handle->buffer = g_malloc (Z_BUFSIZE);
216         handle->zstream.next_out = handle->buffer;
217         handle->zstream.avail_out = Z_BUFSIZE;
218
219         /* FIXME bugzilla.eazel.com 1174: We want this to be user-configurable.  */
220         if (deflateInit2 (&handle->zstream, Z_DEFAULT_COMPRESSION,
221                           Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL,
222                           Z_DEFAULT_STRATEGY) != Z_OK) {
223                 g_free (handle->buffer);
224                 return FALSE;
225         }
226
227         handle->last_z_result = Z_OK;
228         handle->last_vfs_result = GNOME_VFS_OK;
229
230         return TRUE;
231 }
232
233 \f
234 static GnomeVFSResult
235 result_from_z_result (gint z_result)
236 {
237         switch (z_result) {
238         case Z_OK:
239         case Z_STREAM_END: /* FIXME bugzilla.eazel.com 1173: Is this right? */
240                 return GNOME_VFS_OK;
241         case Z_DATA_ERROR:
242                 return GNOME_VFS_ERROR_CORRUPTED_DATA;
243         default:
244                 return GNOME_VFS_ERROR_INTERNAL;
245         }
246 }
247
248 \f
249 /* Functions to skip data in the file.  */
250
251 static GnomeVFSResult
252 skip_string (GnomeVFSHandle *handle)
253 {
254         GnomeVFSResult result;
255         guchar c;
256         GnomeVFSFileSize bytes_read;
257
258         do {
259                 result = gnome_vfs_read (handle, &c, 1, &bytes_read);
260                 RETURN_IF_FAIL (result);
261
262                 if (bytes_read != 1)
263                         return GNOME_VFS_ERROR_WRONG_FORMAT;
264         } while (c != 0);
265
266         return GNOME_VFS_OK;
267 }
268
269 static gboolean
270 skip (GnomeVFSHandle *handle,
271       GnomeVFSFileSize num_bytes)
272 {
273         GnomeVFSResult result;
274         guchar *tmp;
275         GnomeVFSFileSize bytes_read;
276
277         tmp = g_alloca (num_bytes);
278
279         result = gnome_vfs_read (handle, tmp, num_bytes, &bytes_read);
280         RETURN_IF_FAIL (result);
281
282         if (bytes_read != num_bytes)
283                 return GNOME_VFS_ERROR_WRONG_FORMAT;
284
285         return TRUE;
286 }
287
288 \f
289 /* Utility function to write a gulong value.  */
290
291 static GnomeVFSResult
292 write_guint32 (GnomeVFSHandle *handle,
293                guint32 value)
294 {
295         guint i;
296         guchar buffer[4];
297         GnomeVFSFileSize bytes_written;
298
299         for (i = 0; i < 4; i++) {
300                 buffer[i] = value & 0xff;
301                 value >>= 8;
302         }
303
304         return gnome_vfs_write (handle, buffer, 4, &bytes_written);
305 }
306
307 \f
308 /* GZIP Header functions.  */
309
310 static GnomeVFSResult
311 read_gzip_header (GnomeVFSHandle *handle,
312                   time_t *modification_time)
313 {
314         GnomeVFSResult result;
315         guchar buffer[GZIP_HEADER_SIZE];
316         GnomeVFSFileSize bytes_read;
317         guint mode;
318         guint flags;
319
320         result = gnome_vfs_read (handle, buffer, GZIP_HEADER_SIZE,
321                                  &bytes_read);
322         RETURN_IF_FAIL (result);
323
324         if (bytes_read != GZIP_HEADER_SIZE)
325                 return GNOME_VFS_ERROR_WRONG_FORMAT;
326
327         if (buffer[0] != GZIP_MAGIC_1 || buffer[1] != GZIP_MAGIC_2)
328                 return GNOME_VFS_ERROR_WRONG_FORMAT;
329
330         mode = buffer[2];
331         if (mode != 8) /* Mode: deflate */
332                 return GNOME_VFS_ERROR_WRONG_FORMAT;
333
334         flags = buffer[3];
335
336         if (flags & GZIP_FLAG_RESERVED)
337                 return GNOME_VFS_ERROR_WRONG_FORMAT;
338
339         if (flags & GZIP_FLAG_EXTRA_FIELD) {
340                 guchar tmp[2];
341                 GnomeVFSFileSize bytes_read;
342
343                 if (gnome_vfs_read (handle, tmp, 2, &bytes_read)
344                     || bytes_read != 2)
345                         return GNOME_VFS_ERROR_WRONG_FORMAT;
346                 if (! skip (handle, tmp[0] | (tmp[0] << 8)))
347                         return GNOME_VFS_ERROR_WRONG_FORMAT;
348         }
349
350         if (flags & GZIP_FLAG_ORIG_NAME)
351                 RETURN_IF_FAIL (skip_string (handle));
352
353         if (flags & GZIP_FLAG_COMMENT)
354                 RETURN_IF_FAIL (skip_string (handle));
355
356         if (flags & GZIP_FLAG_HEAD_CRC)
357                 RETURN_IF_FAIL (skip (handle, 2));
358
359         *modification_time = (buffer[4] | (buffer[5] << 8)
360                               | (buffer[6] << 16) | (buffer[7] << 24));
361         return GNOME_VFS_OK;
362 }
363
364 static GnomeVFSResult
365 write_gzip_header (GnomeVFSHandle *handle, time_t modification_time)
366 {
367         GnomeVFSResult result;
368         guchar buffer[GZIP_HEADER_SIZE];
369         GnomeVFSFileSize bytes_written;
370
371         buffer[0] = GZIP_MAGIC_1;     /* magic 1 */
372         buffer[1] = GZIP_MAGIC_2;     /* magic 2 */
373         buffer[2] = Z_DEFLATED;       /* method */
374         buffer[3] = 0;                /* flags */
375         buffer[4] = (guchar) ((modification_time >>  0) & 0xFF);   /* time 1 */
376         buffer[5] = (guchar) ((modification_time >>  8) & 0xFF);   /* time 2 */
377         buffer[6] = (guchar) ((modification_time >> 16) & 0xFF);   /* time 3 */
378         buffer[7] = (guchar) ((modification_time >> 24) & 0xFF);   /* time 4 */
379         buffer[8] = 0;                /* xflags */
380         buffer[9] = 3;                /* OS (Unix) */
381
382         result = gnome_vfs_write (handle, buffer, GZIP_HEADER_SIZE,
383                                   &bytes_written);
384         RETURN_IF_FAIL (result);
385
386         if (bytes_written != GZIP_HEADER_SIZE)
387                 return GNOME_VFS_ERROR_IO;
388
389         return GNOME_VFS_OK;
390 }
391
392 static GnomeVFSResult
393 flush_write (GZipMethodHandle *gzip_handle)
394 {
395         GnomeVFSHandle *parent_handle;
396         GnomeVFSResult result;
397         gboolean done;
398         z_stream *zstream;
399         gint z_result;
400
401         zstream = &gzip_handle->zstream;
402         zstream->avail_in = 0;         /* (Should be zero already anyway.)  */
403
404         parent_handle = gzip_handle->parent_handle;
405
406         done = FALSE;
407         z_result = Z_OK;
408         while (z_result == Z_OK || z_result == Z_STREAM_END) {
409                 GnomeVFSFileSize bytes_written;
410                 GnomeVFSFileSize len;
411
412                 len = Z_BUFSIZE - zstream->avail_out;
413
414                 result = gnome_vfs_write (parent_handle, gzip_handle->buffer,
415                                           len, &bytes_written);
416                 RETURN_IF_FAIL (result);
417
418                 zstream->next_out = gzip_handle->buffer;
419                 zstream->avail_out = Z_BUFSIZE;
420
421                 if (done)
422                         break;
423
424                 z_result = deflate(zstream, Z_FINISH);
425
426                 /* Ignore the second of two consecutive flushes.  */
427                 if (z_result == Z_BUF_ERROR)
428                         z_result = Z_OK;
429
430                 /* Deflate has finished flushing only when it hasn't used up
431                    all the available space in the output buffer.  */
432                 done = (zstream->avail_out != 0 || z_result == Z_STREAM_END);
433         }
434
435         result = write_guint32 (parent_handle, gzip_handle->crc);
436         RETURN_IF_FAIL (result);
437
438         result = write_guint32 (parent_handle, zstream->total_in);
439         RETURN_IF_FAIL (result);
440
441         if (z_result == Z_OK || z_result == Z_STREAM_END)
442                 return GNOME_VFS_OK;
443         else
444                 return result_from_z_result (z_result);
445 }
446
447 \f
448 /* Open.  */
449
450 /* TODO: 
451    - Check that there is no subpath.  */
452 static GnomeVFSResult
453 do_open (GnomeVFSMethod *method,
454          GnomeVFSMethodHandle **method_handle,
455          GnomeVFSURI *uri,
456          GnomeVFSOpenMode open_mode,
457          GnomeVFSContext *context)
458 {
459         GnomeVFSHandle *parent_handle;
460         GnomeVFSURI *parent_uri;
461         GnomeVFSResult result;
462         GZipMethodHandle *gzip_handle;
463         time_t modification_time;
464
465         _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
466         _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
467
468         /* Check that the URI is valid.  */
469         if (!VALID_URI(uri)) return GNOME_VFS_ERROR_INVALID_URI;
470
471         parent_uri = uri->parent;
472
473         if (open_mode & GNOME_VFS_OPEN_RANDOM)
474                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
475
476         result = gnome_vfs_open_uri (&parent_handle, parent_uri, open_mode);
477         RETURN_IF_FAIL (result);
478
479         if (open_mode & GNOME_VFS_OPEN_READ) {
480                 result = read_gzip_header (parent_handle, &modification_time);
481                 if (result != GNOME_VFS_OK) {
482                         gnome_vfs_close (parent_handle);
483                         return result;
484                 }
485
486                 gzip_handle = gzip_method_handle_new (parent_handle,
487                                                       modification_time,
488                                                       uri,
489                                                       open_mode);
490
491                 if (! gzip_method_handle_init_for_inflate (gzip_handle)) {
492                         gnome_vfs_close (parent_handle);
493                         gzip_method_handle_destroy (gzip_handle);
494                         return GNOME_VFS_ERROR_INTERNAL;
495                 }
496         } else {                          /* GNOME_VFS_OPEN_WRITE */
497                 modification_time = time (NULL);
498                 result = write_gzip_header (parent_handle, modification_time);
499                 RETURN_IF_FAIL (result);
500
501                 /* FIXME bugzilla.eazel.com 1172: need to set modification_time */
502                 gzip_handle = gzip_method_handle_new (parent_handle,
503                                                       modification_time,
504                                                       uri,
505                                                       open_mode);
506
507                 if (! gzip_method_handle_init_for_deflate (gzip_handle)) {
508                         gnome_vfs_close (parent_handle);
509                         gzip_method_handle_destroy (gzip_handle);
510                         return GNOME_VFS_ERROR_INTERNAL;
511                 }
512         }
513
514         *method_handle = (GnomeVFSMethodHandle *) gzip_handle;
515
516         return GNOME_VFS_OK;
517 }
518
519 \f
520 /* Create.  */
521
522 static GnomeVFSResult
523 do_create (GnomeVFSMethod *method,
524            GnomeVFSMethodHandle **method_handle,
525            GnomeVFSURI *uri,
526            GnomeVFSOpenMode mode,
527            gboolean exclusive,
528            guint perm,
529            GnomeVFSContext *context)
530 {
531         _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
532         _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
533
534         return GNOME_VFS_ERROR_NOT_SUPPORTED; /* FIXME bugzilla.eazel.com 1170 */
535 }
536
537 \f
538 /* Close.  */
539
540 static GnomeVFSResult
541 do_close (GnomeVFSMethod *method,
542           GnomeVFSMethodHandle *method_handle,
543           GnomeVFSContext *context)
544 {
545         GZipMethodHandle *gzip_handle;
546         GnomeVFSResult result;
547
548         _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
549
550         gzip_handle = (GZipMethodHandle *) method_handle;
551
552         if (gzip_handle->open_mode & GNOME_VFS_OPEN_WRITE)
553                 result = flush_write (gzip_handle);
554         else
555                 result = GNOME_VFS_OK;
556
557         if (result == GNOME_VFS_OK)
558                 result = gnome_vfs_close (gzip_handle->parent_handle);
559
560         gzip_method_handle_destroy (gzip_handle);
561
562         return result;
563 }
564
565 \f
566 /* Read. */
567 static GnomeVFSResult
568 fill_buffer (GZipMethodHandle *gzip_handle,
569              GnomeVFSFileSize num_bytes)
570 {
571         GnomeVFSResult result;
572         GnomeVFSFileSize count;
573         z_stream *zstream;
574
575         zstream = &gzip_handle->zstream;
576
577         if (zstream->avail_in > 0)
578                 return GNOME_VFS_OK;
579
580         result = gnome_vfs_read (gzip_handle->parent_handle,
581                                  gzip_handle->buffer,
582                                  Z_BUFSIZE,
583                                  &count);
584
585         if (result != GNOME_VFS_OK) {
586                 if (zstream->avail_out == num_bytes)
587                         return result;
588                 gzip_handle->last_vfs_result = result;
589         } else {
590                 zstream->next_in = gzip_handle->buffer;
591                 zstream->avail_in = count;
592         }
593
594         return GNOME_VFS_OK;
595 }
596
597 /* FIXME bugzilla.eazel.com 1165: TODO:
598    - Concatenated GZIP file handling.  */
599 static GnomeVFSResult
600 do_read (GnomeVFSMethod *method,
601          GnomeVFSMethodHandle *method_handle,
602          gpointer buffer,
603          GnomeVFSFileSize num_bytes,
604          GnomeVFSFileSize *bytes_read,
605          GnomeVFSContext *context)
606 {
607         GZipMethodHandle *gzip_handle;
608         GnomeVFSResult result;
609         z_stream *zstream;
610         int z_result;
611         guchar *crc_start;
612
613         *bytes_read = 0;
614
615         crc_start = buffer;
616
617         gzip_handle = (GZipMethodHandle *) method_handle;
618
619         zstream = &gzip_handle->zstream;
620
621         if (gzip_handle->last_z_result != Z_OK) {
622                 if (gzip_handle->last_z_result == Z_STREAM_END) {
623                         *bytes_read = 0;
624                         return GNOME_VFS_OK;
625                 } else
626                         return result_from_z_result (gzip_handle->last_z_result);
627         } else if (gzip_handle->last_vfs_result != GNOME_VFS_OK) {
628                 return gzip_handle->last_vfs_result;
629         }
630
631         zstream->next_out = buffer;
632         zstream->avail_out = num_bytes;
633
634         while (zstream->avail_out != 0) {
635                 result = fill_buffer (gzip_handle, num_bytes);
636                 RETURN_IF_FAIL (result);
637
638                 z_result = inflate (&gzip_handle->zstream, Z_NO_FLUSH);
639                 if (z_result == Z_STREAM_END) {
640                         gzip_handle->last_z_result = z_result;
641                         break;
642                 } else if (z_result != Z_OK) {  
643                         /* FIXME bugzilla.eazel.com 1165: Concatenated GZIP files?  */
644                         gzip_handle->last_z_result = z_result;
645                 }
646
647                 if (gzip_handle->last_z_result != Z_OK
648                     && zstream->avail_out == num_bytes)
649                         return result_from_z_result (gzip_handle->last_z_result);
650         }
651
652         gzip_handle->crc = crc32 (gzip_handle->crc,
653                                   crc_start,
654                                   (guint) (zstream->next_out - crc_start));
655
656         *bytes_read = num_bytes - zstream->avail_out;
657
658         return GNOME_VFS_OK;
659 }
660
661 \f
662 /* Write.  */
663
664 static GnomeVFSResult
665 do_write (GnomeVFSMethod *method,
666           GnomeVFSMethodHandle *method_handle,
667           gconstpointer buffer,
668           GnomeVFSFileSize num_bytes,
669           GnomeVFSFileSize *bytes_written,
670           GnomeVFSContext *context)
671 {
672         GZipMethodHandle *gzip_handle;
673         GnomeVFSResult result;
674         z_stream *zstream;
675         gint z_result;
676
677         gzip_handle = (GZipMethodHandle *) method_handle;
678         zstream = &gzip_handle->zstream;
679
680         /* This cast sucks.  It is not my fault, though.  :-)  */
681         zstream->next_in = (gpointer) buffer;
682         zstream->avail_in = num_bytes;
683
684         result = GNOME_VFS_OK;
685
686         while (zstream->avail_in != 0 && result == GNOME_VFS_OK) {
687                 if (zstream->avail_out == 0) {
688                         GnomeVFSFileSize written;
689
690                         zstream->next_out = gzip_handle->buffer;
691                         result = gnome_vfs_write (gzip_handle->parent_handle,
692                                                   gzip_handle->buffer,
693                                                   Z_BUFSIZE, &written);
694
695                         if (result != GNOME_VFS_OK)
696                                 break;
697
698                         zstream->avail_out += written;
699                 }
700
701                 z_result = deflate (zstream, Z_NO_FLUSH);
702                 result = result_from_z_result (z_result);
703         }
704
705         gzip_handle->crc = crc32 (gzip_handle->crc, buffer, num_bytes);
706
707         *bytes_written = num_bytes - zstream->avail_in;
708
709         return result;
710 }
711
712 \f
713 static GnomeVFSResult 
714 do_get_file_info  (GnomeVFSMethod *method,
715                    GnomeVFSURI *uri,
716                    GnomeVFSFileInfo *file_info,
717                    GnomeVFSFileInfoOptions options,
718                    GnomeVFSContext *context) {
719         GnomeVFSResult result;
720
721         if (!VALID_URI(uri)) return GNOME_VFS_ERROR_INVALID_URI;
722
723         result = gnome_vfs_get_file_info_uri(uri->parent, file_info, options);
724         if(result == GNOME_VFS_OK) {
725                 gint namelen = strlen(file_info->name);
726                 
727                 /* work out the name */
728                 /* FIXME bugzilla.eazel.com 2790: handle uppercase */
729                 if(namelen > 3 &&
730                                 file_info->name[namelen-1] == 'z' &&
731                                 file_info->name[namelen-2] == 'g' &&
732                                 file_info->name[namelen-3] == '.')
733                         file_info->name[namelen-3] = '\0';
734
735                 /* we can't tell the size without uncompressing it */
736                 //file_info->valid_fields &= ~GNOME_VFS_FILE_INFO_FIELDS_SIZE;
737
738                 /* guess the mime type of the file inside */
739                 /* FIXME bugzilla.eazel.com 2791: guess mime based on contents */
740                 g_free(file_info->mime_type);
741                 file_info->mime_type = g_strdup(gnome_vfs_mime_type_from_name(file_info->name));
742         }
743
744         return result;
745 }
746
747
748 \f
749 static gboolean
750 do_is_local (GnomeVFSMethod *method,
751              const GnomeVFSURI *uri)
752 {
753         g_return_val_if_fail (uri != NULL, FALSE);
754
755         return gnome_vfs_uri_is_local (uri->parent);
756 }
757
758 \f
759 /* Init.  */
760
761 GnomeVFSMethod *
762 vfs_module_init (const char *method_name, const char *args)
763 {
764         return &method;
765 }
766
767 void
768 vfs_module_shutdown (GnomeVFSMethod *method)
769 {
770 }
771