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