gzdecode.php 8.3 KB

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