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