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 "gzlibdecompressor.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_FILE_INFO
42 : : };
43 : :
44 : : /**
45 : : * GZlibDecompressor:
46 : : *
47 : : * `GZlibDecompressor` is an implementation of [iface@Gio.Converter] that
48 : : * decompresses data compressed with zlib.
49 : : */
50 : :
51 : : static void g_zlib_decompressor_iface_init (GConverterIface *iface);
52 : :
53 : : typedef struct {
54 : : gz_header gzheader;
55 : : char filename[257];
56 : : GFileInfo *file_info;
57 : : } HeaderData;
58 : :
59 : : struct _GZlibDecompressor
60 : : {
61 : : GObject parent_instance;
62 : :
63 : : GZlibCompressorFormat format;
64 : : z_stream zstream;
65 : : HeaderData *header_data;
66 : : };
67 : :
68 : : static void
69 : 34 : g_zlib_decompressor_set_gzheader (GZlibDecompressor *decompressor)
70 : : {
71 : : /* On win32, these functions were not exported before 1.2.4 */
72 : : #if !defined (G_OS_WIN32) || ZLIB_VERNUM >= 0x1240
73 : 34 : if (decompressor->format != G_ZLIB_COMPRESSOR_FORMAT_GZIP)
74 : 25 : return;
75 : :
76 : 9 : if (decompressor->header_data != NULL)
77 : : {
78 : 4 : if (decompressor->header_data->file_info)
79 : 2 : g_object_unref (decompressor->header_data->file_info);
80 : :
81 : 4 : memset (decompressor->header_data, 0, sizeof (HeaderData));
82 : : }
83 : : else
84 : : {
85 : 5 : decompressor->header_data = g_new0 (HeaderData, 1);
86 : : }
87 : :
88 : 9 : decompressor->header_data->gzheader.name = (Bytef*) &decompressor->header_data->filename;
89 : : /* We keep one byte to guarantee the string is 0-terminated */
90 : 9 : decompressor->header_data->gzheader.name_max = 256;
91 : :
92 : 9 : if (inflateGetHeader (&decompressor->zstream, &decompressor->header_data->gzheader) != Z_OK)
93 : 0 : g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
94 : : #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
95 : : }
96 : :
97 : 3823 : G_DEFINE_TYPE_WITH_CODE (GZlibDecompressor, g_zlib_decompressor, G_TYPE_OBJECT,
98 : : G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
99 : : g_zlib_decompressor_iface_init))
100 : :
101 : : static void
102 : 26 : g_zlib_decompressor_finalize (GObject *object)
103 : : {
104 : : GZlibDecompressor *decompressor;
105 : :
106 : 26 : decompressor = G_ZLIB_DECOMPRESSOR (object);
107 : :
108 : 26 : inflateEnd (&decompressor->zstream);
109 : :
110 : 26 : if (decompressor->header_data != NULL)
111 : : {
112 : 5 : if (decompressor->header_data->file_info)
113 : 2 : g_object_unref (decompressor->header_data->file_info);
114 : 5 : g_free (decompressor->header_data);
115 : : }
116 : :
117 : 26 : G_OBJECT_CLASS (g_zlib_decompressor_parent_class)->finalize (object);
118 : 26 : }
119 : :
120 : :
121 : : static void
122 : 26 : g_zlib_decompressor_set_property (GObject *object,
123 : : guint prop_id,
124 : : const GValue *value,
125 : : GParamSpec *pspec)
126 : : {
127 : : GZlibDecompressor *decompressor;
128 : :
129 : 26 : decompressor = G_ZLIB_DECOMPRESSOR (object);
130 : :
131 : 26 : switch (prop_id)
132 : : {
133 : 26 : case PROP_FORMAT:
134 : 26 : decompressor->format = g_value_get_enum (value);
135 : 26 : break;
136 : :
137 : 0 : default:
138 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
139 : 0 : break;
140 : : }
141 : :
142 : 26 : }
143 : :
144 : : static void
145 : 8 : g_zlib_decompressor_get_property (GObject *object,
146 : : guint prop_id,
147 : : GValue *value,
148 : : GParamSpec *pspec)
149 : : {
150 : : GZlibDecompressor *decompressor;
151 : :
152 : 8 : decompressor = G_ZLIB_DECOMPRESSOR (object);
153 : :
154 : 8 : switch (prop_id)
155 : : {
156 : 7 : case PROP_FORMAT:
157 : 7 : g_value_set_enum (value, decompressor->format);
158 : 7 : break;
159 : :
160 : 1 : case PROP_FILE_INFO:
161 : 1 : if (decompressor->header_data)
162 : 0 : g_value_set_object (value, decompressor->header_data->file_info);
163 : : else
164 : 1 : g_value_set_object (value, NULL);
165 : 1 : break;
166 : :
167 : 0 : default:
168 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169 : 0 : break;
170 : : }
171 : 8 : }
172 : :
173 : : static void
174 : 26 : g_zlib_decompressor_init (GZlibDecompressor *decompressor)
175 : : {
176 : 26 : }
177 : :
178 : : static void
179 : 26 : g_zlib_decompressor_constructed (GObject *object)
180 : : {
181 : : GZlibDecompressor *decompressor;
182 : : int res;
183 : :
184 : 26 : decompressor = G_ZLIB_DECOMPRESSOR (object);
185 : :
186 : 26 : if (decompressor->format == G_ZLIB_COMPRESSOR_FORMAT_GZIP)
187 : : {
188 : : /* + 16 for gzip */
189 : 5 : res = inflateInit2 (&decompressor->zstream, MAX_WBITS + 16);
190 : : }
191 : 21 : else if (decompressor->format == G_ZLIB_COMPRESSOR_FORMAT_RAW)
192 : : {
193 : : /* Negative for raw */
194 : 3 : res = inflateInit2 (&decompressor->zstream, -MAX_WBITS);
195 : : }
196 : : else /* ZLIB */
197 : 18 : res = inflateInit (&decompressor->zstream);
198 : :
199 : 26 : if (res == Z_MEM_ERROR )
200 : 0 : g_error ("GZlibDecompressor: Not enough memory for zlib use");
201 : :
202 : 26 : if (res != Z_OK)
203 : 0 : g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
204 : :
205 : 26 : g_zlib_decompressor_set_gzheader (decompressor);
206 : 26 : }
207 : :
208 : : static void
209 : 5 : g_zlib_decompressor_class_init (GZlibDecompressorClass *klass)
210 : : {
211 : 5 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
212 : :
213 : 5 : gobject_class->finalize = g_zlib_decompressor_finalize;
214 : 5 : gobject_class->constructed = g_zlib_decompressor_constructed;
215 : 5 : gobject_class->get_property = g_zlib_decompressor_get_property;
216 : 5 : gobject_class->set_property = g_zlib_decompressor_set_property;
217 : :
218 : : /**
219 : : * GZlibDecompressor:format:
220 : : *
221 : : * The format of the compressed data.
222 : : *
223 : : * Since: 2.24
224 : : */
225 : 5 : g_object_class_install_property (gobject_class,
226 : : PROP_FORMAT,
227 : : g_param_spec_enum ("format", NULL, NULL,
228 : : G_TYPE_ZLIB_COMPRESSOR_FORMAT,
229 : : G_ZLIB_COMPRESSOR_FORMAT_ZLIB,
230 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
231 : : G_PARAM_STATIC_STRINGS));
232 : :
233 : : /**
234 : : * GZlibDecompressor:file-info:
235 : : *
236 : : * A #GFileInfo containing the information found in the GZIP header
237 : : * of the data stream processed, or %NULL if the header was not yet
238 : : * fully processed, is not present at all, or the compressor's
239 : : * #GZlibDecompressor:format property is not %G_ZLIB_COMPRESSOR_FORMAT_GZIP.
240 : : *
241 : : * Since: 2.26
242 : : */
243 : 5 : g_object_class_install_property (gobject_class,
244 : : PROP_FILE_INFO,
245 : : g_param_spec_object ("file-info", NULL, NULL,
246 : : G_TYPE_FILE_INFO,
247 : : G_PARAM_READABLE |
248 : : G_PARAM_STATIC_STRINGS));
249 : 5 : }
250 : :
251 : : /**
252 : : * g_zlib_decompressor_new:
253 : : * @format: The format to use for the compressed data
254 : : *
255 : : * Creates a new #GZlibDecompressor.
256 : : *
257 : : * Returns: a new #GZlibDecompressor
258 : : *
259 : : * Since: 2.24
260 : : **/
261 : : GZlibDecompressor *
262 : 25 : g_zlib_decompressor_new (GZlibCompressorFormat format)
263 : : {
264 : : GZlibDecompressor *decompressor;
265 : :
266 : 25 : decompressor = g_object_new (G_TYPE_ZLIB_DECOMPRESSOR,
267 : : "format", format,
268 : : NULL);
269 : :
270 : 25 : return decompressor;
271 : : }
272 : :
273 : : /**
274 : : * g_zlib_decompressor_get_file_info:
275 : : * @decompressor: a #GZlibDecompressor
276 : : *
277 : : * Retrieves the #GFileInfo constructed from the GZIP header data
278 : : * of compressed data processed by @compressor, or %NULL if @decompressor's
279 : : * #GZlibDecompressor:format property is not %G_ZLIB_COMPRESSOR_FORMAT_GZIP,
280 : : * or the header data was not fully processed yet, or it not present in the
281 : : * data stream at all.
282 : : *
283 : : * Returns: (nullable) (transfer none): a #GFileInfo, or %NULL
284 : : *
285 : : * Since: 2.26
286 : : */
287 : : GFileInfo *
288 : 0 : g_zlib_decompressor_get_file_info (GZlibDecompressor *decompressor)
289 : : {
290 : 0 : g_return_val_if_fail (G_IS_ZLIB_DECOMPRESSOR (decompressor), NULL);
291 : :
292 : 0 : if (decompressor->header_data)
293 : 0 : return decompressor->header_data->file_info;
294 : :
295 : 0 : return NULL;
296 : : }
297 : :
298 : : static void
299 : 8 : g_zlib_decompressor_reset (GConverter *converter)
300 : : {
301 : 8 : GZlibDecompressor *decompressor = G_ZLIB_DECOMPRESSOR (converter);
302 : : int res;
303 : :
304 : 8 : res = inflateReset (&decompressor->zstream);
305 : 8 : if (res != Z_OK)
306 : 0 : g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
307 : :
308 : 8 : g_zlib_decompressor_set_gzheader (decompressor);
309 : 8 : }
310 : :
311 : : static GConverterResult
312 : 3692 : g_zlib_decompressor_convert (GConverter *converter,
313 : : const void *inbuf,
314 : : gsize inbuf_size,
315 : : void *outbuf,
316 : : gsize outbuf_size,
317 : : GConverterFlags flags,
318 : : gsize *bytes_read,
319 : : gsize *bytes_written,
320 : : GError **error)
321 : : {
322 : : GZlibDecompressor *decompressor;
323 : : int res;
324 : :
325 : 3692 : decompressor = G_ZLIB_DECOMPRESSOR (converter);
326 : :
327 : 3692 : decompressor->zstream.next_in = (void *)inbuf;
328 : 3692 : decompressor->zstream.avail_in = inbuf_size;
329 : :
330 : 3692 : decompressor->zstream.next_out = outbuf;
331 : 3692 : decompressor->zstream.avail_out = outbuf_size;
332 : :
333 : 3692 : res = inflate (&decompressor->zstream, Z_NO_FLUSH);
334 : :
335 : 3692 : if (res == Z_DATA_ERROR || res == Z_NEED_DICT)
336 : : {
337 : 3 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
338 : : _("Invalid compressed data"));
339 : 3 : return G_CONVERTER_ERROR;
340 : : }
341 : :
342 : 3689 : if (res == Z_MEM_ERROR)
343 : : {
344 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
345 : : _("Not enough memory"));
346 : 0 : return G_CONVERTER_ERROR;
347 : : }
348 : :
349 : 3689 : if (res == Z_STREAM_ERROR)
350 : : {
351 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
352 : : _("Internal error: %s"), decompressor->zstream.msg);
353 : 0 : return G_CONVERTER_ERROR;
354 : : }
355 : :
356 : 3689 : if (res == Z_BUF_ERROR)
357 : : {
358 : 6 : if (flags & G_CONVERTER_FLUSH)
359 : 0 : return G_CONVERTER_FLUSHED;
360 : :
361 : : /* Z_FINISH not set, so this means no progress could be made */
362 : : /* We do have output space, so this should only happen if we
363 : : have no input but need some */
364 : :
365 : 6 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
366 : : _("Need more input"));
367 : 6 : return G_CONVERTER_ERROR;
368 : : }
369 : :
370 : 3683 : g_assert (res == Z_OK || res == Z_STREAM_END);
371 : :
372 : 3683 : *bytes_read = inbuf_size - decompressor->zstream.avail_in;
373 : 3683 : *bytes_written = outbuf_size - decompressor->zstream.avail_out;
374 : :
375 : : #if !defined (G_OS_WIN32) || ZLIB_VERNUM >= 0x1240
376 : 3683 : if (decompressor->header_data != NULL &&
377 : 1224 : decompressor->header_data->gzheader.done == 1)
378 : : {
379 : 4 : HeaderData *data = decompressor->header_data;
380 : :
381 : : /* So we don't notify again */
382 : 4 : data->gzheader.done = 2;
383 : :
384 : 4 : data->file_info = g_file_info_new ();
385 : 4 : g_file_info_set_attribute_uint64 (data->file_info,
386 : : G_FILE_ATTRIBUTE_TIME_MODIFIED,
387 : : data->gzheader.time);
388 : 4 : g_file_info_set_attribute_uint32 (data->file_info,
389 : : G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
390 : : 0);
391 : 4 : g_file_info_set_attribute_uint32 (data->file_info,
392 : : G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
393 : : 0);
394 : :
395 : 4 : if (data->filename[0] != '\0')
396 : 2 : g_file_info_set_attribute_byte_string (data->file_info,
397 : : G_FILE_ATTRIBUTE_STANDARD_NAME,
398 : 2 : data->filename);
399 : :
400 : 4 : g_object_notify (G_OBJECT (decompressor), "file-info");
401 : : }
402 : : #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
403 : :
404 : 3683 : if (res == Z_STREAM_END)
405 : 20 : return G_CONVERTER_FINISHED;
406 : 3663 : return G_CONVERTER_CONVERTED;
407 : : }
408 : :
409 : : static void
410 : 5 : g_zlib_decompressor_iface_init (GConverterIface *iface)
411 : : {
412 : 5 : iface->convert = g_zlib_decompressor_convert;
413 : 5 : iface->reset = g_zlib_decompressor_reset;
414 : 5 : }
|