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