Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2009 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This 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 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Alexander Larsson <alexl@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include "gzlibcompressor.h"
26 : :
27 : : #include <errno.h>
28 : : #include <zlib.h>
29 : : #include <string.h>
30 : :
31 : : #include "gfileinfo.h"
32 : : #include "gioerror.h"
33 : : #include "gioenums.h"
34 : : #include "gioenumtypes.h"
35 : : #include "glibintl.h"
36 : :
37 : :
38 : : enum {
39 : : PROP_0,
40 : : PROP_FORMAT,
41 : : PROP_LEVEL,
42 : : PROP_FILE_INFO
43 : : };
44 : :
45 : : /**
46 : : * GZlibCompressor:
47 : : *
48 : : * `GZlibCompressor` is an implementation of [iface@Gio.Converter] that
49 : : * compresses data using zlib.
50 : : */
51 : :
52 : : static void g_zlib_compressor_iface_init (GConverterIface *iface);
53 : :
54 : : struct _GZlibCompressor
55 : : {
56 : : GObject parent_instance;
57 : :
58 : : GZlibCompressorFormat format;
59 : : int level;
60 : : z_stream zstream;
61 : : gz_header gzheader;
62 : : GFileInfo *file_info;
63 : : };
64 : :
65 : : static void
66 : 35 : g_zlib_compressor_set_gzheader (GZlibCompressor *compressor)
67 : : {
68 : : /* On win32, these functions were not exported before 1.2.4 */
69 : : #if !defined (G_OS_WIN32) || ZLIB_VERNUM >= 0x1240
70 : : const gchar *filename;
71 : :
72 : 35 : if (compressor->format != G_ZLIB_COMPRESSOR_FORMAT_GZIP ||
73 : 17 : compressor->file_info == NULL)
74 : 31 : return;
75 : :
76 : 4 : memset (&compressor->gzheader, 0, sizeof (gz_header));
77 : 4 : compressor->gzheader.os = 0x03; /* Unix */
78 : :
79 : 4 : filename = g_file_info_get_name (compressor->file_info);
80 : 4 : compressor->gzheader.name = (Bytef *) filename;
81 : 4 : compressor->gzheader.name_max = filename ? strlen (filename) + 1 : 0;
82 : :
83 : 4 : compressor->gzheader.time =
84 : 4 : (uLong) g_file_info_get_attribute_uint64 (compressor->file_info,
85 : : G_FILE_ATTRIBUTE_TIME_MODIFIED);
86 : :
87 : 4 : if (deflateSetHeader (&compressor->zstream, &compressor->gzheader) != Z_OK)
88 : 0 : g_warning ("unexpected zlib error: %s", compressor->zstream.msg);
89 : : #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
90 : : }
91 : :
92 : 4619 : G_DEFINE_TYPE_WITH_CODE (GZlibCompressor, g_zlib_compressor, G_TYPE_OBJECT,
93 : : G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
94 : : g_zlib_compressor_iface_init))
95 : :
96 : : static void
97 : 21 : g_zlib_compressor_finalize (GObject *object)
98 : : {
99 : : GZlibCompressor *compressor;
100 : :
101 : 21 : compressor = G_ZLIB_COMPRESSOR (object);
102 : :
103 : 21 : deflateEnd (&compressor->zstream);
104 : :
105 : 21 : if (compressor->file_info)
106 : 6 : g_object_unref (compressor->file_info);
107 : :
108 : 21 : G_OBJECT_CLASS (g_zlib_compressor_parent_class)->finalize (object);
109 : 21 : }
110 : :
111 : :
112 : : static void
113 : 48 : g_zlib_compressor_set_property (GObject *object,
114 : : guint prop_id,
115 : : const GValue *value,
116 : : GParamSpec *pspec)
117 : : {
118 : : GZlibCompressor *compressor;
119 : :
120 : 48 : compressor = G_ZLIB_COMPRESSOR (object);
121 : :
122 : 48 : switch (prop_id)
123 : : {
124 : 21 : case PROP_FORMAT:
125 : 21 : compressor->format = g_value_get_enum (value);
126 : 21 : break;
127 : :
128 : 21 : case PROP_LEVEL:
129 : 21 : compressor->level = g_value_get_int (value);
130 : 21 : break;
131 : :
132 : 6 : case PROP_FILE_INFO:
133 : 6 : g_zlib_compressor_set_file_info (compressor, g_value_get_object (value));
134 : 6 : break;
135 : :
136 : 0 : default:
137 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
138 : 0 : break;
139 : : }
140 : :
141 : 48 : }
142 : :
143 : : static void
144 : 15 : g_zlib_compressor_get_property (GObject *object,
145 : : guint prop_id,
146 : : GValue *value,
147 : : GParamSpec *pspec)
148 : : {
149 : : GZlibCompressor *compressor;
150 : :
151 : 15 : compressor = G_ZLIB_COMPRESSOR (object);
152 : :
153 : 15 : switch (prop_id)
154 : : {
155 : 7 : case PROP_FORMAT:
156 : 7 : g_value_set_enum (value, compressor->format);
157 : 7 : break;
158 : :
159 : 7 : case PROP_LEVEL:
160 : 7 : g_value_set_int (value, compressor->level);
161 : 7 : break;
162 : :
163 : 1 : case PROP_FILE_INFO:
164 : 1 : g_value_set_object (value, compressor->file_info);
165 : 1 : break;
166 : :
167 : 0 : default:
168 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169 : 0 : break;
170 : : }
171 : 15 : }
172 : :
173 : : static void
174 : 21 : g_zlib_compressor_init (GZlibCompressor *compressor)
175 : : {
176 : 21 : }
177 : :
178 : : static void
179 : 21 : g_zlib_compressor_constructed (GObject *object)
180 : : {
181 : : GZlibCompressor *compressor;
182 : : int res;
183 : :
184 : 21 : compressor = G_ZLIB_COMPRESSOR (object);
185 : :
186 : 21 : if (compressor->format == G_ZLIB_COMPRESSOR_FORMAT_GZIP)
187 : : {
188 : : /* + 16 for gzip */
189 : 11 : res = deflateInit2 (&compressor->zstream,
190 : : compressor->level, Z_DEFLATED,
191 : : MAX_WBITS + 16, 8,
192 : : Z_DEFAULT_STRATEGY);
193 : : }
194 : 10 : else if (compressor->format == G_ZLIB_COMPRESSOR_FORMAT_RAW)
195 : : {
196 : : /* negative wbits for raw */
197 : 3 : res = deflateInit2 (&compressor->zstream,
198 : : compressor->level, Z_DEFLATED,
199 : : -MAX_WBITS, 8,
200 : : Z_DEFAULT_STRATEGY);
201 : : }
202 : : else /* ZLIB */
203 : 7 : res = deflateInit (&compressor->zstream, compressor->level);
204 : :
205 : 21 : if (res == Z_MEM_ERROR )
206 : 0 : g_error ("GZlibCompressor: Not enough memory for zlib use");
207 : :
208 : 21 : if (res != Z_OK)
209 : 0 : g_warning ("unexpected zlib error: %s", compressor->zstream.msg);
210 : :
211 : 21 : g_zlib_compressor_set_gzheader (compressor);
212 : 21 : }
213 : :
214 : : static void
215 : 7 : g_zlib_compressor_class_init (GZlibCompressorClass *klass)
216 : : {
217 : 7 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
218 : :
219 : 7 : gobject_class->finalize = g_zlib_compressor_finalize;
220 : 7 : gobject_class->constructed = g_zlib_compressor_constructed;
221 : 7 : gobject_class->get_property = g_zlib_compressor_get_property;
222 : 7 : gobject_class->set_property = g_zlib_compressor_set_property;
223 : :
224 : : /**
225 : : * GZlibCompressor:format:
226 : : *
227 : : * The format of the compressed data.
228 : : *
229 : : * Since: 2.24
230 : : */
231 : 7 : g_object_class_install_property (gobject_class,
232 : : PROP_FORMAT,
233 : : g_param_spec_enum ("format", NULL, NULL,
234 : : G_TYPE_ZLIB_COMPRESSOR_FORMAT,
235 : : G_ZLIB_COMPRESSOR_FORMAT_ZLIB,
236 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
237 : : G_PARAM_STATIC_STRINGS));
238 : :
239 : : /**
240 : : * GZlibCompressor:level:
241 : : *
242 : : * The level of compression from `0` (no compression) to `9` (most
243 : : * compression). `-1` for the default level.
244 : : *
245 : : * Since: 2.24
246 : : */
247 : 7 : g_object_class_install_property (gobject_class,
248 : : PROP_LEVEL,
249 : : g_param_spec_int ("level", NULL, NULL,
250 : : -1, 9,
251 : : -1,
252 : : G_PARAM_READWRITE |
253 : : G_PARAM_CONSTRUCT_ONLY |
254 : : G_PARAM_STATIC_STRINGS));
255 : :
256 : : /**
257 : : * GZlibCompressor:file-info:
258 : : *
259 : : * If set to a non-%NULL #GFileInfo object, and #GZlibCompressor:format is
260 : : * %G_ZLIB_COMPRESSOR_FORMAT_GZIP, the compressor will write the file name
261 : : * and modification time from the file info to the GZIP header.
262 : : *
263 : : * Since: 2.26
264 : : */
265 : 7 : g_object_class_install_property (gobject_class,
266 : : PROP_FILE_INFO,
267 : : g_param_spec_object ("file-info", NULL, NULL,
268 : : G_TYPE_FILE_INFO,
269 : : G_PARAM_READWRITE |
270 : : G_PARAM_STATIC_STRINGS));
271 : 7 : }
272 : :
273 : : /**
274 : : * g_zlib_compressor_new:
275 : : * @format: The format to use for the compressed data
276 : : * @level: compression level (0-9), -1 for default
277 : : *
278 : : * Creates a new #GZlibCompressor.
279 : : *
280 : : * Returns: a new #GZlibCompressor
281 : : *
282 : : * Since: 2.24
283 : : **/
284 : : GZlibCompressor *
285 : 20 : g_zlib_compressor_new (GZlibCompressorFormat format,
286 : : int level)
287 : : {
288 : : GZlibCompressor *compressor;
289 : :
290 : 20 : compressor = g_object_new (G_TYPE_ZLIB_COMPRESSOR,
291 : : "format", format,
292 : : "level", level,
293 : : NULL);
294 : :
295 : 20 : return compressor;
296 : : }
297 : :
298 : : /**
299 : : * g_zlib_compressor_get_file_info:
300 : : * @compressor: a #GZlibCompressor
301 : : *
302 : : * Returns the #GZlibCompressor:file-info property.
303 : : *
304 : : * Returns: (nullable) (transfer none): a #GFileInfo, or %NULL
305 : : *
306 : : * Since: 2.26
307 : : */
308 : : GFileInfo *
309 : 6 : g_zlib_compressor_get_file_info (GZlibCompressor *compressor)
310 : : {
311 : 6 : g_return_val_if_fail (G_IS_ZLIB_COMPRESSOR (compressor), NULL);
312 : :
313 : 6 : return compressor->file_info;
314 : : }
315 : :
316 : : /**
317 : : * g_zlib_compressor_set_file_info:
318 : : * @compressor: a #GZlibCompressor
319 : : * @file_info: (nullable): a #GFileInfo
320 : : *
321 : : * Sets @file_info in @compressor. If non-%NULL, and @compressor's
322 : : * #GZlibCompressor:format property is %G_ZLIB_COMPRESSOR_FORMAT_GZIP,
323 : : * it will be used to set the file name and modification time in
324 : : * the GZIP header of the compressed data.
325 : : *
326 : : * Note: it is an error to call this function while a compression is in
327 : : * progress; it may only be called immediately after creation of @compressor,
328 : : * or after resetting it with g_converter_reset().
329 : : *
330 : : * Since: 2.26
331 : : */
332 : : void
333 : 6 : g_zlib_compressor_set_file_info (GZlibCompressor *compressor,
334 : : GFileInfo *file_info)
335 : : {
336 : 6 : g_return_if_fail (G_IS_ZLIB_COMPRESSOR (compressor));
337 : :
338 : 6 : if (file_info == compressor->file_info)
339 : 0 : return;
340 : :
341 : 6 : if (compressor->file_info)
342 : 0 : g_object_unref (compressor->file_info);
343 : 6 : if (file_info)
344 : 6 : g_object_ref (file_info);
345 : 6 : compressor->file_info = file_info;
346 : 6 : g_object_notify (G_OBJECT (compressor), "file-info");
347 : :
348 : 6 : g_zlib_compressor_set_gzheader (compressor);
349 : : }
350 : :
351 : : static void
352 : 8 : g_zlib_compressor_reset (GConverter *converter)
353 : : {
354 : 8 : GZlibCompressor *compressor = G_ZLIB_COMPRESSOR (converter);
355 : : int res;
356 : :
357 : 8 : res = deflateReset (&compressor->zstream);
358 : 8 : if (res != Z_OK)
359 : 0 : g_warning ("unexpected zlib error: %s", compressor->zstream.msg);
360 : :
361 : : /* deflateReset reset the header too, so re-set it */
362 : 8 : g_zlib_compressor_set_gzheader (compressor);
363 : 8 : }
364 : :
365 : : static GConverterResult
366 : 4452 : g_zlib_compressor_convert (GConverter *converter,
367 : : const void *inbuf,
368 : : gsize inbuf_size,
369 : : void *outbuf,
370 : : gsize outbuf_size,
371 : : GConverterFlags flags,
372 : : gsize *bytes_read,
373 : : gsize *bytes_written,
374 : : GError **error)
375 : : {
376 : : GZlibCompressor *compressor;
377 : : int res;
378 : : int flush;
379 : :
380 : 4452 : compressor = G_ZLIB_COMPRESSOR (converter);
381 : :
382 : 4452 : compressor->zstream.next_in = (void *)inbuf;
383 : 4452 : compressor->zstream.avail_in = inbuf_size;
384 : :
385 : 4452 : compressor->zstream.next_out = outbuf;
386 : 4452 : compressor->zstream.avail_out = outbuf_size;
387 : :
388 : 4452 : flush = Z_NO_FLUSH;
389 : 4452 : if (flags & G_CONVERTER_INPUT_AT_END)
390 : 21 : flush = Z_FINISH;
391 : 4431 : else if (flags & G_CONVERTER_FLUSH)
392 : 8 : flush = Z_SYNC_FLUSH;
393 : :
394 : 4452 : res = deflate (&compressor->zstream, flush);
395 : :
396 : 4452 : if (res == Z_MEM_ERROR)
397 : : {
398 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
399 : : _("Not enough memory"));
400 : 0 : return G_CONVERTER_ERROR;
401 : : }
402 : :
403 : 4452 : if (res == Z_STREAM_ERROR)
404 : : {
405 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
406 : : _("Internal error: %s"), compressor->zstream.msg);
407 : 0 : return G_CONVERTER_ERROR;
408 : : }
409 : :
410 : 4452 : if (res == Z_BUF_ERROR)
411 : : {
412 : 4 : if (flags & G_CONVERTER_FLUSH)
413 : 4 : return G_CONVERTER_FLUSHED;
414 : :
415 : : /* We do have output space, so this should only happen if we
416 : : have no input but need some */
417 : :
418 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
419 : : _("Need more input"));
420 : 0 : return G_CONVERTER_ERROR;
421 : : }
422 : :
423 : 4448 : if (res == Z_OK || res == Z_STREAM_END)
424 : : {
425 : 4448 : *bytes_read = inbuf_size - compressor->zstream.avail_in;
426 : 4448 : *bytes_written = outbuf_size - compressor->zstream.avail_out;
427 : :
428 : 4448 : if (res == Z_STREAM_END)
429 : 20 : return G_CONVERTER_FINISHED;
430 : 4428 : return G_CONVERTER_CONVERTED;
431 : : }
432 : :
433 : : g_assert_not_reached ();
434 : : }
435 : :
436 : : static void
437 : 7 : g_zlib_compressor_iface_init (GConverterIface *iface)
438 : : {
439 : 7 : iface->convert = g_zlib_compressor_convert;
440 : 7 : iface->reset = g_zlib_compressor_reset;
441 : 7 : }
|