1<?php 2 3declare(strict_types=1); 4/** 5 * SimplePie 6 * 7 * A PHP-Based RSS and Atom Feed Framework. 8 * Takes the hard work out of managing a complete RSS/Atom solution. 9 * 10 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without modification, are 14 * permitted provided that the following conditions are met: 15 * 16 * * Redistributions of source code must retain the above copyright notice, this list of 17 * conditions and the following disclaimer. 18 * 19 * * Redistributions in binary form must reproduce the above copyright notice, this list 20 * of conditions and the following disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * * Neither the name of the SimplePie Team nor the names of its contributors may be used 24 * to endorse or promote products derived from this software without specific prior 25 * written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 28 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 29 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS 30 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 * 37 * @package SimplePie 38 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue 39 * @author Ryan Parman 40 * @author Sam Sneddon 41 * @author Ryan McCue 42 * @link http://simplepie.org/ SimplePie 43 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 44 */ 45 46namespace SimplePie; 47 48/** 49 * Decode 'gzip' encoded HTTP data 50 * 51 * @package SimplePie 52 * @subpackage HTTP 53 * @link http://www.gzip.org/format.txt 54 */ 55class Gzdecode 56{ 57 /** 58 * Compressed data 59 * 60 * @access private 61 * @var string 62 * @see gzdecode::$data 63 */ 64 public $compressed_data; 65 66 /** 67 * Size of compressed data 68 * 69 * @access private 70 * @var int 71 */ 72 public $compressed_size; 73 74 /** 75 * Minimum size of a valid gzip string 76 * 77 * @access private 78 * @var int 79 */ 80 public $min_compressed_size = 18; 81 82 /** 83 * Current position of pointer 84 * 85 * @access private 86 * @var int 87 */ 88 public $position = 0; 89 90 /** 91 * Flags (FLG) 92 * 93 * @access private 94 * @var int 95 */ 96 public $flags; 97 98 /** 99 * Uncompressed data 100 * 101 * @access public 102 * @see gzdecode::$compressed_data 103 * @var string 104 */ 105 public $data; 106 107 /** 108 * Modified time 109 * 110 * @access public 111 * @var int 112 */ 113 public $MTIME; 114 115 /** 116 * Extra Flags 117 * 118 * @access public 119 * @var int 120 */ 121 public $XFL; 122 123 /** 124 * Operating System 125 * 126 * @access public 127 * @var int 128 */ 129 public $OS; 130 131 /** 132 * Subfield ID 1 133 * 134 * @access public 135 * @see gzdecode::$extra_field 136 * @see gzdecode::$SI2 137 * @var string 138 */ 139 public $SI1; 140 141 /** 142 * Subfield ID 2 143 * 144 * @access public 145 * @see gzdecode::$extra_field 146 * @see gzdecode::$SI1 147 * @var string 148 */ 149 public $SI2; 150 151 /** 152 * Extra field content 153 * 154 * @access public 155 * @see gzdecode::$SI1 156 * @see gzdecode::$SI2 157 * @var string 158 */ 159 public $extra_field; 160 161 /** 162 * Original filename 163 * 164 * @access public 165 * @var string 166 */ 167 public $filename; 168 169 /** 170 * Human readable comment 171 * 172 * @access public 173 * @var string 174 */ 175 public $comment; 176 177 /** 178 * Don't allow anything to be set 179 * 180 * @param string $name 181 * @param mixed $value 182 */ 183 public function __set($name, $value) 184 { 185 trigger_error("Cannot write property $name", E_USER_ERROR); 186 } 187 188 /** 189 * Set the compressed string and related properties 190 * 191 * @param string $data 192 */ 193 public function __construct($data) 194 { 195 $this->compressed_data = $data; 196 $this->compressed_size = strlen($data); 197 } 198 199 /** 200 * Decode the GZIP stream 201 * 202 * @return bool Successfulness 203 */ 204 public function parse() 205 { 206 if ($this->compressed_size >= $this->min_compressed_size) { 207 $len = 0; 208 209 // Check ID1, ID2, and CM 210 if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") { 211 return false; 212 } 213 214 // Get the FLG (FLaGs) 215 $this->flags = ord($this->compressed_data[3]); 216 217 // FLG bits above (1 << 4) are reserved 218 if ($this->flags > 0x1F) { 219 return false; 220 } 221 222 // Advance the pointer after the above 223 $this->position += 4; 224 225 // MTIME 226 $mtime = substr($this->compressed_data, $this->position, 4); 227 // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness 228 if (current(unpack('S', "\x00\x01")) === 1) { 229 $mtime = strrev($mtime); 230 } 231 $this->MTIME = current(unpack('l', $mtime)); 232 $this->position += 4; 233 234 // Get the XFL (eXtra FLags) 235 $this->XFL = ord($this->compressed_data[$this->position++]); 236 237 // Get the OS (Operating System) 238 $this->OS = ord($this->compressed_data[$this->position++]); 239 240 // Parse the FEXTRA 241 if ($this->flags & 4) { 242 // Read subfield IDs 243 $this->SI1 = $this->compressed_data[$this->position++]; 244 $this->SI2 = $this->compressed_data[$this->position++]; 245 246 // SI2 set to zero is reserved for future use 247 if ($this->SI2 === "\x00") { 248 return false; 249 } 250 251 // Get the length of the extra field 252 $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); 253 $this->position += 2; 254 255 // Check the length of the string is still valid 256 $this->min_compressed_size += $len + 4; 257 if ($this->compressed_size >= $this->min_compressed_size) { 258 // Set the extra field to the given data 259 $this->extra_field = substr($this->compressed_data, $this->position, $len); 260 $this->position += $len; 261 } else { 262 return false; 263 } 264 } 265 266 // Parse the FNAME 267 if ($this->flags & 8) { 268 // Get the length of the filename 269 $len = strcspn($this->compressed_data, "\x00", $this->position); 270 271 // Check the length of the string is still valid 272 $this->min_compressed_size += $len + 1; 273 if ($this->compressed_size >= $this->min_compressed_size) { 274 // Set the original filename to the given string 275 $this->filename = substr($this->compressed_data, $this->position, $len); 276 $this->position += $len + 1; 277 } else { 278 return false; 279 } 280 } 281 282 // Parse the FCOMMENT 283 if ($this->flags & 16) { 284 // Get the length of the comment 285 $len = strcspn($this->compressed_data, "\x00", $this->position); 286 287 // Check the length of the string is still valid 288 $this->min_compressed_size += $len + 1; 289 if ($this->compressed_size >= $this->min_compressed_size) { 290 // Set the original comment to the given string 291 $this->comment = substr($this->compressed_data, $this->position, $len); 292 $this->position += $len + 1; 293 } else { 294 return false; 295 } 296 } 297 298 // Parse the FHCRC 299 if ($this->flags & 2) { 300 // Check the length of the string is still valid 301 $this->min_compressed_size += $len + 2; 302 if ($this->compressed_size >= $this->min_compressed_size) { 303 // Read the CRC 304 $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); 305 306 // Check the CRC matches 307 if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) { 308 $this->position += 2; 309 } else { 310 return false; 311 } 312 } else { 313 return false; 314 } 315 } 316 317 // Decompress the actual data 318 if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) { 319 return false; 320 } 321 322 $this->position = $this->compressed_size - 8; 323 324 // Check CRC of data 325 $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); 326 $this->position += 4; 327 /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) 328 { 329 return false; 330 }*/ 331 332 // Check ISIZE of data 333 $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); 334 $this->position += 4; 335 if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) { 336 return false; 337 } 338 339 // Wow, against all odds, we've actually got a valid gzip string 340 return true; 341 } 342 343 return false; 344 } 345} 346 347class_alias('SimplePie\Gzdecode', 'SimplePie_gzdecode'); 348