ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / bzip2-method.c
1 /*
2  * bzip2-method.c - Bzip2 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
20  * 330, Boston, MA 02111-1307, USA.
21  *
22  * Author: Cody Russell  <cody@jhu.edu>
23  */
24
25 #include <config.h>
26
27 #include <glib/gmessages.h>
28 #include <glib/gstrfuncs.h>
29 #include <libgnomevfs/gnome-vfs-handle.h>
30 #include <libgnomevfs/gnome-vfs-mime.h>
31 #include <libgnomevfs/gnome-vfs-module.h>
32 #include <libgnomevfs/gnome-vfs-ops.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <string.h>
36 #include <time.h>
37
38 #include <bzlib.h>
39
40 #ifdef HAVE_OLDER_BZIP2
41 #define BZ2_bzDecompressInit  bzDecompressInit
42 #define BZ2_bzCompressInit    bzCompressInit
43 #define BZ2_bzDecompress      bzDecompress
44 #define BZ2_bzCompress        bzCompress
45 #endif
46
47 #define BZ_BUFSIZE   5000
48
49 struct _Bzip2MethodHandle {
50         GnomeVFSURI      *uri;
51         GnomeVFSHandle   *parent_handle;
52         GnomeVFSOpenMode open_mode;
53
54         BZFILE           *file;
55         GnomeVFSResult   last_vfs_result;
56         gint             last_bz_result;
57         bz_stream        bzstream;
58         guchar           *buffer;
59 };
60
61 typedef struct _Bzip2MethodHandle Bzip2MethodHandle;
62
63 static GnomeVFSResult do_open (GnomeVFSMethod *method,
64                                GnomeVFSMethodHandle **method_handle,
65                                GnomeVFSURI *uri,
66                                GnomeVFSOpenMode mode,
67                                GnomeVFSContext *context);
68
69 static GnomeVFSResult do_create (GnomeVFSMethod *method,
70                                  GnomeVFSMethodHandle **method_handle,
71                                  GnomeVFSURI *uri,
72                                  GnomeVFSOpenMode mode,
73                                  gboolean exclusive,
74                                  guint perm,
75                                  GnomeVFSContext *context);
76
77 static GnomeVFSResult do_close (GnomeVFSMethod *method,
78                                 GnomeVFSMethodHandle *method_handle,
79                                 GnomeVFSContext *context);
80
81 static GnomeVFSResult do_read (GnomeVFSMethod *method,
82                                GnomeVFSMethodHandle *method_handle,
83                                gpointer buffer,
84                                GnomeVFSFileSize num_bytes,
85                                GnomeVFSFileSize *bytes_read,
86                                GnomeVFSContext *context);
87
88 static GnomeVFSResult do_write (GnomeVFSMethod *method,
89                                 GnomeVFSMethodHandle *method_handle,
90                                 gconstpointer buffer,
91                                 GnomeVFSFileSize num_bytes,
92                                 GnomeVFSFileSize *bytes_written,
93                                 GnomeVFSContext *context);
94
95 static GnomeVFSResult do_get_file_info  (GnomeVFSMethod *method,
96                                          GnomeVFSURI *uri,
97                                          GnomeVFSFileInfo *file_info,
98                                          GnomeVFSFileInfoOptions options,
99                                          GnomeVFSContext *context);
100
101 static gboolean do_is_local (GnomeVFSMethod *method, const GnomeVFSURI *uri);
102
103 static GnomeVFSMethod method = {
104         sizeof (GnomeVFSMethod),
105         do_open,
106         do_create,
107         do_close,
108         do_read,
109         do_write,
110         NULL,           /* seek            */
111         NULL,           /* tell            */
112         NULL,           /* truncate_handle */
113         NULL,           /* open_directory  */
114         NULL,           /* close_directory */
115         NULL,           /* read_directory  */
116         do_get_file_info,
117         NULL,           /* get_file_info_from_handle */
118         do_is_local,
119         NULL,           /* make_directory  */
120         NULL,           /* remove_directory */
121         NULL,           /* move */
122         NULL,           /* unlink */
123         NULL,           /* set_file_info */
124         NULL,           /* truncate */
125         NULL,           /* find_directory */
126         NULL            /* create_symbolic_link */
127 };
128
129 #define RETURN_IF_FAIL(action)                  \
130 G_STMT_START {                                  \
131         GnomeVFSResult __tmp_result;            \
132                                                 \
133         __tmp_result = (action);                \
134         if (__tmp_result != GNOME_VFS_OK)       \
135                 return __tmp_result;            \
136 } G_STMT_END
137
138 #define VALID_URI(u) ((u)->parent!=NULL&&(((u)->text==NULL)||((u)->text[0]=='\0')||(((u)->text[0]=='/')&&((u)->text[1]=='\0'))))
139
140 static Bzip2MethodHandle *
141 bzip2_method_handle_new (GnomeVFSHandle *parent_handle,
142                          GnomeVFSURI *uri,
143                          GnomeVFSOpenMode open_mode)
144 {
145         Bzip2MethodHandle *new;
146
147         new = g_new (Bzip2MethodHandle, 1);
148
149         new->parent_handle = parent_handle;
150         new->uri = gnome_vfs_uri_ref (uri);
151         new->open_mode = open_mode;
152
153         new->buffer = NULL;
154
155         return new;
156 }
157
158 static void
159 bzip2_method_handle_destroy (Bzip2MethodHandle *handle)
160 {
161         gnome_vfs_uri_unref (handle->uri);
162         g_free (handle->buffer);
163         g_free (handle);
164 }
165
166 static gboolean
167 bzip2_method_handle_init_for_decompress (Bzip2MethodHandle *handle)
168 {
169         handle->bzstream.bzalloc = NULL;
170         handle->bzstream.bzfree  = NULL;
171         handle->bzstream.opaque  = NULL;
172
173         g_free (handle->buffer);
174
175         handle->buffer = g_malloc (BZ_BUFSIZE);
176         handle->bzstream.next_in = handle->buffer;
177         handle->bzstream.avail_in = 0;
178
179         /* FIXME bugzilla.eazel.com 1177: Make small, and possibly verbosity, configurable! */
180         if (BZ2_bzDecompressInit (&handle->bzstream, 0, 0) != BZ_OK) {
181                 g_free (handle->buffer);
182                 return FALSE;
183         }
184
185         handle->last_bz_result = BZ_OK;
186         handle->last_vfs_result = GNOME_VFS_OK;
187
188         return TRUE;
189 }
190
191 static gboolean
192 bzip2_method_handle_init_for_compress (Bzip2MethodHandle *handle) G_GNUC_UNUSED;
193
194 static gboolean
195 bzip2_method_handle_init_for_compress (Bzip2MethodHandle *handle)
196 {
197         handle->bzstream.bzalloc = NULL;
198         handle->bzstream.bzfree  = NULL;
199         handle->bzstream.opaque  = NULL;
200
201         g_free (handle->buffer);
202
203         handle->buffer = g_malloc (BZ_BUFSIZE);
204         handle->bzstream.next_out = handle->buffer;
205         handle->bzstream.avail_out = BZ_BUFSIZE;
206
207         /* FIXME bugzilla.eazel.com 1174: We want this to be user configurable.  */
208         if (BZ2_bzCompressInit (&handle->bzstream, 3, 0, 30) != BZ_OK) {
209                 g_free (handle->buffer);
210                 return FALSE;
211         }
212
213         handle->last_bz_result = BZ_OK;
214         handle->last_vfs_result = GNOME_VFS_OK;
215
216         return TRUE;
217 }
218
219 static GnomeVFSResult
220 result_from_bz_result (gint bz_result)
221 {
222         switch (bz_result) {
223         case BZ_OK:
224         case BZ_STREAM_END:
225                 return GNOME_VFS_OK;
226
227         case BZ_MEM_ERROR:
228                 return GNOME_VFS_ERROR_NO_MEMORY;
229
230         case BZ_PARAM_ERROR:
231                 return GNOME_VFS_ERROR_BAD_PARAMETERS;
232
233         case BZ_DATA_ERROR:
234                 return GNOME_VFS_ERROR_CORRUPTED_DATA;
235
236         case BZ_UNEXPECTED_EOF:
237                 return GNOME_VFS_ERROR_EOF;
238
239         case BZ_SEQUENCE_ERROR:
240                 return GNOME_VFS_ERROR_NOT_PERMITTED;
241
242         default:
243                 return GNOME_VFS_ERROR_INTERNAL;
244         }
245 }
246
247 static GnomeVFSResult
248 flush_write (Bzip2MethodHandle *bzip2_handle)
249 {
250         GnomeVFSHandle *parent_handle;
251         GnomeVFSResult result;
252         gboolean done;
253         bz_stream *bzstream;
254         gint bz_result;
255
256         bzstream = &bzip2_handle->bzstream;
257         bzstream->avail_in = 0;
258         parent_handle = bzip2_handle->parent_handle;
259
260         done = FALSE;
261         bz_result = BZ_OK;
262         while (bz_result == BZ_OK || bz_result == BZ_STREAM_END) {
263                 GnomeVFSFileSize bytes_written;
264                 GnomeVFSFileSize len;
265
266                 len = BZ_BUFSIZE - bzstream->avail_out;
267
268                 result = gnome_vfs_write (parent_handle, bzip2_handle->buffer,
269                                           len, &bytes_written);
270                 RETURN_IF_FAIL (result);
271
272                 bzstream->next_out = bzip2_handle->buffer;
273                 bzstream->avail_out = BZ_BUFSIZE;
274
275                 if (done)
276                         break;
277
278                 bz_result = BZ2_bzCompress (bzstream, BZ_FINISH);
279
280                 done = (bzstream->avail_out != 0 || bz_result == BZ_STREAM_END);
281         }
282
283         if (bz_result == BZ_OK || bz_result == BZ_STREAM_END)
284                 return GNOME_VFS_OK;
285         else
286                 return result_from_bz_result (bz_result);
287 }
288
289 /* Open */
290 /* TODO: Check that there is no subpath. */
291
292 static GnomeVFSResult
293 do_open (GnomeVFSMethod *method,
294          GnomeVFSMethodHandle **method_handle,
295          GnomeVFSURI *uri,
296          GnomeVFSOpenMode open_mode,
297          GnomeVFSContext *context)
298 {
299         GnomeVFSHandle *parent_handle;
300         GnomeVFSURI *parent_uri;
301         GnomeVFSResult result;
302         Bzip2MethodHandle *bzip2_handle;
303
304         _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
305         _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
306
307         /* Check that the URI is valid.  */
308         if (!VALID_URI(uri)) return GNOME_VFS_ERROR_INVALID_URI;
309
310         if (open_mode & GNOME_VFS_OPEN_WRITE)
311                 return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
312
313         parent_uri = uri->parent;
314
315         if (open_mode & GNOME_VFS_OPEN_RANDOM)
316                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
317
318         result = gnome_vfs_open_uri (&parent_handle, parent_uri, open_mode);
319         RETURN_IF_FAIL (result);
320
321         bzip2_handle = bzip2_method_handle_new (parent_handle, uri, open_mode);
322
323         if (result != GNOME_VFS_OK) {
324                 gnome_vfs_close (parent_handle);
325                 bzip2_method_handle_destroy (bzip2_handle);
326                 return result;
327         }
328
329         if (!bzip2_method_handle_init_for_decompress (bzip2_handle)) {
330                 gnome_vfs_close (parent_handle);
331                 bzip2_method_handle_destroy (bzip2_handle);
332                 return GNOME_VFS_ERROR_INTERNAL;
333         }
334
335         *method_handle = (GnomeVFSMethodHandle *) bzip2_handle;
336
337         return GNOME_VFS_OK;
338 }
339
340 /* Create */
341
342 static GnomeVFSResult
343 do_create (GnomeVFSMethod *method,
344            GnomeVFSMethodHandle **method_handle,
345            GnomeVFSURI *uri,
346            GnomeVFSOpenMode mode,
347            gboolean exclusive,
348            guint perm,
349            GnomeVFSContext *context)
350 {
351         _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
352         _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
353
354         return GNOME_VFS_ERROR_NOT_SUPPORTED;
355 }
356
357 /* Close */
358
359 static GnomeVFSResult
360 do_close (GnomeVFSMethod *method,
361           GnomeVFSMethodHandle *method_handle,
362           GnomeVFSContext *context)
363 {
364         Bzip2MethodHandle *bzip2_handle;
365         GnomeVFSResult result;
366
367         _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
368
369         bzip2_handle = (Bzip2MethodHandle *) method_handle;
370
371         if (bzip2_handle->open_mode & GNOME_VFS_OPEN_WRITE)
372                 result = flush_write (bzip2_handle);
373         else
374                 result = GNOME_VFS_OK;
375
376         if (result == GNOME_VFS_OK)
377                 result = gnome_vfs_close (bzip2_handle->parent_handle);
378
379         bzip2_method_handle_destroy (bzip2_handle);
380
381         return result;
382 }
383
384 /* Read */
385
386 static GnomeVFSResult
387 fill_buffer (Bzip2MethodHandle *bzip2_handle,
388              GnomeVFSFileSize num_bytes)
389 {
390         GnomeVFSResult result;
391         GnomeVFSFileSize count;
392         bz_stream *bzstream;
393
394         bzstream = &bzip2_handle->bzstream;
395
396         if (bzstream->avail_in > 0)
397                 return GNOME_VFS_OK;
398
399         result = gnome_vfs_read (bzip2_handle->parent_handle,
400                                  bzip2_handle->buffer,
401                                  BZ_BUFSIZE,
402                                  &count);
403
404         if (result != GNOME_VFS_OK) {
405                 if (bzstream->avail_out == num_bytes)
406                         return result;
407                 bzip2_handle->last_vfs_result = result;
408         } else {
409                 bzstream->next_in = bzip2_handle->buffer;
410                 bzstream->avail_in = count;
411         }
412
413         return GNOME_VFS_OK;
414 }
415
416 /* TODO: Concatenated Bzip2 file handling. */
417
418 static GnomeVFSResult
419 do_read (GnomeVFSMethod *method,
420          GnomeVFSMethodHandle *method_handle,
421          gpointer buffer,
422          GnomeVFSFileSize num_bytes,
423          GnomeVFSFileSize *bytes_read,
424          GnomeVFSContext *context)
425 {
426         Bzip2MethodHandle *bzip2_handle;
427         GnomeVFSResult result;
428         bz_stream *bzstream;
429         int bz_result;
430
431         *bytes_read = 0;
432
433         bzip2_handle = (Bzip2MethodHandle *) method_handle;
434         bzstream = &bzip2_handle->bzstream;
435
436         if (bzip2_handle->last_bz_result != BZ_OK) {
437                 if (bzip2_handle->last_bz_result == BZ_STREAM_END)
438                         return GNOME_VFS_OK;
439                 else
440                         return result_from_bz_result (bzip2_handle->last_bz_result);
441         } else if (bzip2_handle->last_vfs_result != GNOME_VFS_OK) {
442                 return bzip2_handle->last_vfs_result;
443         }
444
445         bzstream->next_out = buffer;
446         bzstream->avail_out = num_bytes;
447
448         while (bzstream->avail_out != 0) {
449                 result = fill_buffer (bzip2_handle, num_bytes);
450                 RETURN_IF_FAIL (result);
451
452                 bz_result = BZ2_bzDecompress (&bzip2_handle->bzstream);
453
454                 if (bzip2_handle->last_bz_result != BZ_OK
455                     && bzstream->avail_out == num_bytes) {
456                         bzip2_handle->last_bz_result = bz_result;
457                         return result_from_bz_result (bzip2_handle->last_bz_result);
458                 }
459
460                 *bytes_read = num_bytes - bzstream->avail_out;
461
462                 if (bz_result == BZ_STREAM_END) {
463                         bzip2_handle->last_bz_result = bz_result;
464                         break;
465                 }
466
467         }
468
469         return GNOME_VFS_OK;
470 }
471
472 /* Write. */
473
474 static GnomeVFSResult
475 do_write (GnomeVFSMethod *method,
476           GnomeVFSMethodHandle *method_handle,
477           gconstpointer buffer,
478           GnomeVFSFileSize num_bytes,
479           GnomeVFSFileSize *bytes_written,
480           GnomeVFSContext *context)
481 {
482         Bzip2MethodHandle *bzip2_handle;
483         GnomeVFSResult result;
484         bz_stream *bzstream;
485         gint bz_result;
486
487         bzip2_handle = (Bzip2MethodHandle *) method_handle;
488         bzstream = &bzip2_handle->bzstream;
489
490         bzstream->next_in = (gpointer) buffer;
491         bzstream->avail_in = num_bytes;
492
493         result = GNOME_VFS_OK;
494
495         while (bzstream->avail_in != 0 && result == GNOME_VFS_OK) {
496                 if (bzstream->avail_out == 0) {
497                         GnomeVFSFileSize written;
498
499                         bzstream->next_out = bzip2_handle->buffer;
500                         result = gnome_vfs_write (bzip2_handle->parent_handle,
501                                                   bzip2_handle->buffer,
502                                                   BZ_BUFSIZE, &written);
503                         if (result != GNOME_VFS_OK)
504                                 break;
505
506                         bzstream->avail_out += written;
507                 }
508
509                 bz_result = BZ2_bzCompress (bzstream, BZ_RUN);
510                 result = result_from_bz_result (bz_result);
511         }
512
513         *bytes_written = num_bytes - bzstream->avail_in;
514
515         return result;
516 }
517
518 static gboolean
519 do_is_local (GnomeVFSMethod *method, const GnomeVFSURI *uri)
520 {
521         g_return_val_if_fail (uri != NULL, FALSE);
522         return gnome_vfs_uri_is_local (uri->parent);
523 }
524
525 static GnomeVFSResult 
526 do_get_file_info  (GnomeVFSMethod *method,
527                    GnomeVFSURI *uri,
528                    GnomeVFSFileInfo *file_info,
529                    GnomeVFSFileInfoOptions options,
530                    GnomeVFSContext *context) 
531 {
532         GnomeVFSResult result;
533
534         /* Check that the URI is valid.  */
535         if (!VALID_URI(uri)) return GNOME_VFS_ERROR_INVALID_URI;
536
537         result = gnome_vfs_get_file_info_uri(uri->parent, file_info, options);
538
539         if(result == GNOME_VFS_OK) {
540                 gint namelen = strlen(file_info->name);
541                 
542                 /* work out the name */
543                 /* FIXME bugzilla.eazel.com 2790: handle uppercase */
544                 if(namelen > 4 &&
545                                 file_info->name[namelen-1] == '2' &&
546                                 file_info->name[namelen-2] == 'z' &&
547                                 file_info->name[namelen-3] == 'b' &&
548                                 file_info->name[namelen-4] == '.')
549                         file_info->name[namelen-4] = '\0';
550
551                 /* we can't tell the size without uncompressing it */
552                 //file_info->valid_fields &= ~GNOME_VFS_FILE_INFO_FIELDS_SIZE;
553
554                 /* guess the mime type of the file inside */
555                 /* FIXME bugzilla.eazel.com 2791: guess mime based on contents */
556                 g_free(file_info->mime_type);
557                 file_info->mime_type = g_strdup(gnome_vfs_mime_type_from_name(file_info->name));
558         }
559
560         return result;
561 }
562
563 GnomeVFSMethod *
564 vfs_module_init (const char *method_name, const char *args)
565 {
566         return &method;
567 }
568
569 void
570 vfs_module_shutdown (GnomeVFSMethod *method)
571 {
572         return;
573 }