"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-outfile-zip.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-outfile-zip.c: zip archive output.
4  *
5  * Copyright (C) 2002-2006 Jon K Hellan (hellan@acm.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Outc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-outfile-impl.h>
24 #include <gsf/gsf-outfile-zip.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-utils.h>
27 #include <gsf/gsf-zip-impl.h>
28
29 #include <string.h>
30 #include <time.h>
31 #include <zlib.h>
32
33 #undef G_LOG_DOMAIN
34 #define G_LOG_DOMAIN "libgsf:zip"
35
36 enum {
37         PROP_0,
38         PROP_SINK,
39         PROP_ENTRY_NAME,
40         PROP_COMPRESSION_LEVEL
41 };
42
43 static GObjectClass *parent_class;
44
45 struct _GsfOutfileZip {
46         GsfOutfile parent;
47
48         GsfOutput     *sink;
49         GsfOutfileZip *root;
50
51         char *entry_name;
52
53         GsfZipVDir    *vdir;
54         GPtrArray     *root_order;      /* only valid for the root */
55
56         z_stream  *stream;
57         GsfZipCompressionMethod compression_method;
58
59         gboolean   writing;
60
61         guint8   *buf;
62         size_t    buf_size;
63 };
64
65 typedef struct {
66         GsfOutfileClass  parent_class;
67 } GsfOutfileZipClass;
68
69 #define GSF_OUTFILE_ZIP_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), GSF_OUTFILE_ZIP_TYPE, GsfOutfileZipClass))
70 #define GSF_IS_OUTFILE_ZIP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSF_OUTFILE_ZIP_TYPE))
71
72 static void
73 disconnect_children (GsfOutfileZip *zip)
74 {
75         unsigned i;
76
77         if (!zip->root_order)
78                 return;
79
80         for (i = 0 ; i < zip->root_order->len ; i++) {
81                 GsfOutfileZip *child =
82                         g_ptr_array_index (zip->root_order, i);
83                 if (child)
84                         g_object_unref (child);
85         }
86         g_ptr_array_free (zip->root_order, TRUE);
87         zip->root_order = NULL;
88 }
89
90 static void
91 gsf_outfile_zip_finalize (GObject *obj)
92 {
93         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (obj);
94
95         /* If the closing failed, we might have stuff here.  */
96         disconnect_children (zip);
97
98         if (zip->sink != NULL) {
99                 g_object_unref (zip->sink);
100                 zip->sink = NULL;
101         }
102
103         g_free (zip->entry_name);
104
105         if (zip->stream)
106                 (void) deflateEnd (zip->stream);
107         g_free (zip->stream);
108         g_free (zip->buf);
109
110         if (zip == zip->root)
111                 gsf_vdir_free (zip->vdir, TRUE); /* Frees vdirs recursively */
112
113         parent_class->finalize (obj);
114 }
115
116 static GObject *
117 gsf_outfile_zip_constructor (GType                  type,
118                              guint                  n_construct_properties,
119                              GObjectConstructParam *construct_params)
120 {
121         GsfOutfileZip *zip =(GsfOutfileZip *)
122                 (parent_class->constructor (type,
123                                             n_construct_properties,
124                                             construct_params));
125
126         if (!zip->entry_name) {
127                 zip->vdir = gsf_vdir_new ("", TRUE, NULL);
128                 zip->root_order = g_ptr_array_new ();
129                 zip->root = zip;
130
131                 /* The names are the same */
132                 gsf_output_set_name (GSF_OUTPUT (zip), gsf_output_name (zip->sink));
133                 gsf_output_set_container (GSF_OUTPUT (zip), NULL);
134         }
135
136         return (GObject *)zip;
137 }
138
139 static gboolean
140 gsf_outfile_zip_seek (G_GNUC_UNUSED GsfOutput *output,
141                       G_GNUC_UNUSED gsf_off_t offset,
142                       G_GNUC_UNUSED GSeekType whence)
143 {
144         return FALSE;
145 }
146
147 static gboolean
148 zip_dirent_write (GsfOutput *sink, GsfZipDirent *dirent)
149 {
150         static guint8 const dirent_signature[] =
151                 { 'P', 'K', 0x01, 0x02 };
152         guint8 buf[ZIP_DIRENT_SIZE];
153         int nlen = strlen (dirent->name);
154         gboolean ret;
155
156         memset (buf, 0, sizeof buf);
157         memcpy (buf, dirent_signature, sizeof dirent_signature);
158         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_ENCODER, 0x317); /* Unix */
159         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_EXTRACT, 0x14);
160         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_FLAGS, dirent->flags);
161         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_COMPR_METHOD,
162                             dirent->compr_method);
163         GSF_LE_SET_GUINT32 (buf + ZIP_DIRENT_DOSTIME, dirent->dostime);
164         GSF_LE_SET_GUINT32 (buf + ZIP_DIRENT_CRC32, dirent->crc32);
165         GSF_LE_SET_GUINT32 (buf + ZIP_DIRENT_CSIZE, dirent->csize);
166         GSF_LE_SET_GUINT32 (buf + ZIP_DIRENT_USIZE, dirent->usize);
167         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_NAME_SIZE, nlen);
168         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_EXTRAS_SIZE, 0);
169         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_COMMENT_SIZE, 0);
170         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_DISKSTART, 0);
171         GSF_LE_SET_GUINT16 (buf + ZIP_DIRENT_FILE_TYPE, 0);
172         /* Hardcode file mode 644 */
173         GSF_LE_SET_GUINT32 (buf + ZIP_DIRENT_FILE_MODE, 0644 << 16);
174         GSF_LE_SET_GUINT32 (buf + ZIP_DIRENT_OFFSET, dirent->offset);
175
176         ret = gsf_output_write (sink, sizeof buf, buf);
177         if (ret)
178                 ret = gsf_output_write (sink, nlen, dirent->name);
179
180         return ret;
181 }
182
183 static gboolean
184 zip_trailer_write (GsfOutfileZip *zip, unsigned entries, gsf_off_t dirpos)
185 {
186         static guint8 const trailer_signature[] =
187                 { 'P', 'K', 0x05, 0x06 };
188         guint8 buf[ZIP_TRAILER_SIZE];
189         gsf_off_t pos = gsf_output_tell (zip->sink);
190
191         memset (buf, 0, sizeof buf);
192         memcpy (buf, trailer_signature, sizeof trailer_signature);
193         GSF_LE_SET_GUINT16 (buf + ZIP_TRAILER_ENTRIES, entries);
194         GSF_LE_SET_GUINT16 (buf + ZIP_TRAILER_TOTAL_ENTRIES, entries);
195         GSF_LE_SET_GUINT32 (buf + ZIP_TRAILER_DIR_SIZE, pos - dirpos);
196         GSF_LE_SET_GUINT32 (buf + ZIP_TRAILER_DIR_POS, dirpos);
197
198         return gsf_output_write (zip->sink, sizeof buf, buf);
199 }
200
201 static gboolean
202 zip_close_root (GsfOutput *output)
203 {
204         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (output);
205         GsfOutfileZip *child;
206         gsf_off_t dirpos = gsf_output_tell (zip->sink);
207         GPtrArray *elem = zip->root_order;
208         unsigned entries = elem->len;
209         unsigned i;
210
211         /* Check that children are closed */
212         for (i = 0 ; i < elem->len ; i++) {
213                 child = g_ptr_array_index (elem, i);
214                 if (!gsf_output_is_closed (GSF_OUTPUT (child))) {
215                         g_warning ("Child still open");
216                         return FALSE;
217                 }
218         }
219
220         /* Write directory */
221         for (i = 0 ; i < entries ; i++) {
222                 child = g_ptr_array_index (elem, i);
223                 if (!zip_dirent_write (zip->sink, child->vdir->dirent))
224                         return FALSE;
225         }
226
227         disconnect_children (zip);
228
229         return zip_trailer_write (zip, entries, dirpos);
230 }
231
232 static void
233 stream_name_write_to_buf (GsfOutfileZip *zip, GString *res)
234 {
235         GsfOutput  *output = GSF_OUTPUT (zip);
236         GsfOutfile *container;
237
238         if (zip == zip->root)
239                 return;
240
241         container = gsf_output_container (output);
242         if (container) {
243                 stream_name_write_to_buf (GSF_OUTFILE_ZIP (container), res);
244                 if (res->len) {
245                         /* Forward slash is specified by the format.  */
246                         g_string_append_c (res, '/');
247                 }
248         }
249
250         if (zip->entry_name)
251                 g_string_append (res, zip->entry_name);
252 }
253
254 static char *
255 stream_name_build (GsfOutfileZip *zip)
256 {
257         GString *str = g_string_sized_new (80);
258         stream_name_write_to_buf (zip, str);
259         return g_string_free (str, FALSE);
260 }
261
262 static guint32
263 zip_time_make (time_t t)
264 {
265         struct tm *localnow = localtime (&t);
266         guint32 ztime;
267
268         ztime = (localnow->tm_year - 80) & 0x7f;
269         ztime = (ztime << 4) | ((localnow->tm_mon + 1)  & 0x0f);
270         ztime = (ztime << 5) | (localnow->tm_mday & 0x1f);
271         ztime = (ztime << 5) | (localnow->tm_hour & 0x1f);
272         ztime = (ztime << 6) | (localnow->tm_min  & 0x3f);
273         ztime = (ztime << 5) | ((localnow->tm_sec / 2) & 0x1f);
274
275         return ztime;
276 }
277
278 static void
279 zip_dirent_update_flags (GsfZipDirent *dirent)
280 {
281         if (dirent->compr_method == GSF_ZIP_STORED)
282                 dirent->flags &= ~8;
283         else
284                 dirent->flags |= 8;
285 }
286
287 static GsfZipDirent*
288 zip_dirent_new_out (GsfOutfileZip *zip)
289 {
290         GsfZipDirent *dirent = gsf_zip_dirent_new ();
291         dirent->name = stream_name_build (zip);
292         dirent->compr_method = zip->compression_method;
293         dirent->dostime = zip_time_make (time (NULL));
294         zip_dirent_update_flags (dirent);
295         return dirent;
296 }
297
298 static gboolean
299 zip_header_write (GsfOutfileZip *zip)
300 {
301         static guint8 const header_signature[] =
302                 { 'P', 'K', 0x03, 0x04 };
303         guint8 hbuf[ZIP_HEADER_SIZE];
304         GsfZipDirent *dirent = zip->vdir->dirent;
305         char *name = dirent->name;
306         int   nlen = strlen (name);
307         gboolean ret;
308
309         memset (hbuf, 0, sizeof hbuf);
310         memcpy (hbuf, header_signature, sizeof header_signature);
311         GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_VERSION, 0x14);
312         GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_FLAGS, dirent->flags);
313         GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_COMP_METHOD,
314                             dirent->compr_method);
315         GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_TIME, dirent->dostime);
316         GSF_LE_SET_GUINT16 (hbuf + ZIP_HEADER_NAME_LEN, nlen);
317         ret = gsf_output_write (zip->sink, sizeof hbuf, hbuf);
318         if (ret)
319                 ret = gsf_output_write (zip->sink, nlen, name);
320
321         return ret;
322 }
323
324 static gboolean
325 zip_init_write (GsfOutput *output)
326 {
327         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (output);
328         GsfZipDirent *dirent;
329         int      ret;
330
331         if (zip->root->writing) {
332                 g_warning ("Already writing to another stream in archive");
333                 return FALSE;
334         }
335
336         if (!gsf_output_wrap (G_OBJECT (output), zip->sink))
337                 return FALSE;
338
339         dirent = zip_dirent_new_out (zip);
340         dirent->offset = gsf_output_tell (zip->sink);
341         if (zip->vdir->dirent)
342                 g_warning ("Leak.");
343
344         zip->vdir->dirent = dirent;
345         zip_header_write (zip);
346         zip->writing = TRUE;
347         zip->root->writing = TRUE;
348         dirent->crc32 = crc32 (0L, Z_NULL, 0);
349         if (zip->compression_method == GSF_ZIP_DEFLATED) {
350                 if (!zip->stream) {
351                         zip->stream = g_new0 (z_stream, 1);
352                 }
353                 ret = deflateInit2 (zip->stream, Z_DEFAULT_COMPRESSION,
354                                     Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL,
355                                     Z_DEFAULT_STRATEGY);
356                 if (ret != Z_OK)
357                         return FALSE;
358                 if (!zip->buf) {
359                         zip->buf_size = ZIP_BUF_SIZE;
360                         zip->buf = g_new (guint8, zip->buf_size);
361                 }
362                 zip->stream->next_out  = zip->buf;
363                 zip->stream->avail_out = zip->buf_size;
364         }
365
366         return TRUE;
367 }
368
369 static gboolean
370 zip_output_block (GsfOutfileZip *zip)
371 {
372         size_t num_bytes = zip->buf_size - zip->stream->avail_out;
373         GsfZipDirent *dirent = zip->vdir->dirent;
374
375         if (!gsf_output_write (zip->sink, num_bytes, zip->buf)) {
376                 return FALSE;
377         }
378         dirent->csize += num_bytes;
379         zip->stream->next_out  = zip->buf;
380         zip->stream->avail_out = zip->buf_size;
381
382         return TRUE;
383 }
384
385 static gboolean
386 zip_flush (GsfOutfileZip *zip)
387 {
388         int zret;
389
390         do {
391                 zret = deflate (zip->stream, Z_FINISH);
392                 if (zret == Z_OK || (zret == Z_BUF_ERROR && zip->stream->avail_out == 0)) {
393                         /*  In this case Z_OK or Z_BUF_ERROR means more buffer 
394                             space is needed  */
395                         if (!zip_output_block (zip))
396                                 return FALSE;
397                 }
398         } while (zret == Z_OK || zret == Z_BUF_ERROR);
399         if (zret != Z_STREAM_END)
400                 return FALSE;
401         if (!zip_output_block (zip))
402                 return FALSE;
403
404         return TRUE;
405 }
406
407 /* Write the per stream data descriptor */
408 static gboolean
409 zip_ddesc_write (GsfOutfileZip *zip)
410 {
411         static guint8 const ddesc_signature[] =
412                 { 'P', 'K', 0x07, 0x08 };
413         guint8 buf[16];
414         GsfZipDirent *dirent = zip->vdir->dirent;
415
416         memcpy (buf, ddesc_signature, sizeof ddesc_signature);
417         GSF_LE_SET_GUINT32 (buf + 4, dirent->crc32);
418         GSF_LE_SET_GUINT32 (buf + 8, dirent->csize);
419         GSF_LE_SET_GUINT32 (buf + 12, dirent->usize);
420         if (!gsf_output_write (zip->sink, sizeof buf, buf)) {
421                 return FALSE;
422         }
423
424         return TRUE;
425 }
426
427 static gboolean
428 zip_header_write_sizes (GsfOutfileZip *zip)
429 {
430         guint8 hbuf[ZIP_HEADER_SIZE];
431         GsfZipDirent *dirent = zip->vdir->dirent;
432         gsf_off_t pos = gsf_output_tell (zip->sink);
433
434         if (!gsf_output_seek (zip->sink, dirent->offset + ZIP_HEADER_CRC,
435                               G_SEEK_SET))
436                 return FALSE;
437
438         GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_CRC, dirent->crc32);
439         GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_COMP_SIZE, dirent->csize);
440         GSF_LE_SET_GUINT32 (hbuf + ZIP_HEADER_UNCOMP_SIZE, dirent->usize);
441         if (!gsf_output_write (zip->sink, 12, hbuf + ZIP_HEADER_CRC))
442                 return FALSE;
443         if (!gsf_output_seek (zip->sink, pos, G_SEEK_SET))
444                 return FALSE;
445
446         return TRUE;
447 }
448
449 static gboolean
450 zip_close_stream (GsfOutput *output)
451 {
452         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (output);
453         gboolean result;
454
455         if (!zip->writing)
456                 if (!zip_init_write (output))
457                         return FALSE;
458
459         if (zip->compression_method == GSF_ZIP_DEFLATED) {
460                 if (!zip_flush (zip))
461                         return FALSE;
462
463                 if (!zip_ddesc_write (zip)) /* Write data descriptor */
464                         return FALSE;
465         } else {
466                 if (!zip_header_write_sizes (zip)) /* Write crc, sizes */
467                         return FALSE;
468         }
469         zip->root->writing = FALSE;
470
471         result = gsf_output_unwrap (G_OBJECT (output), zip->sink);
472
473         /* Free unneeded memory */
474         if (zip->stream) {
475                 (void) deflateEnd (zip->stream);
476                 g_free (zip->stream);
477                 zip->stream = NULL;
478                 g_free (zip->buf);
479                 zip->buf = NULL;
480         }
481
482         return result;
483 }
484
485 static gboolean
486 gsf_outfile_zip_close (GsfOutput *output)
487 {
488         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (output);
489         gboolean ret;
490
491         /* The root dir */
492         if (zip == zip->root)
493                 ret = zip_close_root (output);
494         else if (zip->vdir->is_directory)
495                 /* Directories: Do nothing. Should change this to actually
496                  * write dirs which don't have children. */
497                 ret = TRUE;
498         else
499                 ret = zip_close_stream (output);
500
501         return ret;
502 }
503
504 static gboolean
505 gsf_outfile_zip_write (GsfOutput *output,
506                        size_t num_bytes, guint8 const *data)
507 {
508         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (output);
509         GsfZipDirent *dirent;
510         int ret;
511
512         g_return_val_if_fail (zip && zip->vdir, FALSE);
513         g_return_val_if_fail (!zip->vdir->is_directory, FALSE);
514         g_return_val_if_fail (data, FALSE);
515
516         if (!zip->writing)
517                 if (!zip_init_write (output))
518                         return FALSE;
519
520         dirent = zip->vdir->dirent;
521         if (zip->compression_method == GSF_ZIP_DEFLATED) {
522                 zip->stream->next_in  = (unsigned char *) data;
523                 zip->stream->avail_in = num_bytes;
524
525                 while (zip->stream->avail_in > 0) {
526                         if (zip->stream->avail_out == 0) {
527                                 if (!zip_output_block (zip))
528                                         return FALSE;
529                         }
530                         ret = deflate (zip->stream, Z_NO_FLUSH);
531                         if (ret != Z_OK)
532                                 return FALSE;
533                 }
534         } else {
535                 if (!gsf_output_write (zip->sink, num_bytes, data))
536                         return FALSE;
537                 dirent->csize += num_bytes;
538         }
539         dirent->crc32 = crc32 (dirent->crc32, data, num_bytes);
540         dirent->usize += num_bytes;
541
542         return TRUE;
543 }
544
545 static void
546 root_register_child (GsfOutfileZip *root, GsfOutfileZip *child)
547 {
548         child->root = root;
549         if (!child->vdir->is_directory) {
550                 g_object_ref (child);
551                 g_ptr_array_add (root->root_order, child);
552         }
553 }
554
555 static void
556 gsf_outfile_zip_set_sink (GsfOutfileZip *zip, GsfOutput *sink)
557 {
558         if (sink)
559                 g_object_ref (sink);
560         if (zip->sink)
561                 g_object_unref (zip->sink);
562         zip->sink = sink;
563 }
564
565 static GsfOutput *
566 gsf_outfile_zip_new_child (GsfOutfile *parent,
567                            char const *name, gboolean is_dir,
568                            char const *first_property_name, va_list args)
569 {
570         GsfOutfileZip *zip_parent = (GsfOutfileZip *)parent;
571         GsfOutfileZip *child;
572         size_t n_params = 0;
573         GParameter *params = NULL;
574         char *display_name;
575
576         g_return_val_if_fail (zip_parent != NULL, NULL);
577         g_return_val_if_fail (zip_parent->vdir, NULL);
578         g_return_val_if_fail (zip_parent->vdir->is_directory, NULL);
579         g_return_val_if_fail (name && *name, NULL);
580
581         gsf_property_settings_collect (GSF_OUTFILE_ZIP_TYPE,
582                                        &params, &n_params,
583                                        "sink", zip_parent->sink,
584                                        "entry-name", name,
585                                        NULL);
586         gsf_property_settings_collect_valist (GSF_OUTFILE_ZIP_TYPE,
587                                               &params, &n_params,
588                                               first_property_name,
589                                               args);
590         child = (GsfOutfileZip *)g_object_newv (GSF_OUTFILE_ZIP_TYPE,
591                                                 n_params,
592                                                 params);
593         gsf_property_settings_free (params, n_params);
594
595         child->vdir = gsf_vdir_new (name, is_dir, NULL);
596
597         /* FIXME: It isn't clear what encoding name is in.  */
598         display_name = g_filename_display_name (name);
599         gsf_output_set_name (GSF_OUTPUT (child), display_name);
600         g_free (display_name);
601
602         gsf_output_set_container (GSF_OUTPUT (child), parent);
603         gsf_vdir_add_child (zip_parent->vdir, child->vdir);
604         root_register_child (zip_parent->root, child);
605
606         return GSF_OUTPUT (child);
607 }
608
609 static void
610 gsf_outfile_zip_init (GObject *obj)
611 {
612         GsfOutfileZip *zip = GSF_OUTFILE_ZIP (obj);
613
614         zip->sink = NULL;
615         zip->root = NULL;
616         zip->entry_name = NULL;
617         zip->vdir = NULL;
618         zip->root_order = NULL;
619         zip->stream = NULL;
620         zip->compression_method = GSF_ZIP_DEFLATED;
621         zip->writing = FALSE;
622         zip->buf = NULL;
623         zip->buf_size = 0;
624 }
625
626 static void
627 gsf_outfile_zip_get_property (GObject     *object,
628                               guint        property_id,
629                               GValue      *value,
630                               GParamSpec  *pspec)
631 {
632         GsfOutfileZip *zip = (GsfOutfileZip *)object;
633
634         switch (property_id) {
635         case PROP_SINK:
636                 g_value_set_object (value, zip->sink);
637                 break;
638         case PROP_ENTRY_NAME:
639                 g_value_set_string (value, zip->entry_name);
640                 break;
641         case PROP_COMPRESSION_LEVEL:
642                 g_value_set_int (value,
643                                  zip->vdir->dirent
644                                  ? zip->vdir->dirent->compr_method
645                                  : 0);
646                 break;
647         default:
648                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
649                 break;
650         }
651 }
652
653 static void
654 gsf_outfile_zip_set_property (GObject      *object,
655                               guint         property_id,
656                               GValue const *value,
657                               GParamSpec   *pspec)
658 {
659         GsfOutfileZip *zip = (GsfOutfileZip *)object;
660
661         switch (property_id) {
662         case PROP_SINK:
663                 gsf_outfile_zip_set_sink (zip, g_value_get_object (value));
664                 break;
665         case PROP_ENTRY_NAME:
666                 zip->entry_name = g_strdup (g_value_get_string (value));
667                 break;
668         case PROP_COMPRESSION_LEVEL: {
669                 int level = g_value_get_int (value);
670                 switch (level) {
671                 case GSF_ZIP_STORED:
672                 case GSF_ZIP_DEFLATED:
673                         zip->compression_method = level;
674                         break;
675                 default:
676                         g_warning ("Unsupported compression level %d", level);
677                 }
678                 break;
679         }
680         default:
681                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
682                 break;
683         }
684 }
685
686 static void
687 gsf_outfile_zip_class_init (GObjectClass *gobject_class)
688 {
689         GsfOutputClass  *output_class  = GSF_OUTPUT_CLASS (gobject_class);
690         GsfOutfileClass *outfile_class = GSF_OUTFILE_CLASS (gobject_class);
691
692         gobject_class->constructor      = gsf_outfile_zip_constructor;
693         gobject_class->finalize         = gsf_outfile_zip_finalize;
694         gobject_class->get_property     = gsf_outfile_zip_get_property;
695         gobject_class->set_property     = gsf_outfile_zip_set_property;
696
697         output_class->Write             = gsf_outfile_zip_write;
698         output_class->Seek              = gsf_outfile_zip_seek;
699         output_class->Close             = gsf_outfile_zip_close;
700         outfile_class->new_child        = gsf_outfile_zip_new_child;
701
702         parent_class = g_type_class_peek_parent (gobject_class);
703
704         g_object_class_install_property
705                 (gobject_class,
706                  PROP_SINK,
707                  g_param_spec_object ("sink", "Sink",
708                                       "Where the archive is written.",
709                                       GSF_OUTPUT_TYPE,
710                                       GSF_PARAM_STATIC |
711                                       G_PARAM_READWRITE |
712                                       G_PARAM_CONSTRUCT_ONLY));
713         g_object_class_install_property
714                 (gobject_class,
715                  PROP_ENTRY_NAME,
716                  g_param_spec_string ("entry-name", "Entry Name",
717                                       "The filename of this member in the archive without path.",
718                                       NULL,
719                                       GSF_PARAM_STATIC |
720                                       G_PARAM_READWRITE |
721                                       G_PARAM_CONSTRUCT_ONLY));
722         g_object_class_install_property
723                 (gobject_class,
724                  PROP_COMPRESSION_LEVEL,
725                  g_param_spec_int ("compression-level",
726                                    "Compression Level",
727                                    "The level of compression used, zero meaning none.",
728                                    0, 10,
729                                    GSF_ZIP_DEFLATED,
730                                    GSF_PARAM_STATIC |
731                                    G_PARAM_READWRITE |
732                                    G_PARAM_CONSTRUCT_ONLY));
733 }
734
735 GSF_CLASS (GsfOutfileZip, gsf_outfile_zip,
736            gsf_outfile_zip_class_init, gsf_outfile_zip_init,
737            GSF_OUTFILE_TYPE)
738
739 /**
740  * gsf_outfile_zip_new :
741  * @sink: a #GsfOutput to hold the ZIP file
742  * @err: Location to store error, or %NULL; currently unused.
743  *
744  * Creates the root directory of a Zip file and manages the addition of
745  * children.
746  *
747  * <note>This adds a reference to @sink.</note>
748  *
749  * Returns: the new zip file handler
750  **/
751 GsfOutfile *
752 gsf_outfile_zip_new (GsfOutput *sink, G_GNUC_UNUSED GError **err)
753 {
754         g_return_val_if_fail (GSF_IS_OUTPUT (sink), NULL);
755
756         return (GsfOutfile *)g_object_new (GSF_OUTFILE_ZIP_TYPE,
757                                            "sink", sink,
758                                            NULL);
759 }
760
761 /* deprecated has no effect */
762 gboolean
763 gsf_outfile_zip_set_compression_method (G_GNUC_UNUSED GsfOutfileZip *zip,
764                                         G_GNUC_UNUSED GsfZipCompressionMethod method)
765 {
766         return TRUE;
767 }