xref: /dokuwiki/vendor/simplepie/simplepie/src/Gzdecode.php (revision 8e88a29b81301f78509349ab1152bb09c229123e)
1<?php
2
3// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
4// SPDX-License-Identifier: BSD-3-Clause
5
6declare(strict_types=1);
7
8namespace SimplePie;
9
10/**
11 * Decode 'gzip' encoded HTTP data
12 *
13 * @link http://www.gzip.org/format.txt
14 * @link https://www.php.net/manual/en/function.gzdecode.php
15 * @deprecated since SimplePie 1.9.0, use `gzdecode` function instead.
16 */
17class Gzdecode
18{
19    /**
20     * Compressed data
21     *
22     * @access private
23     * @var string
24     * @see gzdecode::$data
25     */
26    public $compressed_data;
27
28    /**
29     * Size of compressed data
30     *
31     * @access private
32     * @var int
33     */
34    public $compressed_size;
35
36    /**
37     * Minimum size of a valid gzip string
38     *
39     * @access private
40     * @var int
41     */
42    public $min_compressed_size = 18;
43
44    /**
45     * Current position of pointer
46     *
47     * @access private
48     * @var int
49     */
50    public $position = 0;
51
52    /**
53     * Flags (FLG)
54     *
55     * @access private
56     * @var int
57     */
58    public $flags;
59
60    /**
61     * Uncompressed data
62     *
63     * @access public
64     * @see gzdecode::$compressed_data
65     * @var string
66     */
67    public $data;
68
69    /**
70     * Modified time
71     *
72     * @access public
73     * @var int
74     */
75    public $MTIME;
76
77    /**
78     * Extra Flags
79     *
80     * @access public
81     * @var int
82     */
83    public $XFL;
84
85    /**
86     * Operating System
87     *
88     * @access public
89     * @var int
90     */
91    public $OS;
92
93    /**
94     * Subfield ID 1
95     *
96     * @access public
97     * @see gzdecode::$extra_field
98     * @see gzdecode::$SI2
99     * @var string
100     */
101    public $SI1;
102
103    /**
104     * Subfield ID 2
105     *
106     * @access public
107     * @see gzdecode::$extra_field
108     * @see gzdecode::$SI1
109     * @var string
110     */
111    public $SI2;
112
113    /**
114     * Extra field content
115     *
116     * @access public
117     * @see gzdecode::$SI1
118     * @see gzdecode::$SI2
119     * @var string
120     */
121    public $extra_field;
122
123    /**
124     * Original filename
125     *
126     * @access public
127     * @var string
128     */
129    public $filename;
130
131    /**
132     * Human readable comment
133     *
134     * @access public
135     * @var string
136     */
137    public $comment;
138
139    /**
140     * Don't allow anything to be set
141     *
142     * @param string $name
143     * @param mixed $value
144     */
145    public function __set(string $name, $value)
146    {
147        throw new Exception("Cannot write property $name");
148    }
149
150    /**
151     * Set the compressed string and related properties
152     *
153     * @param string $data
154     */
155    public function __construct(string $data)
156    {
157        $this->compressed_data = $data;
158        $this->compressed_size = strlen($data);
159    }
160
161    /**
162     * Decode the GZIP stream
163     *
164     * @return bool Successfulness
165     */
166    public function parse()
167    {
168        if ($this->compressed_size >= $this->min_compressed_size) {
169            $len = 0;
170
171            // Check ID1, ID2, and CM
172            if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") {
173                return false;
174            }
175
176            // Get the FLG (FLaGs)
177            $this->flags = ord($this->compressed_data[3]);
178
179            // FLG bits above (1 << 4) are reserved
180            if ($this->flags > 0x1F) {
181                return false;
182            }
183
184            // Advance the pointer after the above
185            $this->position += 4;
186
187            // MTIME
188            $mtime = substr($this->compressed_data, $this->position, 4);
189            // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
190            if (current((array) unpack('S', "\x00\x01")) === 1) {
191                $mtime = strrev($mtime);
192            }
193            $this->MTIME = current((array) unpack('l', $mtime));
194            $this->position += 4;
195
196            // Get the XFL (eXtra FLags)
197            $this->XFL = ord($this->compressed_data[$this->position++]);
198
199            // Get the OS (Operating System)
200            $this->OS = ord($this->compressed_data[$this->position++]);
201
202            // Parse the FEXTRA
203            if ($this->flags & 4) {
204                // Read subfield IDs
205                $this->SI1 = $this->compressed_data[$this->position++];
206                $this->SI2 = $this->compressed_data[$this->position++];
207
208                // SI2 set to zero is reserved for future use
209                if ($this->SI2 === "\x00") {
210                    return false;
211                }
212
213                // Get the length of the extra field
214                $len = current((array) unpack('v', substr($this->compressed_data, $this->position, 2)));
215                $this->position += 2;
216
217                // Check the length of the string is still valid
218                $this->min_compressed_size += $len + 4;
219                if ($this->compressed_size >= $this->min_compressed_size) {
220                    // Set the extra field to the given data
221                    $this->extra_field = substr($this->compressed_data, $this->position, $len);
222                    $this->position += $len;
223                } else {
224                    return false;
225                }
226            }
227
228            // Parse the FNAME
229            if ($this->flags & 8) {
230                // Get the length of the filename
231                $len = strcspn($this->compressed_data, "\x00", $this->position);
232
233                // Check the length of the string is still valid
234                $this->min_compressed_size += $len + 1;
235                if ($this->compressed_size >= $this->min_compressed_size) {
236                    // Set the original filename to the given string
237                    $this->filename = substr($this->compressed_data, $this->position, $len);
238                    $this->position += $len + 1;
239                } else {
240                    return false;
241                }
242            }
243
244            // Parse the FCOMMENT
245            if ($this->flags & 16) {
246                // Get the length of the comment
247                $len = strcspn($this->compressed_data, "\x00", $this->position);
248
249                // Check the length of the string is still valid
250                $this->min_compressed_size += $len + 1;
251                if ($this->compressed_size >= $this->min_compressed_size) {
252                    // Set the original comment to the given string
253                    $this->comment = substr($this->compressed_data, $this->position, $len);
254                    $this->position += $len + 1;
255                } else {
256                    return false;
257                }
258            }
259
260            // Parse the FHCRC
261            if ($this->flags & 2) {
262                // Check the length of the string is still valid
263                $this->min_compressed_size += $len + 2;
264                if ($this->compressed_size >= $this->min_compressed_size) {
265                    // Read the CRC
266                    $crc = current((array) unpack('v', substr($this->compressed_data, $this->position, 2)));
267
268                    // Check the CRC matches
269                    if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) {
270                        $this->position += 2;
271                    } else {
272                        return false;
273                    }
274                } else {
275                    return false;
276                }
277            }
278
279            // Decompress the actual data
280            if (($data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) {
281                return false;
282            }
283
284            $this->data = $data;
285            $this->position = $this->compressed_size - 8;
286
287            // Check CRC of data
288            $crc = current((array) unpack('V', substr($this->compressed_data, $this->position, 4)));
289            $this->position += 4;
290            /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
291            {
292                return false;
293            }*/
294
295            // Check ISIZE of data
296            $isize = current((array) unpack('V', substr($this->compressed_data, $this->position, 4)));
297            $this->position += 4;
298            if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) {
299                return false;
300            }
301
302            // Wow, against all odds, we've actually got a valid gzip string
303            return true;
304        }
305
306        return false;
307    }
308}
309
310class_alias('SimplePie\Gzdecode', 'SimplePie_gzdecode');
311