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 [class@Gio.FileInfo] containing the information found in the gzip header
237 : : * of the data stream processed.
238 : : *
239 : : * This will be `NULL` if the header was not yet fully processed, is not
240 : : * present at all, or the compressor’s [property@Gio.ZlibDecompressor:format]
241 : : * property is not [enum@Gio.ZlibCompressorFormat.GZIP].
242 : : *
243 : : * Since: 2.26
244 : : */
245 : 5 : g_object_class_install_property (gobject_class,
246 : : PROP_FILE_INFO,
247 : : g_param_spec_object ("file-info", NULL, NULL,
248 : : G_TYPE_FILE_INFO,
249 : : G_PARAM_READABLE |
250 : : G_PARAM_STATIC_STRINGS));
251 : 5 : }
252 : :
253 : : /**
254 : : * g_zlib_decompressor_new:
255 : : * @format: the format to use for the compressed data
256 : : *
257 : : * Creates a new decompressor.
258 : : *
259 : : * Returns: a new [class@Gio.ZlibDecompressor]
260 : : * Since: 2.24
261 : : **/
262 : : GZlibDecompressor *
263 : 25 : g_zlib_decompressor_new (GZlibCompressorFormat format)
264 : : {
265 : : GZlibDecompressor *decompressor;
266 : :
267 : 25 : decompressor = g_object_new (G_TYPE_ZLIB_DECOMPRESSOR,
268 : : "format", format,
269 : : NULL);
270 : :
271 : 25 : return decompressor;
272 : : }
273 : :
274 : : /**
275 : : * g_zlib_decompressor_get_file_info:
276 : : * @decompressor: a #GZlibDecompressor
277 : : *
278 : : * Gets the [property@Gio.ZlibDecompressor:file-info] property.
279 : : *
280 : : * Returns: (nullable) (transfer none): file info from the gzip header, if available
281 : : * Since: 2.26
282 : : */
283 : : GFileInfo *
284 : 0 : g_zlib_decompressor_get_file_info (GZlibDecompressor *decompressor)
285 : : {
286 : 0 : g_return_val_if_fail (G_IS_ZLIB_DECOMPRESSOR (decompressor), NULL);
287 : :
288 : 0 : if (decompressor->header_data)
289 : 0 : return decompressor->header_data->file_info;
290 : :
291 : 0 : return NULL;
292 : : }
293 : :
294 : : static void
295 : 8 : g_zlib_decompressor_reset (GConverter *converter)
296 : : {
297 : 8 : GZlibDecompressor *decompressor = G_ZLIB_DECOMPRESSOR (converter);
298 : : int res;
299 : :
300 : 8 : res = inflateReset (&decompressor->zstream);
301 : 8 : if (res != Z_OK)
302 : 0 : g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
303 : :
304 : 8 : g_zlib_decompressor_set_gzheader (decompressor);
305 : 8 : }
306 : :
307 : : static GConverterResult
308 : 3692 : g_zlib_decompressor_convert (GConverter *converter,
309 : : const void *inbuf,
310 : : gsize inbuf_size,
311 : : void *outbuf,
312 : : gsize outbuf_size,
313 : : GConverterFlags flags,
314 : : gsize *bytes_read,
315 : : gsize *bytes_written,
316 : : GError **error)
317 : : {
318 : : GZlibDecompressor *decompressor;
319 : : int res;
320 : :
321 : 3692 : decompressor = G_ZLIB_DECOMPRESSOR (converter);
322 : :
323 : 3692 : decompressor->zstream.next_in = (void *)inbuf;
324 : 3692 : decompressor->zstream.avail_in = inbuf_size;
325 : :
326 : 3692 : decompressor->zstream.next_out = outbuf;
327 : 3692 : decompressor->zstream.avail_out = outbuf_size;
328 : :
329 : 3692 : res = inflate (&decompressor->zstream, Z_NO_FLUSH);
330 : :
331 : 3692 : if (res == Z_DATA_ERROR || res == Z_NEED_DICT)
332 : : {
333 : 3 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
334 : : _("Invalid compressed data"));
335 : 3 : return G_CONVERTER_ERROR;
336 : : }
337 : :
338 : 3689 : if (res == Z_MEM_ERROR)
339 : : {
340 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
341 : : _("Not enough memory"));
342 : 0 : return G_CONVERTER_ERROR;
343 : : }
344 : :
345 : 3689 : if (res == Z_STREAM_ERROR)
346 : : {
347 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
348 : : _("Internal error: %s"), decompressor->zstream.msg);
349 : 0 : return G_CONVERTER_ERROR;
350 : : }
351 : :
352 : 3689 : if (res == Z_BUF_ERROR)
353 : : {
354 : 6 : if (flags & G_CONVERTER_FLUSH)
355 : 0 : return G_CONVERTER_FLUSHED;
356 : :
357 : : /* Z_FINISH not set, so this means no progress could be made */
358 : : /* We do have output space, so this should only happen if we
359 : : have no input but need some */
360 : :
361 : 6 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
362 : : _("Need more input"));
363 : 6 : return G_CONVERTER_ERROR;
364 : : }
365 : :
366 : 3683 : g_assert (res == Z_OK || res == Z_STREAM_END);
367 : :
368 : 3683 : *bytes_read = inbuf_size - decompressor->zstream.avail_in;
369 : 3683 : *bytes_written = outbuf_size - decompressor->zstream.avail_out;
370 : :
371 : : #if !defined (G_OS_WIN32) || ZLIB_VERNUM >= 0x1240
372 : 3683 : if (decompressor->header_data != NULL &&
373 : 1224 : decompressor->header_data->gzheader.done == 1)
374 : : {
375 : 4 : HeaderData *data = decompressor->header_data;
376 : :
377 : : /* So we don't notify again */
378 : 4 : data->gzheader.done = 2;
379 : :
380 : 4 : data->file_info = g_file_info_new ();
381 : 4 : g_file_info_set_attribute_uint64 (data->file_info,
382 : : G_FILE_ATTRIBUTE_TIME_MODIFIED,
383 : : data->gzheader.time);
384 : 4 : g_file_info_set_attribute_uint32 (data->file_info,
385 : : G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
386 : : 0);
387 : 4 : g_file_info_set_attribute_uint32 (data->file_info,
388 : : G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
389 : : 0);
390 : :
391 : 4 : if (data->filename[0] != '\0')
392 : 2 : g_file_info_set_attribute_byte_string (data->file_info,
393 : : G_FILE_ATTRIBUTE_STANDARD_NAME,
394 : 2 : data->filename);
395 : :
396 : 4 : g_object_notify (G_OBJECT (decompressor), "file-info");
397 : : }
398 : : #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
399 : :
400 : 3683 : if (res == Z_STREAM_END)
401 : 20 : return G_CONVERTER_FINISHED;
402 : 3663 : return G_CONVERTER_CONVERTED;
403 : : }
404 : :
405 : : static void
406 : 5 : g_zlib_decompressor_iface_init (GConverterIface *iface)
407 : : {
408 : 5 : iface->convert = g_zlib_decompressor_convert;
409 : 5 : iface->reset = g_zlib_decompressor_reset;
410 : 5 : }
|