(root)/
glib-2.79.0/
gio/
gzlibdecompressor.c
       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  }