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-2012, Ryan Parman, Geoffrey 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: * @version 1.3
37: * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38: * @author Ryan Parman
39: * @author Geoffrey Sneddon
40: * @author Ryan McCue
41: * @link http://simplepie.org/ SimplePie
42: * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43: */
44:
45:
46: /**
47: * Decode 'gzip' encoded HTTP data
48: *
49: * @package SimplePie
50: * @subpackage HTTP
51: * @link http://www.gzip.org/format.txt
52: */
53: class SimplePie_gzdecode
54: {
55: /**
56: * Compressed data
57: *
58: * @access private
59: * @var string
60: * @see gzdecode::$data
61: */
62: var $compressed_data;
63:
64: /**
65: * Size of compressed data
66: *
67: * @access private
68: * @var int
69: */
70: var $compressed_size;
71:
72: /**
73: * Minimum size of a valid gzip string
74: *
75: * @access private
76: * @var int
77: */
78: var $min_compressed_size = 18;
79:
80: /**
81: * Current position of pointer
82: *
83: * @access private
84: * @var int
85: */
86: var $position = 0;
87:
88: /**
89: * Flags (FLG)
90: *
91: * @access private
92: * @var int
93: */
94: var $flags;
95:
96: /**
97: * Uncompressed data
98: *
99: * @access public
100: * @see gzdecode::$compressed_data
101: * @var string
102: */
103: var $data;
104:
105: /**
106: * Modified time
107: *
108: * @access public
109: * @var int
110: */
111: var $MTIME;
112:
113: /**
114: * Extra Flags
115: *
116: * @access public
117: * @var int
118: */
119: var $XFL;
120:
121: /**
122: * Operating System
123: *
124: * @access public
125: * @var int
126: */
127: var $OS;
128:
129: /**
130: * Subfield ID 1
131: *
132: * @access public
133: * @see gzdecode::$extra_field
134: * @see gzdecode::$SI2
135: * @var string
136: */
137: var $SI1;
138:
139: /**
140: * Subfield ID 2
141: *
142: * @access public
143: * @see gzdecode::$extra_field
144: * @see gzdecode::$SI1
145: * @var string
146: */
147: var $SI2;
148:
149: /**
150: * Extra field content
151: *
152: * @access public
153: * @see gzdecode::$SI1
154: * @see gzdecode::$SI2
155: * @var string
156: */
157: var $extra_field;
158:
159: /**
160: * Original filename
161: *
162: * @access public
163: * @var string
164: */
165: var $filename;
166:
167: /**
168: * Human readable comment
169: *
170: * @access public
171: * @var string
172: */
173: var $comment;
174:
175: /**
176: * Don't allow anything to be set
177: *
178: * @param string $name
179: * @param mixed $value
180: */
181: public function __set($name, $value)
182: {
183: trigger_error("Cannot write property $name", E_USER_ERROR);
184: }
185:
186: /**
187: * Set the compressed string and related properties
188: *
189: * @param string $data
190: */
191: public function __construct($data)
192: {
193: $this->compressed_data = $data;
194: $this->compressed_size = strlen($data);
195: }
196:
197: /**
198: * Decode the GZIP stream
199: *
200: * @return bool Successfulness
201: */
202: public function parse()
203: {
204: if ($this->compressed_size >= $this->min_compressed_size)
205: {
206: // Check ID1, ID2, and CM
207: if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
208: {
209: return false;
210: }
211:
212: // Get the FLG (FLaGs)
213: $this->flags = ord($this->compressed_data[3]);
214:
215: // FLG bits above (1 << 4) are reserved
216: if ($this->flags > 0x1F)
217: {
218: return false;
219: }
220:
221: // Advance the pointer after the above
222: $this->position += 4;
223:
224: // MTIME
225: $mtime = substr($this->compressed_data, $this->position, 4);
226: // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
227: if (current(unpack('S', "\x00\x01")) === 1)
228: {
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: {
243: // Read subfield IDs
244: $this->SI1 = $this->compressed_data[$this->position++];
245: $this->SI2 = $this->compressed_data[$this->position++];
246:
247: // SI2 set to zero is reserved for future use
248: if ($this->SI2 === "\x00")
249: {
250: return false;
251: }
252:
253: // Get the length of the extra field
254: $len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
255: $this->position += 2;
256:
257: // Check the length of the string is still valid
258: $this->min_compressed_size += $len + 4;
259: if ($this->compressed_size >= $this->min_compressed_size)
260: {
261: // Set the extra field to the given data
262: $this->extra_field = substr($this->compressed_data, $this->position, $len);
263: $this->position += $len;
264: }
265: else
266: {
267: return false;
268: }
269: }
270:
271: // Parse the FNAME
272: if ($this->flags & 8)
273: {
274: // Get the length of the filename
275: $len = strcspn($this->compressed_data, "\x00", $this->position);
276:
277: // Check the length of the string is still valid
278: $this->min_compressed_size += $len + 1;
279: if ($this->compressed_size >= $this->min_compressed_size)
280: {
281: // Set the original filename to the given string
282: $this->filename = substr($this->compressed_data, $this->position, $len);
283: $this->position += $len + 1;
284: }
285: else
286: {
287: return false;
288: }
289: }
290:
291: // Parse the FCOMMENT
292: if ($this->flags & 16)
293: {
294: // Get the length of the comment
295: $len = strcspn($this->compressed_data, "\x00", $this->position);
296:
297: // Check the length of the string is still valid
298: $this->min_compressed_size += $len + 1;
299: if ($this->compressed_size >= $this->min_compressed_size)
300: {
301: // Set the original comment to the given string
302: $this->comment = substr($this->compressed_data, $this->position, $len);
303: $this->position += $len + 1;
304: }
305: else
306: {
307: return false;
308: }
309: }
310:
311: // Parse the FHCRC
312: if ($this->flags & 2)
313: {
314: // Check the length of the string is still valid
315: $this->min_compressed_size += $len + 2;
316: if ($this->compressed_size >= $this->min_compressed_size)
317: {
318: // Read the CRC
319: $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
320:
321: // Check the CRC matches
322: if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
323: {
324: $this->position += 2;
325: }
326: else
327: {
328: return false;
329: }
330: }
331: else
332: {
333: return false;
334: }
335: }
336:
337: // Decompress the actual data
338: if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
339: {
340: return false;
341: }
342: else
343: {
344: $this->position = $this->compressed_size - 8;
345: }
346:
347: // Check CRC of data
348: $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
349: $this->position += 4;
350: /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
351: {
352: return false;
353: }*/
354:
355: // Check ISIZE of data
356: $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
357: $this->position += 4;
358: if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
359: {
360: return false;
361: }
362:
363: // Wow, against all odds, we've actually got a valid gzip string
364: return true;
365: }
366: else
367: {
368: return false;
369: }
370: }
371: }
372: