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 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 if (decompressor->format != G_ZLIB_COMPRESSOR_FORMAT_GZIP)
74 return;
75
76 if (decompressor->header_data != NULL)
77 {
78 if (decompressor->header_data->file_info)
79 g_object_unref (decompressor->header_data->file_info);
80
81 memset (decompressor->header_data, 0, sizeof (HeaderData));
82 }
83 else
84 {
85 decompressor->header_data = g_new0 (HeaderData, 1);
86 }
87
88 decompressor->header_data->gzheader.name = (Bytef*) &decompressor->header_data->filename;
89 /* We keep one byte to guarantee the string is 0-terminated */
90 decompressor->header_data->gzheader.name_max = 256;
91
92 if (inflateGetHeader (&decompressor->zstream, &decompressor->header_data->gzheader) != Z_OK)
93 g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
94 #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
95 }
96
97 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 g_zlib_decompressor_finalize (GObject *object)
103 {
104 GZlibDecompressor *decompressor;
105
106 decompressor = G_ZLIB_DECOMPRESSOR (object);
107
108 inflateEnd (&decompressor->zstream);
109
110 if (decompressor->header_data != NULL)
111 {
112 if (decompressor->header_data->file_info)
113 g_object_unref (decompressor->header_data->file_info);
114 g_free (decompressor->header_data);
115 }
116
117 G_OBJECT_CLASS (g_zlib_decompressor_parent_class)->finalize (object);
118 }
119
120
121 static void
122 g_zlib_decompressor_set_property (GObject *object,
123 guint prop_id,
124 const GValue *value,
125 GParamSpec *pspec)
126 {
127 GZlibDecompressor *decompressor;
128
129 decompressor = G_ZLIB_DECOMPRESSOR (object);
130
131 switch (prop_id)
132 {
133 case PROP_FORMAT:
134 decompressor->format = g_value_get_enum (value);
135 break;
136
137 default:
138 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
139 break;
140 }
141
142 }
143
144 static void
145 g_zlib_decompressor_get_property (GObject *object,
146 guint prop_id,
147 GValue *value,
148 GParamSpec *pspec)
149 {
150 GZlibDecompressor *decompressor;
151
152 decompressor = G_ZLIB_DECOMPRESSOR (object);
153
154 switch (prop_id)
155 {
156 case PROP_FORMAT:
157 g_value_set_enum (value, decompressor->format);
158 break;
159
160 case PROP_FILE_INFO:
161 if (decompressor->header_data)
162 g_value_set_object (value, decompressor->header_data->file_info);
163 else
164 g_value_set_object (value, NULL);
165 break;
166
167 default:
168 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169 break;
170 }
171 }
172
173 static void
174 g_zlib_decompressor_init (GZlibDecompressor *decompressor)
175 {
176 }
177
178 static void
179 g_zlib_decompressor_constructed (GObject *object)
180 {
181 GZlibDecompressor *decompressor;
182 int res;
183
184 decompressor = G_ZLIB_DECOMPRESSOR (object);
185
186 if (decompressor->format == G_ZLIB_COMPRESSOR_FORMAT_GZIP)
187 {
188 /* + 16 for gzip */
189 res = inflateInit2 (&decompressor->zstream, MAX_WBITS + 16);
190 }
191 else if (decompressor->format == G_ZLIB_COMPRESSOR_FORMAT_RAW)
192 {
193 /* Negative for raw */
194 res = inflateInit2 (&decompressor->zstream, -MAX_WBITS);
195 }
196 else /* ZLIB */
197 res = inflateInit (&decompressor->zstream);
198
199 if (res == Z_MEM_ERROR )
200 g_error ("GZlibDecompressor: Not enough memory for zlib use");
201
202 if (res != Z_OK)
203 g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
204
205 g_zlib_decompressor_set_gzheader (decompressor);
206 }
207
208 static void
209 g_zlib_decompressor_class_init (GZlibDecompressorClass *klass)
210 {
211 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
212
213 gobject_class->finalize = g_zlib_decompressor_finalize;
214 gobject_class->constructed = g_zlib_decompressor_constructed;
215 gobject_class->get_property = g_zlib_decompressor_get_property;
216 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 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 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 }
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 g_zlib_decompressor_new (GZlibCompressorFormat format)
263 {
264 GZlibDecompressor *decompressor;
265
266 decompressor = g_object_new (G_TYPE_ZLIB_DECOMPRESSOR,
267 "format", format,
268 NULL);
269
270 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 g_zlib_decompressor_get_file_info (GZlibDecompressor *decompressor)
289 {
290 g_return_val_if_fail (G_IS_ZLIB_DECOMPRESSOR (decompressor), NULL);
291
292 if (decompressor->header_data)
293 return decompressor->header_data->file_info;
294
295 return NULL;
296 }
297
298 static void
299 g_zlib_decompressor_reset (GConverter *converter)
300 {
301 GZlibDecompressor *decompressor = G_ZLIB_DECOMPRESSOR (converter);
302 int res;
303
304 res = inflateReset (&decompressor->zstream);
305 if (res != Z_OK)
306 g_warning ("unexpected zlib error: %s", decompressor->zstream.msg);
307
308 g_zlib_decompressor_set_gzheader (decompressor);
309 }
310
311 static GConverterResult
312 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 decompressor = G_ZLIB_DECOMPRESSOR (converter);
326
327 decompressor->zstream.next_in = (void *)inbuf;
328 decompressor->zstream.avail_in = inbuf_size;
329
330 decompressor->zstream.next_out = outbuf;
331 decompressor->zstream.avail_out = outbuf_size;
332
333 res = inflate (&decompressor->zstream, Z_NO_FLUSH);
334
335 if (res == Z_DATA_ERROR || res == Z_NEED_DICT)
336 {
337 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
338 _("Invalid compressed data"));
339 return G_CONVERTER_ERROR;
340 }
341
342 if (res == Z_MEM_ERROR)
343 {
344 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
345 _("Not enough memory"));
346 return G_CONVERTER_ERROR;
347 }
348
349 if (res == Z_STREAM_ERROR)
350 {
351 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
352 _("Internal error: %s"), decompressor->zstream.msg);
353 return G_CONVERTER_ERROR;
354 }
355
356 if (res == Z_BUF_ERROR)
357 {
358 if (flags & G_CONVERTER_FLUSH)
359 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 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
366 _("Need more input"));
367 return G_CONVERTER_ERROR;
368 }
369
370 g_assert (res == Z_OK || res == Z_STREAM_END);
371
372 *bytes_read = inbuf_size - decompressor->zstream.avail_in;
373 *bytes_written = outbuf_size - decompressor->zstream.avail_out;
374
375 #if !defined (G_OS_WIN32) || ZLIB_VERNUM >= 0x1240
376 if (decompressor->header_data != NULL &&
377 decompressor->header_data->gzheader.done == 1)
378 {
379 HeaderData *data = decompressor->header_data;
380
381 /* So we don't notify again */
382 data->gzheader.done = 2;
383
384 data->file_info = g_file_info_new ();
385 g_file_info_set_attribute_uint64 (data->file_info,
386 G_FILE_ATTRIBUTE_TIME_MODIFIED,
387 data->gzheader.time);
388 g_file_info_set_attribute_uint32 (data->file_info,
389 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
390 0);
391 g_file_info_set_attribute_uint32 (data->file_info,
392 G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
393 0);
394
395 if (data->filename[0] != '\0')
396 g_file_info_set_attribute_byte_string (data->file_info,
397 G_FILE_ATTRIBUTE_STANDARD_NAME,
398 data->filename);
399
400 g_object_notify (G_OBJECT (decompressor), "file-info");
401 }
402 #endif /* !G_OS_WIN32 || ZLIB >= 1.2.4 */
403
404 if (res == Z_STREAM_END)
405 return G_CONVERTER_FINISHED;
406 return G_CONVERTER_CONVERTED;
407 }
408
409 static void
410 g_zlib_decompressor_iface_init (GConverterIface *iface)
411 {
412 iface->convert = g_zlib_decompressor_convert;
413 iface->reset = g_zlib_decompressor_reset;
414 }