1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:
44:
45:
46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59:
60: class SimplePie_Content_Type_Sniffer
61: {
62: 63: 64: 65: 66:
67: var $file;
68:
69: 70: 71: 72: 73:
74: public function __construct($file)
75: {
76: $this->file = $file;
77: }
78:
79: 80: 81: 82: 83:
84: public function get_type()
85: {
86: if (isset($this->file->headers['content-type']))
87: {
88: if (!isset($this->file->headers['content-encoding'])
89: && ($this->file->headers['content-type'] === 'text/plain'
90: || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
91: || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
92: || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
93: {
94: return $this->text_or_binary();
95: }
96:
97: if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
98: {
99: $official = substr($this->file->headers['content-type'], 0, $pos);
100: }
101: else
102: {
103: $official = $this->file->headers['content-type'];
104: }
105: $official = trim(strtolower($official));
106:
107: if ($official === 'unknown/unknown'
108: || $official === 'application/unknown')
109: {
110: return $this->unknown();
111: }
112: elseif (substr($official, -4) === '+xml'
113: || $official === 'text/xml'
114: || $official === 'application/xml')
115: {
116: return $official;
117: }
118: elseif (substr($official, 0, 6) === 'image/')
119: {
120: if ($return = $this->image())
121: {
122: return $return;
123: }
124: else
125: {
126: return $official;
127: }
128: }
129: elseif ($official === 'text/html')
130: {
131: return $this->feed_or_html();
132: }
133: else
134: {
135: return $official;
136: }
137: }
138: else
139: {
140: return $this->unknown();
141: }
142: }
143:
144: 145: 146: 147: 148:
149: public function text_or_binary()
150: {
151: if (substr($this->file->body, 0, 2) === "\xFE\xFF"
152: || substr($this->file->body, 0, 2) === "\xFF\xFE"
153: || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
154: || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
155: {
156: return 'text/plain';
157: }
158: elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
159: {
160: return 'application/octect-stream';
161: }
162: else
163: {
164: return 'text/plain';
165: }
166: }
167:
168: 169: 170: 171: 172:
173: public function unknown()
174: {
175: $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
176: if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
177: || strtolower(substr($this->file->body, $ws, 5)) === '<html'
178: || strtolower(substr($this->file->body, $ws, 7)) === '<script')
179: {
180: return 'text/html';
181: }
182: elseif (substr($this->file->body, 0, 5) === '%PDF-')
183: {
184: return 'application/pdf';
185: }
186: elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
187: {
188: return 'application/postscript';
189: }
190: elseif (substr($this->file->body, 0, 6) === 'GIF87a'
191: || substr($this->file->body, 0, 6) === 'GIF89a')
192: {
193: return 'image/gif';
194: }
195: elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
196: {
197: return 'image/png';
198: }
199: elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
200: {
201: return 'image/jpeg';
202: }
203: elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
204: {
205: return 'image/bmp';
206: }
207: elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
208: {
209: return 'image/vnd.microsoft.icon';
210: }
211: else
212: {
213: return $this->text_or_binary();
214: }
215: }
216:
217: 218: 219: 220: 221:
222: public function image()
223: {
224: if (substr($this->file->body, 0, 6) === 'GIF87a'
225: || substr($this->file->body, 0, 6) === 'GIF89a')
226: {
227: return 'image/gif';
228: }
229: elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
230: {
231: return 'image/png';
232: }
233: elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
234: {
235: return 'image/jpeg';
236: }
237: elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
238: {
239: return 'image/bmp';
240: }
241: elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
242: {
243: return 'image/vnd.microsoft.icon';
244: }
245: else
246: {
247: return false;
248: }
249: }
250:
251: 252: 253: 254: 255:
256: public function feed_or_html()
257: {
258: $len = strlen($this->file->body);
259: $pos = strspn($this->file->body, "\x09\x0A\x0D\x20");
260:
261: while ($pos < $len)
262: {
263: switch ($this->file->body[$pos])
264: {
265: case "\x09":
266: case "\x0A":
267: case "\x0D":
268: case "\x20":
269: $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
270: continue 2;
271:
272: case '<':
273: $pos++;
274: break;
275:
276: default:
277: return 'text/html';
278: }
279:
280: if (substr($this->file->body, $pos, 3) === '!--')
281: {
282: $pos += 3;
283: if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
284: {
285: $pos += 3;
286: }
287: else
288: {
289: return 'text/html';
290: }
291: }
292: elseif (substr($this->file->body, $pos, 1) === '!')
293: {
294: if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
295: {
296: $pos++;
297: }
298: else
299: {
300: return 'text/html';
301: }
302: }
303: elseif (substr($this->file->body, $pos, 1) === '?')
304: {
305: if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
306: {
307: $pos += 2;
308: }
309: else
310: {
311: return 'text/html';
312: }
313: }
314: elseif (substr($this->file->body, $pos, 3) === 'rss'
315: || substr($this->file->body, $pos, 7) === 'rdf:RDF')
316: {
317: return 'application/rss+xml';
318: }
319: elseif (substr($this->file->body, $pos, 4) === 'feed')
320: {
321: return 'application/atom+xml';
322: }
323: else
324: {
325: return 'text/html';
326: }
327: }
328:
329: return 'text/html';
330: }
331: }
332:
333: