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 * Please note: This file is automatically generated by a build script. The
9 * full original source is always available from http://simplepie.org/
10 *
11 * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without modification, are
15 * permitted provided that the following conditions are met:
16 *
17 * 	* Redistributions of source code must retain the above copyright notice, this list of
18 * 	  conditions and the following disclaimer.
19 *
20 * 	* Redistributions in binary form must reproduce the above copyright notice, this list
21 * 	  of conditions and the following disclaimer in the documentation and/or other materials
22 * 	  provided with the distribution.
23 *
24 * 	* Neither the name of the SimplePie Team nor the names of its contributors may be used
25 * 	  to endorse or promote products derived from this software without specific prior
26 * 	  written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
29 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
30 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
31 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 *
38 * @package SimplePie
39 * @version 1.3.1
40 * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
41 * @author Ryan Parman
42 * @author Geoffrey Sneddon
43 * @author Ryan McCue
44 * @link http://simplepie.org/ SimplePie
45 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
46 */
47
48/**
49 * SimplePie Name
50 */
51define('SIMPLEPIE_NAME', 'SimplePie');
52
53/**
54 * SimplePie Version
55 */
56define('SIMPLEPIE_VERSION', '1.3.1');
57
58/**
59 * SimplePie Build
60 * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc)
61 */
62define('SIMPLEPIE_BUILD', '20121030175911');
63
64/**
65 * SimplePie Website URL
66 */
67define('SIMPLEPIE_URL', 'http://simplepie.org');
68
69/**
70 * SimplePie Useragent
71 * @see SimplePie::set_useragent()
72 */
73define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
74
75/**
76 * SimplePie Linkback
77 */
78define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>');
79
80/**
81 * No Autodiscovery
82 * @see SimplePie::set_autodiscovery_level()
83 */
84define('SIMPLEPIE_LOCATOR_NONE', 0);
85
86/**
87 * Feed Link Element Autodiscovery
88 * @see SimplePie::set_autodiscovery_level()
89 */
90define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
91
92/**
93 * Local Feed Extension Autodiscovery
94 * @see SimplePie::set_autodiscovery_level()
95 */
96define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
97
98/**
99 * Local Feed Body Autodiscovery
100 * @see SimplePie::set_autodiscovery_level()
101 */
102define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
103
104/**
105 * Remote Feed Extension Autodiscovery
106 * @see SimplePie::set_autodiscovery_level()
107 */
108define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
109
110/**
111 * Remote Feed Body Autodiscovery
112 * @see SimplePie::set_autodiscovery_level()
113 */
114define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
115
116/**
117 * All Feed Autodiscovery
118 * @see SimplePie::set_autodiscovery_level()
119 */
120define('SIMPLEPIE_LOCATOR_ALL', 31);
121
122/**
123 * No known feed type
124 */
125define('SIMPLEPIE_TYPE_NONE', 0);
126
127/**
128 * RSS 0.90
129 */
130define('SIMPLEPIE_TYPE_RSS_090', 1);
131
132/**
133 * RSS 0.91 (Netscape)
134 */
135define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
136
137/**
138 * RSS 0.91 (Userland)
139 */
140define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
141
142/**
143 * RSS 0.91 (both Netscape and Userland)
144 */
145define('SIMPLEPIE_TYPE_RSS_091', 6);
146
147/**
148 * RSS 0.92
149 */
150define('SIMPLEPIE_TYPE_RSS_092', 8);
151
152/**
153 * RSS 0.93
154 */
155define('SIMPLEPIE_TYPE_RSS_093', 16);
156
157/**
158 * RSS 0.94
159 */
160define('SIMPLEPIE_TYPE_RSS_094', 32);
161
162/**
163 * RSS 1.0
164 */
165define('SIMPLEPIE_TYPE_RSS_10', 64);
166
167/**
168 * RSS 2.0
169 */
170define('SIMPLEPIE_TYPE_RSS_20', 128);
171
172/**
173 * RDF-based RSS
174 */
175define('SIMPLEPIE_TYPE_RSS_RDF', 65);
176
177/**
178 * Non-RDF-based RSS (truly intended as syndication format)
179 */
180define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
181
182/**
183 * All RSS
184 */
185define('SIMPLEPIE_TYPE_RSS_ALL', 255);
186
187/**
188 * Atom 0.3
189 */
190define('SIMPLEPIE_TYPE_ATOM_03', 256);
191
192/**
193 * Atom 1.0
194 */
195define('SIMPLEPIE_TYPE_ATOM_10', 512);
196
197/**
198 * All Atom
199 */
200define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
201
202/**
203 * All feed types
204 */
205define('SIMPLEPIE_TYPE_ALL', 1023);
206
207/**
208 * No construct
209 */
210define('SIMPLEPIE_CONSTRUCT_NONE', 0);
211
212/**
213 * Text construct
214 */
215define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
216
217/**
218 * HTML construct
219 */
220define('SIMPLEPIE_CONSTRUCT_HTML', 2);
221
222/**
223 * XHTML construct
224 */
225define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
226
227/**
228 * base64-encoded construct
229 */
230define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
231
232/**
233 * IRI construct
234 */
235define('SIMPLEPIE_CONSTRUCT_IRI', 16);
236
237/**
238 * A construct that might be HTML
239 */
240define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
241
242/**
243 * All constructs
244 */
245define('SIMPLEPIE_CONSTRUCT_ALL', 63);
246
247/**
248 * Don't change case
249 */
250define('SIMPLEPIE_SAME_CASE', 1);
251
252/**
253 * Change to lowercase
254 */
255define('SIMPLEPIE_LOWERCASE', 2);
256
257/**
258 * Change to uppercase
259 */
260define('SIMPLEPIE_UPPERCASE', 4);
261
262/**
263 * PCRE for HTML attributes
264 */
265define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*');
266
267/**
268 * PCRE for XML attributes
269 */
270define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
271
272/**
273 * XML Namespace
274 */
275define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
276
277/**
278 * Atom 1.0 Namespace
279 */
280define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
281
282/**
283 * Atom 0.3 Namespace
284 */
285define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
286
287/**
288 * RDF Namespace
289 */
290define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
291
292/**
293 * RSS 0.90 Namespace
294 */
295define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
296
297/**
298 * RSS 1.0 Namespace
299 */
300define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
301
302/**
303 * RSS 1.0 Content Module Namespace
304 */
305define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/');
306
307/**
308 * RSS 2.0 Namespace
309 * (Stupid, I know, but I'm certain it will confuse people less with support.)
310 */
311define('SIMPLEPIE_NAMESPACE_RSS_20', '');
312
313/**
314 * DC 1.0 Namespace
315 */
316define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
317
318/**
319 * DC 1.1 Namespace
320 */
321define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
322
323/**
324 * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
325 */
326define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
327
328/**
329 * GeoRSS Namespace
330 */
331define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
332
333/**
334 * Media RSS Namespace
335 */
336define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/');
337
338/**
339 * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec.
340 */
341define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss');
342
343/**
344 * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5.
345 */
346define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss');
347
348/**
349 * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace.
350 */
351define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/');
352
353/**
354 * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace.
355 */
356define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss');
357
358/**
359 * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL.
360 */
361define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/');
362
363/**
364 * iTunes RSS Namespace
365 */
366define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
367
368/**
369 * XHTML Namespace
370 */
371define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
372
373/**
374 * IANA Link Relations Registry
375 */
376define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
377
378/**
379 * No file source
380 */
381define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
382
383/**
384 * Remote file source
385 */
386define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
387
388/**
389 * Local file source
390 */
391define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
392
393/**
394 * fsockopen() file source
395 */
396define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
397
398/**
399 * cURL file source
400 */
401define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
402
403/**
404 * file_get_contents() file source
405 */
406define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
407
408/**
409 * SimplePie
410 *
411 * @package SimplePie
412 * @subpackage API
413 */
414class SimplePie
415{
416	/**
417	 * @var array Raw data
418	 * @access private
419	 */
420	public $data = array();
421
422	/**
423	 * @var mixed Error string
424	 * @access private
425	 */
426	public $error;
427
428	/**
429	 * @var object Instance of SimplePie_Sanitize (or other class)
430	 * @see SimplePie::set_sanitize_class()
431	 * @access private
432	 */
433	public $sanitize;
434
435	/**
436	 * @var string SimplePie Useragent
437	 * @see SimplePie::set_useragent()
438	 * @access private
439	 */
440	public $useragent = SIMPLEPIE_USERAGENT;
441
442	/**
443	 * @var string Feed URL
444	 * @see SimplePie::set_feed_url()
445	 * @access private
446	 */
447	public $feed_url;
448
449	/**
450	 * @var object Instance of SimplePie_File to use as a feed
451	 * @see SimplePie::set_file()
452	 * @access private
453	 */
454	public $file;
455
456	/**
457	 * @var string Raw feed data
458	 * @see SimplePie::set_raw_data()
459	 * @access private
460	 */
461	public $raw_data;
462
463	/**
464	 * @var int Timeout for fetching remote files
465	 * @see SimplePie::set_timeout()
466	 * @access private
467	 */
468	public $timeout = 10;
469
470	/**
471	 * @var bool Forces fsockopen() to be used for remote files instead
472	 * of cURL, even if a new enough version is installed
473	 * @see SimplePie::force_fsockopen()
474	 * @access private
475	 */
476	public $force_fsockopen = false;
477
478	/**
479	 * @var bool Force the given data/URL to be treated as a feed no matter what
480	 * it appears like
481	 * @see SimplePie::force_feed()
482	 * @access private
483	 */
484	public $force_feed = false;
485
486	/**
487	 * @var bool Enable/Disable Caching
488	 * @see SimplePie::enable_cache()
489	 * @access private
490	 */
491	public $cache = true;
492
493	/**
494	 * @var int Cache duration (in seconds)
495	 * @see SimplePie::set_cache_duration()
496	 * @access private
497	 */
498	public $cache_duration = 3600;
499
500	/**
501	 * @var int Auto-discovery cache duration (in seconds)
502	 * @see SimplePie::set_autodiscovery_cache_duration()
503	 * @access private
504	 */
505	public $autodiscovery_cache_duration = 604800; // 7 Days.
506
507	/**
508	 * @var string Cache location (relative to executing script)
509	 * @see SimplePie::set_cache_location()
510	 * @access private
511	 */
512	public $cache_location = './cache';
513
514	/**
515	 * @var string Function that creates the cache filename
516	 * @see SimplePie::set_cache_name_function()
517	 * @access private
518	 */
519	public $cache_name_function = 'md5';
520
521	/**
522	 * @var bool Reorder feed by date descending
523	 * @see SimplePie::enable_order_by_date()
524	 * @access private
525	 */
526	public $order_by_date = true;
527
528	/**
529	 * @var mixed Force input encoding to be set to the follow value
530	 * (false, or anything type-cast to false, disables this feature)
531	 * @see SimplePie::set_input_encoding()
532	 * @access private
533	 */
534	public $input_encoding = false;
535
536	/**
537	 * @var int Feed Autodiscovery Level
538	 * @see SimplePie::set_autodiscovery_level()
539	 * @access private
540	 */
541	public $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
542
543	/**
544	 * Class registry object
545	 *
546	 * @var SimplePie_Registry
547	 */
548	public $registry;
549
550	/**
551	 * @var int Maximum number of feeds to check with autodiscovery
552	 * @see SimplePie::set_max_checked_feeds()
553	 * @access private
554	 */
555	public $max_checked_feeds = 10;
556
557	/**
558	 * @var array All the feeds found during the autodiscovery process
559	 * @see SimplePie::get_all_discovered_feeds()
560	 * @access private
561	 */
562	public $all_discovered_feeds = array();
563
564	/**
565	 * @var string Web-accessible path to the handler_image.php file.
566	 * @see SimplePie::set_image_handler()
567	 * @access private
568	 */
569	public $image_handler = '';
570
571	/**
572	 * @var array Stores the URLs when multiple feeds are being initialized.
573	 * @see SimplePie::set_feed_url()
574	 * @access private
575	 */
576	public $multifeed_url = array();
577
578	/**
579	 * @var array Stores SimplePie objects when multiple feeds initialized.
580	 * @access private
581	 */
582	public $multifeed_objects = array();
583
584	/**
585	 * @var array Stores the get_object_vars() array for use with multifeeds.
586	 * @see SimplePie::set_feed_url()
587	 * @access private
588	 */
589	public $config_settings = null;
590
591	/**
592	 * @var integer Stores the number of items to return per-feed with multifeeds.
593	 * @see SimplePie::set_item_limit()
594	 * @access private
595	 */
596	public $item_limit = 0;
597
598	/**
599	 * @var array Stores the default attributes to be stripped by strip_attributes().
600	 * @see SimplePie::strip_attributes()
601	 * @access private
602	 */
603	public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
604
605	/**
606	 * @var array Stores the default tags to be stripped by strip_htmltags().
607	 * @see SimplePie::strip_htmltags()
608	 * @access private
609	 */
610	public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
611
612	/**
613	 * The SimplePie class contains feed level data and options
614	 *
615	 * To use SimplePie, create the SimplePie object with no parameters. You can
616	 * then set configuration options using the provided methods. After setting
617	 * them, you must initialise the feed using $feed->init(). At that point the
618	 * object's methods and properties will be available to you.
619	 *
620	 * Previously, it was possible to pass in the feed URL along with cache
621	 * options directly into the constructor. This has been removed as of 1.3 as
622	 * it caused a lot of confusion.
623	 *
624	 * @since 1.0 Preview Release
625	 */
626	public function __construct()
627	{
628		if (version_compare(PHP_VERSION, '5.2', '<'))
629		{
630			trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.');
631			die();
632		}
633
634		// Other objects, instances created here so we can set options on them
635		$this->sanitize = new SimplePie_Sanitize();
636		$this->registry = new SimplePie_Registry();
637
638		if (func_num_args() > 0)
639		{
640			$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
641			trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.', $level);
642
643			$args = func_get_args();
644			switch (count($args)) {
645				case 3:
646					$this->set_cache_duration($args[2]);
647				case 2:
648					$this->set_cache_location($args[1]);
649				case 1:
650					$this->set_feed_url($args[0]);
651					$this->init();
652			}
653		}
654	}
655
656	/**
657	 * Used for converting object to a string
658	 */
659	public function __toString()
660	{
661		return md5(serialize($this->data));
662	}
663
664	/**
665	 * Remove items that link back to this before destroying this object
666	 */
667	public function __destruct()
668	{
669		if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
670		{
671			if (!empty($this->data['items']))
672			{
673				foreach ($this->data['items'] as $item)
674				{
675					$item->__destruct();
676				}
677				unset($item, $this->data['items']);
678			}
679			if (!empty($this->data['ordered_items']))
680			{
681				foreach ($this->data['ordered_items'] as $item)
682				{
683					$item->__destruct();
684				}
685				unset($item, $this->data['ordered_items']);
686			}
687		}
688	}
689
690	/**
691	 * Force the given data/URL to be treated as a feed
692	 *
693	 * This tells SimplePie to ignore the content-type provided by the server.
694	 * Be careful when using this option, as it will also disable autodiscovery.
695	 *
696	 * @since 1.1
697	 * @param bool $enable Force the given data/URL to be treated as a feed
698	 */
699	public function force_feed($enable = false)
700	{
701		$this->force_feed = (bool) $enable;
702	}
703
704	/**
705	 * Set the URL of the feed you want to parse
706	 *
707	 * This allows you to enter the URL of the feed you want to parse, or the
708	 * website you want to try to use auto-discovery on. This takes priority
709	 * over any set raw data.
710	 *
711	 * You can set multiple feeds to mash together by passing an array instead
712	 * of a string for the $url. Remember that with each additional feed comes
713	 * additional processing and resources.
714	 *
715	 * @since 1.0 Preview Release
716	 * @see set_raw_data()
717	 * @param string|array $url This is the URL (or array of URLs) that you want to parse.
718	 */
719	public function set_feed_url($url)
720	{
721		$this->multifeed_url = array();
722		if (is_array($url))
723		{
724			foreach ($url as $value)
725			{
726				$this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1));
727			}
728		}
729		else
730		{
731			$this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1));
732		}
733	}
734
735	/**
736	 * Set an instance of {@see SimplePie_File} to use as a feed
737	 *
738	 * @param SimplePie_File &$file
739	 * @return bool True on success, false on failure
740	 */
741	public function set_file(&$file)
742	{
743		if ($file instanceof SimplePie_File)
744		{
745			$this->feed_url = $file->url;
746			$this->file =& $file;
747			return true;
748		}
749		return false;
750	}
751
752	/**
753	 * Set the raw XML data to parse
754	 *
755	 * Allows you to use a string of RSS/Atom data instead of a remote feed.
756	 *
757	 * If you have a feed available as a string in PHP, you can tell SimplePie
758	 * to parse that data string instead of a remote feed. Any set feed URL
759	 * takes precedence.
760	 *
761	 * @since 1.0 Beta 3
762	 * @param string $data RSS or Atom data as a string.
763	 * @see set_feed_url()
764	 */
765	public function set_raw_data($data)
766	{
767		$this->raw_data = $data;
768	}
769
770	/**
771	 * Set the the default timeout for fetching remote feeds
772	 *
773	 * This allows you to change the maximum time the feed's server to respond
774	 * and send the feed back.
775	 *
776	 * @since 1.0 Beta 3
777	 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
778	 */
779	public function set_timeout($timeout = 10)
780	{
781		$this->timeout = (int) $timeout;
782	}
783
784	/**
785	 * Force SimplePie to use fsockopen() instead of cURL
786	 *
787	 * @since 1.0 Beta 3
788	 * @param bool $enable Force fsockopen() to be used
789	 */
790	public function force_fsockopen($enable = false)
791	{
792		$this->force_fsockopen = (bool) $enable;
793	}
794
795	/**
796	 * Enable/disable caching in SimplePie.
797	 *
798	 * This option allows you to disable caching all-together in SimplePie.
799	 * However, disabling the cache can lead to longer load times.
800	 *
801	 * @since 1.0 Preview Release
802	 * @param bool $enable Enable caching
803	 */
804	public function enable_cache($enable = true)
805	{
806		$this->cache = (bool) $enable;
807	}
808
809	/**
810	 * Set the length of time (in seconds) that the contents of a feed will be
811	 * cached
812	 *
813	 * @param int $seconds The feed content cache duration
814	 */
815	public function set_cache_duration($seconds = 3600)
816	{
817		$this->cache_duration = (int) $seconds;
818	}
819
820	/**
821	 * Set the length of time (in seconds) that the autodiscovered feed URL will
822	 * be cached
823	 *
824	 * @param int $seconds The autodiscovered feed URL cache duration.
825	 */
826	public function set_autodiscovery_cache_duration($seconds = 604800)
827	{
828		$this->autodiscovery_cache_duration = (int) $seconds;
829	}
830
831	/**
832	 * Set the file system location where the cached files should be stored
833	 *
834	 * @param string $location The file system location.
835	 */
836	public function set_cache_location($location = './cache')
837	{
838		$this->cache_location = (string) $location;
839	}
840
841	/**
842	 * Set whether feed items should be sorted into reverse chronological order
843	 *
844	 * @param bool $enable Sort as reverse chronological order.
845	 */
846	public function enable_order_by_date($enable = true)
847	{
848		$this->order_by_date = (bool) $enable;
849	}
850
851	/**
852	 * Set the character encoding used to parse the feed
853	 *
854	 * This overrides the encoding reported by the feed, however it will fall
855	 * back to the normal encoding detection if the override fails
856	 *
857	 * @param string $encoding Character encoding
858	 */
859	public function set_input_encoding($encoding = false)
860	{
861		if ($encoding)
862		{
863			$this->input_encoding = (string) $encoding;
864		}
865		else
866		{
867			$this->input_encoding = false;
868		}
869	}
870
871	/**
872	 * Set how much feed autodiscovery to do
873	 *
874	 * @see SIMPLEPIE_LOCATOR_NONE
875	 * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
876	 * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
877	 * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
878	 * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
879	 * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
880	 * @see SIMPLEPIE_LOCATOR_ALL
881	 * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator)
882	 */
883	public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
884	{
885		$this->autodiscovery = (int) $level;
886	}
887
888	/**
889	 * Get the class registry
890	 *
891	 * Use this to override SimplePie's default classes
892	 * @see SimplePie_Registry
893	 * @return SimplePie_Registry
894	 */
895	public function &get_registry()
896	{
897		return $this->registry;
898	}
899
900	/**#@+
901	 * Useful when you are overloading or extending SimplePie's default classes.
902	 *
903	 * @deprecated Use {@see get_registry()} instead
904	 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
905	 * @param string $class Name of custom class
906	 * @return boolean True on success, false otherwise
907	 */
908	/**
909	 * Set which class SimplePie uses for caching
910	 */
911	public function set_cache_class($class = 'SimplePie_Cache')
912	{
913		return $this->registry->register('Cache', $class, true);
914	}
915
916	/**
917	 * Set which class SimplePie uses for auto-discovery
918	 */
919	public function set_locator_class($class = 'SimplePie_Locator')
920	{
921		return $this->registry->register('Locator', $class, true);
922	}
923
924	/**
925	 * Set which class SimplePie uses for XML parsing
926	 */
927	public function set_parser_class($class = 'SimplePie_Parser')
928	{
929		return $this->registry->register('Parser', $class, true);
930	}
931
932	/**
933	 * Set which class SimplePie uses for remote file fetching
934	 */
935	public function set_file_class($class = 'SimplePie_File')
936	{
937		return $this->registry->register('File', $class, true);
938	}
939
940	/**
941	 * Set which class SimplePie uses for data sanitization
942	 */
943	public function set_sanitize_class($class = 'SimplePie_Sanitize')
944	{
945		return $this->registry->register('Sanitize', $class, true);
946	}
947
948	/**
949	 * Set which class SimplePie uses for handling feed items
950	 */
951	public function set_item_class($class = 'SimplePie_Item')
952	{
953		return $this->registry->register('Item', $class, true);
954	}
955
956	/**
957	 * Set which class SimplePie uses for handling author data
958	 */
959	public function set_author_class($class = 'SimplePie_Author')
960	{
961		return $this->registry->register('Author', $class, true);
962	}
963
964	/**
965	 * Set which class SimplePie uses for handling category data
966	 */
967	public function set_category_class($class = 'SimplePie_Category')
968	{
969		return $this->registry->register('Category', $class, true);
970	}
971
972	/**
973	 * Set which class SimplePie uses for feed enclosures
974	 */
975	public function set_enclosure_class($class = 'SimplePie_Enclosure')
976	{
977		return $this->registry->register('Enclosure', $class, true);
978	}
979
980	/**
981	 * Set which class SimplePie uses for `<media:text>` captions
982	 */
983	public function set_caption_class($class = 'SimplePie_Caption')
984	{
985		return $this->registry->register('Caption', $class, true);
986	}
987
988	/**
989	 * Set which class SimplePie uses for `<media:copyright>`
990	 */
991	public function set_copyright_class($class = 'SimplePie_Copyright')
992	{
993		return $this->registry->register('Copyright', $class, true);
994	}
995
996	/**
997	 * Set which class SimplePie uses for `<media:credit>`
998	 */
999	public function set_credit_class($class = 'SimplePie_Credit')
1000	{
1001		return $this->registry->register('Credit', $class, true);
1002	}
1003
1004	/**
1005	 * Set which class SimplePie uses for `<media:rating>`
1006	 */
1007	public function set_rating_class($class = 'SimplePie_Rating')
1008	{
1009		return $this->registry->register('Rating', $class, true);
1010	}
1011
1012	/**
1013	 * Set which class SimplePie uses for `<media:restriction>`
1014	 */
1015	public function set_restriction_class($class = 'SimplePie_Restriction')
1016	{
1017		return $this->registry->register('Restriction', $class, true);
1018	}
1019
1020	/**
1021	 * Set which class SimplePie uses for content-type sniffing
1022	 */
1023	public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
1024	{
1025		return $this->registry->register('Content_Type_Sniffer', $class, true);
1026	}
1027
1028	/**
1029	 * Set which class SimplePie uses item sources
1030	 */
1031	public function set_source_class($class = 'SimplePie_Source')
1032	{
1033		return $this->registry->register('Source', $class, true);
1034	}
1035	/**#@-*/
1036
1037	/**
1038	 * Set the user agent string
1039	 *
1040	 * @param string $ua New user agent string.
1041	 */
1042	public function set_useragent($ua = SIMPLEPIE_USERAGENT)
1043	{
1044		$this->useragent = (string) $ua;
1045	}
1046
1047	/**
1048	 * Set callback function to create cache filename with
1049	 *
1050	 * @param mixed $function Callback function
1051	 */
1052	public function set_cache_name_function($function = 'md5')
1053	{
1054		if (is_callable($function))
1055		{
1056			$this->cache_name_function = $function;
1057		}
1058	}
1059
1060	/**
1061	 * Set options to make SP as fast as possible
1062	 *
1063	 * Forgoes a substantial amount of data sanitization in favor of speed. This
1064	 * turns SimplePie into a dumb parser of feeds.
1065	 *
1066	 * @param bool $set Whether to set them or not
1067	 */
1068	public function set_stupidly_fast($set = false)
1069	{
1070		if ($set)
1071		{
1072			$this->enable_order_by_date(false);
1073			$this->remove_div(false);
1074			$this->strip_comments(false);
1075			$this->strip_htmltags(false);
1076			$this->strip_attributes(false);
1077			$this->set_image_handler(false);
1078		}
1079	}
1080
1081	/**
1082	 * Set maximum number of feeds to check with autodiscovery
1083	 *
1084	 * @param int $max Maximum number of feeds to check
1085	 */
1086	public function set_max_checked_feeds($max = 10)
1087	{
1088		$this->max_checked_feeds = (int) $max;
1089	}
1090
1091	public function remove_div($enable = true)
1092	{
1093		$this->sanitize->remove_div($enable);
1094	}
1095
1096	public function strip_htmltags($tags = '', $encode = null)
1097	{
1098		if ($tags === '')
1099		{
1100			$tags = $this->strip_htmltags;
1101		}
1102		$this->sanitize->strip_htmltags($tags);
1103		if ($encode !== null)
1104		{
1105			$this->sanitize->encode_instead_of_strip($tags);
1106		}
1107	}
1108
1109	public function encode_instead_of_strip($enable = true)
1110	{
1111		$this->sanitize->encode_instead_of_strip($enable);
1112	}
1113
1114	public function strip_attributes($attribs = '')
1115	{
1116		if ($attribs === '')
1117		{
1118			$attribs = $this->strip_attributes;
1119		}
1120		$this->sanitize->strip_attributes($attribs);
1121	}
1122
1123	/**
1124	 * Set the output encoding
1125	 *
1126	 * Allows you to override SimplePie's output to match that of your webpage.
1127	 * This is useful for times when your webpages are not being served as
1128	 * UTF-8.  This setting will be obeyed by {@see handle_content_type()}, and
1129	 * is similar to {@see set_input_encoding()}.
1130	 *
1131	 * It should be noted, however, that not all character encodings can support
1132	 * all characters.  If your page is being served as ISO-8859-1 and you try
1133	 * to display a Japanese feed, you'll likely see garbled characters.
1134	 * Because of this, it is highly recommended to ensure that your webpages
1135	 * are served as UTF-8.
1136	 *
1137	 * The number of supported character encodings depends on whether your web
1138	 * host supports {@link http://php.net/mbstring mbstring},
1139	 * {@link http://php.net/iconv iconv}, or both. See
1140	 * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for
1141	 * more information.
1142	 *
1143	 * @param string $encoding
1144	 */
1145	public function set_output_encoding($encoding = 'UTF-8')
1146	{
1147		$this->sanitize->set_output_encoding($encoding);
1148	}
1149
1150	public function strip_comments($strip = false)
1151	{
1152		$this->sanitize->strip_comments($strip);
1153	}
1154
1155	/**
1156	 * Set element/attribute key/value pairs of HTML attributes
1157	 * containing URLs that need to be resolved relative to the feed
1158	 *
1159	 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
1160	 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
1161	 * |q|@cite
1162	 *
1163	 * @since 1.0
1164	 * @param array|null $element_attribute Element/attribute key/value pairs, null for default
1165	 */
1166	public function set_url_replacements($element_attribute = null)
1167	{
1168		$this->sanitize->set_url_replacements($element_attribute);
1169	}
1170
1171	/**
1172	 * Set the handler to enable the display of cached images.
1173	 *
1174	 * @param str $page Web-accessible path to the handler_image.php file.
1175	 * @param str $qs The query string that the value should be passed to.
1176	 */
1177	public function set_image_handler($page = false, $qs = 'i')
1178	{
1179		if ($page !== false)
1180		{
1181			$this->sanitize->set_image_handler($page . '?' . $qs . '=');
1182		}
1183		else
1184		{
1185			$this->image_handler = '';
1186		}
1187	}
1188
1189	/**
1190	 * Set the limit for items returned per-feed with multifeeds
1191	 *
1192	 * @param integer $limit The maximum number of items to return.
1193	 */
1194	public function set_item_limit($limit = 0)
1195	{
1196		$this->item_limit = (int) $limit;
1197	}
1198
1199	/**
1200	 * Initialize the feed object
1201	 *
1202	 * This is what makes everything happen.  Period.  This is where all of the
1203	 * configuration options get processed, feeds are fetched, cached, and
1204	 * parsed, and all of that other good stuff.
1205	 *
1206	 * @return boolean True if successful, false otherwise
1207	 */
1208	public function init()
1209	{
1210		// Check absolute bare minimum requirements.
1211		if (!extension_loaded('xml') || !extension_loaded('pcre'))
1212		{
1213			return false;
1214		}
1215		// Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader.
1216		elseif (!extension_loaded('xmlreader'))
1217		{
1218			static $xml_is_sane = null;
1219			if ($xml_is_sane === null)
1220			{
1221				$parser_check = xml_parser_create();
1222				xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1223				xml_parser_free($parser_check);
1224				$xml_is_sane = isset($values[0]['value']);
1225			}
1226			if (!$xml_is_sane)
1227			{
1228				return false;
1229			}
1230		}
1231
1232		if (method_exists($this->sanitize, 'set_registry'))
1233		{
1234			$this->sanitize->set_registry($this->registry);
1235		}
1236
1237		// Pass whatever was set with config options over to the sanitizer.
1238		// Pass the classes in for legacy support; new classes should use the registry instead
1239		$this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache'));
1240		$this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen);
1241
1242		if (!empty($this->multifeed_url))
1243		{
1244			$i = 0;
1245			$success = 0;
1246			$this->multifeed_objects = array();
1247			$this->error = array();
1248			foreach ($this->multifeed_url as $url)
1249			{
1250				$this->multifeed_objects[$i] = clone $this;
1251				$this->multifeed_objects[$i]->set_feed_url($url);
1252				$single_success = $this->multifeed_objects[$i]->init();
1253				$success |= $single_success;
1254				if (!$single_success)
1255				{
1256					$this->error[$i] = $this->multifeed_objects[$i]->error();
1257				}
1258				$i++;
1259			}
1260			return (bool) $success;
1261		}
1262		elseif ($this->feed_url === null && $this->raw_data === null)
1263		{
1264			return false;
1265		}
1266
1267		$this->error = null;
1268		$this->data = array();
1269		$this->multifeed_objects = array();
1270		$cache = false;
1271
1272		if ($this->feed_url !== null)
1273		{
1274			$parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url));
1275
1276			// Decide whether to enable caching
1277			if ($this->cache && $parsed_feed_url['scheme'] !== '')
1278			{
1279				$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'));
1280			}
1281
1282			// Fetch the data via SimplePie_File into $this->raw_data
1283			if (($fetched = $this->fetch_data($cache)) === true)
1284			{
1285				return true;
1286			}
1287			elseif ($fetched === false) {
1288				return false;
1289			}
1290
1291			list($headers, $sniffed) = $fetched;
1292		}
1293
1294		// Set up array of possible encodings
1295		$encodings = array();
1296
1297		// First check to see if input has been overridden.
1298		if ($this->input_encoding !== false)
1299		{
1300			$encodings[] = $this->input_encoding;
1301		}
1302
1303		$application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
1304		$text_types = array('text/xml', 'text/xml-external-parsed-entity');
1305
1306		// RFC 3023 (only applies to sniffed content)
1307		if (isset($sniffed))
1308		{
1309			if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
1310			{
1311				if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1312				{
1313					$encodings[] = strtoupper($charset[1]);
1314				}
1315				$encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1316				$encodings[] = 'UTF-8';
1317			}
1318			elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
1319			{
1320				if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1321				{
1322					$encodings[] = $charset[1];
1323				}
1324				$encodings[] = 'US-ASCII';
1325			}
1326			// Text MIME-type default
1327			elseif (substr($sniffed, 0, 5) === 'text/')
1328			{
1329				$encodings[] = 'US-ASCII';
1330			}
1331		}
1332
1333		// Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
1334		$encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1335		$encodings[] = 'UTF-8';
1336		$encodings[] = 'ISO-8859-1';
1337
1338		// There's no point in trying an encoding twice
1339		$encodings = array_unique($encodings);
1340
1341		// Loop through each possible encoding, till we return something, or run out of possibilities
1342		foreach ($encodings as $encoding)
1343		{
1344			// Change the encoding to UTF-8 (as we always use UTF-8 internally)
1345			if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8')))
1346			{
1347				// Create new parser
1348				$parser = $this->registry->create('Parser');
1349
1350				// If it's parsed fine
1351				if ($parser->parse($utf8_data, 'UTF-8'))
1352				{
1353					$this->data = $parser->get_data();
1354					if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE))
1355					{
1356						$this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed.";
1357						$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1358						return false;
1359					}
1360
1361					if (isset($headers))
1362					{
1363						$this->data['headers'] = $headers;
1364					}
1365					$this->data['build'] = SIMPLEPIE_BUILD;
1366
1367					// Cache the file if caching is enabled
1368					if ($cache && !$cache->save($this))
1369					{
1370						trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1371					}
1372					return true;
1373				}
1374			}
1375		}
1376
1377		if (isset($parser))
1378		{
1379			// We have an error, just set SimplePie_Misc::error to it and quit
1380			$this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column());
1381		}
1382		else
1383		{
1384			$this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.';
1385		}
1386
1387		$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1388
1389		return false;
1390	}
1391
1392	/**
1393	 * Fetch the data via SimplePie_File
1394	 *
1395	 * If the data is already cached, attempt to fetch it from there instead
1396	 * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache
1397	 * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
1398	 */
1399	protected function fetch_data(&$cache)
1400	{
1401		// If it's enabled, use the cache
1402		if ($cache)
1403		{
1404			// Load the Cache
1405			$this->data = $cache->load();
1406			if (!empty($this->data))
1407			{
1408				// If the cache is for an outdated build of SimplePie
1409				if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
1410				{
1411					$cache->unlink();
1412					$this->data = array();
1413				}
1414				// If we've hit a collision just rerun it with caching disabled
1415				elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
1416				{
1417					$cache = false;
1418					$this->data = array();
1419				}
1420				// If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
1421				elseif (isset($this->data['feed_url']))
1422				{
1423					// If the autodiscovery cache is still valid use it.
1424					if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
1425					{
1426						// Do not need to do feed autodiscovery yet.
1427						if ($this->data['feed_url'] !== $this->data['url'])
1428						{
1429							$this->set_feed_url($this->data['feed_url']);
1430							return $this->init();
1431						}
1432
1433						$cache->unlink();
1434						$this->data = array();
1435					}
1436				}
1437				// Check if the cache has been updated
1438				elseif ($cache->mtime() + $this->cache_duration < time())
1439				{
1440					// If we have last-modified and/or etag set
1441					if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
1442					{
1443						$headers = array(
1444							'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
1445						);
1446						if (isset($this->data['headers']['last-modified']))
1447						{
1448							$headers['if-modified-since'] = $this->data['headers']['last-modified'];
1449						}
1450						if (isset($this->data['headers']['etag']))
1451						{
1452							$headers['if-none-match'] = $this->data['headers']['etag'];
1453						}
1454
1455						$file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen));
1456
1457						if ($file->success)
1458						{
1459							if ($file->status_code === 304)
1460							{
1461								$cache->touch();
1462								return true;
1463							}
1464						}
1465						else
1466						{
1467							unset($file);
1468						}
1469					}
1470				}
1471				// If the cache is still valid, just return true
1472				else
1473				{
1474					$this->raw_data = false;
1475					return true;
1476				}
1477			}
1478			// If the cache is empty, delete it
1479			else
1480			{
1481				$cache->unlink();
1482				$this->data = array();
1483			}
1484		}
1485		// If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it.
1486		if (!isset($file))
1487		{
1488			if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url)
1489			{
1490				$file =& $this->file;
1491			}
1492			else
1493			{
1494				$headers = array(
1495					'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
1496				);
1497				$file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen));
1498			}
1499		}
1500		// If the file connection has an error, set SimplePie::error to that and quit
1501		if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
1502		{
1503			$this->error = $file->error;
1504			return !empty($this->data);
1505		}
1506
1507		if (!$this->force_feed)
1508		{
1509			// Check if the supplied URL is a feed, if it isn't, look for it.
1510			$locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds));
1511
1512			if (!$locate->is_feed($file))
1513			{
1514				// We need to unset this so that if SimplePie::set_file() has been called that object is untouched
1515				unset($file);
1516				try
1517				{
1518					if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds)))
1519					{
1520						$this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed.";
1521						$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1522						return false;
1523					}
1524				}
1525				catch (SimplePie_Exception $e)
1526				{
1527					// This is usually because DOMDocument doesn't exist
1528					$this->error = $e->getMessage();
1529					$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()));
1530					return false;
1531				}
1532				if ($cache)
1533				{
1534					$this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
1535					if (!$cache->save($this))
1536					{
1537						trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1538					}
1539					$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
1540				}
1541				$this->feed_url = $file->url;
1542			}
1543			$locate = null;
1544		}
1545
1546		$this->raw_data = $file->body;
1547
1548		$headers = $file->headers;
1549		$sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
1550		$sniffed = $sniffer->get_type();
1551
1552		return array($headers, $sniffed);
1553	}
1554
1555	/**
1556	 * Get the error message for the occured error
1557	 *
1558	 * @return string|array Error message, or array of messages for multifeeds
1559	 */
1560	public function error()
1561	{
1562		return $this->error;
1563	}
1564
1565	/**
1566	 * Get the raw XML
1567	 *
1568	 * This is the same as the old `$feed->enable_xml_dump(true)`, but returns
1569	 * the data instead of printing it.
1570	 *
1571	 * @return string|boolean Raw XML data, false if the cache is used
1572	 */
1573	public function get_raw_data()
1574	{
1575		return $this->raw_data;
1576	}
1577
1578	/**
1579	 * Get the character encoding used for output
1580	 *
1581	 * @since Preview Release
1582	 * @return string
1583	 */
1584	public function get_encoding()
1585	{
1586		return $this->sanitize->output_encoding;
1587	}
1588
1589	/**
1590	 * Send the content-type header with correct encoding
1591	 *
1592	 * This method ensures that the SimplePie-enabled page is being served with
1593	 * the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
1594	 * and character encoding HTTP headers (character encoding determined by the
1595	 * {@see set_output_encoding} config option).
1596	 *
1597	 * This won't work properly if any content or whitespace has already been
1598	 * sent to the browser, because it relies on PHP's
1599	 * {@link http://php.net/header header()} function, and these are the
1600	 * circumstances under which the function works.
1601	 *
1602	 * Because it's setting these settings for the entire page (as is the nature
1603	 * of HTTP headers), this should only be used once per page (again, at the
1604	 * top).
1605	 *
1606	 * @param string $mime MIME type to serve the page as
1607	 */
1608	public function handle_content_type($mime = 'text/html')
1609	{
1610		if (!headers_sent())
1611		{
1612			$header = "Content-type: $mime;";
1613			if ($this->get_encoding())
1614			{
1615				$header .= ' charset=' . $this->get_encoding();
1616			}
1617			else
1618			{
1619				$header .= ' charset=UTF-8';
1620			}
1621			header($header);
1622		}
1623	}
1624
1625	/**
1626	 * Get the type of the feed
1627	 *
1628	 * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against
1629	 * using {@link http://php.net/language.operators.bitwise bitwise operators}
1630	 *
1631	 * @since 0.8 (usage changed to using constants in 1.0)
1632	 * @see SIMPLEPIE_TYPE_NONE Unknown.
1633	 * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90.
1634	 * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
1635	 * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
1636	 * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91.
1637	 * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92.
1638	 * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93.
1639	 * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94.
1640	 * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0.
1641	 * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x.
1642	 * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS.
1643	 * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
1644	 * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS.
1645	 * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3.
1646	 * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0.
1647	 * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom.
1648	 * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type.
1649	 * @return int SIMPLEPIE_TYPE_* constant
1650	 */
1651	public function get_type()
1652	{
1653		if (!isset($this->data['type']))
1654		{
1655			$this->data['type'] = SIMPLEPIE_TYPE_ALL;
1656			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
1657			{
1658				$this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
1659			}
1660			elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
1661			{
1662				$this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
1663			}
1664			elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
1665			{
1666				if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
1667				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
1668				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
1669				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
1670				{
1671					$this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
1672				}
1673				if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
1674				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
1675				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
1676				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
1677				{
1678					$this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
1679				}
1680			}
1681			elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
1682			{
1683				$this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
1684				if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1685				{
1686					switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1687					{
1688						case '0.91':
1689							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
1690							if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1691							{
1692								switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1693								{
1694									case '0':
1695										$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
1696										break;
1697
1698									case '24':
1699										$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
1700										break;
1701								}
1702							}
1703							break;
1704
1705						case '0.92':
1706							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
1707							break;
1708
1709						case '0.93':
1710							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
1711							break;
1712
1713						case '0.94':
1714							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
1715							break;
1716
1717						case '2.0':
1718							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
1719							break;
1720					}
1721				}
1722			}
1723			else
1724			{
1725				$this->data['type'] = SIMPLEPIE_TYPE_NONE;
1726			}
1727		}
1728		return $this->data['type'];
1729	}
1730
1731	/**
1732	 * Get the URL for the feed
1733	 *
1734	 * May or may not be different from the URL passed to {@see set_feed_url()},
1735	 * depending on whether auto-discovery was used.
1736	 *
1737	 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
1738	 * @todo If we have a perm redirect we should return the new URL
1739	 * @todo When we make the above change, let's support <itunes:new-feed-url> as well
1740	 * @todo Also, |atom:link|@rel=self
1741	 * @return string|null
1742	 */
1743	public function subscribe_url()
1744	{
1745		if ($this->feed_url !== null)
1746		{
1747			return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI);
1748		}
1749		else
1750		{
1751			return null;
1752		}
1753	}
1754
1755	/**
1756	 * Get data for an feed-level element
1757	 *
1758	 * This method allows you to get access to ANY element/attribute that is a
1759	 * sub-element of the opening feed tag.
1760	 *
1761	 * The return value is an indexed array of elements matching the given
1762	 * namespace and tag name. Each element has `attribs`, `data` and `child`
1763	 * subkeys. For `attribs` and `child`, these contain namespace subkeys.
1764	 * `attribs` then has one level of associative name => value data (where
1765	 * `value` is a string) after the namespace. `child` has tag-indexed keys
1766	 * after the namespace, each member of which is an indexed array matching
1767	 * this same format.
1768	 *
1769	 * For example:
1770	 * <pre>
1771	 * // This is probably a bad example because we already support
1772	 * // <media:content> natively, but it shows you how to parse through
1773	 * // the nodes.
1774	 * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group');
1775	 * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'];
1776	 * $file = $content[0]['attribs']['']['url'];
1777	 * echo $file;
1778	 * </pre>
1779	 *
1780	 * @since 1.0
1781	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1782	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1783	 * @param string $tag Tag name
1784	 * @return array
1785	 */
1786	public function get_feed_tags($namespace, $tag)
1787	{
1788		$type = $this->get_type();
1789		if ($type & SIMPLEPIE_TYPE_ATOM_10)
1790		{
1791			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
1792			{
1793				return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
1794			}
1795		}
1796		if ($type & SIMPLEPIE_TYPE_ATOM_03)
1797		{
1798			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
1799			{
1800				return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
1801			}
1802		}
1803		if ($type & SIMPLEPIE_TYPE_RSS_RDF)
1804		{
1805			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
1806			{
1807				return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
1808			}
1809		}
1810		if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1811		{
1812			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
1813			{
1814				return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
1815			}
1816		}
1817		return null;
1818	}
1819
1820	/**
1821	 * Get data for an channel-level element
1822	 *
1823	 * This method allows you to get access to ANY element/attribute in the
1824	 * channel/header section of the feed.
1825	 *
1826	 * See {@see SimplePie::get_feed_tags()} for a description of the return value
1827	 *
1828	 * @since 1.0
1829	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1830	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1831	 * @param string $tag Tag name
1832	 * @return array
1833	 */
1834	public function get_channel_tags($namespace, $tag)
1835	{
1836		$type = $this->get_type();
1837		if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
1838		{
1839			if ($return = $this->get_feed_tags($namespace, $tag))
1840			{
1841				return $return;
1842			}
1843		}
1844		if ($type & SIMPLEPIE_TYPE_RSS_10)
1845		{
1846			if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
1847			{
1848				if (isset($channel[0]['child'][$namespace][$tag]))
1849				{
1850					return $channel[0]['child'][$namespace][$tag];
1851				}
1852			}
1853		}
1854		if ($type & SIMPLEPIE_TYPE_RSS_090)
1855		{
1856			if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
1857			{
1858				if (isset($channel[0]['child'][$namespace][$tag]))
1859				{
1860					return $channel[0]['child'][$namespace][$tag];
1861				}
1862			}
1863		}
1864		if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1865		{
1866			if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
1867			{
1868				if (isset($channel[0]['child'][$namespace][$tag]))
1869				{
1870					return $channel[0]['child'][$namespace][$tag];
1871				}
1872			}
1873		}
1874		return null;
1875	}
1876
1877	/**
1878	 * Get data for an channel-level element
1879	 *
1880	 * This method allows you to get access to ANY element/attribute in the
1881	 * image/logo section of the feed.
1882	 *
1883	 * See {@see SimplePie::get_feed_tags()} for a description of the return value
1884	 *
1885	 * @since 1.0
1886	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1887	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1888	 * @param string $tag Tag name
1889	 * @return array
1890	 */
1891	public function get_image_tags($namespace, $tag)
1892	{
1893		$type = $this->get_type();
1894		if ($type & SIMPLEPIE_TYPE_RSS_10)
1895		{
1896			if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
1897			{
1898				if (isset($image[0]['child'][$namespace][$tag]))
1899				{
1900					return $image[0]['child'][$namespace][$tag];
1901				}
1902			}
1903		}
1904		if ($type & SIMPLEPIE_TYPE_RSS_090)
1905		{
1906			if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
1907			{
1908				if (isset($image[0]['child'][$namespace][$tag]))
1909				{
1910					return $image[0]['child'][$namespace][$tag];
1911				}
1912			}
1913		}
1914		if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1915		{
1916			if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
1917			{
1918				if (isset($image[0]['child'][$namespace][$tag]))
1919				{
1920					return $image[0]['child'][$namespace][$tag];
1921				}
1922			}
1923		}
1924		return null;
1925	}
1926
1927	/**
1928	 * Get the base URL value from the feed
1929	 *
1930	 * Uses `<xml:base>` if available, otherwise uses the first link in the
1931	 * feed, or failing that, the URL of the feed itself.
1932	 *
1933	 * @see get_link
1934	 * @see subscribe_url
1935	 *
1936	 * @param array $element
1937	 * @return string
1938	 */
1939	public function get_base($element = array())
1940	{
1941		if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
1942		{
1943			return $element['xml_base'];
1944		}
1945		elseif ($this->get_link() !== null)
1946		{
1947			return $this->get_link();
1948		}
1949		else
1950		{
1951			return $this->subscribe_url();
1952		}
1953	}
1954
1955	/**
1956	 * Sanitize feed data
1957	 *
1958	 * @access private
1959	 * @see SimplePie_Sanitize::sanitize()
1960	 * @param string $data Data to sanitize
1961	 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
1962	 * @param string $base Base URL to resolve URLs against
1963	 * @return string Sanitized data
1964	 */
1965	public function sanitize($data, $type, $base = '')
1966	{
1967		return $this->sanitize->sanitize($data, $type, $base);
1968	}
1969
1970	/**
1971	 * Get the title of the feed
1972	 *
1973	 * Uses `<atom:title>`, `<title>` or `<dc:title>`
1974	 *
1975	 * @since 1.0 (previously called `get_feed_title` since 0.8)
1976	 * @return string|null
1977	 */
1978	public function get_title()
1979	{
1980		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
1981		{
1982			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
1983		}
1984		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
1985		{
1986			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
1987		}
1988		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
1989		{
1990			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1991		}
1992		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
1993		{
1994			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1995		}
1996		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
1997		{
1998			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1999		}
2000		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2001		{
2002			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2003		}
2004		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2005		{
2006			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2007		}
2008		else
2009		{
2010			return null;
2011		}
2012	}
2013
2014	/**
2015	 * Get a category for the feed
2016	 *
2017	 * @since Unknown
2018	 * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
2019	 * @return SimplePie_Category|null
2020	 */
2021	public function get_category($key = 0)
2022	{
2023		$categories = $this->get_categories();
2024		if (isset($categories[$key]))
2025		{
2026			return $categories[$key];
2027		}
2028		else
2029		{
2030			return null;
2031		}
2032	}
2033
2034	/**
2035	 * Get all categories for the feed
2036	 *
2037	 * Uses `<atom:category>`, `<category>` or `<dc:subject>`
2038	 *
2039	 * @since Unknown
2040	 * @return array|null List of {@see SimplePie_Category} objects
2041	 */
2042	public function get_categories()
2043	{
2044		$categories = array();
2045
2046		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
2047		{
2048			$term = null;
2049			$scheme = null;
2050			$label = null;
2051			if (isset($category['attribs']['']['term']))
2052			{
2053				$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
2054			}
2055			if (isset($category['attribs']['']['scheme']))
2056			{
2057				$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2058			}
2059			if (isset($category['attribs']['']['label']))
2060			{
2061				$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2062			}
2063			$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
2064		}
2065		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
2066		{
2067			// This is really the label, but keep this as the term also for BC.
2068			// Label will also work on retrieving because that falls back to term.
2069			$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2070			if (isset($category['attribs']['']['domain']))
2071			{
2072				$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
2073			}
2074			else
2075			{
2076				$scheme = null;
2077			}
2078			$categories[] = $this->registry->create('Category', array($term, $scheme, null));
2079		}
2080		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
2081		{
2082			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2083		}
2084		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
2085		{
2086			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2087		}
2088
2089		if (!empty($categories))
2090		{
2091			return array_unique($categories);
2092		}
2093		else
2094		{
2095			return null;
2096		}
2097	}
2098
2099	/**
2100	 * Get an author for the feed
2101	 *
2102	 * @since 1.1
2103	 * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
2104	 * @return SimplePie_Author|null
2105	 */
2106	public function get_author($key = 0)
2107	{
2108		$authors = $this->get_authors();
2109		if (isset($authors[$key]))
2110		{
2111			return $authors[$key];
2112		}
2113		else
2114		{
2115			return null;
2116		}
2117	}
2118
2119	/**
2120	 * Get all authors for the feed
2121	 *
2122	 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
2123	 *
2124	 * @since 1.1
2125	 * @return array|null List of {@see SimplePie_Author} objects
2126	 */
2127	public function get_authors()
2128	{
2129		$authors = array();
2130		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
2131		{
2132			$name = null;
2133			$uri = null;
2134			$email = null;
2135			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2136			{
2137				$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2138			}
2139			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2140			{
2141				$uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2142			}
2143			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2144			{
2145				$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2146			}
2147			if ($name !== null || $email !== null || $uri !== null)
2148			{
2149				$authors[] = $this->registry->create('Author', array($name, $uri, $email));
2150			}
2151		}
2152		if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
2153		{
2154			$name = null;
2155			$url = null;
2156			$email = null;
2157			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2158			{
2159				$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2160			}
2161			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2162			{
2163				$url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2164			}
2165			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2166			{
2167				$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2168			}
2169			if ($name !== null || $email !== null || $url !== null)
2170			{
2171				$authors[] = $this->registry->create('Author', array($name, $url, $email));
2172			}
2173		}
2174		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
2175		{
2176			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2177		}
2178		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
2179		{
2180			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2181		}
2182		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
2183		{
2184			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2185		}
2186
2187		if (!empty($authors))
2188		{
2189			return array_unique($authors);
2190		}
2191		else
2192		{
2193			return null;
2194		}
2195	}
2196
2197	/**
2198	 * Get a contributor for the feed
2199	 *
2200	 * @since 1.1
2201	 * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
2202	 * @return SimplePie_Author|null
2203	 */
2204	public function get_contributor($key = 0)
2205	{
2206		$contributors = $this->get_contributors();
2207		if (isset($contributors[$key]))
2208		{
2209			return $contributors[$key];
2210		}
2211		else
2212		{
2213			return null;
2214		}
2215	}
2216
2217	/**
2218	 * Get all contributors for the feed
2219	 *
2220	 * Uses `<atom:contributor>`
2221	 *
2222	 * @since 1.1
2223	 * @return array|null List of {@see SimplePie_Author} objects
2224	 */
2225	public function get_contributors()
2226	{
2227		$contributors = array();
2228		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
2229		{
2230			$name = null;
2231			$uri = null;
2232			$email = null;
2233			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2234			{
2235				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2236			}
2237			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2238			{
2239				$uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2240			}
2241			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2242			{
2243				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2244			}
2245			if ($name !== null || $email !== null || $uri !== null)
2246			{
2247				$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
2248			}
2249		}
2250		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
2251		{
2252			$name = null;
2253			$url = null;
2254			$email = null;
2255			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2256			{
2257				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2258			}
2259			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2260			{
2261				$url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2262			}
2263			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2264			{
2265				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2266			}
2267			if ($name !== null || $email !== null || $url !== null)
2268			{
2269				$contributors[] = $this->registry->create('Author', array($name, $url, $email));
2270			}
2271		}
2272
2273		if (!empty($contributors))
2274		{
2275			return array_unique($contributors);
2276		}
2277		else
2278		{
2279			return null;
2280		}
2281	}
2282
2283	/**
2284	 * Get a single link for the feed
2285	 *
2286	 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2287	 * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
2288	 * @param string $rel The relationship of the link to return
2289	 * @return string|null Link URL
2290	 */
2291	public function get_link($key = 0, $rel = 'alternate')
2292	{
2293		$links = $this->get_links($rel);
2294		if (isset($links[$key]))
2295		{
2296			return $links[$key];
2297		}
2298		else
2299		{
2300			return null;
2301		}
2302	}
2303
2304	/**
2305	 * Get the permalink for the item
2306	 *
2307	 * Returns the first link available with a relationship of "alternate".
2308	 * Identical to {@see get_link()} with key 0
2309	 *
2310	 * @see get_link
2311	 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2312	 * @internal Added for parity between the parent-level and the item/entry-level.
2313	 * @return string|null Link URL
2314	 */
2315	public function get_permalink()
2316	{
2317		return $this->get_link(0);
2318	}
2319
2320	/**
2321	 * Get all links for the feed
2322	 *
2323	 * Uses `<atom:link>` or `<link>`
2324	 *
2325	 * @since Beta 2
2326	 * @param string $rel The relationship of links to return
2327	 * @return array|null Links found for the feed (strings)
2328	 */
2329	public function get_links($rel = 'alternate')
2330	{
2331		if (!isset($this->data['links']))
2332		{
2333			$this->data['links'] = array();
2334			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
2335			{
2336				foreach ($links as $link)
2337				{
2338					if (isset($link['attribs']['']['href']))
2339					{
2340						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2341						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2342					}
2343				}
2344			}
2345			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
2346			{
2347				foreach ($links as $link)
2348				{
2349					if (isset($link['attribs']['']['href']))
2350					{
2351						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2352						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2353
2354					}
2355				}
2356			}
2357			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2358			{
2359				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2360			}
2361			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2362			{
2363				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2364			}
2365			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2366			{
2367				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2368			}
2369
2370			$keys = array_keys($this->data['links']);
2371			foreach ($keys as $key)
2372			{
2373				if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
2374				{
2375					if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
2376					{
2377						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
2378						$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
2379					}
2380					else
2381					{
2382						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
2383					}
2384				}
2385				elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
2386				{
2387					$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
2388				}
2389				$this->data['links'][$key] = array_unique($this->data['links'][$key]);
2390			}
2391		}
2392
2393		if (isset($this->data['links'][$rel]))
2394		{
2395			return $this->data['links'][$rel];
2396		}
2397		else
2398		{
2399			return null;
2400		}
2401	}
2402
2403	public function get_all_discovered_feeds()
2404	{
2405		return $this->all_discovered_feeds;
2406	}
2407
2408	/**
2409	 * Get the content for the item
2410	 *
2411	 * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`,
2412	 * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>`
2413	 *
2414	 * @since 1.0 (previously called `get_feed_description()` since 0.8)
2415	 * @return string|null
2416	 */
2417	public function get_description()
2418	{
2419		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
2420		{
2421			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2422		}
2423		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
2424		{
2425			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2426		}
2427		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
2428		{
2429			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2430		}
2431		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
2432		{
2433			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2434		}
2435		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
2436		{
2437			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2438		}
2439		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
2440		{
2441			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2442		}
2443		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
2444		{
2445			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2446		}
2447		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
2448		{
2449			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2450		}
2451		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
2452		{
2453			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2454		}
2455		else
2456		{
2457			return null;
2458		}
2459	}
2460
2461	/**
2462	 * Get the copyright info for the feed
2463	 *
2464	 * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>`
2465	 *
2466	 * @since 1.0 (previously called `get_feed_copyright()` since 0.8)
2467	 * @return string|null
2468	 */
2469	public function get_copyright()
2470	{
2471		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
2472		{
2473			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2474		}
2475		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
2476		{
2477			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2478		}
2479		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
2480		{
2481			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2482		}
2483		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
2484		{
2485			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2486		}
2487		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
2488		{
2489			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2490		}
2491		else
2492		{
2493			return null;
2494		}
2495	}
2496
2497	/**
2498	 * Get the language for the feed
2499	 *
2500	 * Uses `<language>`, `<dc:language>`, or @xml_lang
2501	 *
2502	 * @since 1.0 (previously called `get_feed_language()` since 0.8)
2503	 * @return string|null
2504	 */
2505	public function get_language()
2506	{
2507		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
2508		{
2509			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2510		}
2511		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
2512		{
2513			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2514		}
2515		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
2516		{
2517			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2518		}
2519		elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
2520		{
2521			return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2522		}
2523		elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
2524		{
2525			return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2526		}
2527		elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
2528		{
2529			return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2530		}
2531		elseif (isset($this->data['headers']['content-language']))
2532		{
2533			return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
2534		}
2535		else
2536		{
2537			return null;
2538		}
2539	}
2540
2541	/**
2542	 * Get the latitude coordinates for the item
2543	 *
2544	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2545	 *
2546	 * Uses `<geo:lat>` or `<georss:point>`
2547	 *
2548	 * @since 1.0
2549	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2550	 * @link http://www.georss.org/ GeoRSS
2551	 * @return string|null
2552	 */
2553	public function get_latitude()
2554	{
2555
2556		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2557		{
2558			return (float) $return[0]['data'];
2559		}
2560		elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2561		{
2562			return (float) $match[1];
2563		}
2564		else
2565		{
2566			return null;
2567		}
2568	}
2569
2570	/**
2571	 * Get the longitude coordinates for the feed
2572	 *
2573	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2574	 *
2575	 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
2576	 *
2577	 * @since 1.0
2578	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2579	 * @link http://www.georss.org/ GeoRSS
2580	 * @return string|null
2581	 */
2582	public function get_longitude()
2583	{
2584		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2585		{
2586			return (float) $return[0]['data'];
2587		}
2588		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2589		{
2590			return (float) $return[0]['data'];
2591		}
2592		elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2593		{
2594			return (float) $match[2];
2595		}
2596		else
2597		{
2598			return null;
2599		}
2600	}
2601
2602	/**
2603	 * Get the feed logo's title
2604	 *
2605	 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title.
2606	 *
2607	 * Uses `<image><title>` or `<image><dc:title>`
2608	 *
2609	 * @return string|null
2610	 */
2611	public function get_image_title()
2612	{
2613		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2614		{
2615			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2616		}
2617		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2618		{
2619			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2620		}
2621		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2622		{
2623			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2624		}
2625		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2626		{
2627			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2628		}
2629		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2630		{
2631			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2632		}
2633		else
2634		{
2635			return null;
2636		}
2637	}
2638
2639	/**
2640	 * Get the feed logo's URL
2641	 *
2642	 * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to
2643	 * have a "feed logo" URL. This points directly to the image itself.
2644	 *
2645	 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2646	 * `<image><title>` or `<image><dc:title>`
2647	 *
2648	 * @return string|null
2649	 */
2650	public function get_image_url()
2651	{
2652		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
2653		{
2654			return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
2655		}
2656		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
2657		{
2658			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2659		}
2660		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
2661		{
2662			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2663		}
2664		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
2665		{
2666			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2667		}
2668		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
2669		{
2670			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2671		}
2672		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2673		{
2674			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2675		}
2676		else
2677		{
2678			return null;
2679		}
2680	}
2681
2682	/**
2683	 * Get the feed logo's link
2684	 *
2685	 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This
2686	 * points to a human-readable page that the image should link to.
2687	 *
2688	 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2689	 * `<image><title>` or `<image><dc:title>`
2690	 *
2691	 * @return string|null
2692	 */
2693	public function get_image_link()
2694	{
2695		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2696		{
2697			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2698		}
2699		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2700		{
2701			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2702		}
2703		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2704		{
2705			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2706		}
2707		else
2708		{
2709			return null;
2710		}
2711	}
2712
2713	/**
2714	 * Get the feed logo's link
2715	 *
2716	 * RSS 2.0 feeds are allowed to have a "feed logo" width.
2717	 *
2718	 * Uses `<image><width>` or defaults to 88.0 if no width is specified and
2719	 * the feed is an RSS 2.0 feed.
2720	 *
2721	 * @return int|float|null
2722	 */
2723	public function get_image_width()
2724	{
2725		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
2726		{
2727			return round($return[0]['data']);
2728		}
2729		elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2730		{
2731			return 88.0;
2732		}
2733		else
2734		{
2735			return null;
2736		}
2737	}
2738
2739	/**
2740	 * Get the feed logo's height
2741	 *
2742	 * RSS 2.0 feeds are allowed to have a "feed logo" height.
2743	 *
2744	 * Uses `<image><height>` or defaults to 31.0 if no height is specified and
2745	 * the feed is an RSS 2.0 feed.
2746	 *
2747	 * @return int|float|null
2748	 */
2749	public function get_image_height()
2750	{
2751		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
2752		{
2753			return round($return[0]['data']);
2754		}
2755		elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2756		{
2757			return 31.0;
2758		}
2759		else
2760		{
2761			return null;
2762		}
2763	}
2764
2765	/**
2766	 * Get the number of items in the feed
2767	 *
2768	 * This is well-suited for {@link http://php.net/for for()} loops with
2769	 * {@see get_item()}
2770	 *
2771	 * @param int $max Maximum value to return. 0 for no limit
2772	 * @return int Number of items in the feed
2773	 */
2774	public function get_item_quantity($max = 0)
2775	{
2776		$max = (int) $max;
2777		$qty = count($this->get_items());
2778		if ($max === 0)
2779		{
2780			return $qty;
2781		}
2782		else
2783		{
2784			return ($qty > $max) ? $max : $qty;
2785		}
2786	}
2787
2788	/**
2789	 * Get a single item from the feed
2790	 *
2791	 * This is better suited for {@link http://php.net/for for()} loops, whereas
2792	 * {@see get_items()} is better suited for
2793	 * {@link http://php.net/foreach foreach()} loops.
2794	 *
2795	 * @see get_item_quantity()
2796	 * @since Beta 2
2797	 * @param int $key The item that you want to return.  Remember that arrays begin with 0, not 1
2798	 * @return SimplePie_Item|null
2799	 */
2800	public function get_item($key = 0)
2801	{
2802		$items = $this->get_items();
2803		if (isset($items[$key]))
2804		{
2805			return $items[$key];
2806		}
2807		else
2808		{
2809			return null;
2810		}
2811	}
2812
2813	/**
2814	 * Get all items from the feed
2815	 *
2816	 * This is better suited for {@link http://php.net/for for()} loops, whereas
2817	 * {@see get_items()} is better suited for
2818	 * {@link http://php.net/foreach foreach()} loops.
2819	 *
2820	 * @see get_item_quantity
2821	 * @since Beta 2
2822	 * @param int $start Index to start at
2823	 * @param int $end Number of items to return. 0 for all items after `$start`
2824	 * @return array|null List of {@see SimplePie_Item} objects
2825	 */
2826	public function get_items($start = 0, $end = 0)
2827	{
2828		if (!isset($this->data['items']))
2829		{
2830			if (!empty($this->multifeed_objects))
2831			{
2832				$this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
2833			}
2834			else
2835			{
2836				$this->data['items'] = array();
2837				if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry'))
2838				{
2839					$keys = array_keys($items);
2840					foreach ($keys as $key)
2841					{
2842						$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2843					}
2844				}
2845				if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry'))
2846				{
2847					$keys = array_keys($items);
2848					foreach ($keys as $key)
2849					{
2850						$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2851					}
2852				}
2853				if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item'))
2854				{
2855					$keys = array_keys($items);
2856					foreach ($keys as $key)
2857					{
2858						$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2859					}
2860				}
2861				if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item'))
2862				{
2863					$keys = array_keys($items);
2864					foreach ($keys as $key)
2865					{
2866						$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2867					}
2868				}
2869				if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item'))
2870				{
2871					$keys = array_keys($items);
2872					foreach ($keys as $key)
2873					{
2874						$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2875					}
2876				}
2877			}
2878		}
2879
2880		if (!empty($this->data['items']))
2881		{
2882			// If we want to order it by date, check if all items have a date, and then sort it
2883			if ($this->order_by_date && empty($this->multifeed_objects))
2884			{
2885				if (!isset($this->data['ordered_items']))
2886				{
2887					$do_sort = true;
2888					foreach ($this->data['items'] as $item)
2889					{
2890						if (!$item->get_date('U'))
2891						{
2892							$do_sort = false;
2893							break;
2894						}
2895					}
2896					$item = null;
2897					$this->data['ordered_items'] = $this->data['items'];
2898					if ($do_sort)
2899					{
2900						usort($this->data['ordered_items'], array(get_class($this), 'sort_items'));
2901					}
2902				}
2903				$items = $this->data['ordered_items'];
2904			}
2905			else
2906			{
2907				$items = $this->data['items'];
2908			}
2909
2910			// Slice the data as desired
2911			if ($end === 0)
2912			{
2913				return array_slice($items, $start);
2914			}
2915			else
2916			{
2917				return array_slice($items, $start, $end);
2918			}
2919		}
2920		else
2921		{
2922			return array();
2923		}
2924	}
2925
2926	/**
2927	 * Set the favicon handler
2928	 *
2929	 * @deprecated Use your own favicon handling instead
2930	 */
2931	public function set_favicon_handler($page = false, $qs = 'i')
2932	{
2933		$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2934		trigger_error('Favicon handling has been removed, please use your own handling', $level);
2935		return false;
2936	}
2937
2938	/**
2939	 * Get the favicon for the current feed
2940	 *
2941	 * @deprecated Use your own favicon handling instead
2942	 */
2943	public function get_favicon()
2944	{
2945		$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2946		trigger_error('Favicon handling has been removed, please use your own handling', $level);
2947
2948		if (($url = $this->get_link()) !== null)
2949		{
2950			return 'http://g.etfv.co/' . urlencode($url);
2951		}
2952
2953		return false;
2954	}
2955
2956	/**
2957	 * Magic method handler
2958	 *
2959	 * @param string $method Method name
2960	 * @param array $args Arguments to the method
2961	 * @return mixed
2962	 */
2963	public function __call($method, $args)
2964	{
2965		if (strpos($method, 'subscribe_') === 0)
2966		{
2967			$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2968			trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level);
2969			return '';
2970		}
2971		if ($method === 'enable_xml_dump')
2972		{
2973			$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2974			trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level);
2975			return false;
2976		}
2977
2978		$class = get_class($this);
2979		$trace = debug_backtrace();
2980		$file = $trace[0]['file'];
2981		$line = $trace[0]['line'];
2982		trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
2983	}
2984
2985	/**
2986	 * Sorting callback for items
2987	 *
2988	 * @access private
2989	 * @param SimplePie $a
2990	 * @param SimplePie $b
2991	 * @return boolean
2992	 */
2993	public static function sort_items($a, $b)
2994	{
2995		return $a->get_date('U') <= $b->get_date('U');
2996	}
2997
2998	/**
2999	 * Merge items from several feeds into one
3000	 *
3001	 * If you're merging multiple feeds together, they need to all have dates
3002	 * for the items or else SimplePie will refuse to sort them.
3003	 *
3004	 * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings
3005	 * @param array $urls List of SimplePie feed objects to merge
3006	 * @param int $start Starting item
3007	 * @param int $end Number of items to return
3008	 * @param int $limit Maximum number of items per feed
3009	 * @return array
3010	 */
3011	public static function merge_items($urls, $start = 0, $end = 0, $limit = 0)
3012	{
3013		if (is_array($urls) && sizeof($urls) > 0)
3014		{
3015			$items = array();
3016			foreach ($urls as $arg)
3017			{
3018				if ($arg instanceof SimplePie)
3019				{
3020					$items = array_merge($items, $arg->get_items(0, $limit));
3021				}
3022				else
3023				{
3024					trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
3025				}
3026			}
3027
3028			$do_sort = true;
3029			foreach ($items as $item)
3030			{
3031				if (!$item->get_date('U'))
3032				{
3033					$do_sort = false;
3034					break;
3035				}
3036			}
3037			$item = null;
3038			if ($do_sort)
3039			{
3040				usort($items, array(get_class($urls[0]), 'sort_items'));
3041			}
3042
3043			if ($end === 0)
3044			{
3045				return array_slice($items, $start);
3046			}
3047			else
3048			{
3049				return array_slice($items, $start, $end);
3050			}
3051		}
3052		else
3053		{
3054			trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
3055			return array();
3056		}
3057	}
3058}
3059
3060/**
3061 * Manages all author-related data
3062 *
3063 * Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()}
3064 *
3065 * This class can be overloaded with {@see SimplePie::set_author_class()}
3066 *
3067 * @package SimplePie
3068 * @subpackage API
3069 */
3070class SimplePie_Author
3071{
3072	/**
3073	 * Author's name
3074	 *
3075	 * @var string
3076	 * @see get_name()
3077	 */
3078	var $name;
3079
3080	/**
3081	 * Author's link
3082	 *
3083	 * @var string
3084	 * @see get_link()
3085	 */
3086	var $link;
3087
3088	/**
3089	 * Author's email address
3090	 *
3091	 * @var string
3092	 * @see get_email()
3093	 */
3094	var $email;
3095
3096	/**
3097	 * Constructor, used to input the data
3098	 *
3099	 * @param string $name
3100	 * @param string $link
3101	 * @param string $email
3102	 */
3103	public function __construct($name = null, $link = null, $email = null)
3104	{
3105		$this->name = $name;
3106		$this->link = $link;
3107		$this->email = $email;
3108	}
3109
3110	/**
3111	 * String-ified version
3112	 *
3113	 * @return string
3114	 */
3115	public function __toString()
3116	{
3117		// There is no $this->data here
3118		return md5(serialize($this));
3119	}
3120
3121	/**
3122	 * Author's name
3123	 *
3124	 * @return string|null
3125	 */
3126	public function get_name()
3127	{
3128		if ($this->name !== null)
3129		{
3130			return $this->name;
3131		}
3132		else
3133		{
3134			return null;
3135		}
3136	}
3137
3138	/**
3139	 * Author's link
3140	 *
3141	 * @return string|null
3142	 */
3143	public function get_link()
3144	{
3145		if ($this->link !== null)
3146		{
3147			return $this->link;
3148		}
3149		else
3150		{
3151			return null;
3152		}
3153	}
3154
3155	/**
3156	 * Author's email address
3157	 *
3158	 * @return string|null
3159	 */
3160	public function get_email()
3161	{
3162		if ($this->email !== null)
3163		{
3164			return $this->email;
3165		}
3166		else
3167		{
3168			return null;
3169		}
3170	}
3171}
3172
3173/**
3174 * Base for cache objects
3175 *
3176 * Classes to be used with {@see SimplePie_Cache::register()} are expected
3177 * to implement this interface.
3178 *
3179 * @package SimplePie
3180 * @subpackage Caching
3181 */
3182interface SimplePie_Cache_Base
3183{
3184	/**
3185	 * Feed cache type
3186	 *
3187	 * @var string
3188	 */
3189	const TYPE_FEED = 'spc';
3190
3191	/**
3192	 * Image cache type
3193	 *
3194	 * @var string
3195	 */
3196	const TYPE_IMAGE = 'spi';
3197
3198	/**
3199	 * Create a new cache object
3200	 *
3201	 * @param string $location Location string (from SimplePie::$cache_location)
3202	 * @param string $name Unique ID for the cache
3203	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3204	 */
3205	public function __construct($location, $name, $type);
3206
3207	/**
3208	 * Save data to the cache
3209	 *
3210	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3211	 * @return bool Successfulness
3212	 */
3213	public function save($data);
3214
3215	/**
3216	 * Retrieve the data saved to the cache
3217	 *
3218	 * @return array Data for SimplePie::$data
3219	 */
3220	public function load();
3221
3222	/**
3223	 * Retrieve the last modified time for the cache
3224	 *
3225	 * @return int Timestamp
3226	 */
3227	public function mtime();
3228
3229	/**
3230	 * Set the last modified time to the current time
3231	 *
3232	 * @return bool Success status
3233	 */
3234	public function touch();
3235
3236	/**
3237	 * Remove the cache
3238	 *
3239	 * @return bool Success status
3240	 */
3241	public function unlink();
3242}
3243
3244/**
3245 * Base class for database-based caches
3246 *
3247 * @package SimplePie
3248 * @subpackage Caching
3249 */
3250abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base
3251{
3252	/**
3253	 * Helper for database conversion
3254	 *
3255	 * Converts a given {@see SimplePie} object into data to be stored
3256	 *
3257	 * @param SimplePie $data
3258	 * @return array First item is the serialized data for storage, second item is the unique ID for this item
3259	 */
3260	protected static function prepare_simplepie_object_for_cache($data)
3261	{
3262		$items = $data->get_items();
3263		$items_by_id = array();
3264
3265		if (!empty($items))
3266		{
3267			foreach ($items as $item)
3268			{
3269				$items_by_id[$item->get_id()] = $item;
3270			}
3271
3272			if (count($items_by_id) !== count($items))
3273			{
3274				$items_by_id = array();
3275				foreach ($items as $item)
3276				{
3277					$items_by_id[$item->get_id(true)] = $item;
3278				}
3279			}
3280
3281			if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
3282			{
3283				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
3284			}
3285			elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
3286			{
3287				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
3288			}
3289			elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
3290			{
3291				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
3292			}
3293			elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
3294			{
3295				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
3296			}
3297			else
3298			{
3299				$channel = null;
3300			}
3301
3302			if ($channel !== null)
3303			{
3304				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
3305				{
3306					unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
3307				}
3308				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
3309				{
3310					unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
3311				}
3312				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
3313				{
3314					unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
3315				}
3316				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
3317				{
3318					unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
3319				}
3320				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
3321				{
3322					unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
3323				}
3324			}
3325			if (isset($data->data['items']))
3326			{
3327				unset($data->data['items']);
3328			}
3329			if (isset($data->data['ordered_items']))
3330			{
3331				unset($data->data['ordered_items']);
3332			}
3333		}
3334		return array(serialize($data->data), $items_by_id);
3335	}
3336}
3337
3338/**
3339 * Caches data to the filesystem
3340 *
3341 * @package SimplePie
3342 * @subpackage Caching
3343 */
3344class SimplePie_Cache_File implements SimplePie_Cache_Base
3345{
3346	/**
3347	 * Location string
3348	 *
3349	 * @see SimplePie::$cache_location
3350	 * @var string
3351	 */
3352	protected $location;
3353
3354	/**
3355	 * Filename
3356	 *
3357	 * @var string
3358	 */
3359	protected $filename;
3360
3361	/**
3362	 * File extension
3363	 *
3364	 * @var string
3365	 */
3366	protected $extension;
3367
3368	/**
3369	 * File path
3370	 *
3371	 * @var string
3372	 */
3373	protected $name;
3374
3375	/**
3376	 * Create a new cache object
3377	 *
3378	 * @param string $location Location string (from SimplePie::$cache_location)
3379	 * @param string $name Unique ID for the cache
3380	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3381	 */
3382	public function __construct($location, $name, $type)
3383	{
3384		$this->location = $location;
3385		$this->filename = $name;
3386		$this->extension = $type;
3387		$this->name = "$this->location/$this->filename.$this->extension";
3388	}
3389
3390	/**
3391	 * Save data to the cache
3392	 *
3393	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3394	 * @return bool Successfulness
3395	 */
3396	public function save($data)
3397	{
3398		if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
3399		{
3400			if ($data instanceof SimplePie)
3401			{
3402				$data = $data->data;
3403			}
3404
3405			$data = serialize($data);
3406			return (bool) file_put_contents($this->name, $data);
3407		}
3408		return false;
3409	}
3410
3411	/**
3412	 * Retrieve the data saved to the cache
3413	 *
3414	 * @return array Data for SimplePie::$data
3415	 */
3416	public function load()
3417	{
3418		if (file_exists($this->name) && is_readable($this->name))
3419		{
3420			return unserialize(file_get_contents($this->name));
3421		}
3422		return false;
3423	}
3424
3425	/**
3426	 * Retrieve the last modified time for the cache
3427	 *
3428	 * @return int Timestamp
3429	 */
3430	public function mtime()
3431	{
3432		if (file_exists($this->name))
3433		{
3434			return filemtime($this->name);
3435		}
3436		return false;
3437	}
3438
3439	/**
3440	 * Set the last modified time to the current time
3441	 *
3442	 * @return bool Success status
3443	 */
3444	public function touch()
3445	{
3446		if (file_exists($this->name))
3447		{
3448			return touch($this->name);
3449		}
3450		return false;
3451	}
3452
3453	/**
3454	 * Remove the cache
3455	 *
3456	 * @return bool Success status
3457	 */
3458	public function unlink()
3459	{
3460		if (file_exists($this->name))
3461		{
3462			return unlink($this->name);
3463		}
3464		return false;
3465	}
3466}
3467
3468/**
3469 * Caches data to memcache
3470 *
3471 * Registered for URLs with the "memcache" protocol
3472 *
3473 * For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will
3474 * connect to memcache on `localhost` on port 11211. All tables will be
3475 * prefixed with `sp_` and data will expire after 3600 seconds
3476 *
3477 * @package SimplePie
3478 * @subpackage Caching
3479 * @uses Memcache
3480 */
3481class SimplePie_Cache_Memcache implements SimplePie_Cache_Base
3482{
3483	/**
3484	 * Memcache instance
3485	 *
3486	 * @var Memcache
3487	 */
3488	protected $cache;
3489
3490	/**
3491	 * Options
3492	 *
3493	 * @var array
3494	 */
3495	protected $options;
3496
3497	/**
3498	 * Cache name
3499	 *
3500	 * @var string
3501	 */
3502	protected $name;
3503
3504	/**
3505	 * Create a new cache object
3506	 *
3507	 * @param string $location Location string (from SimplePie::$cache_location)
3508	 * @param string $name Unique ID for the cache
3509	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3510	 */
3511	public function __construct($location, $name, $type)
3512	{
3513		$this->options = array(
3514			'host' => '127.0.0.1',
3515			'port' => 11211,
3516			'extras' => array(
3517				'timeout' => 3600, // one hour
3518				'prefix' => 'simplepie_',
3519			),
3520		);
3521		$parsed = SimplePie_Cache::parse_URL($location);
3522		$this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host'];
3523		$this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port'];
3524		$this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']);
3525		$this->name = $this->options['extras']['prefix'] . md5("$name:$type");
3526
3527		$this->cache = new Memcache();
3528		$this->cache->addServer($this->options['host'], (int) $this->options['port']);
3529	}
3530
3531	/**
3532	 * Save data to the cache
3533	 *
3534	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3535	 * @return bool Successfulness
3536	 */
3537	public function save($data)
3538	{
3539		if ($data instanceof SimplePie)
3540		{
3541			$data = $data->data;
3542		}
3543		return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
3544	}
3545
3546	/**
3547	 * Retrieve the data saved to the cache
3548	 *
3549	 * @return array Data for SimplePie::$data
3550	 */
3551	public function load()
3552	{
3553		$data = $this->cache->get($this->name);
3554
3555		if ($data !== false)
3556		{
3557			return unserialize($data);
3558		}
3559		return false;
3560	}
3561
3562	/**
3563	 * Retrieve the last modified time for the cache
3564	 *
3565	 * @return int Timestamp
3566	 */
3567	public function mtime()
3568	{
3569		$data = $this->cache->get($this->name);
3570
3571		if ($data !== false)
3572		{
3573			// essentially ignore the mtime because Memcache expires on it's own
3574			return time();
3575		}
3576
3577		return false;
3578	}
3579
3580	/**
3581	 * Set the last modified time to the current time
3582	 *
3583	 * @return bool Success status
3584	 */
3585	public function touch()
3586	{
3587		$data = $this->cache->get($this->name);
3588
3589		if ($data !== false)
3590		{
3591			return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration);
3592		}
3593
3594		return false;
3595	}
3596
3597	/**
3598	 * Remove the cache
3599	 *
3600	 * @return bool Success status
3601	 */
3602	public function unlink()
3603	{
3604		return $this->cache->delete($this->name, 0);
3605	}
3606}
3607
3608/**
3609 * Caches data to a MySQL database
3610 *
3611 * Registered for URLs with the "mysql" protocol
3612 *
3613 * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
3614 * connect to the `mydb` database on `localhost` on port 3306, with the user
3615 * `root` and the password `password`. All tables will be prefixed with `sp_`
3616 *
3617 * @package SimplePie
3618 * @subpackage Caching
3619 */
3620class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
3621{
3622	/**
3623	 * PDO instance
3624	 *
3625	 * @var PDO
3626	 */
3627	protected $mysql;
3628
3629	/**
3630	 * Options
3631	 *
3632	 * @var array
3633	 */
3634	protected $options;
3635
3636	/**
3637	 * Cache ID
3638	 *
3639	 * @var string
3640	 */
3641	protected $id;
3642
3643	/**
3644	 * Create a new cache object
3645	 *
3646	 * @param string $location Location string (from SimplePie::$cache_location)
3647	 * @param string $name Unique ID for the cache
3648	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3649	 */
3650	public function __construct($location, $name, $type)
3651	{
3652		$this->options = array(
3653			'user' => null,
3654			'pass' => null,
3655			'host' => '127.0.0.1',
3656			'port' => '3306',
3657			'path' => '',
3658			'extras' => array(
3659				'prefix' => '',
3660			),
3661		);
3662		$this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
3663
3664		// Path is prefixed with a "/"
3665		$this->options['dbname'] = substr($this->options['path'], 1);
3666
3667		try
3668		{
3669			$this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
3670		}
3671		catch (PDOException $e)
3672		{
3673			$this->mysql = null;
3674			return;
3675		}
3676
3677		$this->id = $name . $type;
3678
3679		if (!$query = $this->mysql->query('SHOW TABLES'))
3680		{
3681			$this->mysql = null;
3682			return;
3683		}
3684
3685		$db = array();
3686		while ($row = $query->fetchColumn())
3687		{
3688			$db[] = $row;
3689		}
3690
3691		if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
3692		{
3693			$query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
3694			if ($query === false)
3695			{
3696				$this->mysql = null;
3697			}
3698		}
3699
3700		if (!in_array($this->options['extras']['prefix'] . 'items', $db))
3701		{
3702			$query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
3703			if ($query === false)
3704			{
3705				$this->mysql = null;
3706			}
3707		}
3708	}
3709
3710	/**
3711	 * Save data to the cache
3712	 *
3713	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3714	 * @return bool Successfulness
3715	 */
3716	public function save($data)
3717	{
3718		if ($this->mysql === null)
3719		{
3720			return false;
3721		}
3722
3723		if ($data instanceof SimplePie)
3724		{
3725			$data = clone $data;
3726
3727			$prepared = self::prepare_simplepie_object_for_cache($data);
3728
3729			$query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
3730			$query->bindValue(':feed', $this->id);
3731			if ($query->execute())
3732			{
3733				if ($query->fetchColumn() > 0)
3734				{
3735					$items = count($prepared[1]);
3736					if ($items)
3737					{
3738						$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
3739						$query = $this->mysql->prepare($sql);
3740						$query->bindValue(':items', $items);
3741					}
3742					else
3743					{
3744						$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
3745						$query = $this->mysql->prepare($sql);
3746					}
3747
3748					$query->bindValue(':data', $prepared[0]);
3749					$query->bindValue(':time', time());
3750					$query->bindValue(':feed', $this->id);
3751					if (!$query->execute())
3752					{
3753						return false;
3754					}
3755				}
3756				else
3757				{
3758					$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
3759					$query->bindValue(':feed', $this->id);
3760					$query->bindValue(':count', count($prepared[1]));
3761					$query->bindValue(':data', $prepared[0]);
3762					$query->bindValue(':time', time());
3763					if (!$query->execute())
3764					{
3765						return false;
3766					}
3767				}
3768
3769				$ids = array_keys($prepared[1]);
3770				if (!empty($ids))
3771				{
3772					foreach ($ids as $id)
3773					{
3774						$database_ids[] = $this->mysql->quote($id);
3775					}
3776
3777					$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
3778					$query->bindValue(':feed', $this->id);
3779
3780					if ($query->execute())
3781					{
3782						$existing_ids = array();
3783						while ($row = $query->fetchColumn())
3784						{
3785							$existing_ids[] = $row;
3786						}
3787
3788						$new_ids = array_diff($ids, $existing_ids);
3789
3790						foreach ($new_ids as $new_id)
3791						{
3792							if (!($date = $prepared[1][$new_id]->get_date('U')))
3793							{
3794								$date = time();
3795							}
3796
3797							$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
3798							$query->bindValue(':feed', $this->id);
3799							$query->bindValue(':id', $new_id);
3800							$query->bindValue(':data', serialize($prepared[1][$new_id]->data));
3801							$query->bindValue(':date', $date);
3802							if (!$query->execute())
3803							{
3804								return false;
3805							}
3806						}
3807						return true;
3808					}
3809				}
3810				else
3811				{
3812					return true;
3813				}
3814			}
3815		}
3816		else
3817		{
3818			$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
3819			$query->bindValue(':feed', $this->id);
3820			if ($query->execute())
3821			{
3822				if ($query->rowCount() > 0)
3823				{
3824					$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
3825					$query->bindValue(':data', serialize($data));
3826					$query->bindValue(':time', time());
3827					$query->bindValue(':feed', $this->id);
3828					if ($this->execute())
3829					{
3830						return true;
3831					}
3832				}
3833				else
3834				{
3835					$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
3836					$query->bindValue(':id', $this->id);
3837					$query->bindValue(':data', serialize($data));
3838					$query->bindValue(':time', time());
3839					if ($query->execute())
3840					{
3841						return true;
3842					}
3843				}
3844			}
3845		}
3846		return false;
3847	}
3848
3849	/**
3850	 * Retrieve the data saved to the cache
3851	 *
3852	 * @return array Data for SimplePie::$data
3853	 */
3854	public function load()
3855	{
3856		if ($this->mysql === null)
3857		{
3858			return false;
3859		}
3860
3861		$query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
3862		$query->bindValue(':id', $this->id);
3863		if ($query->execute() && ($row = $query->fetch()))
3864		{
3865			$data = unserialize($row[1]);
3866
3867			if (isset($this->options['items'][0]))
3868			{
3869				$items = (int) $this->options['items'][0];
3870			}
3871			else
3872			{
3873				$items = (int) $row[0];
3874			}
3875
3876			if ($items !== 0)
3877			{
3878				if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
3879				{
3880					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
3881				}
3882				elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
3883				{
3884					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
3885				}
3886				elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
3887				{
3888					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
3889				}
3890				elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
3891				{
3892					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
3893				}
3894				else
3895				{
3896					$feed = null;
3897				}
3898
3899				if ($feed !== null)
3900				{
3901					$sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
3902					if ($items > 0)
3903					{
3904						$sql .= ' LIMIT ' . $items;
3905					}
3906
3907					$query = $this->mysql->prepare($sql);
3908					$query->bindValue(':feed', $this->id);
3909					if ($query->execute())
3910					{
3911						while ($row = $query->fetchColumn())
3912						{
3913							$feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
3914						}
3915					}
3916					else
3917					{
3918						return false;
3919					}
3920				}
3921			}
3922			return $data;
3923		}
3924		return false;
3925	}
3926
3927	/**
3928	 * Retrieve the last modified time for the cache
3929	 *
3930	 * @return int Timestamp
3931	 */
3932	public function mtime()
3933	{
3934		if ($this->mysql === null)
3935		{
3936			return false;
3937		}
3938
3939		$query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
3940		$query->bindValue(':id', $this->id);
3941		if ($query->execute() && ($time = $query->fetchColumn()))
3942		{
3943			return $time;
3944		}
3945		else
3946		{
3947			return false;
3948		}
3949	}
3950
3951	/**
3952	 * Set the last modified time to the current time
3953	 *
3954	 * @return bool Success status
3955	 */
3956	public function touch()
3957	{
3958		if ($this->mysql === null)
3959		{
3960			return false;
3961		}
3962
3963		$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
3964		$query->bindValue(':time', time());
3965		$query->bindValue(':id', $this->id);
3966		if ($query->execute() && $query->rowCount() > 0)
3967		{
3968			return true;
3969		}
3970		else
3971		{
3972			return false;
3973		}
3974	}
3975
3976	/**
3977	 * Remove the cache
3978	 *
3979	 * @return bool Success status
3980	 */
3981	public function unlink()
3982	{
3983		if ($this->mysql === null)
3984		{
3985			return false;
3986		}
3987
3988		$query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
3989		$query->bindValue(':id', $this->id);
3990		$query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
3991		$query2->bindValue(':id', $this->id);
3992		if ($query->execute() && $query2->execute())
3993		{
3994			return true;
3995		}
3996		else
3997		{
3998			return false;
3999		}
4000	}
4001}
4002
4003/**
4004 * Used to create cache objects
4005 *
4006 * This class can be overloaded with {@see SimplePie::set_cache_class()},
4007 * although the preferred way is to create your own handler
4008 * via {@see register()}
4009 *
4010 * @package SimplePie
4011 * @subpackage Caching
4012 */
4013class SimplePie_Cache
4014{
4015	/**
4016	 * Cache handler classes
4017	 *
4018	 * These receive 3 parameters to their constructor, as documented in
4019	 * {@see register()}
4020	 * @var array
4021	 */
4022	protected static $handlers = array(
4023		'mysql' => 'SimplePie_Cache_MySQL',
4024		'memcache' => 'SimplePie_Cache_Memcache',
4025	);
4026
4027	/**
4028	 * Don't call the constructor. Please.
4029	 */
4030	private function __construct() { }
4031
4032	/**
4033	 * Create a new SimplePie_Cache object
4034	 *
4035	 * @param string $location URL location (scheme is used to determine handler)
4036	 * @param string $filename Unique identifier for cache object
4037	 * @param string $extension 'spi' or 'spc'
4038	 * @return SimplePie_Cache_Base Type of object depends on scheme of `$location`
4039	 */
4040	public static function get_handler($location, $filename, $extension)
4041	{
4042		$type = explode(':', $location, 2);
4043		$type = $type[0];
4044		if (!empty(self::$handlers[$type]))
4045		{
4046			$class = self::$handlers[$type];
4047			return new $class($location, $filename, $extension);
4048		}
4049
4050		return new SimplePie_Cache_File($location, $filename, $extension);
4051	}
4052
4053	/**
4054	 * Create a new SimplePie_Cache object
4055	 *
4056	 * @deprecated Use {@see get_handler} instead
4057	 */
4058	public function create($location, $filename, $extension)
4059	{
4060		trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED);
4061		return self::get_handler($location, $filename, $extension);
4062	}
4063
4064	/**
4065	 * Register a handler
4066	 *
4067	 * @param string $type DSN type to register for
4068	 * @param string $class Name of handler class. Must implement SimplePie_Cache_Base
4069	 */
4070	public static function register($type, $class)
4071	{
4072		self::$handlers[$type] = $class;
4073	}
4074
4075	/**
4076	 * Parse a URL into an array
4077	 *
4078	 * @param string $url
4079	 * @return array
4080	 */
4081	public static function parse_URL($url)
4082	{
4083		$params = parse_url($url);
4084		$params['extras'] = array();
4085		if (isset($params['query']))
4086		{
4087			parse_str($params['query'], $params['extras']);
4088		}
4089		return $params;
4090	}
4091}
4092
4093/**
4094 * Handles `<media:text>` captions as defined in Media RSS.
4095 *
4096 * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()}
4097 *
4098 * This class can be overloaded with {@see SimplePie::set_caption_class()}
4099 *
4100 * @package SimplePie
4101 * @subpackage API
4102 */
4103class SimplePie_Caption
4104{
4105	/**
4106	 * Content type
4107	 *
4108	 * @var string
4109	 * @see get_type()
4110	 */
4111	var $type;
4112
4113	/**
4114	 * Language
4115	 *
4116	 * @var string
4117	 * @see get_language()
4118	 */
4119	var $lang;
4120
4121	/**
4122	 * Start time
4123	 *
4124	 * @var string
4125	 * @see get_starttime()
4126	 */
4127	var $startTime;
4128
4129	/**
4130	 * End time
4131	 *
4132	 * @var string
4133	 * @see get_endtime()
4134	 */
4135	var $endTime;
4136
4137	/**
4138	 * Caption text
4139	 *
4140	 * @var string
4141	 * @see get_text()
4142	 */
4143	var $text;
4144
4145	/**
4146	 * Constructor, used to input the data
4147	 *
4148	 * For documentation on all the parameters, see the corresponding
4149	 * properties and their accessors
4150	 */
4151	public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
4152	{
4153		$this->type = $type;
4154		$this->lang = $lang;
4155		$this->startTime = $startTime;
4156		$this->endTime = $endTime;
4157		$this->text = $text;
4158	}
4159
4160	/**
4161	 * String-ified version
4162	 *
4163	 * @return string
4164	 */
4165	public function __toString()
4166	{
4167		// There is no $this->data here
4168		return md5(serialize($this));
4169	}
4170
4171	/**
4172	 * Get the end time
4173	 *
4174	 * @return string|null Time in the format 'hh:mm:ss.SSS'
4175	 */
4176	public function get_endtime()
4177	{
4178		if ($this->endTime !== null)
4179		{
4180			return $this->endTime;
4181		}
4182		else
4183		{
4184			return null;
4185		}
4186	}
4187
4188	/**
4189	 * Get the language
4190	 *
4191	 * @link http://tools.ietf.org/html/rfc3066
4192	 * @return string|null Language code as per RFC 3066
4193	 */
4194	public function get_language()
4195	{
4196		if ($this->lang !== null)
4197		{
4198			return $this->lang;
4199		}
4200		else
4201		{
4202			return null;
4203		}
4204	}
4205
4206	/**
4207	 * Get the start time
4208	 *
4209	 * @return string|null Time in the format 'hh:mm:ss.SSS'
4210	 */
4211	public function get_starttime()
4212	{
4213		if ($this->startTime !== null)
4214		{
4215			return $this->startTime;
4216		}
4217		else
4218		{
4219			return null;
4220		}
4221	}
4222
4223	/**
4224	 * Get the text of the caption
4225	 *
4226	 * @return string|null
4227	 */
4228	public function get_text()
4229	{
4230		if ($this->text !== null)
4231		{
4232			return $this->text;
4233		}
4234		else
4235		{
4236			return null;
4237		}
4238	}
4239
4240	/**
4241	 * Get the content type (not MIME type)
4242	 *
4243	 * @return string|null Either 'text' or 'html'
4244	 */
4245	public function get_type()
4246	{
4247		if ($this->type !== null)
4248		{
4249			return $this->type;
4250		}
4251		else
4252		{
4253			return null;
4254		}
4255	}
4256}
4257
4258/**
4259 * Manages all category-related data
4260 *
4261 * Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()}
4262 *
4263 * This class can be overloaded with {@see SimplePie::set_category_class()}
4264 *
4265 * @package SimplePie
4266 * @subpackage API
4267 */
4268class SimplePie_Category
4269{
4270	/**
4271	 * Category identifier
4272	 *
4273	 * @var string
4274	 * @see get_term
4275	 */
4276	var $term;
4277
4278	/**
4279	 * Categorization scheme identifier
4280	 *
4281	 * @var string
4282	 * @see get_scheme()
4283	 */
4284	var $scheme;
4285
4286	/**
4287	 * Human readable label
4288	 *
4289	 * @var string
4290	 * @see get_label()
4291	 */
4292	var $label;
4293
4294	/**
4295	 * Constructor, used to input the data
4296	 *
4297	 * @param string $term
4298	 * @param string $scheme
4299	 * @param string $label
4300	 */
4301	public function __construct($term = null, $scheme = null, $label = null)
4302	{
4303		$this->term = $term;
4304		$this->scheme = $scheme;
4305		$this->label = $label;
4306	}
4307
4308	/**
4309	 * String-ified version
4310	 *
4311	 * @return string
4312	 */
4313	public function __toString()
4314	{
4315		// There is no $this->data here
4316		return md5(serialize($this));
4317	}
4318
4319	/**
4320	 * Get the category identifier
4321	 *
4322	 * @return string|null
4323	 */
4324	public function get_term()
4325	{
4326		if ($this->term !== null)
4327		{
4328			return $this->term;
4329		}
4330		else
4331		{
4332			return null;
4333		}
4334	}
4335
4336	/**
4337	 * Get the categorization scheme identifier
4338	 *
4339	 * @return string|null
4340	 */
4341	public function get_scheme()
4342	{
4343		if ($this->scheme !== null)
4344		{
4345			return $this->scheme;
4346		}
4347		else
4348		{
4349			return null;
4350		}
4351	}
4352
4353	/**
4354	 * Get the human readable label
4355	 *
4356	 * @return string|null
4357	 */
4358	public function get_label()
4359	{
4360		if ($this->label !== null)
4361		{
4362			return $this->label;
4363		}
4364		else
4365		{
4366			return $this->get_term();
4367		}
4368	}
4369}
4370
4371/**
4372 * Content-type sniffing
4373 *
4374 * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06
4375 *
4376 * This is used since we can't always trust Content-Type headers, and is based
4377 * upon the HTML5 parsing rules.
4378 *
4379 *
4380 * This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()}
4381 *
4382 * @package SimplePie
4383 * @subpackage HTTP
4384 */
4385class SimplePie_Content_Type_Sniffer
4386{
4387	/**
4388	 * File object
4389	 *
4390	 * @var SimplePie_File
4391	 */
4392	var $file;
4393
4394	/**
4395	 * Create an instance of the class with the input file
4396	 *
4397	 * @param SimplePie_Content_Type_Sniffer $file Input file
4398	 */
4399	public function __construct($file)
4400	{
4401		$this->file = $file;
4402	}
4403
4404	/**
4405	 * Get the Content-Type of the specified file
4406	 *
4407	 * @return string Actual Content-Type
4408	 */
4409	public function get_type()
4410	{
4411		if (isset($this->file->headers['content-type']))
4412		{
4413			if (!isset($this->file->headers['content-encoding'])
4414				&& ($this->file->headers['content-type'] === 'text/plain'
4415					|| $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
4416					|| $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
4417					|| $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
4418			{
4419				return $this->text_or_binary();
4420			}
4421
4422			if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
4423			{
4424				$official = substr($this->file->headers['content-type'], 0, $pos);
4425			}
4426			else
4427			{
4428				$official = $this->file->headers['content-type'];
4429			}
4430			$official = trim(strtolower($official));
4431
4432			if ($official === 'unknown/unknown'
4433				|| $official === 'application/unknown')
4434			{
4435				return $this->unknown();
4436			}
4437			elseif (substr($official, -4) === '+xml'
4438				|| $official === 'text/xml'
4439				|| $official === 'application/xml')
4440			{
4441				return $official;
4442			}
4443			elseif (substr($official, 0, 6) === 'image/')
4444			{
4445				if ($return = $this->image())
4446				{
4447					return $return;
4448				}
4449				else
4450				{
4451					return $official;
4452				}
4453			}
4454			elseif ($official === 'text/html')
4455			{
4456				return $this->feed_or_html();
4457			}
4458			else
4459			{
4460				return $official;
4461			}
4462		}
4463		else
4464		{
4465			return $this->unknown();
4466		}
4467	}
4468
4469	/**
4470	 * Sniff text or binary
4471	 *
4472	 * @return string Actual Content-Type
4473	 */
4474	public function text_or_binary()
4475	{
4476		if (substr($this->file->body, 0, 2) === "\xFE\xFF"
4477			|| substr($this->file->body, 0, 2) === "\xFF\xFE"
4478			|| substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
4479			|| substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
4480		{
4481			return 'text/plain';
4482		}
4483		elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
4484		{
4485			return 'application/octect-stream';
4486		}
4487		else
4488		{
4489			return 'text/plain';
4490		}
4491	}
4492
4493	/**
4494	 * Sniff unknown
4495	 *
4496	 * @return string Actual Content-Type
4497	 */
4498	public function unknown()
4499	{
4500		$ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
4501		if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
4502			|| strtolower(substr($this->file->body, $ws, 5)) === '<html'
4503			|| strtolower(substr($this->file->body, $ws, 7)) === '<script')
4504		{
4505			return 'text/html';
4506		}
4507		elseif (substr($this->file->body, 0, 5) === '%PDF-')
4508		{
4509			return 'application/pdf';
4510		}
4511		elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
4512		{
4513			return 'application/postscript';
4514		}
4515		elseif (substr($this->file->body, 0, 6) === 'GIF87a'
4516			|| substr($this->file->body, 0, 6) === 'GIF89a')
4517		{
4518			return 'image/gif';
4519		}
4520		elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
4521		{
4522			return 'image/png';
4523		}
4524		elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
4525		{
4526			return 'image/jpeg';
4527		}
4528		elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
4529		{
4530			return 'image/bmp';
4531		}
4532		elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
4533		{
4534			return 'image/vnd.microsoft.icon';
4535		}
4536		else
4537		{
4538			return $this->text_or_binary();
4539		}
4540	}
4541
4542	/**
4543	 * Sniff images
4544	 *
4545	 * @return string Actual Content-Type
4546	 */
4547	public function image()
4548	{
4549		if (substr($this->file->body, 0, 6) === 'GIF87a'
4550			|| substr($this->file->body, 0, 6) === 'GIF89a')
4551		{
4552			return 'image/gif';
4553		}
4554		elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
4555		{
4556			return 'image/png';
4557		}
4558		elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
4559		{
4560			return 'image/jpeg';
4561		}
4562		elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
4563		{
4564			return 'image/bmp';
4565		}
4566		elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
4567		{
4568			return 'image/vnd.microsoft.icon';
4569		}
4570		else
4571		{
4572			return false;
4573		}
4574	}
4575
4576	/**
4577	 * Sniff HTML
4578	 *
4579	 * @return string Actual Content-Type
4580	 */
4581	public function feed_or_html()
4582	{
4583		$len = strlen($this->file->body);
4584		$pos = strspn($this->file->body, "\x09\x0A\x0D\x20");
4585
4586		while ($pos < $len)
4587		{
4588			switch ($this->file->body[$pos])
4589			{
4590				case "\x09":
4591				case "\x0A":
4592				case "\x0D":
4593				case "\x20":
4594					$pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
4595					continue 2;
4596
4597				case '<':
4598					$pos++;
4599					break;
4600
4601				default:
4602					return 'text/html';
4603			}
4604
4605			if (substr($this->file->body, $pos, 3) === '!--')
4606			{
4607				$pos += 3;
4608				if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
4609				{
4610					$pos += 3;
4611				}
4612				else
4613				{
4614					return 'text/html';
4615				}
4616			}
4617			elseif (substr($this->file->body, $pos, 1) === '!')
4618			{
4619				if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
4620				{
4621					$pos++;
4622				}
4623				else
4624				{
4625					return 'text/html';
4626				}
4627			}
4628			elseif (substr($this->file->body, $pos, 1) === '?')
4629			{
4630				if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
4631				{
4632					$pos += 2;
4633				}
4634				else
4635				{
4636					return 'text/html';
4637				}
4638			}
4639			elseif (substr($this->file->body, $pos, 3) === 'rss'
4640				|| substr($this->file->body, $pos, 7) === 'rdf:RDF')
4641			{
4642				return 'application/rss+xml';
4643			}
4644			elseif (substr($this->file->body, $pos, 4) === 'feed')
4645			{
4646				return 'application/atom+xml';
4647			}
4648			else
4649			{
4650				return 'text/html';
4651			}
4652		}
4653
4654		return 'text/html';
4655	}
4656}
4657
4658/**
4659 * Manages `<media:copyright>` copyright tags as defined in Media RSS
4660 *
4661 * Used by {@see SimplePie_Enclosure::get_copyright()}
4662 *
4663 * This class can be overloaded with {@see SimplePie::set_copyright_class()}
4664 *
4665 * @package SimplePie
4666 * @subpackage API
4667 */
4668class SimplePie_Copyright
4669{
4670	/**
4671	 * Copyright URL
4672	 *
4673	 * @var string
4674	 * @see get_url()
4675	 */
4676	var $url;
4677
4678	/**
4679	 * Attribution
4680	 *
4681	 * @var string
4682	 * @see get_attribution()
4683	 */
4684	var $label;
4685
4686	/**
4687	 * Constructor, used to input the data
4688	 *
4689	 * For documentation on all the parameters, see the corresponding
4690	 * properties and their accessors
4691	 */
4692	public function __construct($url = null, $label = null)
4693	{
4694		$this->url = $url;
4695		$this->label = $label;
4696	}
4697
4698	/**
4699	 * String-ified version
4700	 *
4701	 * @return string
4702	 */
4703	public function __toString()
4704	{
4705		// There is no $this->data here
4706		return md5(serialize($this));
4707	}
4708
4709	/**
4710	 * Get the copyright URL
4711	 *
4712	 * @return string|null URL to copyright information
4713	 */
4714	public function get_url()
4715	{
4716		if ($this->url !== null)
4717		{
4718			return $this->url;
4719		}
4720		else
4721		{
4722			return null;
4723		}
4724	}
4725
4726	/**
4727	 * Get the attribution text
4728	 *
4729	 * @return string|null
4730	 */
4731	public function get_attribution()
4732	{
4733		if ($this->label !== null)
4734		{
4735			return $this->label;
4736		}
4737		else
4738		{
4739			return null;
4740		}
4741	}
4742}
4743
4744/**
4745 * SimplePie class.
4746 *
4747 * Class for backward compatibility.
4748 *
4749 * @deprecated Use {@see SimplePie} directly
4750 * @package SimplePie
4751 * @subpackage API
4752 */
4753class SimplePie_Core extends SimplePie
4754{
4755
4756}
4757
4758/**
4759 * Handles `<media:credit>` as defined in Media RSS
4760 *
4761 * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()}
4762 *
4763 * This class can be overloaded with {@see SimplePie::set_credit_class()}
4764 *
4765 * @package SimplePie
4766 * @subpackage API
4767 */
4768class SimplePie_Credit
4769{
4770	/**
4771	 * Credited role
4772	 *
4773	 * @var string
4774	 * @see get_role()
4775	 */
4776	var $role;
4777
4778	/**
4779	 * Organizational scheme
4780	 *
4781	 * @var string
4782	 * @see get_scheme()
4783	 */
4784	var $scheme;
4785
4786	/**
4787	 * Credited name
4788	 *
4789	 * @var string
4790	 * @see get_name()
4791	 */
4792	var $name;
4793
4794	/**
4795	 * Constructor, used to input the data
4796	 *
4797	 * For documentation on all the parameters, see the corresponding
4798	 * properties and their accessors
4799	 */
4800	public function __construct($role = null, $scheme = null, $name = null)
4801	{
4802		$this->role = $role;
4803		$this->scheme = $scheme;
4804		$this->name = $name;
4805	}
4806
4807	/**
4808	 * String-ified version
4809	 *
4810	 * @return string
4811	 */
4812	public function __toString()
4813	{
4814		// There is no $this->data here
4815		return md5(serialize($this));
4816	}
4817
4818	/**
4819	 * Get the role of the person receiving credit
4820	 *
4821	 * @return string|null
4822	 */
4823	public function get_role()
4824	{
4825		if ($this->role !== null)
4826		{
4827			return $this->role;
4828		}
4829		else
4830		{
4831			return null;
4832		}
4833	}
4834
4835	/**
4836	 * Get the organizational scheme
4837	 *
4838	 * @return string|null
4839	 */
4840	public function get_scheme()
4841	{
4842		if ($this->scheme !== null)
4843		{
4844			return $this->scheme;
4845		}
4846		else
4847		{
4848			return null;
4849		}
4850	}
4851
4852	/**
4853	 * Get the credited person/entity's name
4854	 *
4855	 * @return string|null
4856	 */
4857	public function get_name()
4858	{
4859		if ($this->name !== null)
4860		{
4861			return $this->name;
4862		}
4863		else
4864		{
4865			return null;
4866		}
4867	}
4868}
4869
4870/**
4871 * Decode HTML Entities
4872 *
4873 * This implements HTML5 as of revision 967 (2007-06-28)
4874 *
4875 * @deprecated Use DOMDocument instead!
4876 * @package SimplePie
4877 */
4878class SimplePie_Decode_HTML_Entities
4879{
4880	/**
4881	 * Data to be parsed
4882	 *
4883	 * @access private
4884	 * @var string
4885	 */
4886	var $data = '';
4887
4888	/**
4889	 * Currently consumed bytes
4890	 *
4891	 * @access private
4892	 * @var string
4893	 */
4894	var $consumed = '';
4895
4896	/**
4897	 * Position of the current byte being parsed
4898	 *
4899	 * @access private
4900	 * @var int
4901	 */
4902	var $position = 0;
4903
4904	/**
4905	 * Create an instance of the class with the input data
4906	 *
4907	 * @access public
4908	 * @param string $data Input data
4909	 */
4910	public function __construct($data)
4911	{
4912		$this->data = $data;
4913	}
4914
4915	/**
4916	 * Parse the input data
4917	 *
4918	 * @access public
4919	 * @return string Output data
4920	 */
4921	public function parse()
4922	{
4923		while (($this->position = strpos($this->data, '&', $this->position)) !== false)
4924		{
4925			$this->consume();
4926			$this->entity();
4927			$this->consumed = '';
4928		}
4929		return $this->data;
4930	}
4931
4932	/**
4933	 * Consume the next byte
4934	 *
4935	 * @access private
4936	 * @return mixed The next byte, or false, if there is no more data
4937	 */
4938	public function consume()
4939	{
4940		if (isset($this->data[$this->position]))
4941		{
4942			$this->consumed .= $this->data[$this->position];
4943			return $this->data[$this->position++];
4944		}
4945		else
4946		{
4947			return false;
4948		}
4949	}
4950
4951	/**
4952	 * Consume a range of characters
4953	 *
4954	 * @access private
4955	 * @param string $chars Characters to consume
4956	 * @return mixed A series of characters that match the range, or false
4957	 */
4958	public function consume_range($chars)
4959	{
4960		if ($len = strspn($this->data, $chars, $this->position))
4961		{
4962			$data = substr($this->data, $this->position, $len);
4963			$this->consumed .= $data;
4964			$this->position += $len;
4965			return $data;
4966		}
4967		else
4968		{
4969			return false;
4970		}
4971	}
4972
4973	/**
4974	 * Unconsume one byte
4975	 *
4976	 * @access private
4977	 */
4978	public function unconsume()
4979	{
4980		$this->consumed = substr($this->consumed, 0, -1);
4981		$this->position--;
4982	}
4983
4984	/**
4985	 * Decode an entity
4986	 *
4987	 * @access private
4988	 */
4989	public function entity()
4990	{
4991		switch ($this->consume())
4992		{
4993			case "\x09":
4994			case "\x0A":
4995			case "\x0B":
4996			case "\x0B":
4997			case "\x0C":
4998			case "\x20":
4999			case "\x3C":
5000			case "\x26":
5001			case false:
5002				break;
5003
5004			case "\x23":
5005				switch ($this->consume())
5006				{
5007					case "\x78":
5008					case "\x58":
5009						$range = '0123456789ABCDEFabcdef';
5010						$hex = true;
5011						break;
5012
5013					default:
5014						$range = '0123456789';
5015						$hex = false;
5016						$this->unconsume();
5017						break;
5018				}
5019
5020				if ($codepoint = $this->consume_range($range))
5021				{
5022					static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8");
5023
5024					if ($hex)
5025					{
5026						$codepoint = hexdec($codepoint);
5027					}
5028					else
5029					{
5030						$codepoint = intval($codepoint);
5031					}
5032
5033					if (isset($windows_1252_specials[$codepoint]))
5034					{
5035						$replacement = $windows_1252_specials[$codepoint];
5036					}
5037					else
5038					{
5039						$replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
5040					}
5041
5042					if (!in_array($this->consume(), array(';', false), true))
5043					{
5044						$this->unconsume();
5045					}
5046
5047					$consumed_length = strlen($this->consumed);
5048					$this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
5049					$this->position += strlen($replacement) - $consumed_length;
5050				}
5051				break;
5052
5053			default:
5054				static $entities = array(
5055					'Aacute' => "\xC3\x81",
5056					'aacute' => "\xC3\xA1",
5057					'Aacute;' => "\xC3\x81",
5058					'aacute;' => "\xC3\xA1",
5059					'Acirc' => "\xC3\x82",
5060					'acirc' => "\xC3\xA2",
5061					'Acirc;' => "\xC3\x82",
5062					'acirc;' => "\xC3\xA2",
5063					'acute' => "\xC2\xB4",
5064					'acute;' => "\xC2\xB4",
5065					'AElig' => "\xC3\x86",
5066					'aelig' => "\xC3\xA6",
5067					'AElig;' => "\xC3\x86",
5068					'aelig;' => "\xC3\xA6",
5069					'Agrave' => "\xC3\x80",
5070					'agrave' => "\xC3\xA0",
5071					'Agrave;' => "\xC3\x80",
5072					'agrave;' => "\xC3\xA0",
5073					'alefsym;' => "\xE2\x84\xB5",
5074					'Alpha;' => "\xCE\x91",
5075					'alpha;' => "\xCE\xB1",
5076					'AMP' => "\x26",
5077					'amp' => "\x26",
5078					'AMP;' => "\x26",
5079					'amp;' => "\x26",
5080					'and;' => "\xE2\x88\xA7",
5081					'ang;' => "\xE2\x88\xA0",
5082					'apos;' => "\x27",
5083					'Aring' => "\xC3\x85",
5084					'aring' => "\xC3\xA5",
5085					'Aring;' => "\xC3\x85",
5086					'aring;' => "\xC3\xA5",
5087					'asymp;' => "\xE2\x89\x88",
5088					'Atilde' => "\xC3\x83",
5089					'atilde' => "\xC3\xA3",
5090					'Atilde;' => "\xC3\x83",
5091					'atilde;' => "\xC3\xA3",
5092					'Auml' => "\xC3\x84",
5093					'auml' => "\xC3\xA4",
5094					'Auml;' => "\xC3\x84",
5095					'auml;' => "\xC3\xA4",
5096					'bdquo;' => "\xE2\x80\x9E",
5097					'Beta;' => "\xCE\x92",
5098					'beta;' => "\xCE\xB2",
5099					'brvbar' => "\xC2\xA6",
5100					'brvbar;' => "\xC2\xA6",
5101					'bull;' => "\xE2\x80\xA2",
5102					'cap;' => "\xE2\x88\xA9",
5103					'Ccedil' => "\xC3\x87",
5104					'ccedil' => "\xC3\xA7",
5105					'Ccedil;' => "\xC3\x87",
5106					'ccedil;' => "\xC3\xA7",
5107					'cedil' => "\xC2\xB8",
5108					'cedil;' => "\xC2\xB8",
5109					'cent' => "\xC2\xA2",
5110					'cent;' => "\xC2\xA2",
5111					'Chi;' => "\xCE\xA7",
5112					'chi;' => "\xCF\x87",
5113					'circ;' => "\xCB\x86",
5114					'clubs;' => "\xE2\x99\xA3",
5115					'cong;' => "\xE2\x89\x85",
5116					'COPY' => "\xC2\xA9",
5117					'copy' => "\xC2\xA9",
5118					'COPY;' => "\xC2\xA9",
5119					'copy;' => "\xC2\xA9",
5120					'crarr;' => "\xE2\x86\xB5",
5121					'cup;' => "\xE2\x88\xAA",
5122					'curren' => "\xC2\xA4",
5123					'curren;' => "\xC2\xA4",
5124					'Dagger;' => "\xE2\x80\xA1",
5125					'dagger;' => "\xE2\x80\xA0",
5126					'dArr;' => "\xE2\x87\x93",
5127					'darr;' => "\xE2\x86\x93",
5128					'deg' => "\xC2\xB0",
5129					'deg;' => "\xC2\xB0",
5130					'Delta;' => "\xCE\x94",
5131					'delta;' => "\xCE\xB4",
5132					'diams;' => "\xE2\x99\xA6",
5133					'divide' => "\xC3\xB7",
5134					'divide;' => "\xC3\xB7",
5135					'Eacute' => "\xC3\x89",
5136					'eacute' => "\xC3\xA9",
5137					'Eacute;' => "\xC3\x89",
5138					'eacute;' => "\xC3\xA9",
5139					'Ecirc' => "\xC3\x8A",
5140					'ecirc' => "\xC3\xAA",
5141					'Ecirc;' => "\xC3\x8A",
5142					'ecirc;' => "\xC3\xAA",
5143					'Egrave' => "\xC3\x88",
5144					'egrave' => "\xC3\xA8",
5145					'Egrave;' => "\xC3\x88",
5146					'egrave;' => "\xC3\xA8",
5147					'empty;' => "\xE2\x88\x85",
5148					'emsp;' => "\xE2\x80\x83",
5149					'ensp;' => "\xE2\x80\x82",
5150					'Epsilon;' => "\xCE\x95",
5151					'epsilon;' => "\xCE\xB5",
5152					'equiv;' => "\xE2\x89\xA1",
5153					'Eta;' => "\xCE\x97",
5154					'eta;' => "\xCE\xB7",
5155					'ETH' => "\xC3\x90",
5156					'eth' => "\xC3\xB0",
5157					'ETH;' => "\xC3\x90",
5158					'eth;' => "\xC3\xB0",
5159					'Euml' => "\xC3\x8B",
5160					'euml' => "\xC3\xAB",
5161					'Euml;' => "\xC3\x8B",
5162					'euml;' => "\xC3\xAB",
5163					'euro;' => "\xE2\x82\xAC",
5164					'exist;' => "\xE2\x88\x83",
5165					'fnof;' => "\xC6\x92",
5166					'forall;' => "\xE2\x88\x80",
5167					'frac12' => "\xC2\xBD",
5168					'frac12;' => "\xC2\xBD",
5169					'frac14' => "\xC2\xBC",
5170					'frac14;' => "\xC2\xBC",
5171					'frac34' => "\xC2\xBE",
5172					'frac34;' => "\xC2\xBE",
5173					'frasl;' => "\xE2\x81\x84",
5174					'Gamma;' => "\xCE\x93",
5175					'gamma;' => "\xCE\xB3",
5176					'ge;' => "\xE2\x89\xA5",
5177					'GT' => "\x3E",
5178					'gt' => "\x3E",
5179					'GT;' => "\x3E",
5180					'gt;' => "\x3E",
5181					'hArr;' => "\xE2\x87\x94",
5182					'harr;' => "\xE2\x86\x94",
5183					'hearts;' => "\xE2\x99\xA5",
5184					'hellip;' => "\xE2\x80\xA6",
5185					'Iacute' => "\xC3\x8D",
5186					'iacute' => "\xC3\xAD",
5187					'Iacute;' => "\xC3\x8D",
5188					'iacute;' => "\xC3\xAD",
5189					'Icirc' => "\xC3\x8E",
5190					'icirc' => "\xC3\xAE",
5191					'Icirc;' => "\xC3\x8E",
5192					'icirc;' => "\xC3\xAE",
5193					'iexcl' => "\xC2\xA1",
5194					'iexcl;' => "\xC2\xA1",
5195					'Igrave' => "\xC3\x8C",
5196					'igrave' => "\xC3\xAC",
5197					'Igrave;' => "\xC3\x8C",
5198					'igrave;' => "\xC3\xAC",
5199					'image;' => "\xE2\x84\x91",
5200					'infin;' => "\xE2\x88\x9E",
5201					'int;' => "\xE2\x88\xAB",
5202					'Iota;' => "\xCE\x99",
5203					'iota;' => "\xCE\xB9",
5204					'iquest' => "\xC2\xBF",
5205					'iquest;' => "\xC2\xBF",
5206					'isin;' => "\xE2\x88\x88",
5207					'Iuml' => "\xC3\x8F",
5208					'iuml' => "\xC3\xAF",
5209					'Iuml;' => "\xC3\x8F",
5210					'iuml;' => "\xC3\xAF",
5211					'Kappa;' => "\xCE\x9A",
5212					'kappa;' => "\xCE\xBA",
5213					'Lambda;' => "\xCE\x9B",
5214					'lambda;' => "\xCE\xBB",
5215					'lang;' => "\xE3\x80\x88",
5216					'laquo' => "\xC2\xAB",
5217					'laquo;' => "\xC2\xAB",
5218					'lArr;' => "\xE2\x87\x90",
5219					'larr;' => "\xE2\x86\x90",
5220					'lceil;' => "\xE2\x8C\x88",
5221					'ldquo;' => "\xE2\x80\x9C",
5222					'le;' => "\xE2\x89\xA4",
5223					'lfloor;' => "\xE2\x8C\x8A",
5224					'lowast;' => "\xE2\x88\x97",
5225					'loz;' => "\xE2\x97\x8A",
5226					'lrm;' => "\xE2\x80\x8E",
5227					'lsaquo;' => "\xE2\x80\xB9",
5228					'lsquo;' => "\xE2\x80\x98",
5229					'LT' => "\x3C",
5230					'lt' => "\x3C",
5231					'LT;' => "\x3C",
5232					'lt;' => "\x3C",
5233					'macr' => "\xC2\xAF",
5234					'macr;' => "\xC2\xAF",
5235					'mdash;' => "\xE2\x80\x94",
5236					'micro' => "\xC2\xB5",
5237					'micro;' => "\xC2\xB5",
5238					'middot' => "\xC2\xB7",
5239					'middot;' => "\xC2\xB7",
5240					'minus;' => "\xE2\x88\x92",
5241					'Mu;' => "\xCE\x9C",
5242					'mu;' => "\xCE\xBC",
5243					'nabla;' => "\xE2\x88\x87",
5244					'nbsp' => "\xC2\xA0",
5245					'nbsp;' => "\xC2\xA0",
5246					'ndash;' => "\xE2\x80\x93",
5247					'ne;' => "\xE2\x89\xA0",
5248					'ni;' => "\xE2\x88\x8B",
5249					'not' => "\xC2\xAC",
5250					'not;' => "\xC2\xAC",
5251					'notin;' => "\xE2\x88\x89",
5252					'nsub;' => "\xE2\x8A\x84",
5253					'Ntilde' => "\xC3\x91",
5254					'ntilde' => "\xC3\xB1",
5255					'Ntilde;' => "\xC3\x91",
5256					'ntilde;' => "\xC3\xB1",
5257					'Nu;' => "\xCE\x9D",
5258					'nu;' => "\xCE\xBD",
5259					'Oacute' => "\xC3\x93",
5260					'oacute' => "\xC3\xB3",
5261					'Oacute;' => "\xC3\x93",
5262					'oacute;' => "\xC3\xB3",
5263					'Ocirc' => "\xC3\x94",
5264					'ocirc' => "\xC3\xB4",
5265					'Ocirc;' => "\xC3\x94",
5266					'ocirc;' => "\xC3\xB4",
5267					'OElig;' => "\xC5\x92",
5268					'oelig;' => "\xC5\x93",
5269					'Ograve' => "\xC3\x92",
5270					'ograve' => "\xC3\xB2",
5271					'Ograve;' => "\xC3\x92",
5272					'ograve;' => "\xC3\xB2",
5273					'oline;' => "\xE2\x80\xBE",
5274					'Omega;' => "\xCE\xA9",
5275					'omega;' => "\xCF\x89",
5276					'Omicron;' => "\xCE\x9F",
5277					'omicron;' => "\xCE\xBF",
5278					'oplus;' => "\xE2\x8A\x95",
5279					'or;' => "\xE2\x88\xA8",
5280					'ordf' => "\xC2\xAA",
5281					'ordf;' => "\xC2\xAA",
5282					'ordm' => "\xC2\xBA",
5283					'ordm;' => "\xC2\xBA",
5284					'Oslash' => "\xC3\x98",
5285					'oslash' => "\xC3\xB8",
5286					'Oslash;' => "\xC3\x98",
5287					'oslash;' => "\xC3\xB8",
5288					'Otilde' => "\xC3\x95",
5289					'otilde' => "\xC3\xB5",
5290					'Otilde;' => "\xC3\x95",
5291					'otilde;' => "\xC3\xB5",
5292					'otimes;' => "\xE2\x8A\x97",
5293					'Ouml' => "\xC3\x96",
5294					'ouml' => "\xC3\xB6",
5295					'Ouml;' => "\xC3\x96",
5296					'ouml;' => "\xC3\xB6",
5297					'para' => "\xC2\xB6",
5298					'para;' => "\xC2\xB6",
5299					'part;' => "\xE2\x88\x82",
5300					'permil;' => "\xE2\x80\xB0",
5301					'perp;' => "\xE2\x8A\xA5",
5302					'Phi;' => "\xCE\xA6",
5303					'phi;' => "\xCF\x86",
5304					'Pi;' => "\xCE\xA0",
5305					'pi;' => "\xCF\x80",
5306					'piv;' => "\xCF\x96",
5307					'plusmn' => "\xC2\xB1",
5308					'plusmn;' => "\xC2\xB1",
5309					'pound' => "\xC2\xA3",
5310					'pound;' => "\xC2\xA3",
5311					'Prime;' => "\xE2\x80\xB3",
5312					'prime;' => "\xE2\x80\xB2",
5313					'prod;' => "\xE2\x88\x8F",
5314					'prop;' => "\xE2\x88\x9D",
5315					'Psi;' => "\xCE\xA8",
5316					'psi;' => "\xCF\x88",
5317					'QUOT' => "\x22",
5318					'quot' => "\x22",
5319					'QUOT;' => "\x22",
5320					'quot;' => "\x22",
5321					'radic;' => "\xE2\x88\x9A",
5322					'rang;' => "\xE3\x80\x89",
5323					'raquo' => "\xC2\xBB",
5324					'raquo;' => "\xC2\xBB",
5325					'rArr;' => "\xE2\x87\x92",
5326					'rarr;' => "\xE2\x86\x92",
5327					'rceil;' => "\xE2\x8C\x89",
5328					'rdquo;' => "\xE2\x80\x9D",
5329					'real;' => "\xE2\x84\x9C",
5330					'REG' => "\xC2\xAE",
5331					'reg' => "\xC2\xAE",
5332					'REG;' => "\xC2\xAE",
5333					'reg;' => "\xC2\xAE",
5334					'rfloor;' => "\xE2\x8C\x8B",
5335					'Rho;' => "\xCE\xA1",
5336					'rho;' => "\xCF\x81",
5337					'rlm;' => "\xE2\x80\x8F",
5338					'rsaquo;' => "\xE2\x80\xBA",
5339					'rsquo;' => "\xE2\x80\x99",
5340					'sbquo;' => "\xE2\x80\x9A",
5341					'Scaron;' => "\xC5\xA0",
5342					'scaron;' => "\xC5\xA1",
5343					'sdot;' => "\xE2\x8B\x85",
5344					'sect' => "\xC2\xA7",
5345					'sect;' => "\xC2\xA7",
5346					'shy' => "\xC2\xAD",
5347					'shy;' => "\xC2\xAD",
5348					'Sigma;' => "\xCE\xA3",
5349					'sigma;' => "\xCF\x83",
5350					'sigmaf;' => "\xCF\x82",
5351					'sim;' => "\xE2\x88\xBC",
5352					'spades;' => "\xE2\x99\xA0",
5353					'sub;' => "\xE2\x8A\x82",
5354					'sube;' => "\xE2\x8A\x86",
5355					'sum;' => "\xE2\x88\x91",
5356					'sup;' => "\xE2\x8A\x83",
5357					'sup1' => "\xC2\xB9",
5358					'sup1;' => "\xC2\xB9",
5359					'sup2' => "\xC2\xB2",
5360					'sup2;' => "\xC2\xB2",
5361					'sup3' => "\xC2\xB3",
5362					'sup3;' => "\xC2\xB3",
5363					'supe;' => "\xE2\x8A\x87",
5364					'szlig' => "\xC3\x9F",
5365					'szlig;' => "\xC3\x9F",
5366					'Tau;' => "\xCE\xA4",
5367					'tau;' => "\xCF\x84",
5368					'there4;' => "\xE2\x88\xB4",
5369					'Theta;' => "\xCE\x98",
5370					'theta;' => "\xCE\xB8",
5371					'thetasym;' => "\xCF\x91",
5372					'thinsp;' => "\xE2\x80\x89",
5373					'THORN' => "\xC3\x9E",
5374					'thorn' => "\xC3\xBE",
5375					'THORN;' => "\xC3\x9E",
5376					'thorn;' => "\xC3\xBE",
5377					'tilde;' => "\xCB\x9C",
5378					'times' => "\xC3\x97",
5379					'times;' => "\xC3\x97",
5380					'TRADE;' => "\xE2\x84\xA2",
5381					'trade;' => "\xE2\x84\xA2",
5382					'Uacute' => "\xC3\x9A",
5383					'uacute' => "\xC3\xBA",
5384					'Uacute;' => "\xC3\x9A",
5385					'uacute;' => "\xC3\xBA",
5386					'uArr;' => "\xE2\x87\x91",
5387					'uarr;' => "\xE2\x86\x91",
5388					'Ucirc' => "\xC3\x9B",
5389					'ucirc' => "\xC3\xBB",
5390					'Ucirc;' => "\xC3\x9B",
5391					'ucirc;' => "\xC3\xBB",
5392					'Ugrave' => "\xC3\x99",
5393					'ugrave' => "\xC3\xB9",
5394					'Ugrave;' => "\xC3\x99",
5395					'ugrave;' => "\xC3\xB9",
5396					'uml' => "\xC2\xA8",
5397					'uml;' => "\xC2\xA8",
5398					'upsih;' => "\xCF\x92",
5399					'Upsilon;' => "\xCE\xA5",
5400					'upsilon;' => "\xCF\x85",
5401					'Uuml' => "\xC3\x9C",
5402					'uuml' => "\xC3\xBC",
5403					'Uuml;' => "\xC3\x9C",
5404					'uuml;' => "\xC3\xBC",
5405					'weierp;' => "\xE2\x84\x98",
5406					'Xi;' => "\xCE\x9E",
5407					'xi;' => "\xCE\xBE",
5408					'Yacute' => "\xC3\x9D",
5409					'yacute' => "\xC3\xBD",
5410					'Yacute;' => "\xC3\x9D",
5411					'yacute;' => "\xC3\xBD",
5412					'yen' => "\xC2\xA5",
5413					'yen;' => "\xC2\xA5",
5414					'yuml' => "\xC3\xBF",
5415					'Yuml;' => "\xC5\xB8",
5416					'yuml;' => "\xC3\xBF",
5417					'Zeta;' => "\xCE\x96",
5418					'zeta;' => "\xCE\xB6",
5419					'zwj;' => "\xE2\x80\x8D",
5420					'zwnj;' => "\xE2\x80\x8C"
5421				);
5422
5423				for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
5424				{
5425					$consumed = substr($this->consumed, 1);
5426					if (isset($entities[$consumed]))
5427					{
5428						$match = $consumed;
5429					}
5430				}
5431
5432				if ($match !== null)
5433				{
5434 					$this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
5435					$this->position += strlen($entities[$match]) - strlen($consumed) - 1;
5436				}
5437				break;
5438		}
5439	}
5440}
5441
5442/**
5443 * Handles everything related to enclosures (including Media RSS and iTunes RSS)
5444 *
5445 * Used by {@see SimplePie_Item::get_enclosure()} and {@see SimplePie_Item::get_enclosures()}
5446 *
5447 * This class can be overloaded with {@see SimplePie::set_enclosure_class()}
5448 *
5449 * @package SimplePie
5450 * @subpackage API
5451 */
5452class SimplePie_Enclosure
5453{
5454	/**
5455	 * @var string
5456	 * @see get_bitrate()
5457	 */
5458	var $bitrate;
5459
5460	/**
5461	 * @var array
5462	 * @see get_captions()
5463	 */
5464	var $captions;
5465
5466	/**
5467	 * @var array
5468	 * @see get_categories()
5469	 */
5470	var $categories;
5471
5472	/**
5473	 * @var int
5474	 * @see get_channels()
5475	 */
5476	var $channels;
5477
5478	/**
5479	 * @var SimplePie_Copyright
5480	 * @see get_copyright()
5481	 */
5482	var $copyright;
5483
5484	/**
5485	 * @var array
5486	 * @see get_credits()
5487	 */
5488	var $credits;
5489
5490	/**
5491	 * @var string
5492	 * @see get_description()
5493	 */
5494	var $description;
5495
5496	/**
5497	 * @var int
5498	 * @see get_duration()
5499	 */
5500	var $duration;
5501
5502	/**
5503	 * @var string
5504	 * @see get_expression()
5505	 */
5506	var $expression;
5507
5508	/**
5509	 * @var string
5510	 * @see get_framerate()
5511	 */
5512	var $framerate;
5513
5514	/**
5515	 * @var string
5516	 * @see get_handler()
5517	 */
5518	var $handler;
5519
5520	/**
5521	 * @var array
5522	 * @see get_hashes()
5523	 */
5524	var $hashes;
5525
5526	/**
5527	 * @var string
5528	 * @see get_height()
5529	 */
5530	var $height;
5531
5532	/**
5533	 * @deprecated
5534	 * @var null
5535	 */
5536	var $javascript;
5537
5538	/**
5539	 * @var array
5540	 * @see get_keywords()
5541	 */
5542	var $keywords;
5543
5544	/**
5545	 * @var string
5546	 * @see get_language()
5547	 */
5548	var $lang;
5549
5550	/**
5551	 * @var string
5552	 * @see get_length()
5553	 */
5554	var $length;
5555
5556	/**
5557	 * @var string
5558	 * @see get_link()
5559	 */
5560	var $link;
5561
5562	/**
5563	 * @var string
5564	 * @see get_medium()
5565	 */
5566	var $medium;
5567
5568	/**
5569	 * @var string
5570	 * @see get_player()
5571	 */
5572	var $player;
5573
5574	/**
5575	 * @var array
5576	 * @see get_ratings()
5577	 */
5578	var $ratings;
5579
5580	/**
5581	 * @var array
5582	 * @see get_restrictions()
5583	 */
5584	var $restrictions;
5585
5586	/**
5587	 * @var string
5588	 * @see get_sampling_rate()
5589	 */
5590	var $samplingrate;
5591
5592	/**
5593	 * @var array
5594	 * @see get_thumbnails()
5595	 */
5596	var $thumbnails;
5597
5598	/**
5599	 * @var string
5600	 * @see get_title()
5601	 */
5602	var $title;
5603
5604	/**
5605	 * @var string
5606	 * @see get_type()
5607	 */
5608	var $type;
5609
5610	/**
5611	 * @var string
5612	 * @see get_width()
5613	 */
5614	var $width;
5615
5616	/**
5617	 * Constructor, used to input the data
5618	 *
5619	 * For documentation on all the parameters, see the corresponding
5620	 * properties and their accessors
5621	 *
5622	 * @uses idna_convert If available, this will convert an IDN
5623	 */
5624	public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
5625	{
5626		$this->bitrate = $bitrate;
5627		$this->captions = $captions;
5628		$this->categories = $categories;
5629		$this->channels = $channels;
5630		$this->copyright = $copyright;
5631		$this->credits = $credits;
5632		$this->description = $description;
5633		$this->duration = $duration;
5634		$this->expression = $expression;
5635		$this->framerate = $framerate;
5636		$this->hashes = $hashes;
5637		$this->height = $height;
5638		$this->keywords = $keywords;
5639		$this->lang = $lang;
5640		$this->length = $length;
5641		$this->link = $link;
5642		$this->medium = $medium;
5643		$this->player = $player;
5644		$this->ratings = $ratings;
5645		$this->restrictions = $restrictions;
5646		$this->samplingrate = $samplingrate;
5647		$this->thumbnails = $thumbnails;
5648		$this->title = $title;
5649		$this->type = $type;
5650		$this->width = $width;
5651
5652		if (class_exists('idna_convert'))
5653		{
5654			$idn = new idna_convert();
5655			$parsed = SimplePie_Misc::parse_url($link);
5656			$this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
5657		}
5658		$this->handler = $this->get_handler(); // Needs to load last
5659	}
5660
5661	/**
5662	 * String-ified version
5663	 *
5664	 * @return string
5665	 */
5666	public function __toString()
5667	{
5668		// There is no $this->data here
5669		return md5(serialize($this));
5670	}
5671
5672	/**
5673	 * Get the bitrate
5674	 *
5675	 * @return string|null
5676	 */
5677	public function get_bitrate()
5678	{
5679		if ($this->bitrate !== null)
5680		{
5681			return $this->bitrate;
5682		}
5683		else
5684		{
5685			return null;
5686		}
5687	}
5688
5689	/**
5690	 * Get a single caption
5691	 *
5692	 * @param int $key
5693	 * @return SimplePie_Caption|null
5694	 */
5695	public function get_caption($key = 0)
5696	{
5697		$captions = $this->get_captions();
5698		if (isset($captions[$key]))
5699		{
5700			return $captions[$key];
5701		}
5702		else
5703		{
5704			return null;
5705		}
5706	}
5707
5708	/**
5709	 * Get all captions
5710	 *
5711	 * @return array|null Array of {@see SimplePie_Caption} objects
5712	 */
5713	public function get_captions()
5714	{
5715		if ($this->captions !== null)
5716		{
5717			return $this->captions;
5718		}
5719		else
5720		{
5721			return null;
5722		}
5723	}
5724
5725	/**
5726	 * Get a single category
5727	 *
5728	 * @param int $key
5729	 * @return SimplePie_Category|null
5730	 */
5731	public function get_category($key = 0)
5732	{
5733		$categories = $this->get_categories();
5734		if (isset($categories[$key]))
5735		{
5736			return $categories[$key];
5737		}
5738		else
5739		{
5740			return null;
5741		}
5742	}
5743
5744	/**
5745	 * Get all categories
5746	 *
5747	 * @return array|null Array of {@see SimplePie_Category} objects
5748	 */
5749	public function get_categories()
5750	{
5751		if ($this->categories !== null)
5752		{
5753			return $this->categories;
5754		}
5755		else
5756		{
5757			return null;
5758		}
5759	}
5760
5761	/**
5762	 * Get the number of audio channels
5763	 *
5764	 * @return int|null
5765	 */
5766	public function get_channels()
5767	{
5768		if ($this->channels !== null)
5769		{
5770			return $this->channels;
5771		}
5772		else
5773		{
5774			return null;
5775		}
5776	}
5777
5778	/**
5779	 * Get the copyright information
5780	 *
5781	 * @return SimplePie_Copyright|null
5782	 */
5783	public function get_copyright()
5784	{
5785		if ($this->copyright !== null)
5786		{
5787			return $this->copyright;
5788		}
5789		else
5790		{
5791			return null;
5792		}
5793	}
5794
5795	/**
5796	 * Get a single credit
5797	 *
5798	 * @param int $key
5799	 * @return SimplePie_Credit|null
5800	 */
5801	public function get_credit($key = 0)
5802	{
5803		$credits = $this->get_credits();
5804		if (isset($credits[$key]))
5805		{
5806			return $credits[$key];
5807		}
5808		else
5809		{
5810			return null;
5811		}
5812	}
5813
5814	/**
5815	 * Get all credits
5816	 *
5817	 * @return array|null Array of {@see SimplePie_Credit} objects
5818	 */
5819	public function get_credits()
5820	{
5821		if ($this->credits !== null)
5822		{
5823			return $this->credits;
5824		}
5825		else
5826		{
5827			return null;
5828		}
5829	}
5830
5831	/**
5832	 * Get the description of the enclosure
5833	 *
5834	 * @return string|null
5835	 */
5836	public function get_description()
5837	{
5838		if ($this->description !== null)
5839		{
5840			return $this->description;
5841		}
5842		else
5843		{
5844			return null;
5845		}
5846	}
5847
5848	/**
5849	 * Get the duration of the enclosure
5850	 *
5851	 * @param string $convert Convert seconds into hh:mm:ss
5852	 * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found)
5853	 */
5854	public function get_duration($convert = false)
5855	{
5856		if ($this->duration !== null)
5857		{
5858			if ($convert)
5859			{
5860				$time = SimplePie_Misc::time_hms($this->duration);
5861				return $time;
5862			}
5863			else
5864			{
5865				return $this->duration;
5866			}
5867		}
5868		else
5869		{
5870			return null;
5871		}
5872	}
5873
5874	/**
5875	 * Get the expression
5876	 *
5877	 * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full'
5878	 */
5879	public function get_expression()
5880	{
5881		if ($this->expression !== null)
5882		{
5883			return $this->expression;
5884		}
5885		else
5886		{
5887			return 'full';
5888		}
5889	}
5890
5891	/**
5892	 * Get the file extension
5893	 *
5894	 * @return string|null
5895	 */
5896	public function get_extension()
5897	{
5898		if ($this->link !== null)
5899		{
5900			$url = SimplePie_Misc::parse_url($this->link);
5901			if ($url['path'] !== '')
5902			{
5903				return pathinfo($url['path'], PATHINFO_EXTENSION);
5904			}
5905		}
5906		return null;
5907	}
5908
5909	/**
5910	 * Get the framerate (in frames-per-second)
5911	 *
5912	 * @return string|null
5913	 */
5914	public function get_framerate()
5915	{
5916		if ($this->framerate !== null)
5917		{
5918			return $this->framerate;
5919		}
5920		else
5921		{
5922			return null;
5923		}
5924	}
5925
5926	/**
5927	 * Get the preferred handler
5928	 *
5929	 * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3'
5930	 */
5931	public function get_handler()
5932	{
5933		return $this->get_real_type(true);
5934	}
5935
5936	/**
5937	 * Get a single hash
5938	 *
5939	 * @link http://www.rssboard.org/media-rss#media-hash
5940	 * @param int $key
5941	 * @return string|null Hash as per `media:hash`, prefixed with "$algo:"
5942	 */
5943	public function get_hash($key = 0)
5944	{
5945		$hashes = $this->get_hashes();
5946		if (isset($hashes[$key]))
5947		{
5948			return $hashes[$key];
5949		}
5950		else
5951		{
5952			return null;
5953		}
5954	}
5955
5956	/**
5957	 * Get all credits
5958	 *
5959	 * @return array|null Array of strings, see {@see get_hash()}
5960	 */
5961	public function get_hashes()
5962	{
5963		if ($this->hashes !== null)
5964		{
5965			return $this->hashes;
5966		}
5967		else
5968		{
5969			return null;
5970		}
5971	}
5972
5973	/**
5974	 * Get the height
5975	 *
5976	 * @return string|null
5977	 */
5978	public function get_height()
5979	{
5980		if ($this->height !== null)
5981		{
5982			return $this->height;
5983		}
5984		else
5985		{
5986			return null;
5987		}
5988	}
5989
5990	/**
5991	 * Get the language
5992	 *
5993	 * @link http://tools.ietf.org/html/rfc3066
5994	 * @return string|null Language code as per RFC 3066
5995	 */
5996	public function get_language()
5997	{
5998		if ($this->lang !== null)
5999		{
6000			return $this->lang;
6001		}
6002		else
6003		{
6004			return null;
6005		}
6006	}
6007
6008	/**
6009	 * Get a single keyword
6010	 *
6011	 * @param int $key
6012	 * @return string|null
6013	 */
6014	public function get_keyword($key = 0)
6015	{
6016		$keywords = $this->get_keywords();
6017		if (isset($keywords[$key]))
6018		{
6019			return $keywords[$key];
6020		}
6021		else
6022		{
6023			return null;
6024		}
6025	}
6026
6027	/**
6028	 * Get all keywords
6029	 *
6030	 * @return array|null Array of strings
6031	 */
6032	public function get_keywords()
6033	{
6034		if ($this->keywords !== null)
6035		{
6036			return $this->keywords;
6037		}
6038		else
6039		{
6040			return null;
6041		}
6042	}
6043
6044	/**
6045	 * Get length
6046	 *
6047	 * @return float Length in bytes
6048	 */
6049	public function get_length()
6050	{
6051		if ($this->length !== null)
6052		{
6053			return $this->length;
6054		}
6055		else
6056		{
6057			return null;
6058		}
6059	}
6060
6061	/**
6062	 * Get the URL
6063	 *
6064	 * @return string|null
6065	 */
6066	public function get_link()
6067	{
6068		if ($this->link !== null)
6069		{
6070			return urldecode($this->link);
6071		}
6072		else
6073		{
6074			return null;
6075		}
6076	}
6077
6078	/**
6079	 * Get the medium
6080	 *
6081	 * @link http://www.rssboard.org/media-rss#media-content
6082	 * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable'
6083	 */
6084	public function get_medium()
6085	{
6086		if ($this->medium !== null)
6087		{
6088			return $this->medium;
6089		}
6090		else
6091		{
6092			return null;
6093		}
6094	}
6095
6096	/**
6097	 * Get the player URL
6098	 *
6099	 * Typically the same as {@see get_permalink()}
6100	 * @return string|null Player URL
6101	 */
6102	public function get_player()
6103	{
6104		if ($this->player !== null)
6105		{
6106			return $this->player;
6107		}
6108		else
6109		{
6110			return null;
6111		}
6112	}
6113
6114	/**
6115	 * Get a single rating
6116	 *
6117	 * @param int $key
6118	 * @return SimplePie_Rating|null
6119	 */
6120	public function get_rating($key = 0)
6121	{
6122		$ratings = $this->get_ratings();
6123		if (isset($ratings[$key]))
6124		{
6125			return $ratings[$key];
6126		}
6127		else
6128		{
6129			return null;
6130		}
6131	}
6132
6133	/**
6134	 * Get all ratings
6135	 *
6136	 * @return array|null Array of {@see SimplePie_Rating} objects
6137	 */
6138	public function get_ratings()
6139	{
6140		if ($this->ratings !== null)
6141		{
6142			return $this->ratings;
6143		}
6144		else
6145		{
6146			return null;
6147		}
6148	}
6149
6150	/**
6151	 * Get a single restriction
6152	 *
6153	 * @param int $key
6154	 * @return SimplePie_Restriction|null
6155	 */
6156	public function get_restriction($key = 0)
6157	{
6158		$restrictions = $this->get_restrictions();
6159		if (isset($restrictions[$key]))
6160		{
6161			return $restrictions[$key];
6162		}
6163		else
6164		{
6165			return null;
6166		}
6167	}
6168
6169	/**
6170	 * Get all restrictions
6171	 *
6172	 * @return array|null Array of {@see SimplePie_Restriction} objects
6173	 */
6174	public function get_restrictions()
6175	{
6176		if ($this->restrictions !== null)
6177		{
6178			return $this->restrictions;
6179		}
6180		else
6181		{
6182			return null;
6183		}
6184	}
6185
6186	/**
6187	 * Get the sampling rate (in kHz)
6188	 *
6189	 * @return string|null
6190	 */
6191	public function get_sampling_rate()
6192	{
6193		if ($this->samplingrate !== null)
6194		{
6195			return $this->samplingrate;
6196		}
6197		else
6198		{
6199			return null;
6200		}
6201	}
6202
6203	/**
6204	 * Get the file size (in MiB)
6205	 *
6206	 * @return float|null File size in mebibytes (1048 bytes)
6207	 */
6208	public function get_size()
6209	{
6210		$length = $this->get_length();
6211		if ($length !== null)
6212		{
6213			return round($length/1048576, 2);
6214		}
6215		else
6216		{
6217			return null;
6218		}
6219	}
6220
6221	/**
6222	 * Get a single thumbnail
6223	 *
6224	 * @param int $key
6225	 * @return string|null Thumbnail URL
6226	 */
6227	public function get_thumbnail($key = 0)
6228	{
6229		$thumbnails = $this->get_thumbnails();
6230		if (isset($thumbnails[$key]))
6231		{
6232			return $thumbnails[$key];
6233		}
6234		else
6235		{
6236			return null;
6237		}
6238	}
6239
6240	/**
6241	 * Get all thumbnails
6242	 *
6243	 * @return array|null Array of thumbnail URLs
6244	 */
6245	public function get_thumbnails()
6246	{
6247		if ($this->thumbnails !== null)
6248		{
6249			return $this->thumbnails;
6250		}
6251		else
6252		{
6253			return null;
6254		}
6255	}
6256
6257	/**
6258	 * Get the title
6259	 *
6260	 * @return string|null
6261	 */
6262	public function get_title()
6263	{
6264		if ($this->title !== null)
6265		{
6266			return $this->title;
6267		}
6268		else
6269		{
6270			return null;
6271		}
6272	}
6273
6274	/**
6275	 * Get mimetype of the enclosure
6276	 *
6277	 * @see get_real_type()
6278	 * @return string|null MIME type
6279	 */
6280	public function get_type()
6281	{
6282		if ($this->type !== null)
6283		{
6284			return $this->type;
6285		}
6286		else
6287		{
6288			return null;
6289		}
6290	}
6291
6292	/**
6293	 * Get the width
6294	 *
6295	 * @return string|null
6296	 */
6297	public function get_width()
6298	{
6299		if ($this->width !== null)
6300		{
6301			return $this->width;
6302		}
6303		else
6304		{
6305			return null;
6306		}
6307	}
6308
6309	/**
6310	 * Embed the enclosure using `<embed>`
6311	 *
6312	 * @deprecated Use the second parameter to {@see embed} instead
6313	 *
6314	 * @param array|string $options See first paramter to {@see embed}
6315	 * @return string HTML string to output
6316	 */
6317	public function native_embed($options='')
6318	{
6319		return $this->embed($options, true);
6320	}
6321
6322	/**
6323	 * Embed the enclosure using Javascript
6324	 *
6325	 * `$options` is an array or comma-separated key:value string, with the
6326	 * following properties:
6327	 *
6328	 * - `alt` (string): Alternate content for when an end-user does not have
6329	 *    the appropriate handler installed or when a file type is
6330	 *    unsupported. Can be any text or HTML. Defaults to blank.
6331	 * - `altclass` (string): If a file type is unsupported, the end-user will
6332	 *    see the alt text (above) linked directly to the content. That link
6333	 *    will have this value as its class name. Defaults to blank.
6334	 * - `audio` (string): This is an image that should be used as a
6335	 *    placeholder for audio files before they're loaded (QuickTime-only).
6336	 *    Can be any relative or absolute URL. Defaults to blank.
6337	 * - `bgcolor` (string): The background color for the media, if not
6338	 *    already transparent. Defaults to `#ffffff`.
6339	 * - `height` (integer): The height of the embedded media. Accepts any
6340	 *    numeric pixel value (such as `360`) or `auto`. Defaults to `auto`,
6341	 *    and it is recommended that you use this default.
6342	 * - `loop` (boolean): Do you want the media to loop when its done?
6343	 *    Defaults to `false`.
6344	 * - `mediaplayer` (string): The location of the included
6345	 *    `mediaplayer.swf` file. This allows for the playback of Flash Video
6346	 *    (`.flv`) files, and is the default handler for non-Odeo MP3's.
6347	 *    Defaults to blank.
6348	 * - `video` (string): This is an image that should be used as a
6349	 *    placeholder for video files before they're loaded (QuickTime-only).
6350	 *    Can be any relative or absolute URL. Defaults to blank.
6351	 * - `width` (integer): The width of the embedded media. Accepts any
6352	 *    numeric pixel value (such as `480`) or `auto`. Defaults to `auto`,
6353	 *    and it is recommended that you use this default.
6354	 * - `widescreen` (boolean): Is the enclosure widescreen or standard?
6355	 *    This applies only to video enclosures, and will automatically resize
6356	 *    the content appropriately.  Defaults to `false`, implying 4:3 mode.
6357	 *
6358	 * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto`
6359	 * will default to 480x360 video resolution.  Widescreen (16:9) mode with
6360	 * `width` and `height` set to `auto` will default to 480x270 video resolution.
6361	 *
6362	 * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
6363	 * @param array|string $options Comma-separated key:value list, or array
6364	 * @param bool $native Use `<embed>`
6365	 * @return string HTML string to output
6366	 */
6367	public function embed($options = '', $native = false)
6368	{
6369		// Set up defaults
6370		$audio = '';
6371		$video = '';
6372		$alt = '';
6373		$altclass = '';
6374		$loop = 'false';
6375		$width = 'auto';
6376		$height = 'auto';
6377		$bgcolor = '#ffffff';
6378		$mediaplayer = '';
6379		$widescreen = false;
6380		$handler = $this->get_handler();
6381		$type = $this->get_real_type();
6382
6383		// Process options and reassign values as necessary
6384		if (is_array($options))
6385		{
6386			extract($options);
6387		}
6388		else
6389		{
6390			$options = explode(',', $options);
6391			foreach($options as $option)
6392			{
6393				$opt = explode(':', $option, 2);
6394				if (isset($opt[0], $opt[1]))
6395				{
6396					$opt[0] = trim($opt[0]);
6397					$opt[1] = trim($opt[1]);
6398					switch ($opt[0])
6399					{
6400						case 'audio':
6401							$audio = $opt[1];
6402							break;
6403
6404						case 'video':
6405							$video = $opt[1];
6406							break;
6407
6408						case 'alt':
6409							$alt = $opt[1];
6410							break;
6411
6412						case 'altclass':
6413							$altclass = $opt[1];
6414							break;
6415
6416						case 'loop':
6417							$loop = $opt[1];
6418							break;
6419
6420						case 'width':
6421							$width = $opt[1];
6422							break;
6423
6424						case 'height':
6425							$height = $opt[1];
6426							break;
6427
6428						case 'bgcolor':
6429							$bgcolor = $opt[1];
6430							break;
6431
6432						case 'mediaplayer':
6433							$mediaplayer = $opt[1];
6434							break;
6435
6436						case 'widescreen':
6437							$widescreen = $opt[1];
6438							break;
6439					}
6440				}
6441			}
6442		}
6443
6444		$mime = explode('/', $type, 2);
6445		$mime = $mime[0];
6446
6447		// Process values for 'auto'
6448		if ($width === 'auto')
6449		{
6450			if ($mime === 'video')
6451			{
6452				if ($height === 'auto')
6453				{
6454					$width = 480;
6455				}
6456				elseif ($widescreen)
6457				{
6458					$width = round((intval($height)/9)*16);
6459				}
6460				else
6461				{
6462					$width = round((intval($height)/3)*4);
6463				}
6464			}
6465			else
6466			{
6467				$width = '100%';
6468			}
6469		}
6470
6471		if ($height === 'auto')
6472		{
6473			if ($mime === 'audio')
6474			{
6475				$height = 0;
6476			}
6477			elseif ($mime === 'video')
6478			{
6479				if ($width === 'auto')
6480				{
6481					if ($widescreen)
6482					{
6483						$height = 270;
6484					}
6485					else
6486					{
6487						$height = 360;
6488					}
6489				}
6490				elseif ($widescreen)
6491				{
6492					$height = round((intval($width)/16)*9);
6493				}
6494				else
6495				{
6496					$height = round((intval($width)/4)*3);
6497				}
6498			}
6499			else
6500			{
6501				$height = 376;
6502			}
6503		}
6504		elseif ($mime === 'audio')
6505		{
6506			$height = 0;
6507		}
6508
6509		// Set proper placeholder value
6510		if ($mime === 'audio')
6511		{
6512			$placeholder = $audio;
6513		}
6514		elseif ($mime === 'video')
6515		{
6516			$placeholder = $video;
6517		}
6518
6519		$embed = '';
6520
6521		// Flash
6522		if ($handler === 'flash')
6523		{
6524			if ($native)
6525			{
6526				$embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
6527			}
6528			else
6529			{
6530				$embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
6531			}
6532		}
6533
6534		// Flash Media Player file types.
6535		// Preferred handler for MP3 file types.
6536		elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== ''))
6537		{
6538			$height += 20;
6539			if ($native)
6540			{
6541				$embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
6542			}
6543			else
6544			{
6545				$embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
6546			}
6547		}
6548
6549		// QuickTime 7 file types.  Need to test with QuickTime 6.
6550		// Only handle MP3's if the Flash Media Player is not present.
6551		elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === ''))
6552		{
6553			$height += 16;
6554			if ($native)
6555			{
6556				if ($placeholder !== '')
6557				{
6558					$embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
6559				}
6560				else
6561				{
6562					$embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
6563				}
6564			}
6565			else
6566			{
6567				$embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
6568			}
6569		}
6570
6571		// Windows Media
6572		elseif ($handler === 'wmedia')
6573		{
6574			$height += 45;
6575			if ($native)
6576			{
6577				$embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
6578			}
6579			else
6580			{
6581				$embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
6582			}
6583		}
6584
6585		// Everything else
6586		else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
6587
6588		return $embed;
6589	}
6590
6591	/**
6592	 * Get the real media type
6593	 *
6594	 * Often, feeds lie to us, necessitating a bit of deeper inspection. This
6595	 * converts types to their canonical representations based on the file
6596	 * extension
6597	 *
6598	 * @see get_type()
6599	 * @param bool $find_handler Internal use only, use {@see get_handler()} instead
6600	 * @return string MIME type
6601	 */
6602	public function get_real_type($find_handler = false)
6603	{
6604		// Mime-types by handler.
6605		$types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash
6606		$types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player
6607		$types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime
6608		$types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media
6609		$types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3
6610
6611		if ($this->get_type() !== null)
6612		{
6613			$type = strtolower($this->type);
6614		}
6615		else
6616		{
6617			$type = null;
6618		}
6619
6620		// If we encounter an unsupported mime-type, check the file extension and guess intelligently.
6621		if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3)))
6622		{
6623			switch (strtolower($this->get_extension()))
6624			{
6625				// Audio mime-types
6626				case 'aac':
6627				case 'adts':
6628					$type = 'audio/acc';
6629					break;
6630
6631				case 'aif':
6632				case 'aifc':
6633				case 'aiff':
6634				case 'cdda':
6635					$type = 'audio/aiff';
6636					break;
6637
6638				case 'bwf':
6639					$type = 'audio/wav';
6640					break;
6641
6642				case 'kar':
6643				case 'mid':
6644				case 'midi':
6645				case 'smf':
6646					$type = 'audio/midi';
6647					break;
6648
6649				case 'm4a':
6650					$type = 'audio/x-m4a';
6651					break;
6652
6653				case 'mp3':
6654				case 'swa':
6655					$type = 'audio/mp3';
6656					break;
6657
6658				case 'wav':
6659					$type = 'audio/wav';
6660					break;
6661
6662				case 'wax':
6663					$type = 'audio/x-ms-wax';
6664					break;
6665
6666				case 'wma':
6667					$type = 'audio/x-ms-wma';
6668					break;
6669
6670				// Video mime-types
6671				case '3gp':
6672				case '3gpp':
6673					$type = 'video/3gpp';
6674					break;
6675
6676				case '3g2':
6677				case '3gp2':
6678					$type = 'video/3gpp2';
6679					break;
6680
6681				case 'asf':
6682					$type = 'video/x-ms-asf';
6683					break;
6684
6685				case 'flv':
6686					$type = 'video/x-flv';
6687					break;
6688
6689				case 'm1a':
6690				case 'm1s':
6691				case 'm1v':
6692				case 'm15':
6693				case 'm75':
6694				case 'mp2':
6695				case 'mpa':
6696				case 'mpeg':
6697				case 'mpg':
6698				case 'mpm':
6699				case 'mpv':
6700					$type = 'video/mpeg';
6701					break;
6702
6703				case 'm4v':
6704					$type = 'video/x-m4v';
6705					break;
6706
6707				case 'mov':
6708				case 'qt':
6709					$type = 'video/quicktime';
6710					break;
6711
6712				case 'mp4':
6713				case 'mpg4':
6714					$type = 'video/mp4';
6715					break;
6716
6717				case 'sdv':
6718					$type = 'video/sd-video';
6719					break;
6720
6721				case 'wm':
6722					$type = 'video/x-ms-wm';
6723					break;
6724
6725				case 'wmv':
6726					$type = 'video/x-ms-wmv';
6727					break;
6728
6729				case 'wvx':
6730					$type = 'video/x-ms-wvx';
6731					break;
6732
6733				// Flash mime-types
6734				case 'spl':
6735					$type = 'application/futuresplash';
6736					break;
6737
6738				case 'swf':
6739					$type = 'application/x-shockwave-flash';
6740					break;
6741			}
6742		}
6743
6744		if ($find_handler)
6745		{
6746			if (in_array($type, $types_flash))
6747			{
6748				return 'flash';
6749			}
6750			elseif (in_array($type, $types_fmedia))
6751			{
6752				return 'fmedia';
6753			}
6754			elseif (in_array($type, $types_quicktime))
6755			{
6756				return 'quicktime';
6757			}
6758			elseif (in_array($type, $types_wmedia))
6759			{
6760				return 'wmedia';
6761			}
6762			elseif (in_array($type, $types_mp3))
6763			{
6764				return 'mp3';
6765			}
6766			else
6767			{
6768				return null;
6769			}
6770		}
6771		else
6772		{
6773			return $type;
6774		}
6775	}
6776}
6777
6778/**
6779 * General SimplePie exception class
6780 *
6781 * @package SimplePie
6782 */
6783class SimplePie_Exception extends Exception
6784{
6785}
6786
6787/**
6788 * Used for fetching remote files and reading local files
6789 *
6790 * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support
6791 *
6792 * This class can be overloaded with {@see SimplePie::set_file_class()}
6793 *
6794 * @package SimplePie
6795 * @subpackage HTTP
6796 * @todo Move to properly supporting RFC2616 (HTTP/1.1)
6797 */
6798class SimplePie_File
6799{
6800	var $url;
6801	var $useragent;
6802	var $success = true;
6803	var $headers = array();
6804	var $body;
6805	var $status_code;
6806	var $redirects = 0;
6807	var $error;
6808	var $method = SIMPLEPIE_FILE_SOURCE_NONE;
6809
6810	public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
6811	{
6812		if (class_exists('idna_convert'))
6813		{
6814			$idn = new idna_convert();
6815			$parsed = SimplePie_Misc::parse_url($url);
6816			$url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
6817		}
6818		$this->url = $url;
6819		$this->useragent = $useragent;
6820		if (preg_match('/^http(s)?:\/\//i', $url))
6821		{
6822			if ($useragent === null)
6823			{
6824				$useragent = ini_get('user_agent');
6825				$this->useragent = $useragent;
6826			}
6827			if (!is_array($headers))
6828			{
6829				$headers = array();
6830			}
6831			if (!$force_fsockopen && function_exists('curl_exec'))
6832			{
6833				$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
6834				$fp = curl_init();
6835				$headers2 = array();
6836				foreach ($headers as $key => $value)
6837				{
6838					$headers2[] = "$key: $value";
6839				}
6840				if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
6841				{
6842					curl_setopt($fp, CURLOPT_ENCODING, '');
6843				}
6844				curl_setopt($fp, CURLOPT_URL, $url);
6845				curl_setopt($fp, CURLOPT_HEADER, 1);
6846				curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
6847				curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
6848				curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
6849				curl_setopt($fp, CURLOPT_REFERER, $url);
6850				curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
6851				curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
6852				if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
6853				{
6854					curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
6855					curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
6856				}
6857
6858				$this->headers = curl_exec($fp);
6859				if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
6860				{
6861					curl_setopt($fp, CURLOPT_ENCODING, 'none');
6862					$this->headers = curl_exec($fp);
6863				}
6864				if (curl_errno($fp))
6865				{
6866					$this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
6867					$this->success = false;
6868				}
6869				else
6870				{
6871					$info = curl_getinfo($fp);
6872					curl_close($fp);
6873					$this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
6874					$this->headers = array_pop($this->headers);
6875					$parser = new SimplePie_HTTP_Parser($this->headers);
6876					if ($parser->parse())
6877					{
6878						$this->headers = $parser->headers;
6879						$this->body = $parser->body;
6880						$this->status_code = $parser->status_code;
6881						if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
6882						{
6883							$this->redirects++;
6884							$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
6885							return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
6886						}
6887					}
6888				}
6889			}
6890			else
6891			{
6892				$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
6893				$url_parts = parse_url($url);
6894				$socket_host = $url_parts['host'];
6895				if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
6896				{
6897					$socket_host = "ssl://$url_parts[host]";
6898					$url_parts['port'] = 443;
6899				}
6900				if (!isset($url_parts['port']))
6901				{
6902					$url_parts['port'] = 80;
6903				}
6904				$fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
6905				if (!$fp)
6906				{
6907					$this->error = 'fsockopen error: ' . $errstr;
6908					$this->success = false;
6909				}
6910				else
6911				{
6912					stream_set_timeout($fp, $timeout);
6913					if (isset($url_parts['path']))
6914					{
6915						if (isset($url_parts['query']))
6916						{
6917							$get = "$url_parts[path]?$url_parts[query]";
6918						}
6919						else
6920						{
6921							$get = $url_parts['path'];
6922						}
6923					}
6924					else
6925					{
6926						$get = '/';
6927					}
6928					$out = "GET $get HTTP/1.1\r\n";
6929					$out .= "Host: $url_parts[host]\r\n";
6930					$out .= "User-Agent: $useragent\r\n";
6931					if (extension_loaded('zlib'))
6932					{
6933						$out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
6934					}
6935
6936					if (isset($url_parts['user']) && isset($url_parts['pass']))
6937					{
6938						$out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
6939					}
6940					foreach ($headers as $key => $value)
6941					{
6942						$out .= "$key: $value\r\n";
6943					}
6944					$out .= "Connection: Close\r\n\r\n";
6945					fwrite($fp, $out);
6946
6947					$info = stream_get_meta_data($fp);
6948
6949					$this->headers = '';
6950					while (!$info['eof'] && !$info['timed_out'])
6951					{
6952						$this->headers .= fread($fp, 1160);
6953						$info = stream_get_meta_data($fp);
6954					}
6955					if (!$info['timed_out'])
6956					{
6957						$parser = new SimplePie_HTTP_Parser($this->headers);
6958						if ($parser->parse())
6959						{
6960							$this->headers = $parser->headers;
6961							$this->body = $parser->body;
6962							$this->status_code = $parser->status_code;
6963							if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
6964							{
6965								$this->redirects++;
6966								$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
6967								return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
6968							}
6969							if (isset($this->headers['content-encoding']))
6970							{
6971								// Hey, we act dumb elsewhere, so let's do that here too
6972								switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
6973								{
6974									case 'gzip':
6975									case 'x-gzip':
6976										$decoder = new SimplePie_gzdecode($this->body);
6977										if (!$decoder->parse())
6978										{
6979											$this->error = 'Unable to decode HTTP "gzip" stream';
6980											$this->success = false;
6981										}
6982										else
6983										{
6984											$this->body = $decoder->data;
6985										}
6986										break;
6987
6988									case 'deflate':
6989										if (($decompressed = gzinflate($this->body)) !== false)
6990										{
6991											$this->body = $decompressed;
6992										}
6993										else if (($decompressed = gzuncompress($this->body)) !== false)
6994										{
6995											$this->body = $decompressed;
6996										}
6997										else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false)
6998										{
6999											$this->body = $decompressed;
7000										}
7001										else
7002										{
7003											$this->error = 'Unable to decode HTTP "deflate" stream';
7004											$this->success = false;
7005										}
7006										break;
7007
7008									default:
7009										$this->error = 'Unknown content coding';
7010										$this->success = false;
7011								}
7012							}
7013						}
7014					}
7015					else
7016					{
7017						$this->error = 'fsocket timed out';
7018						$this->success = false;
7019					}
7020					fclose($fp);
7021				}
7022			}
7023		}
7024		else
7025		{
7026			$this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
7027			if (!$this->body = file_get_contents($url))
7028			{
7029				$this->error = 'file_get_contents could not read the file';
7030				$this->success = false;
7031			}
7032		}
7033	}
7034}
7035
7036/**
7037 * Decode 'gzip' encoded HTTP data
7038 *
7039 * @package SimplePie
7040 * @subpackage HTTP
7041 * @link http://www.gzip.org/format.txt
7042 */
7043class SimplePie_gzdecode
7044{
7045	/**
7046	 * Compressed data
7047	 *
7048	 * @access private
7049	 * @var string
7050	 * @see gzdecode::$data
7051	 */
7052	var $compressed_data;
7053
7054	/**
7055	 * Size of compressed data
7056	 *
7057	 * @access private
7058	 * @var int
7059	 */
7060	var $compressed_size;
7061
7062	/**
7063	 * Minimum size of a valid gzip string
7064	 *
7065	 * @access private
7066	 * @var int
7067	 */
7068	var $min_compressed_size = 18;
7069
7070	/**
7071	 * Current position of pointer
7072	 *
7073	 * @access private
7074	 * @var int
7075	 */
7076	var $position = 0;
7077
7078	/**
7079	 * Flags (FLG)
7080	 *
7081	 * @access private
7082	 * @var int
7083	 */
7084	var $flags;
7085
7086	/**
7087	 * Uncompressed data
7088	 *
7089	 * @access public
7090	 * @see gzdecode::$compressed_data
7091	 * @var string
7092	 */
7093	var $data;
7094
7095	/**
7096	 * Modified time
7097	 *
7098	 * @access public
7099	 * @var int
7100	 */
7101	var $MTIME;
7102
7103	/**
7104	 * Extra Flags
7105	 *
7106	 * @access public
7107	 * @var int
7108	 */
7109	var $XFL;
7110
7111	/**
7112	 * Operating System
7113	 *
7114	 * @access public
7115	 * @var int
7116	 */
7117	var $OS;
7118
7119	/**
7120	 * Subfield ID 1
7121	 *
7122	 * @access public
7123	 * @see gzdecode::$extra_field
7124	 * @see gzdecode::$SI2
7125	 * @var string
7126	 */
7127	var $SI1;
7128
7129	/**
7130	 * Subfield ID 2
7131	 *
7132	 * @access public
7133	 * @see gzdecode::$extra_field
7134	 * @see gzdecode::$SI1
7135	 * @var string
7136	 */
7137	var $SI2;
7138
7139	/**
7140	 * Extra field content
7141	 *
7142	 * @access public
7143	 * @see gzdecode::$SI1
7144	 * @see gzdecode::$SI2
7145	 * @var string
7146	 */
7147	var $extra_field;
7148
7149	/**
7150	 * Original filename
7151	 *
7152	 * @access public
7153	 * @var string
7154	 */
7155	var $filename;
7156
7157	/**
7158	 * Human readable comment
7159	 *
7160	 * @access public
7161	 * @var string
7162	 */
7163	var $comment;
7164
7165	/**
7166	 * Don't allow anything to be set
7167	 *
7168	 * @param string $name
7169	 * @param mixed $value
7170	 */
7171	public function __set($name, $value)
7172	{
7173		trigger_error("Cannot write property $name", E_USER_ERROR);
7174	}
7175
7176	/**
7177	 * Set the compressed string and related properties
7178	 *
7179	 * @param string $data
7180	 */
7181	public function __construct($data)
7182	{
7183		$this->compressed_data = $data;
7184		$this->compressed_size = strlen($data);
7185	}
7186
7187	/**
7188	 * Decode the GZIP stream
7189	 *
7190	 * @return bool Successfulness
7191	 */
7192	public function parse()
7193	{
7194		if ($this->compressed_size >= $this->min_compressed_size)
7195		{
7196			// Check ID1, ID2, and CM
7197			if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
7198			{
7199				return false;
7200			}
7201
7202			// Get the FLG (FLaGs)
7203			$this->flags = ord($this->compressed_data[3]);
7204
7205			// FLG bits above (1 << 4) are reserved
7206			if ($this->flags > 0x1F)
7207			{
7208				return false;
7209			}
7210
7211			// Advance the pointer after the above
7212			$this->position += 4;
7213
7214			// MTIME
7215			$mtime = substr($this->compressed_data, $this->position, 4);
7216			// Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
7217			if (current(unpack('S', "\x00\x01")) === 1)
7218			{
7219				$mtime = strrev($mtime);
7220			}
7221			$this->MTIME = current(unpack('l', $mtime));
7222			$this->position += 4;
7223
7224			// Get the XFL (eXtra FLags)
7225			$this->XFL = ord($this->compressed_data[$this->position++]);
7226
7227			// Get the OS (Operating System)
7228			$this->OS = ord($this->compressed_data[$this->position++]);
7229
7230			// Parse the FEXTRA
7231			if ($this->flags & 4)
7232			{
7233				// Read subfield IDs
7234				$this->SI1 = $this->compressed_data[$this->position++];
7235				$this->SI2 = $this->compressed_data[$this->position++];
7236
7237				// SI2 set to zero is reserved for future use
7238				if ($this->SI2 === "\x00")
7239				{
7240					return false;
7241				}
7242
7243				// Get the length of the extra field
7244				$len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
7245				$this->position += 2;
7246
7247				// Check the length of the string is still valid
7248				$this->min_compressed_size += $len + 4;
7249				if ($this->compressed_size >= $this->min_compressed_size)
7250				{
7251					// Set the extra field to the given data
7252					$this->extra_field = substr($this->compressed_data, $this->position, $len);
7253					$this->position += $len;
7254				}
7255				else
7256				{
7257					return false;
7258				}
7259			}
7260
7261			// Parse the FNAME
7262			if ($this->flags & 8)
7263			{
7264				// Get the length of the filename
7265				$len = strcspn($this->compressed_data, "\x00", $this->position);
7266
7267				// Check the length of the string is still valid
7268				$this->min_compressed_size += $len + 1;
7269				if ($this->compressed_size >= $this->min_compressed_size)
7270				{
7271					// Set the original filename to the given string
7272					$this->filename = substr($this->compressed_data, $this->position, $len);
7273					$this->position += $len + 1;
7274				}
7275				else
7276				{
7277					return false;
7278				}
7279			}
7280
7281			// Parse the FCOMMENT
7282			if ($this->flags & 16)
7283			{
7284				// Get the length of the comment
7285				$len = strcspn($this->compressed_data, "\x00", $this->position);
7286
7287				// Check the length of the string is still valid
7288				$this->min_compressed_size += $len + 1;
7289				if ($this->compressed_size >= $this->min_compressed_size)
7290				{
7291					// Set the original comment to the given string
7292					$this->comment = substr($this->compressed_data, $this->position, $len);
7293					$this->position += $len + 1;
7294				}
7295				else
7296				{
7297					return false;
7298				}
7299			}
7300
7301			// Parse the FHCRC
7302			if ($this->flags & 2)
7303			{
7304				// Check the length of the string is still valid
7305				$this->min_compressed_size += $len + 2;
7306				if ($this->compressed_size >= $this->min_compressed_size)
7307				{
7308					// Read the CRC
7309					$crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
7310
7311					// Check the CRC matches
7312					if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
7313					{
7314						$this->position += 2;
7315					}
7316					else
7317					{
7318						return false;
7319					}
7320				}
7321				else
7322				{
7323					return false;
7324				}
7325			}
7326
7327			// Decompress the actual data
7328			if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
7329			{
7330				return false;
7331			}
7332			else
7333			{
7334				$this->position = $this->compressed_size - 8;
7335			}
7336
7337			// Check CRC of data
7338			$crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
7339			$this->position += 4;
7340			/*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
7341			{
7342				return false;
7343			}*/
7344
7345			// Check ISIZE of data
7346			$isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
7347			$this->position += 4;
7348			if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
7349			{
7350				return false;
7351			}
7352
7353			// Wow, against all odds, we've actually got a valid gzip string
7354			return true;
7355		}
7356		else
7357		{
7358			return false;
7359		}
7360	}
7361}
7362
7363/**
7364 * HTTP Response Parser
7365 *
7366 * @package SimplePie
7367 * @subpackage HTTP
7368 */
7369class SimplePie_HTTP_Parser
7370{
7371	/**
7372	 * HTTP Version
7373	 *
7374	 * @var float
7375	 */
7376	public $http_version = 0.0;
7377
7378	/**
7379	 * Status code
7380	 *
7381	 * @var int
7382	 */
7383	public $status_code = 0;
7384
7385	/**
7386	 * Reason phrase
7387	 *
7388	 * @var string
7389	 */
7390	public $reason = '';
7391
7392	/**
7393	 * Key/value pairs of the headers
7394	 *
7395	 * @var array
7396	 */
7397	public $headers = array();
7398
7399	/**
7400	 * Body of the response
7401	 *
7402	 * @var string
7403	 */
7404	public $body = '';
7405
7406	/**
7407	 * Current state of the state machine
7408	 *
7409	 * @var string
7410	 */
7411	protected $state = 'http_version';
7412
7413	/**
7414	 * Input data
7415	 *
7416	 * @var string
7417	 */
7418	protected $data = '';
7419
7420	/**
7421	 * Input data length (to avoid calling strlen() everytime this is needed)
7422	 *
7423	 * @var int
7424	 */
7425	protected $data_length = 0;
7426
7427	/**
7428	 * Current position of the pointer
7429	 *
7430	 * @var int
7431	 */
7432	protected $position = 0;
7433
7434	/**
7435	 * Name of the hedaer currently being parsed
7436	 *
7437	 * @var string
7438	 */
7439	protected $name = '';
7440
7441	/**
7442	 * Value of the hedaer currently being parsed
7443	 *
7444	 * @var string
7445	 */
7446	protected $value = '';
7447
7448	/**
7449	 * Create an instance of the class with the input data
7450	 *
7451	 * @param string $data Input data
7452	 */
7453	public function __construct($data)
7454	{
7455		$this->data = $data;
7456		$this->data_length = strlen($this->data);
7457	}
7458
7459	/**
7460	 * Parse the input data
7461	 *
7462	 * @return bool true on success, false on failure
7463	 */
7464	public function parse()
7465	{
7466		while ($this->state && $this->state !== 'emit' && $this->has_data())
7467		{
7468			$state = $this->state;
7469			$this->$state();
7470		}
7471		$this->data = '';
7472		if ($this->state === 'emit' || $this->state === 'body')
7473		{
7474			return true;
7475		}
7476		else
7477		{
7478			$this->http_version = '';
7479			$this->status_code = '';
7480			$this->reason = '';
7481			$this->headers = array();
7482			$this->body = '';
7483			return false;
7484		}
7485	}
7486
7487	/**
7488	 * Check whether there is data beyond the pointer
7489	 *
7490	 * @return bool true if there is further data, false if not
7491	 */
7492	protected function has_data()
7493	{
7494		return (bool) ($this->position < $this->data_length);
7495	}
7496
7497	/**
7498	 * See if the next character is LWS
7499	 *
7500	 * @return bool true if the next character is LWS, false if not
7501	 */
7502	protected function is_linear_whitespace()
7503	{
7504		return (bool) ($this->data[$this->position] === "\x09"
7505			|| $this->data[$this->position] === "\x20"
7506			|| ($this->data[$this->position] === "\x0A"
7507				&& isset($this->data[$this->position + 1])
7508				&& ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
7509	}
7510
7511	/**
7512	 * Parse the HTTP version
7513	 */
7514	protected function http_version()
7515	{
7516		if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
7517		{
7518			$len = strspn($this->data, '0123456789.', 5);
7519			$this->http_version = substr($this->data, 5, $len);
7520			$this->position += 5 + $len;
7521			if (substr_count($this->http_version, '.') <= 1)
7522			{
7523				$this->http_version = (float) $this->http_version;
7524				$this->position += strspn($this->data, "\x09\x20", $this->position);
7525				$this->state = 'status';
7526			}
7527			else
7528			{
7529				$this->state = false;
7530			}
7531		}
7532		else
7533		{
7534			$this->state = false;
7535		}
7536	}
7537
7538	/**
7539	 * Parse the status code
7540	 */
7541	protected function status()
7542	{
7543		if ($len = strspn($this->data, '0123456789', $this->position))
7544		{
7545			$this->status_code = (int) substr($this->data, $this->position, $len);
7546			$this->position += $len;
7547			$this->state = 'reason';
7548		}
7549		else
7550		{
7551			$this->state = false;
7552		}
7553	}
7554
7555	/**
7556	 * Parse the reason phrase
7557	 */
7558	protected function reason()
7559	{
7560		$len = strcspn($this->data, "\x0A", $this->position);
7561		$this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
7562		$this->position += $len + 1;
7563		$this->state = 'new_line';
7564	}
7565
7566	/**
7567	 * Deal with a new line, shifting data around as needed
7568	 */
7569	protected function new_line()
7570	{
7571		$this->value = trim($this->value, "\x0D\x20");
7572		if ($this->name !== '' && $this->value !== '')
7573		{
7574			$this->name = strtolower($this->name);
7575			// We should only use the last Content-Type header. c.f. issue #1
7576			if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
7577			{
7578				$this->headers[$this->name] .= ', ' . $this->value;
7579			}
7580			else
7581			{
7582				$this->headers[$this->name] = $this->value;
7583			}
7584		}
7585		$this->name = '';
7586		$this->value = '';
7587		if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
7588		{
7589			$this->position += 2;
7590			$this->state = 'body';
7591		}
7592		elseif ($this->data[$this->position] === "\x0A")
7593		{
7594			$this->position++;
7595			$this->state = 'body';
7596		}
7597		else
7598		{
7599			$this->state = 'name';
7600		}
7601	}
7602
7603	/**
7604	 * Parse a header name
7605	 */
7606	protected function name()
7607	{
7608		$len = strcspn($this->data, "\x0A:", $this->position);
7609		if (isset($this->data[$this->position + $len]))
7610		{
7611			if ($this->data[$this->position + $len] === "\x0A")
7612			{
7613				$this->position += $len;
7614				$this->state = 'new_line';
7615			}
7616			else
7617			{
7618				$this->name = substr($this->data, $this->position, $len);
7619				$this->position += $len + 1;
7620				$this->state = 'value';
7621			}
7622		}
7623		else
7624		{
7625			$this->state = false;
7626		}
7627	}
7628
7629	/**
7630	 * Parse LWS, replacing consecutive LWS characters with a single space
7631	 */
7632	protected function linear_whitespace()
7633	{
7634		do
7635		{
7636			if (substr($this->data, $this->position, 2) === "\x0D\x0A")
7637			{
7638				$this->position += 2;
7639			}
7640			elseif ($this->data[$this->position] === "\x0A")
7641			{
7642				$this->position++;
7643			}
7644			$this->position += strspn($this->data, "\x09\x20", $this->position);
7645		} while ($this->has_data() && $this->is_linear_whitespace());
7646		$this->value .= "\x20";
7647	}
7648
7649	/**
7650	 * See what state to move to while within non-quoted header values
7651	 */
7652	protected function value()
7653	{
7654		if ($this->is_linear_whitespace())
7655		{
7656			$this->linear_whitespace();
7657		}
7658		else
7659		{
7660			switch ($this->data[$this->position])
7661			{
7662				case '"':
7663					// Workaround for ETags: we have to include the quotes as
7664					// part of the tag.
7665					if (strtolower($this->name) === 'etag')
7666					{
7667						$this->value .= '"';
7668						$this->position++;
7669						$this->state = 'value_char';
7670						break;
7671					}
7672					$this->position++;
7673					$this->state = 'quote';
7674					break;
7675
7676				case "\x0A":
7677					$this->position++;
7678					$this->state = 'new_line';
7679					break;
7680
7681				default:
7682					$this->state = 'value_char';
7683					break;
7684			}
7685		}
7686	}
7687
7688	/**
7689	 * Parse a header value while outside quotes
7690	 */
7691	protected function value_char()
7692	{
7693		$len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
7694		$this->value .= substr($this->data, $this->position, $len);
7695		$this->position += $len;
7696		$this->state = 'value';
7697	}
7698
7699	/**
7700	 * See what state to move to while within quoted header values
7701	 */
7702	protected function quote()
7703	{
7704		if ($this->is_linear_whitespace())
7705		{
7706			$this->linear_whitespace();
7707		}
7708		else
7709		{
7710			switch ($this->data[$this->position])
7711			{
7712				case '"':
7713					$this->position++;
7714					$this->state = 'value';
7715					break;
7716
7717				case "\x0A":
7718					$this->position++;
7719					$this->state = 'new_line';
7720					break;
7721
7722				case '\\':
7723					$this->position++;
7724					$this->state = 'quote_escaped';
7725					break;
7726
7727				default:
7728					$this->state = 'quote_char';
7729					break;
7730			}
7731		}
7732	}
7733
7734	/**
7735	 * Parse a header value while within quotes
7736	 */
7737	protected function quote_char()
7738	{
7739		$len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
7740		$this->value .= substr($this->data, $this->position, $len);
7741		$this->position += $len;
7742		$this->state = 'value';
7743	}
7744
7745	/**
7746	 * Parse an escaped character within quotes
7747	 */
7748	protected function quote_escaped()
7749	{
7750		$this->value .= $this->data[$this->position];
7751		$this->position++;
7752		$this->state = 'quote';
7753	}
7754
7755	/**
7756	 * Parse the body
7757	 */
7758	protected function body()
7759	{
7760		$this->body = substr($this->data, $this->position);
7761		if (!empty($this->headers['transfer-encoding']))
7762		{
7763			unset($this->headers['transfer-encoding']);
7764			$this->state = 'chunked';
7765		}
7766		else
7767		{
7768			$this->state = 'emit';
7769		}
7770	}
7771
7772	/**
7773	 * Parsed a "Transfer-Encoding: chunked" body
7774	 */
7775	protected function chunked()
7776	{
7777		if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
7778		{
7779			$this->state = 'emit';
7780			return;
7781		}
7782
7783		$decoded = '';
7784		$encoded = $this->body;
7785
7786		while (true)
7787		{
7788			$is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
7789			if (!$is_chunked)
7790			{
7791				// Looks like it's not chunked after all
7792				$this->state = 'emit';
7793				return;
7794			}
7795
7796			$length = hexdec(trim($matches[1]));
7797			if ($length === 0)
7798			{
7799				// Ignore trailer headers
7800				$this->state = 'emit';
7801				$this->body = $decoded;
7802				return;
7803			}
7804
7805			$chunk_length = strlen($matches[0]);
7806			$decoded .= $part = substr($encoded, $chunk_length, $length);
7807			$encoded = substr($encoded, $chunk_length + $length + 2);
7808
7809			if (trim($encoded) === '0' || empty($encoded))
7810			{
7811				$this->state = 'emit';
7812				$this->body = $decoded;
7813				return;
7814			}
7815		}
7816	}
7817}
7818
7819/**
7820 * IRI parser/serialiser/normaliser
7821 *
7822 * @package SimplePie
7823 * @subpackage HTTP
7824 * @author Geoffrey Sneddon
7825 * @author Steve Minutillo
7826 * @author Ryan McCue
7827 * @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue
7828 * @license http://www.opensource.org/licenses/bsd-license.php
7829 */
7830class SimplePie_IRI
7831{
7832	/**
7833	 * Scheme
7834	 *
7835	 * @var string
7836	 */
7837	protected $scheme = null;
7838
7839	/**
7840	 * User Information
7841	 *
7842	 * @var string
7843	 */
7844	protected $iuserinfo = null;
7845
7846	/**
7847	 * ihost
7848	 *
7849	 * @var string
7850	 */
7851	protected $ihost = null;
7852
7853	/**
7854	 * Port
7855	 *
7856	 * @var string
7857	 */
7858	protected $port = null;
7859
7860	/**
7861	 * ipath
7862	 *
7863	 * @var string
7864	 */
7865	protected $ipath = '';
7866
7867	/**
7868	 * iquery
7869	 *
7870	 * @var string
7871	 */
7872	protected $iquery = null;
7873
7874	/**
7875	 * ifragment
7876	 *
7877	 * @var string
7878	 */
7879	protected $ifragment = null;
7880
7881	/**
7882	 * Normalization database
7883	 *
7884	 * Each key is the scheme, each value is an array with each key as the IRI
7885	 * part and value as the default value for that part.
7886	 */
7887	protected $normalization = array(
7888		'acap' => array(
7889			'port' => 674
7890		),
7891		'dict' => array(
7892			'port' => 2628
7893		),
7894		'file' => array(
7895			'ihost' => 'localhost'
7896		),
7897		'http' => array(
7898			'port' => 80,
7899			'ipath' => '/'
7900		),
7901		'https' => array(
7902			'port' => 443,
7903			'ipath' => '/'
7904		),
7905	);
7906
7907	/**
7908	 * Return the entire IRI when you try and read the object as a string
7909	 *
7910	 * @return string
7911	 */
7912	public function __toString()
7913	{
7914		return $this->get_iri();
7915	}
7916
7917	/**
7918	 * Overload __set() to provide access via properties
7919	 *
7920	 * @param string $name Property name
7921	 * @param mixed $value Property value
7922	 */
7923	public function __set($name, $value)
7924	{
7925		if (method_exists($this, 'set_' . $name))
7926		{
7927			call_user_func(array($this, 'set_' . $name), $value);
7928		}
7929		elseif (
7930			   $name === 'iauthority'
7931			|| $name === 'iuserinfo'
7932			|| $name === 'ihost'
7933			|| $name === 'ipath'
7934			|| $name === 'iquery'
7935			|| $name === 'ifragment'
7936		)
7937		{
7938			call_user_func(array($this, 'set_' . substr($name, 1)), $value);
7939		}
7940	}
7941
7942	/**
7943	 * Overload __get() to provide access via properties
7944	 *
7945	 * @param string $name Property name
7946	 * @return mixed
7947	 */
7948	public function __get($name)
7949	{
7950		// isset() returns false for null, we don't want to do that
7951		// Also why we use array_key_exists below instead of isset()
7952		$props = get_object_vars($this);
7953
7954		if (
7955			$name === 'iri' ||
7956			$name === 'uri' ||
7957			$name === 'iauthority' ||
7958			$name === 'authority'
7959		)
7960		{
7961			$return = $this->{"get_$name"}();
7962		}
7963		elseif (array_key_exists($name, $props))
7964		{
7965			$return = $this->$name;
7966		}
7967		// host -> ihost
7968		elseif (($prop = 'i' . $name) && array_key_exists($prop, $props))
7969		{
7970			$name = $prop;
7971			$return = $this->$prop;
7972		}
7973		// ischeme -> scheme
7974		elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props))
7975		{
7976			$name = $prop;
7977			$return = $this->$prop;
7978		}
7979		else
7980		{
7981			trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
7982			$return = null;
7983		}
7984
7985		if ($return === null && isset($this->normalization[$this->scheme][$name]))
7986		{
7987			return $this->normalization[$this->scheme][$name];
7988		}
7989		else
7990		{
7991			return $return;
7992		}
7993	}
7994
7995	/**
7996	 * Overload __isset() to provide access via properties
7997	 *
7998	 * @param string $name Property name
7999	 * @return bool
8000	 */
8001	public function __isset($name)
8002	{
8003		if (method_exists($this, 'get_' . $name) || isset($this->$name))
8004		{
8005			return true;
8006		}
8007		else
8008		{
8009			return false;
8010		}
8011	}
8012
8013	/**
8014	 * Overload __unset() to provide access via properties
8015	 *
8016	 * @param string $name Property name
8017	 */
8018	public function __unset($name)
8019	{
8020		if (method_exists($this, 'set_' . $name))
8021		{
8022			call_user_func(array($this, 'set_' . $name), '');
8023		}
8024	}
8025
8026	/**
8027	 * Create a new IRI object, from a specified string
8028	 *
8029	 * @param string $iri
8030	 */
8031	public function __construct($iri = null)
8032	{
8033		$this->set_iri($iri);
8034	}
8035
8036	/**
8037	 * Create a new IRI object by resolving a relative IRI
8038	 *
8039	 * Returns false if $base is not absolute, otherwise an IRI.
8040	 *
8041	 * @param IRI|string $base (Absolute) Base IRI
8042	 * @param IRI|string $relative Relative IRI
8043	 * @return IRI|false
8044	 */
8045	public static function absolutize($base, $relative)
8046	{
8047		if (!($relative instanceof SimplePie_IRI))
8048		{
8049			$relative = new SimplePie_IRI($relative);
8050		}
8051		if (!$relative->is_valid())
8052		{
8053			return false;
8054		}
8055		elseif ($relative->scheme !== null)
8056		{
8057			return clone $relative;
8058		}
8059		else
8060		{
8061			if (!($base instanceof SimplePie_IRI))
8062			{
8063				$base = new SimplePie_IRI($base);
8064			}
8065			if ($base->scheme !== null && $base->is_valid())
8066			{
8067				if ($relative->get_iri() !== '')
8068				{
8069					if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null)
8070					{
8071						$target = clone $relative;
8072						$target->scheme = $base->scheme;
8073					}
8074					else
8075					{
8076						$target = new SimplePie_IRI;
8077						$target->scheme = $base->scheme;
8078						$target->iuserinfo = $base->iuserinfo;
8079						$target->ihost = $base->ihost;
8080						$target->port = $base->port;
8081						if ($relative->ipath !== '')
8082						{
8083							if ($relative->ipath[0] === '/')
8084							{
8085								$target->ipath = $relative->ipath;
8086							}
8087							elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '')
8088							{
8089								$target->ipath = '/' . $relative->ipath;
8090							}
8091							elseif (($last_segment = strrpos($base->ipath, '/')) !== false)
8092							{
8093								$target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
8094							}
8095							else
8096							{
8097								$target->ipath = $relative->ipath;
8098							}
8099							$target->ipath = $target->remove_dot_segments($target->ipath);
8100							$target->iquery = $relative->iquery;
8101						}
8102						else
8103						{
8104							$target->ipath = $base->ipath;
8105							if ($relative->iquery !== null)
8106							{
8107								$target->iquery = $relative->iquery;
8108							}
8109							elseif ($base->iquery !== null)
8110							{
8111								$target->iquery = $base->iquery;
8112							}
8113						}
8114						$target->ifragment = $relative->ifragment;
8115					}
8116				}
8117				else
8118				{
8119					$target = clone $base;
8120					$target->ifragment = null;
8121				}
8122				$target->scheme_normalization();
8123				return $target;
8124			}
8125			else
8126			{
8127				return false;
8128			}
8129		}
8130	}
8131
8132	/**
8133	 * Parse an IRI into scheme/authority/path/query/fragment segments
8134	 *
8135	 * @param string $iri
8136	 * @return array
8137	 */
8138	protected function parse_iri($iri)
8139	{
8140		$iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
8141		if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match))
8142		{
8143			if ($match[1] === '')
8144			{
8145				$match['scheme'] = null;
8146			}
8147			if (!isset($match[3]) || $match[3] === '')
8148			{
8149				$match['authority'] = null;
8150			}
8151			if (!isset($match[5]))
8152			{
8153				$match['path'] = '';
8154			}
8155			if (!isset($match[6]) || $match[6] === '')
8156			{
8157				$match['query'] = null;
8158			}
8159			if (!isset($match[8]) || $match[8] === '')
8160			{
8161				$match['fragment'] = null;
8162			}
8163			return $match;
8164		}
8165		else
8166		{
8167			// This can occur when a paragraph is accidentally parsed as a URI
8168			return false;
8169		}
8170	}
8171
8172	/**
8173	 * Remove dot segments from a path
8174	 *
8175	 * @param string $input
8176	 * @return string
8177	 */
8178	protected function remove_dot_segments($input)
8179	{
8180		$output = '';
8181		while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
8182		{
8183			// A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
8184			if (strpos($input, '../') === 0)
8185			{
8186				$input = substr($input, 3);
8187			}
8188			elseif (strpos($input, './') === 0)
8189			{
8190				$input = substr($input, 2);
8191			}
8192			// B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
8193			elseif (strpos($input, '/./') === 0)
8194			{
8195				$input = substr($input, 2);
8196			}
8197			elseif ($input === '/.')
8198			{
8199				$input = '/';
8200			}
8201			// C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
8202			elseif (strpos($input, '/../') === 0)
8203			{
8204				$input = substr($input, 3);
8205				$output = substr_replace($output, '', strrpos($output, '/'));
8206			}
8207			elseif ($input === '/..')
8208			{
8209				$input = '/';
8210				$output = substr_replace($output, '', strrpos($output, '/'));
8211			}
8212			// D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
8213			elseif ($input === '.' || $input === '..')
8214			{
8215				$input = '';
8216			}
8217			// E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
8218			elseif (($pos = strpos($input, '/', 1)) !== false)
8219			{
8220				$output .= substr($input, 0, $pos);
8221				$input = substr_replace($input, '', 0, $pos);
8222			}
8223			else
8224			{
8225				$output .= $input;
8226				$input = '';
8227			}
8228		}
8229		return $output . $input;
8230	}
8231
8232	/**
8233	 * Replace invalid character with percent encoding
8234	 *
8235	 * @param string $string Input string
8236	 * @param string $extra_chars Valid characters not in iunreserved or
8237	 *                            iprivate (this is ASCII-only)
8238	 * @param bool $iprivate Allow iprivate
8239	 * @return string
8240	 */
8241	protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
8242	{
8243		// Normalize as many pct-encoded sections as possible
8244		$string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
8245
8246		// Replace invalid percent characters
8247		$string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
8248
8249		// Add unreserved and % to $extra_chars (the latter is safe because all
8250		// pct-encoded sections are now valid).
8251		$extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
8252
8253		// Now replace any bytes that aren't allowed with their pct-encoded versions
8254		$position = 0;
8255		$strlen = strlen($string);
8256		while (($position += strspn($string, $extra_chars, $position)) < $strlen)
8257		{
8258			$value = ord($string[$position]);
8259
8260			// Start position
8261			$start = $position;
8262
8263			// By default we are valid
8264			$valid = true;
8265
8266			// No one byte sequences are valid due to the while.
8267			// Two byte sequence:
8268			if (($value & 0xE0) === 0xC0)
8269			{
8270				$character = ($value & 0x1F) << 6;
8271				$length = 2;
8272				$remaining = 1;
8273			}
8274			// Three byte sequence:
8275			elseif (($value & 0xF0) === 0xE0)
8276			{
8277				$character = ($value & 0x0F) << 12;
8278				$length = 3;
8279				$remaining = 2;
8280			}
8281			// Four byte sequence:
8282			elseif (($value & 0xF8) === 0xF0)
8283			{
8284				$character = ($value & 0x07) << 18;
8285				$length = 4;
8286				$remaining = 3;
8287			}
8288			// Invalid byte:
8289			else
8290			{
8291				$valid = false;
8292				$length = 1;
8293				$remaining = 0;
8294			}
8295
8296			if ($remaining)
8297			{
8298				if ($position + $length <= $strlen)
8299				{
8300					for ($position++; $remaining; $position++)
8301					{
8302						$value = ord($string[$position]);
8303
8304						// Check that the byte is valid, then add it to the character:
8305						if (($value & 0xC0) === 0x80)
8306						{
8307							$character |= ($value & 0x3F) << (--$remaining * 6);
8308						}
8309						// If it is invalid, count the sequence as invalid and reprocess the current byte:
8310						else
8311						{
8312							$valid = false;
8313							$position--;
8314							break;
8315						}
8316					}
8317				}
8318				else
8319				{
8320					$position = $strlen - 1;
8321					$valid = false;
8322				}
8323			}
8324
8325			// Percent encode anything invalid or not in ucschar
8326			if (
8327				// Invalid sequences
8328				!$valid
8329				// Non-shortest form sequences are invalid
8330				|| $length > 1 && $character <= 0x7F
8331				|| $length > 2 && $character <= 0x7FF
8332				|| $length > 3 && $character <= 0xFFFF
8333				// Outside of range of ucschar codepoints
8334				// Noncharacters
8335				|| ($character & 0xFFFE) === 0xFFFE
8336				|| $character >= 0xFDD0 && $character <= 0xFDEF
8337				|| (
8338					// Everything else not in ucschar
8339					   $character > 0xD7FF && $character < 0xF900
8340					|| $character < 0xA0
8341					|| $character > 0xEFFFD
8342				)
8343				&& (
8344					// Everything not in iprivate, if it applies
8345					   !$iprivate
8346					|| $character < 0xE000
8347					|| $character > 0x10FFFD
8348				)
8349			)
8350			{
8351				// If we were a character, pretend we weren't, but rather an error.
8352				if ($valid)
8353					$position--;
8354
8355				for ($j = $start; $j <= $position; $j++)
8356				{
8357					$string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
8358					$j += 2;
8359					$position += 2;
8360					$strlen += 2;
8361				}
8362			}
8363		}
8364
8365		return $string;
8366	}
8367
8368	/**
8369	 * Callback function for preg_replace_callback.
8370	 *
8371	 * Removes sequences of percent encoded bytes that represent UTF-8
8372	 * encoded characters in iunreserved
8373	 *
8374	 * @param array $match PCRE match
8375	 * @return string Replacement
8376	 */
8377	protected function remove_iunreserved_percent_encoded($match)
8378	{
8379		// As we just have valid percent encoded sequences we can just explode
8380		// and ignore the first member of the returned array (an empty string).
8381		$bytes = explode('%', $match[0]);
8382
8383		// Initialize the new string (this is what will be returned) and that
8384		// there are no bytes remaining in the current sequence (unsurprising
8385		// at the first byte!).
8386		$string = '';
8387		$remaining = 0;
8388
8389		// Loop over each and every byte, and set $value to its value
8390		for ($i = 1, $len = count($bytes); $i < $len; $i++)
8391		{
8392			$value = hexdec($bytes[$i]);
8393
8394			// If we're the first byte of sequence:
8395			if (!$remaining)
8396			{
8397				// Start position
8398				$start = $i;
8399
8400				// By default we are valid
8401				$valid = true;
8402
8403				// One byte sequence:
8404				if ($value <= 0x7F)
8405				{
8406					$character = $value;
8407					$length = 1;
8408				}
8409				// Two byte sequence:
8410				elseif (($value & 0xE0) === 0xC0)
8411				{
8412					$character = ($value & 0x1F) << 6;
8413					$length = 2;
8414					$remaining = 1;
8415				}
8416				// Three byte sequence:
8417				elseif (($value & 0xF0) === 0xE0)
8418				{
8419					$character = ($value & 0x0F) << 12;
8420					$length = 3;
8421					$remaining = 2;
8422				}
8423				// Four byte sequence:
8424				elseif (($value & 0xF8) === 0xF0)
8425				{
8426					$character = ($value & 0x07) << 18;
8427					$length = 4;
8428					$remaining = 3;
8429				}
8430				// Invalid byte:
8431				else
8432				{
8433					$valid = false;
8434					$remaining = 0;
8435				}
8436			}
8437			// Continuation byte:
8438			else
8439			{
8440				// Check that the byte is valid, then add it to the character:
8441				if (($value & 0xC0) === 0x80)
8442				{
8443					$remaining--;
8444					$character |= ($value & 0x3F) << ($remaining * 6);
8445				}
8446				// If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
8447				else
8448				{
8449					$valid = false;
8450					$remaining = 0;
8451					$i--;
8452				}
8453			}
8454
8455			// If we've reached the end of the current byte sequence, append it to Unicode::$data
8456			if (!$remaining)
8457			{
8458				// Percent encode anything invalid or not in iunreserved
8459				if (
8460					// Invalid sequences
8461					!$valid
8462					// Non-shortest form sequences are invalid
8463					|| $length > 1 && $character <= 0x7F
8464					|| $length > 2 && $character <= 0x7FF
8465					|| $length > 3 && $character <= 0xFFFF
8466					// Outside of range of iunreserved codepoints
8467					|| $character < 0x2D
8468					|| $character > 0xEFFFD
8469					// Noncharacters
8470					|| ($character & 0xFFFE) === 0xFFFE
8471					|| $character >= 0xFDD0 && $character <= 0xFDEF
8472					// Everything else not in iunreserved (this is all BMP)
8473					|| $character === 0x2F
8474					|| $character > 0x39 && $character < 0x41
8475					|| $character > 0x5A && $character < 0x61
8476					|| $character > 0x7A && $character < 0x7E
8477					|| $character > 0x7E && $character < 0xA0
8478					|| $character > 0xD7FF && $character < 0xF900
8479				)
8480				{
8481					for ($j = $start; $j <= $i; $j++)
8482					{
8483						$string .= '%' . strtoupper($bytes[$j]);
8484					}
8485				}
8486				else
8487				{
8488					for ($j = $start; $j <= $i; $j++)
8489					{
8490						$string .= chr(hexdec($bytes[$j]));
8491					}
8492				}
8493			}
8494		}
8495
8496		// If we have any bytes left over they are invalid (i.e., we are
8497		// mid-way through a multi-byte sequence)
8498		if ($remaining)
8499		{
8500			for ($j = $start; $j < $len; $j++)
8501			{
8502				$string .= '%' . strtoupper($bytes[$j]);
8503			}
8504		}
8505
8506		return $string;
8507	}
8508
8509	protected function scheme_normalization()
8510	{
8511		if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo'])
8512		{
8513			$this->iuserinfo = null;
8514		}
8515		if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost'])
8516		{
8517			$this->ihost = null;
8518		}
8519		if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port'])
8520		{
8521			$this->port = null;
8522		}
8523		if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath'])
8524		{
8525			$this->ipath = '';
8526		}
8527		if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery'])
8528		{
8529			$this->iquery = null;
8530		}
8531		if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment'])
8532		{
8533			$this->ifragment = null;
8534		}
8535	}
8536
8537	/**
8538	 * Check if the object represents a valid IRI. This needs to be done on each
8539	 * call as some things change depending on another part of the IRI.
8540	 *
8541	 * @return bool
8542	 */
8543	public function is_valid()
8544	{
8545		$isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null;
8546		if ($this->ipath !== '' &&
8547			(
8548				$isauthority && (
8549					$this->ipath[0] !== '/' ||
8550					substr($this->ipath, 0, 2) === '//'
8551				) ||
8552				(
8553					$this->scheme === null &&
8554					!$isauthority &&
8555					strpos($this->ipath, ':') !== false &&
8556					(strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/'))
8557				)
8558			)
8559		)
8560		{
8561			return false;
8562		}
8563
8564		return true;
8565	}
8566
8567	/**
8568	 * Set the entire IRI. Returns true on success, false on failure (if there
8569	 * are any invalid characters).
8570	 *
8571	 * @param string $iri
8572	 * @return bool
8573	 */
8574	public function set_iri($iri)
8575	{
8576		static $cache;
8577		if (!$cache)
8578		{
8579			$cache = array();
8580		}
8581
8582		if ($iri === null)
8583		{
8584			return true;
8585		}
8586		elseif (isset($cache[$iri]))
8587		{
8588			list($this->scheme,
8589				 $this->iuserinfo,
8590				 $this->ihost,
8591				 $this->port,
8592				 $this->ipath,
8593				 $this->iquery,
8594				 $this->ifragment,
8595				 $return) = $cache[$iri];
8596			return $return;
8597		}
8598		else
8599		{
8600			$parsed = $this->parse_iri((string) $iri);
8601			if (!$parsed)
8602			{
8603				return false;
8604			}
8605
8606			$return = $this->set_scheme($parsed['scheme'])
8607				&& $this->set_authority($parsed['authority'])
8608				&& $this->set_path($parsed['path'])
8609				&& $this->set_query($parsed['query'])
8610				&& $this->set_fragment($parsed['fragment']);
8611
8612			$cache[$iri] = array($this->scheme,
8613								 $this->iuserinfo,
8614								 $this->ihost,
8615								 $this->port,
8616								 $this->ipath,
8617								 $this->iquery,
8618								 $this->ifragment,
8619								 $return);
8620			return $return;
8621		}
8622	}
8623
8624	/**
8625	 * Set the scheme. Returns true on success, false on failure (if there are
8626	 * any invalid characters).
8627	 *
8628	 * @param string $scheme
8629	 * @return bool
8630	 */
8631	public function set_scheme($scheme)
8632	{
8633		if ($scheme === null)
8634		{
8635			$this->scheme = null;
8636		}
8637		elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme))
8638		{
8639			$this->scheme = null;
8640			return false;
8641		}
8642		else
8643		{
8644			$this->scheme = strtolower($scheme);
8645		}
8646		return true;
8647	}
8648
8649	/**
8650	 * Set the authority. Returns true on success, false on failure (if there are
8651	 * any invalid characters).
8652	 *
8653	 * @param string $authority
8654	 * @return bool
8655	 */
8656	public function set_authority($authority)
8657	{
8658		static $cache;
8659		if (!$cache)
8660			$cache = array();
8661
8662		if ($authority === null)
8663		{
8664			$this->iuserinfo = null;
8665			$this->ihost = null;
8666			$this->port = null;
8667			return true;
8668		}
8669		elseif (isset($cache[$authority]))
8670		{
8671			list($this->iuserinfo,
8672				 $this->ihost,
8673				 $this->port,
8674				 $return) = $cache[$authority];
8675
8676			return $return;
8677		}
8678		else
8679		{
8680			$remaining = $authority;
8681			if (($iuserinfo_end = strrpos($remaining, '@')) !== false)
8682			{
8683				$iuserinfo = substr($remaining, 0, $iuserinfo_end);
8684				$remaining = substr($remaining, $iuserinfo_end + 1);
8685			}
8686			else
8687			{
8688				$iuserinfo = null;
8689			}
8690			if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false)
8691			{
8692				if (($port = substr($remaining, $port_start + 1)) === false)
8693				{
8694					$port = null;
8695				}
8696				$remaining = substr($remaining, 0, $port_start);
8697			}
8698			else
8699			{
8700				$port = null;
8701			}
8702
8703			$return = $this->set_userinfo($iuserinfo) &&
8704					  $this->set_host($remaining) &&
8705					  $this->set_port($port);
8706
8707			$cache[$authority] = array($this->iuserinfo,
8708									   $this->ihost,
8709									   $this->port,
8710									   $return);
8711
8712			return $return;
8713		}
8714	}
8715
8716	/**
8717	 * Set the iuserinfo.
8718	 *
8719	 * @param string $iuserinfo
8720	 * @return bool
8721	 */
8722	public function set_userinfo($iuserinfo)
8723	{
8724		if ($iuserinfo === null)
8725		{
8726			$this->iuserinfo = null;
8727		}
8728		else
8729		{
8730			$this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
8731			$this->scheme_normalization();
8732		}
8733
8734		return true;
8735	}
8736
8737	/**
8738	 * Set the ihost. Returns true on success, false on failure (if there are
8739	 * any invalid characters).
8740	 *
8741	 * @param string $ihost
8742	 * @return bool
8743	 */
8744	public function set_host($ihost)
8745	{
8746		if ($ihost === null)
8747		{
8748			$this->ihost = null;
8749			return true;
8750		}
8751		elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']')
8752		{
8753			if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1)))
8754			{
8755				$this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']';
8756			}
8757			else
8758			{
8759				$this->ihost = null;
8760				return false;
8761			}
8762		}
8763		else
8764		{
8765			$ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
8766
8767			// Lowercase, but ignore pct-encoded sections (as they should
8768			// remain uppercase). This must be done after the previous step
8769			// as that can add unescaped characters.
8770			$position = 0;
8771			$strlen = strlen($ihost);
8772			while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen)
8773			{
8774				if ($ihost[$position] === '%')
8775				{
8776					$position += 3;
8777				}
8778				else
8779				{
8780					$ihost[$position] = strtolower($ihost[$position]);
8781					$position++;
8782				}
8783			}
8784
8785			$this->ihost = $ihost;
8786		}
8787
8788		$this->scheme_normalization();
8789
8790		return true;
8791	}
8792
8793	/**
8794	 * Set the port. Returns true on success, false on failure (if there are
8795	 * any invalid characters).
8796	 *
8797	 * @param string $port
8798	 * @return bool
8799	 */
8800	public function set_port($port)
8801	{
8802		if ($port === null)
8803		{
8804			$this->port = null;
8805			return true;
8806		}
8807		elseif (strspn($port, '0123456789') === strlen($port))
8808		{
8809			$this->port = (int) $port;
8810			$this->scheme_normalization();
8811			return true;
8812		}
8813		else
8814		{
8815			$this->port = null;
8816			return false;
8817		}
8818	}
8819
8820	/**
8821	 * Set the ipath.
8822	 *
8823	 * @param string $ipath
8824	 * @return bool
8825	 */
8826	public function set_path($ipath)
8827	{
8828		static $cache;
8829		if (!$cache)
8830		{
8831			$cache = array();
8832		}
8833
8834		$ipath = (string) $ipath;
8835
8836		if (isset($cache[$ipath]))
8837		{
8838			$this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
8839		}
8840		else
8841		{
8842			$valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
8843			$removed = $this->remove_dot_segments($valid);
8844
8845			$cache[$ipath] = array($valid, $removed);
8846			$this->ipath =  ($this->scheme !== null) ? $removed : $valid;
8847		}
8848
8849		$this->scheme_normalization();
8850		return true;
8851	}
8852
8853	/**
8854	 * Set the iquery.
8855	 *
8856	 * @param string $iquery
8857	 * @return bool
8858	 */
8859	public function set_query($iquery)
8860	{
8861		if ($iquery === null)
8862		{
8863			$this->iquery = null;
8864		}
8865		else
8866		{
8867			$this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
8868			$this->scheme_normalization();
8869		}
8870		return true;
8871	}
8872
8873	/**
8874	 * Set the ifragment.
8875	 *
8876	 * @param string $ifragment
8877	 * @return bool
8878	 */
8879	public function set_fragment($ifragment)
8880	{
8881		if ($ifragment === null)
8882		{
8883			$this->ifragment = null;
8884		}
8885		else
8886		{
8887			$this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
8888			$this->scheme_normalization();
8889		}
8890		return true;
8891	}
8892
8893	/**
8894	 * Convert an IRI to a URI (or parts thereof)
8895	 *
8896	 * @return string
8897	 */
8898	public function to_uri($string)
8899	{
8900		static $non_ascii;
8901		if (!$non_ascii)
8902		{
8903			$non_ascii = implode('', range("\x80", "\xFF"));
8904		}
8905
8906		$position = 0;
8907		$strlen = strlen($string);
8908		while (($position += strcspn($string, $non_ascii, $position)) < $strlen)
8909		{
8910			$string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
8911			$position += 3;
8912			$strlen += 2;
8913		}
8914
8915		return $string;
8916	}
8917
8918	/**
8919	 * Get the complete IRI
8920	 *
8921	 * @return string
8922	 */
8923	public function get_iri()
8924	{
8925		if (!$this->is_valid())
8926		{
8927			return false;
8928		}
8929
8930		$iri = '';
8931		if ($this->scheme !== null)
8932		{
8933			$iri .= $this->scheme . ':';
8934		}
8935		if (($iauthority = $this->get_iauthority()) !== null)
8936		{
8937			$iri .= '//' . $iauthority;
8938		}
8939		if ($this->ipath !== '')
8940		{
8941			$iri .= $this->ipath;
8942		}
8943		elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '')
8944		{
8945			$iri .= $this->normalization[$this->scheme]['ipath'];
8946		}
8947		if ($this->iquery !== null)
8948		{
8949			$iri .= '?' . $this->iquery;
8950		}
8951		if ($this->ifragment !== null)
8952		{
8953			$iri .= '#' . $this->ifragment;
8954		}
8955
8956		return $iri;
8957	}
8958
8959	/**
8960	 * Get the complete URI
8961	 *
8962	 * @return string
8963	 */
8964	public function get_uri()
8965	{
8966		return $this->to_uri($this->get_iri());
8967	}
8968
8969	/**
8970	 * Get the complete iauthority
8971	 *
8972	 * @return string
8973	 */
8974	protected function get_iauthority()
8975	{
8976		if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null)
8977		{
8978			$iauthority = '';
8979			if ($this->iuserinfo !== null)
8980			{
8981				$iauthority .= $this->iuserinfo . '@';
8982			}
8983			if ($this->ihost !== null)
8984			{
8985				$iauthority .= $this->ihost;
8986			}
8987			if ($this->port !== null)
8988			{
8989				$iauthority .= ':' . $this->port;
8990			}
8991			return $iauthority;
8992		}
8993		else
8994		{
8995			return null;
8996		}
8997	}
8998
8999	/**
9000	 * Get the complete authority
9001	 *
9002	 * @return string
9003	 */
9004	protected function get_authority()
9005	{
9006		$iauthority = $this->get_iauthority();
9007		if (is_string($iauthority))
9008			return $this->to_uri($iauthority);
9009		else
9010			return $iauthority;
9011	}
9012}
9013
9014/**
9015 * Manages all item-related data
9016 *
9017 * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()}
9018 *
9019 * This class can be overloaded with {@see SimplePie::set_item_class()}
9020 *
9021 * @package SimplePie
9022 * @subpackage API
9023 */
9024class SimplePie_Item
9025{
9026	/**
9027	 * Parent feed
9028	 *
9029	 * @access private
9030	 * @var SimplePie
9031	 */
9032	var $feed;
9033
9034	/**
9035	 * Raw data
9036	 *
9037	 * @access private
9038	 * @var array
9039	 */
9040	var $data = array();
9041
9042	/**
9043	 * Registry object
9044	 *
9045	 * @see set_registry
9046	 * @var SimplePie_Registry
9047	 */
9048	protected $registry;
9049
9050	/**
9051	 * Create a new item object
9052	 *
9053	 * This is usually used by {@see SimplePie::get_items} and
9054	 * {@see SimplePie::get_item}. Avoid creating this manually.
9055	 *
9056	 * @param SimplePie $feed Parent feed
9057	 * @param array $data Raw data
9058	 */
9059	public function __construct($feed, $data)
9060	{
9061		$this->feed = $feed;
9062		$this->data = $data;
9063	}
9064
9065	/**
9066	 * Set the registry handler
9067	 *
9068	 * This is usually used by {@see SimplePie_Registry::create}
9069	 *
9070	 * @since 1.3
9071	 * @param SimplePie_Registry $registry
9072	 */
9073	public function set_registry(SimplePie_Registry $registry)
9074	{
9075		$this->registry = $registry;
9076	}
9077
9078	/**
9079	 * Get a string representation of the item
9080	 *
9081	 * @return string
9082	 */
9083	public function __toString()
9084	{
9085		return md5(serialize($this->data));
9086	}
9087
9088	/**
9089	 * Remove items that link back to this before destroying this object
9090	 */
9091	public function __destruct()
9092	{
9093		if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
9094		{
9095			unset($this->feed);
9096		}
9097	}
9098
9099	/**
9100	 * Get data for an item-level element
9101	 *
9102	 * This method allows you to get access to ANY element/attribute that is a
9103	 * sub-element of the item/entry tag.
9104	 *
9105	 * See {@see SimplePie::get_feed_tags()} for a description of the return value
9106	 *
9107	 * @since 1.0
9108	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
9109	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
9110	 * @param string $tag Tag name
9111	 * @return array
9112	 */
9113	public function get_item_tags($namespace, $tag)
9114	{
9115		if (isset($this->data['child'][$namespace][$tag]))
9116		{
9117			return $this->data['child'][$namespace][$tag];
9118		}
9119		else
9120		{
9121			return null;
9122		}
9123	}
9124
9125	/**
9126	 * Get the base URL value from the parent feed
9127	 *
9128	 * Uses `<xml:base>`
9129	 *
9130	 * @param array $element
9131	 * @return string
9132	 */
9133	public function get_base($element = array())
9134	{
9135		return $this->feed->get_base($element);
9136	}
9137
9138	/**
9139	 * Sanitize feed data
9140	 *
9141	 * @access private
9142	 * @see SimplePie::sanitize()
9143	 * @param string $data Data to sanitize
9144	 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
9145	 * @param string $base Base URL to resolve URLs against
9146	 * @return string Sanitized data
9147	 */
9148	public function sanitize($data, $type, $base = '')
9149	{
9150		return $this->feed->sanitize($data, $type, $base);
9151	}
9152
9153	/**
9154	 * Get the parent feed
9155	 *
9156	 * Note: this may not work as you think for multifeeds!
9157	 *
9158	 * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
9159	 * @since 1.0
9160	 * @return SimplePie
9161	 */
9162	public function get_feed()
9163	{
9164		return $this->feed;
9165	}
9166
9167	/**
9168	 * Get the unique identifier for the item
9169	 *
9170	 * This is usually used when writing code to check for new items in a feed.
9171	 *
9172	 * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
9173	 * for RDF. If none of these are supplied (or `$hash` is true), creates an
9174	 * MD5 hash based on the permalink and title. If either of those are not
9175	 * supplied, creates a hash based on the full feed data.
9176	 *
9177	 * @since Beta 2
9178	 * @param boolean $hash Should we force using a hash instead of the supplied ID?
9179	 * @return string
9180	 */
9181	public function get_id($hash = false)
9182	{
9183		if (!$hash)
9184		{
9185			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
9186			{
9187				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9188			}
9189			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
9190			{
9191				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9192			}
9193			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9194			{
9195				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9196			}
9197			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
9198			{
9199				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9200			}
9201			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
9202			{
9203				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9204			}
9205			elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about']))
9206			{
9207				return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT);
9208			}
9209			elseif (($return = $this->get_permalink()) !== null)
9210			{
9211				return $return;
9212			}
9213			elseif (($return = $this->get_title()) !== null)
9214			{
9215				return $return;
9216			}
9217		}
9218		if ($this->get_permalink() !== null || $this->get_title() !== null)
9219		{
9220			return md5($this->get_permalink() . $this->get_title());
9221		}
9222		else
9223		{
9224			return md5(serialize($this->data));
9225		}
9226	}
9227
9228	/**
9229	 * Get the title of the item
9230	 *
9231	 * Uses `<atom:title>`, `<title>` or `<dc:title>`
9232	 *
9233	 * @since Beta 2 (previously called `get_item_title` since 0.8)
9234	 * @return string|null
9235	 */
9236	public function get_title()
9237	{
9238		if (!isset($this->data['title']))
9239		{
9240			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
9241			{
9242				$this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9243			}
9244			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
9245			{
9246				$this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9247			}
9248			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
9249			{
9250				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9251			}
9252			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
9253			{
9254				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9255			}
9256			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
9257			{
9258				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9259			}
9260			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
9261			{
9262				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9263			}
9264			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
9265			{
9266				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9267			}
9268			else
9269			{
9270				$this->data['title'] = null;
9271			}
9272		}
9273		return $this->data['title'];
9274	}
9275
9276	/**
9277	 * Get the content for the item
9278	 *
9279	 * Prefers summaries over full content , but will return full content if a
9280	 * summary does not exist.
9281	 *
9282	 * To prefer full content instead, use {@see get_content}
9283	 *
9284	 * Uses `<atom:summary>`, `<description>`, `<dc:description>` or
9285	 * `<itunes:subtitle>`
9286	 *
9287	 * @since 0.8
9288	 * @param boolean $description_only Should we avoid falling back to the content?
9289	 * @return string|null
9290	 */
9291	public function get_description($description_only = false)
9292	{
9293		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary'))
9294		{
9295			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9296		}
9297		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary'))
9298		{
9299			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9300		}
9301		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
9302		{
9303			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9304		}
9305		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
9306		{
9307			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9308		}
9309		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
9310		{
9311			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9312		}
9313		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
9314		{
9315			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9316		}
9317		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
9318		{
9319			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9320		}
9321		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
9322		{
9323			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9324		}
9325		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
9326		{
9327			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9328		}
9329
9330		elseif (!$description_only)
9331		{
9332			return $this->get_content(true);
9333		}
9334		else
9335		{
9336			return null;
9337		}
9338	}
9339
9340	/**
9341	 * Get the content for the item
9342	 *
9343	 * Prefers full content over summaries, but will return a summary if full
9344	 * content does not exist.
9345	 *
9346	 * To prefer summaries instead, use {@see get_description}
9347	 *
9348	 * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
9349	 *
9350	 * @since 1.0
9351	 * @param boolean $content_only Should we avoid falling back to the description?
9352	 * @return string|null
9353	 */
9354	public function get_content($content_only = false)
9355	{
9356		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content'))
9357		{
9358			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9359		}
9360		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content'))
9361		{
9362			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9363		}
9364		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded'))
9365		{
9366			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9367		}
9368		elseif (!$content_only)
9369		{
9370			return $this->get_description(true);
9371		}
9372		else
9373		{
9374			return null;
9375		}
9376	}
9377
9378	/**
9379	 * Get a category for the item
9380	 *
9381	 * @since Beta 3 (previously called `get_categories()` since Beta 2)
9382	 * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
9383	 * @return SimplePie_Category|null
9384	 */
9385	public function get_category($key = 0)
9386	{
9387		$categories = $this->get_categories();
9388		if (isset($categories[$key]))
9389		{
9390			return $categories[$key];
9391		}
9392		else
9393		{
9394			return null;
9395		}
9396	}
9397
9398	/**
9399	 * Get all categories for the item
9400	 *
9401	 * Uses `<atom:category>`, `<category>` or `<dc:subject>`
9402	 *
9403	 * @since Beta 3
9404	 * @return array|null List of {@see SimplePie_Category} objects
9405	 */
9406	public function get_categories()
9407	{
9408		$categories = array();
9409
9410		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
9411		{
9412			$term = null;
9413			$scheme = null;
9414			$label = null;
9415			if (isset($category['attribs']['']['term']))
9416			{
9417				$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
9418			}
9419			if (isset($category['attribs']['']['scheme']))
9420			{
9421				$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
9422			}
9423			if (isset($category['attribs']['']['label']))
9424			{
9425				$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
9426			}
9427			$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
9428		}
9429		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
9430		{
9431			// This is really the label, but keep this as the term also for BC.
9432			// Label will also work on retrieving because that falls back to term.
9433			$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9434			if (isset($category['attribs']['']['domain']))
9435			{
9436				$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
9437			}
9438			else
9439			{
9440				$scheme = null;
9441			}
9442			$categories[] = $this->registry->create('Category', array($term, $scheme, null));
9443		}
9444		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
9445		{
9446			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9447		}
9448		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
9449		{
9450			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9451		}
9452
9453		if (!empty($categories))
9454		{
9455			return array_unique($categories);
9456		}
9457		else
9458		{
9459			return null;
9460		}
9461	}
9462
9463	/**
9464	 * Get an author for the item
9465	 *
9466	 * @since Beta 2
9467	 * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
9468	 * @return SimplePie_Author|null
9469	 */
9470	public function get_author($key = 0)
9471	{
9472		$authors = $this->get_authors();
9473		if (isset($authors[$key]))
9474		{
9475			return $authors[$key];
9476		}
9477		else
9478		{
9479			return null;
9480		}
9481	}
9482
9483	/**
9484	 * Get a contributor for the item
9485	 *
9486	 * @since 1.1
9487	 * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
9488	 * @return SimplePie_Author|null
9489	 */
9490	public function get_contributor($key = 0)
9491	{
9492		$contributors = $this->get_contributors();
9493		if (isset($contributors[$key]))
9494		{
9495			return $contributors[$key];
9496		}
9497		else
9498		{
9499			return null;
9500		}
9501	}
9502
9503	/**
9504	 * Get all contributors for the item
9505	 *
9506	 * Uses `<atom:contributor>`
9507	 *
9508	 * @since 1.1
9509	 * @return array|null List of {@see SimplePie_Author} objects
9510	 */
9511	public function get_contributors()
9512	{
9513		$contributors = array();
9514		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
9515		{
9516			$name = null;
9517			$uri = null;
9518			$email = null;
9519			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9520			{
9521				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9522			}
9523			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9524			{
9525				$uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
9526			}
9527			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9528			{
9529				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9530			}
9531			if ($name !== null || $email !== null || $uri !== null)
9532			{
9533				$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
9534			}
9535		}
9536		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
9537		{
9538			$name = null;
9539			$url = null;
9540			$email = null;
9541			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9542			{
9543				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9544			}
9545			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9546			{
9547				$url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
9548			}
9549			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9550			{
9551				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9552			}
9553			if ($name !== null || $email !== null || $url !== null)
9554			{
9555				$contributors[] = $this->registry->create('Author', array($name, $url, $email));
9556			}
9557		}
9558
9559		if (!empty($contributors))
9560		{
9561			return array_unique($contributors);
9562		}
9563		else
9564		{
9565			return null;
9566		}
9567	}
9568
9569	/**
9570	 * Get all authors for the item
9571	 *
9572	 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
9573	 *
9574	 * @since Beta 2
9575	 * @return array|null List of {@see SimplePie_Author} objects
9576	 */
9577	public function get_authors()
9578	{
9579		$authors = array();
9580		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
9581		{
9582			$name = null;
9583			$uri = null;
9584			$email = null;
9585			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9586			{
9587				$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9588			}
9589			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9590			{
9591				$uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
9592			}
9593			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9594			{
9595				$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9596			}
9597			if ($name !== null || $email !== null || $uri !== null)
9598			{
9599				$authors[] = $this->registry->create('Author', array($name, $uri, $email));
9600			}
9601		}
9602		if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
9603		{
9604			$name = null;
9605			$url = null;
9606			$email = null;
9607			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9608			{
9609				$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9610			}
9611			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9612			{
9613				$url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
9614			}
9615			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9616			{
9617				$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9618			}
9619			if ($name !== null || $email !== null || $url !== null)
9620			{
9621				$authors[] = $this->registry->create('Author', array($name, $url, $email));
9622			}
9623		}
9624		if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
9625		{
9626			$authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)));
9627		}
9628		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
9629		{
9630			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9631		}
9632		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
9633		{
9634			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9635		}
9636		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
9637		{
9638			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9639		}
9640
9641		if (!empty($authors))
9642		{
9643			return array_unique($authors);
9644		}
9645		elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
9646		{
9647			return $authors;
9648		}
9649		elseif ($authors = $this->feed->get_authors())
9650		{
9651			return $authors;
9652		}
9653		else
9654		{
9655			return null;
9656		}
9657	}
9658
9659	/**
9660	 * Get the copyright info for the item
9661	 *
9662	 * Uses `<atom:rights>` or `<dc:rights>`
9663	 *
9664	 * @since 1.1
9665	 * @return string
9666	 */
9667	public function get_copyright()
9668	{
9669		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
9670		{
9671			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9672		}
9673		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
9674		{
9675			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9676		}
9677		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
9678		{
9679			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9680		}
9681		else
9682		{
9683			return null;
9684		}
9685	}
9686
9687	/**
9688	 * Get the posting date/time for the item
9689	 *
9690	 * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
9691	 * `<atom:modified>`, `<pubDate>` or `<dc:date>`
9692	 *
9693	 * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9694	 * {@see get_gmdate}
9695	 *
9696	 * @since Beta 2 (previously called `get_item_date` since 0.8)
9697	 *
9698	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9699	 * @return int|string|null
9700	 */
9701	public function get_date($date_format = 'j F Y, g:i a')
9702	{
9703		if (!isset($this->data['date']))
9704		{
9705			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
9706			{
9707				$this->data['date']['raw'] = $return[0]['data'];
9708			}
9709			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9710			{
9711				$this->data['date']['raw'] = $return[0]['data'];
9712			}
9713			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
9714			{
9715				$this->data['date']['raw'] = $return[0]['data'];
9716			}
9717			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
9718			{
9719				$this->data['date']['raw'] = $return[0]['data'];
9720			}
9721			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
9722			{
9723				$this->data['date']['raw'] = $return[0]['data'];
9724			}
9725			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
9726			{
9727				$this->data['date']['raw'] = $return[0]['data'];
9728			}
9729			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
9730			{
9731				$this->data['date']['raw'] = $return[0]['data'];
9732			}
9733			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
9734			{
9735				$this->data['date']['raw'] = $return[0]['data'];
9736			}
9737
9738			if (!empty($this->data['date']['raw']))
9739			{
9740				$parser = $this->registry->call('Parse_Date', 'get');
9741				$this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
9742			}
9743			else
9744			{
9745				$this->data['date'] = null;
9746			}
9747		}
9748		if ($this->data['date'])
9749		{
9750			$date_format = (string) $date_format;
9751			switch ($date_format)
9752			{
9753				case '':
9754					return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
9755
9756				case 'U':
9757					return $this->data['date']['parsed'];
9758
9759				default:
9760					return date($date_format, $this->data['date']['parsed']);
9761			}
9762		}
9763		else
9764		{
9765			return null;
9766		}
9767	}
9768
9769	/**
9770	 * Get the update date/time for the item
9771	 *
9772	 * Uses `<atom:updated>`
9773	 *
9774	 * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9775	 * {@see get_gmdate}
9776	 *
9777	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9778	 * @return int|string|null
9779	 */
9780	public function get_updated_date($date_format = 'j F Y, g:i a')
9781	{
9782		if (!isset($this->data['updated']))
9783		{
9784			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9785			{
9786				$this->data['updated']['raw'] = $return[0]['data'];
9787			}
9788
9789			if (!empty($this->data['updated']['raw']))
9790			{
9791				$parser = $this->registry->call('Parse_Date', 'get');
9792				$this->data['updated']['parsed'] = $parser->parse($this->data['date']['raw']);
9793			}
9794			else
9795			{
9796				$this->data['updated'] = null;
9797			}
9798		}
9799		if ($this->data['updated'])
9800		{
9801			$date_format = (string) $date_format;
9802			switch ($date_format)
9803			{
9804				case '':
9805					return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
9806
9807				case 'U':
9808					return $this->data['updated']['parsed'];
9809
9810				default:
9811					return date($date_format, $this->data['updated']['parsed']);
9812			}
9813		}
9814		else
9815		{
9816			return null;
9817		}
9818	}
9819
9820	/**
9821	 * Get the localized posting date/time for the item
9822	 *
9823	 * Returns the date formatted in the localized language. To display in
9824	 * languages other than the server's default, you need to change the locale
9825	 * with {@link http://php.net/setlocale setlocale()}. The available
9826	 * localizations depend on which ones are installed on your web server.
9827	 *
9828	 * @since 1.0
9829	 *
9830	 * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
9831	 * @return int|string|null
9832	 */
9833	public function get_local_date($date_format = '%c')
9834	{
9835		if (!$date_format)
9836		{
9837			return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
9838		}
9839		elseif (($date = $this->get_date('U')) !== null && $date !== false)
9840		{
9841			return strftime($date_format, $date);
9842		}
9843		else
9844		{
9845			return null;
9846		}
9847	}
9848
9849	/**
9850	 * Get the posting date/time for the item (UTC time)
9851	 *
9852	 * @see get_date
9853	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
9854	 * @return int|string|null
9855	 */
9856	public function get_gmdate($date_format = 'j F Y, g:i a')
9857	{
9858		$date = $this->get_date('U');
9859		if ($date === null)
9860		{
9861			return null;
9862		}
9863
9864		return gmdate($date_format, $date);
9865	}
9866
9867	/**
9868	 * Get the update date/time for the item (UTC time)
9869	 *
9870	 * @see get_updated_date
9871	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
9872	 * @return int|string|null
9873	 */
9874	public function get_updated_gmdate($date_format = 'j F Y, g:i a')
9875	{
9876		$date = $this->get_updated_date('U');
9877		if ($date === null)
9878		{
9879			return null;
9880		}
9881
9882		return gmdate($date_format, $date);
9883	}
9884
9885	/**
9886	 * Get the permalink for the item
9887	 *
9888	 * Returns the first link available with a relationship of "alternate".
9889	 * Identical to {@see get_link()} with key 0
9890	 *
9891	 * @see get_link
9892	 * @since 0.8
9893	 * @return string|null Permalink URL
9894	 */
9895	public function get_permalink()
9896	{
9897		$link = $this->get_link();
9898		$enclosure = $this->get_enclosure(0);
9899		if ($link !== null)
9900		{
9901			return $link;
9902		}
9903		elseif ($enclosure !== null)
9904		{
9905			return $enclosure->get_link();
9906		}
9907		else
9908		{
9909			return null;
9910		}
9911	}
9912
9913	/**
9914	 * Get a single link for the item
9915	 *
9916	 * @since Beta 3
9917	 * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
9918	 * @param string $rel The relationship of the link to return
9919	 * @return string|null Link URL
9920	 */
9921	public function get_link($key = 0, $rel = 'alternate')
9922	{
9923		$links = $this->get_links($rel);
9924		if ($links[$key] !== null)
9925		{
9926			return $links[$key];
9927		}
9928		else
9929		{
9930			return null;
9931		}
9932	}
9933
9934	/**
9935	 * Get all links for the item
9936	 *
9937	 * Uses `<atom:link>`, `<link>` or `<guid>`
9938	 *
9939	 * @since Beta 2
9940	 * @param string $rel The relationship of links to return
9941	 * @return array|null Links found for the item (strings)
9942	 */
9943	public function get_links($rel = 'alternate')
9944	{
9945		if (!isset($this->data['links']))
9946		{
9947			$this->data['links'] = array();
9948			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
9949			{
9950				if (isset($link['attribs']['']['href']))
9951				{
9952					$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
9953					$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
9954
9955				}
9956			}
9957			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
9958			{
9959				if (isset($link['attribs']['']['href']))
9960				{
9961					$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
9962					$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
9963				}
9964			}
9965			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
9966			{
9967				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9968			}
9969			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
9970			{
9971				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9972			}
9973			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
9974			{
9975				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9976			}
9977			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9978			{
9979				if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
9980				{
9981					$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9982				}
9983			}
9984
9985			$keys = array_keys($this->data['links']);
9986			foreach ($keys as $key)
9987			{
9988				if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
9989				{
9990					if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
9991					{
9992						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
9993						$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
9994					}
9995					else
9996					{
9997						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
9998					}
9999				}
10000				elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
10001				{
10002					$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
10003				}
10004				$this->data['links'][$key] = array_unique($this->data['links'][$key]);
10005			}
10006		}
10007		if (isset($this->data['links'][$rel]))
10008		{
10009			return $this->data['links'][$rel];
10010		}
10011		else
10012		{
10013			return null;
10014		}
10015	}
10016
10017	/**
10018	 * Get an enclosure from the item
10019	 *
10020	 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10021	 *
10022	 * @since Beta 2
10023	 * @todo Add ability to prefer one type of content over another (in a media group).
10024	 * @param int $key The enclosure that you want to return.  Remember that arrays begin with 0, not 1
10025	 * @return SimplePie_Enclosure|null
10026	 */
10027	public function get_enclosure($key = 0, $prefer = null)
10028	{
10029		$enclosures = $this->get_enclosures();
10030		if (isset($enclosures[$key]))
10031		{
10032			return $enclosures[$key];
10033		}
10034		else
10035		{
10036			return null;
10037		}
10038	}
10039
10040	/**
10041	 * Get all available enclosures (podcasts, etc.)
10042	 *
10043	 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10044	 *
10045	 * At this point, we're pretty much assuming that all enclosures for an item
10046	 * are the same content.  Anything else is too complicated to
10047	 * properly support.
10048	 *
10049	 * @since Beta 2
10050	 * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
10051	 * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists).
10052	 * @return array|null List of SimplePie_Enclosure items
10053	 */
10054	public function get_enclosures()
10055	{
10056		if (!isset($this->data['enclosures']))
10057		{
10058			$this->data['enclosures'] = array();
10059
10060			// Elements
10061			$captions_parent = null;
10062			$categories_parent = null;
10063			$copyrights_parent = null;
10064			$credits_parent = null;
10065			$description_parent = null;
10066			$duration_parent = null;
10067			$hashes_parent = null;
10068			$keywords_parent = null;
10069			$player_parent = null;
10070			$ratings_parent = null;
10071			$restrictions_parent = null;
10072			$thumbnails_parent = null;
10073			$title_parent = null;
10074
10075			// Let's do the channel and item-level ones first, and just re-use them if we need to.
10076			$parent = $this->get_feed();
10077
10078			// CAPTIONS
10079			if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10080			{
10081				foreach ($captions as $caption)
10082				{
10083					$caption_type = null;
10084					$caption_lang = null;
10085					$caption_startTime = null;
10086					$caption_endTime = null;
10087					$caption_text = null;
10088					if (isset($caption['attribs']['']['type']))
10089					{
10090						$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10091					}
10092					if (isset($caption['attribs']['']['lang']))
10093					{
10094						$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10095					}
10096					if (isset($caption['attribs']['']['start']))
10097					{
10098						$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10099					}
10100					if (isset($caption['attribs']['']['end']))
10101					{
10102						$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10103					}
10104					if (isset($caption['data']))
10105					{
10106						$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10107					}
10108					$captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10109				}
10110			}
10111			elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10112			{
10113				foreach ($captions as $caption)
10114				{
10115					$caption_type = null;
10116					$caption_lang = null;
10117					$caption_startTime = null;
10118					$caption_endTime = null;
10119					$caption_text = null;
10120					if (isset($caption['attribs']['']['type']))
10121					{
10122						$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10123					}
10124					if (isset($caption['attribs']['']['lang']))
10125					{
10126						$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10127					}
10128					if (isset($caption['attribs']['']['start']))
10129					{
10130						$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10131					}
10132					if (isset($caption['attribs']['']['end']))
10133					{
10134						$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10135					}
10136					if (isset($caption['data']))
10137					{
10138						$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10139					}
10140					$captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10141				}
10142			}
10143			if (is_array($captions_parent))
10144			{
10145				$captions_parent = array_values(array_unique($captions_parent));
10146			}
10147
10148			// CATEGORIES
10149			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10150			{
10151				$term = null;
10152				$scheme = null;
10153				$label = null;
10154				if (isset($category['data']))
10155				{
10156					$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10157				}
10158				if (isset($category['attribs']['']['scheme']))
10159				{
10160					$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10161				}
10162				else
10163				{
10164					$scheme = 'http://search.yahoo.com/mrss/category_schema';
10165				}
10166				if (isset($category['attribs']['']['label']))
10167				{
10168					$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10169				}
10170				$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10171			}
10172			foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10173			{
10174				$term = null;
10175				$scheme = null;
10176				$label = null;
10177				if (isset($category['data']))
10178				{
10179					$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10180				}
10181				if (isset($category['attribs']['']['scheme']))
10182				{
10183					$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10184				}
10185				else
10186				{
10187					$scheme = 'http://search.yahoo.com/mrss/category_schema';
10188				}
10189				if (isset($category['attribs']['']['label']))
10190				{
10191					$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10192				}
10193				$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10194			}
10195			foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
10196			{
10197				$term = null;
10198				$scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
10199				$label = null;
10200				if (isset($category['attribs']['']['text']))
10201				{
10202					$label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10203				}
10204				$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10205
10206				if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
10207				{
10208					foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
10209					{
10210						if (isset($subcategory['attribs']['']['text']))
10211						{
10212							$label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10213						}
10214						$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10215					}
10216				}
10217			}
10218			if (is_array($categories_parent))
10219			{
10220				$categories_parent = array_values(array_unique($categories_parent));
10221			}
10222
10223			// COPYRIGHT
10224			if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10225			{
10226				$copyright_url = null;
10227				$copyright_label = null;
10228				if (isset($copyright[0]['attribs']['']['url']))
10229				{
10230					$copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10231				}
10232				if (isset($copyright[0]['data']))
10233				{
10234					$copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10235				}
10236				$copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10237			}
10238			elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10239			{
10240				$copyright_url = null;
10241				$copyright_label = null;
10242				if (isset($copyright[0]['attribs']['']['url']))
10243				{
10244					$copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10245				}
10246				if (isset($copyright[0]['data']))
10247				{
10248					$copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10249				}
10250				$copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10251			}
10252
10253			// CREDITS
10254			if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10255			{
10256				foreach ($credits as $credit)
10257				{
10258					$credit_role = null;
10259					$credit_scheme = null;
10260					$credit_name = null;
10261					if (isset($credit['attribs']['']['role']))
10262					{
10263						$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10264					}
10265					if (isset($credit['attribs']['']['scheme']))
10266					{
10267						$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10268					}
10269					else
10270					{
10271						$credit_scheme = 'urn:ebu';
10272					}
10273					if (isset($credit['data']))
10274					{
10275						$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10276					}
10277					$credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10278				}
10279			}
10280			elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10281			{
10282				foreach ($credits as $credit)
10283				{
10284					$credit_role = null;
10285					$credit_scheme = null;
10286					$credit_name = null;
10287					if (isset($credit['attribs']['']['role']))
10288					{
10289						$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10290					}
10291					if (isset($credit['attribs']['']['scheme']))
10292					{
10293						$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10294					}
10295					else
10296					{
10297						$credit_scheme = 'urn:ebu';
10298					}
10299					if (isset($credit['data']))
10300					{
10301						$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10302					}
10303					$credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10304				}
10305			}
10306			if (is_array($credits_parent))
10307			{
10308				$credits_parent = array_values(array_unique($credits_parent));
10309			}
10310
10311			// DESCRIPTION
10312			if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10313			{
10314				if (isset($description_parent[0]['data']))
10315				{
10316					$description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10317				}
10318			}
10319			elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10320			{
10321				if (isset($description_parent[0]['data']))
10322				{
10323					$description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10324				}
10325			}
10326
10327			// DURATION
10328			if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
10329			{
10330				$seconds = null;
10331				$minutes = null;
10332				$hours = null;
10333				if (isset($duration_parent[0]['data']))
10334				{
10335					$temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10336					if (sizeof($temp) > 0)
10337					{
10338						$seconds = (int) array_pop($temp);
10339					}
10340					if (sizeof($temp) > 0)
10341					{
10342						$minutes = (int) array_pop($temp);
10343						$seconds += $minutes * 60;
10344					}
10345					if (sizeof($temp) > 0)
10346					{
10347						$hours = (int) array_pop($temp);
10348						$seconds += $hours * 3600;
10349					}
10350					unset($temp);
10351					$duration_parent = $seconds;
10352				}
10353			}
10354
10355			// HASHES
10356			if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10357			{
10358				foreach ($hashes_iterator as $hash)
10359				{
10360					$value = null;
10361					$algo = null;
10362					if (isset($hash['data']))
10363					{
10364						$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10365					}
10366					if (isset($hash['attribs']['']['algo']))
10367					{
10368						$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10369					}
10370					else
10371					{
10372						$algo = 'md5';
10373					}
10374					$hashes_parent[] = $algo.':'.$value;
10375				}
10376			}
10377			elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10378			{
10379				foreach ($hashes_iterator as $hash)
10380				{
10381					$value = null;
10382					$algo = null;
10383					if (isset($hash['data']))
10384					{
10385						$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10386					}
10387					if (isset($hash['attribs']['']['algo']))
10388					{
10389						$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10390					}
10391					else
10392					{
10393						$algo = 'md5';
10394					}
10395					$hashes_parent[] = $algo.':'.$value;
10396				}
10397			}
10398			if (is_array($hashes_parent))
10399			{
10400				$hashes_parent = array_values(array_unique($hashes_parent));
10401			}
10402
10403			// KEYWORDS
10404			if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10405			{
10406				if (isset($keywords[0]['data']))
10407				{
10408					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10409					foreach ($temp as $word)
10410					{
10411						$keywords_parent[] = trim($word);
10412					}
10413				}
10414				unset($temp);
10415			}
10416			elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10417			{
10418				if (isset($keywords[0]['data']))
10419				{
10420					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10421					foreach ($temp as $word)
10422					{
10423						$keywords_parent[] = trim($word);
10424					}
10425				}
10426				unset($temp);
10427			}
10428			elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10429			{
10430				if (isset($keywords[0]['data']))
10431				{
10432					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10433					foreach ($temp as $word)
10434					{
10435						$keywords_parent[] = trim($word);
10436					}
10437				}
10438				unset($temp);
10439			}
10440			elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10441			{
10442				if (isset($keywords[0]['data']))
10443				{
10444					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10445					foreach ($temp as $word)
10446					{
10447						$keywords_parent[] = trim($word);
10448					}
10449				}
10450				unset($temp);
10451			}
10452			if (is_array($keywords_parent))
10453			{
10454				$keywords_parent = array_values(array_unique($keywords_parent));
10455			}
10456
10457			// PLAYER
10458			if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10459			{
10460				if (isset($player_parent[0]['attribs']['']['url']))
10461				{
10462					$player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10463				}
10464			}
10465			elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10466			{
10467				if (isset($player_parent[0]['attribs']['']['url']))
10468				{
10469					$player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10470				}
10471			}
10472
10473			// RATINGS
10474			if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10475			{
10476				foreach ($ratings as $rating)
10477				{
10478					$rating_scheme = null;
10479					$rating_value = null;
10480					if (isset($rating['attribs']['']['scheme']))
10481					{
10482						$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10483					}
10484					else
10485					{
10486						$rating_scheme = 'urn:simple';
10487					}
10488					if (isset($rating['data']))
10489					{
10490						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10491					}
10492					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10493				}
10494			}
10495			elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10496			{
10497				foreach ($ratings as $rating)
10498				{
10499					$rating_scheme = 'urn:itunes';
10500					$rating_value = null;
10501					if (isset($rating['data']))
10502					{
10503						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10504					}
10505					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10506				}
10507			}
10508			elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10509			{
10510				foreach ($ratings as $rating)
10511				{
10512					$rating_scheme = null;
10513					$rating_value = null;
10514					if (isset($rating['attribs']['']['scheme']))
10515					{
10516						$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10517					}
10518					else
10519					{
10520						$rating_scheme = 'urn:simple';
10521					}
10522					if (isset($rating['data']))
10523					{
10524						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10525					}
10526					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10527				}
10528			}
10529			elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10530			{
10531				foreach ($ratings as $rating)
10532				{
10533					$rating_scheme = 'urn:itunes';
10534					$rating_value = null;
10535					if (isset($rating['data']))
10536					{
10537						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10538					}
10539					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10540				}
10541			}
10542			if (is_array($ratings_parent))
10543			{
10544				$ratings_parent = array_values(array_unique($ratings_parent));
10545			}
10546
10547			// RESTRICTIONS
10548			if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10549			{
10550				foreach ($restrictions as $restriction)
10551				{
10552					$restriction_relationship = null;
10553					$restriction_type = null;
10554					$restriction_value = null;
10555					if (isset($restriction['attribs']['']['relationship']))
10556					{
10557						$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10558					}
10559					if (isset($restriction['attribs']['']['type']))
10560					{
10561						$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10562					}
10563					if (isset($restriction['data']))
10564					{
10565						$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10566					}
10567					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10568				}
10569			}
10570			elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10571			{
10572				foreach ($restrictions as $restriction)
10573				{
10574					$restriction_relationship = 'allow';
10575					$restriction_type = null;
10576					$restriction_value = 'itunes';
10577					if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10578					{
10579						$restriction_relationship = 'deny';
10580					}
10581					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10582				}
10583			}
10584			elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10585			{
10586				foreach ($restrictions as $restriction)
10587				{
10588					$restriction_relationship = null;
10589					$restriction_type = null;
10590					$restriction_value = null;
10591					if (isset($restriction['attribs']['']['relationship']))
10592					{
10593						$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10594					}
10595					if (isset($restriction['attribs']['']['type']))
10596					{
10597						$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10598					}
10599					if (isset($restriction['data']))
10600					{
10601						$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10602					}
10603					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10604				}
10605			}
10606			elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10607			{
10608				foreach ($restrictions as $restriction)
10609				{
10610					$restriction_relationship = 'allow';
10611					$restriction_type = null;
10612					$restriction_value = 'itunes';
10613					if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10614					{
10615						$restriction_relationship = 'deny';
10616					}
10617					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10618				}
10619			}
10620			if (is_array($restrictions_parent))
10621			{
10622				$restrictions_parent = array_values(array_unique($restrictions_parent));
10623			}
10624			else
10625			{
10626				$restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default'));
10627			}
10628
10629			// THUMBNAILS
10630			if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10631			{
10632				foreach ($thumbnails as $thumbnail)
10633				{
10634					if (isset($thumbnail['attribs']['']['url']))
10635					{
10636						$thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10637					}
10638				}
10639			}
10640			elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10641			{
10642				foreach ($thumbnails as $thumbnail)
10643				{
10644					if (isset($thumbnail['attribs']['']['url']))
10645					{
10646						$thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10647					}
10648				}
10649			}
10650
10651			// TITLES
10652			if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10653			{
10654				if (isset($title_parent[0]['data']))
10655				{
10656					$title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10657				}
10658			}
10659			elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10660			{
10661				if (isset($title_parent[0]['data']))
10662				{
10663					$title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10664				}
10665			}
10666
10667			// Clear the memory
10668			unset($parent);
10669
10670			// Attributes
10671			$bitrate = null;
10672			$channels = null;
10673			$duration = null;
10674			$expression = null;
10675			$framerate = null;
10676			$height = null;
10677			$javascript = null;
10678			$lang = null;
10679			$length = null;
10680			$medium = null;
10681			$samplingrate = null;
10682			$type = null;
10683			$url = null;
10684			$width = null;
10685
10686			// Elements
10687			$captions = null;
10688			$categories = null;
10689			$copyrights = null;
10690			$credits = null;
10691			$description = null;
10692			$hashes = null;
10693			$keywords = null;
10694			$player = null;
10695			$ratings = null;
10696			$restrictions = null;
10697			$thumbnails = null;
10698			$title = null;
10699
10700			// If we have media:group tags, loop through them.
10701			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
10702			{
10703				if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
10704				{
10705					// If we have media:content tags, loop through them.
10706					foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
10707					{
10708						if (isset($content['attribs']['']['url']))
10709						{
10710							// Attributes
10711							$bitrate = null;
10712							$channels = null;
10713							$duration = null;
10714							$expression = null;
10715							$framerate = null;
10716							$height = null;
10717							$javascript = null;
10718							$lang = null;
10719							$length = null;
10720							$medium = null;
10721							$samplingrate = null;
10722							$type = null;
10723							$url = null;
10724							$width = null;
10725
10726							// Elements
10727							$captions = null;
10728							$categories = null;
10729							$copyrights = null;
10730							$credits = null;
10731							$description = null;
10732							$hashes = null;
10733							$keywords = null;
10734							$player = null;
10735							$ratings = null;
10736							$restrictions = null;
10737							$thumbnails = null;
10738							$title = null;
10739
10740							// Start checking the attributes of media:content
10741							if (isset($content['attribs']['']['bitrate']))
10742							{
10743								$bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10744							}
10745							if (isset($content['attribs']['']['channels']))
10746							{
10747								$channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
10748							}
10749							if (isset($content['attribs']['']['duration']))
10750							{
10751								$duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
10752							}
10753							else
10754							{
10755								$duration = $duration_parent;
10756							}
10757							if (isset($content['attribs']['']['expression']))
10758							{
10759								$expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
10760							}
10761							if (isset($content['attribs']['']['framerate']))
10762							{
10763								$framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
10764							}
10765							if (isset($content['attribs']['']['height']))
10766							{
10767								$height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
10768							}
10769							if (isset($content['attribs']['']['lang']))
10770							{
10771								$lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10772							}
10773							if (isset($content['attribs']['']['fileSize']))
10774							{
10775								$length = ceil($content['attribs']['']['fileSize']);
10776							}
10777							if (isset($content['attribs']['']['medium']))
10778							{
10779								$medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
10780							}
10781							if (isset($content['attribs']['']['samplingrate']))
10782							{
10783								$samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10784							}
10785							if (isset($content['attribs']['']['type']))
10786							{
10787								$type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10788							}
10789							if (isset($content['attribs']['']['width']))
10790							{
10791								$width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
10792							}
10793							$url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10794
10795							// Checking the other optional media: elements. Priority: media:content, media:group, item, channel
10796
10797							// CAPTIONS
10798							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
10799							{
10800								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
10801								{
10802									$caption_type = null;
10803									$caption_lang = null;
10804									$caption_startTime = null;
10805									$caption_endTime = null;
10806									$caption_text = null;
10807									if (isset($caption['attribs']['']['type']))
10808									{
10809										$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10810									}
10811									if (isset($caption['attribs']['']['lang']))
10812									{
10813										$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10814									}
10815									if (isset($caption['attribs']['']['start']))
10816									{
10817										$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10818									}
10819									if (isset($caption['attribs']['']['end']))
10820									{
10821										$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10822									}
10823									if (isset($caption['data']))
10824									{
10825										$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10826									}
10827									$captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10828								}
10829								if (is_array($captions))
10830								{
10831									$captions = array_values(array_unique($captions));
10832								}
10833							}
10834							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
10835							{
10836								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
10837								{
10838									$caption_type = null;
10839									$caption_lang = null;
10840									$caption_startTime = null;
10841									$caption_endTime = null;
10842									$caption_text = null;
10843									if (isset($caption['attribs']['']['type']))
10844									{
10845										$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10846									}
10847									if (isset($caption['attribs']['']['lang']))
10848									{
10849										$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10850									}
10851									if (isset($caption['attribs']['']['start']))
10852									{
10853										$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10854									}
10855									if (isset($caption['attribs']['']['end']))
10856									{
10857										$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10858									}
10859									if (isset($caption['data']))
10860									{
10861										$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10862									}
10863									$captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10864								}
10865								if (is_array($captions))
10866								{
10867									$captions = array_values(array_unique($captions));
10868								}
10869							}
10870							else
10871							{
10872								$captions = $captions_parent;
10873							}
10874
10875							// CATEGORIES
10876							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
10877							{
10878								foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
10879								{
10880									$term = null;
10881									$scheme = null;
10882									$label = null;
10883									if (isset($category['data']))
10884									{
10885										$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10886									}
10887									if (isset($category['attribs']['']['scheme']))
10888									{
10889										$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10890									}
10891									else
10892									{
10893										$scheme = 'http://search.yahoo.com/mrss/category_schema';
10894									}
10895									if (isset($category['attribs']['']['label']))
10896									{
10897										$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10898									}
10899									$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
10900								}
10901							}
10902							if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
10903							{
10904								foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
10905								{
10906									$term = null;
10907									$scheme = null;
10908									$label = null;
10909									if (isset($category['data']))
10910									{
10911										$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10912									}
10913									if (isset($category['attribs']['']['scheme']))
10914									{
10915										$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10916									}
10917									else
10918									{
10919										$scheme = 'http://search.yahoo.com/mrss/category_schema';
10920									}
10921									if (isset($category['attribs']['']['label']))
10922									{
10923										$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10924									}
10925									$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
10926								}
10927							}
10928							if (is_array($categories) && is_array($categories_parent))
10929							{
10930								$categories = array_values(array_unique(array_merge($categories, $categories_parent)));
10931							}
10932							elseif (is_array($categories))
10933							{
10934								$categories = array_values(array_unique($categories));
10935							}
10936							elseif (is_array($categories_parent))
10937							{
10938								$categories = array_values(array_unique($categories_parent));
10939							}
10940
10941							// COPYRIGHTS
10942							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
10943							{
10944								$copyright_url = null;
10945								$copyright_label = null;
10946								if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
10947								{
10948									$copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10949								}
10950								if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
10951								{
10952									$copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10953								}
10954								$copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10955							}
10956							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
10957							{
10958								$copyright_url = null;
10959								$copyright_label = null;
10960								if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
10961								{
10962									$copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10963								}
10964								if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
10965								{
10966									$copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10967								}
10968								$copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10969							}
10970							else
10971							{
10972								$copyrights = $copyrights_parent;
10973							}
10974
10975							// CREDITS
10976							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
10977							{
10978								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
10979								{
10980									$credit_role = null;
10981									$credit_scheme = null;
10982									$credit_name = null;
10983									if (isset($credit['attribs']['']['role']))
10984									{
10985										$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10986									}
10987									if (isset($credit['attribs']['']['scheme']))
10988									{
10989										$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10990									}
10991									else
10992									{
10993										$credit_scheme = 'urn:ebu';
10994									}
10995									if (isset($credit['data']))
10996									{
10997										$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10998									}
10999									$credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11000								}
11001								if (is_array($credits))
11002								{
11003									$credits = array_values(array_unique($credits));
11004								}
11005							}
11006							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11007							{
11008								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11009								{
11010									$credit_role = null;
11011									$credit_scheme = null;
11012									$credit_name = null;
11013									if (isset($credit['attribs']['']['role']))
11014									{
11015										$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11016									}
11017									if (isset($credit['attribs']['']['scheme']))
11018									{
11019										$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11020									}
11021									else
11022									{
11023										$credit_scheme = 'urn:ebu';
11024									}
11025									if (isset($credit['data']))
11026									{
11027										$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11028									}
11029									$credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11030								}
11031								if (is_array($credits))
11032								{
11033									$credits = array_values(array_unique($credits));
11034								}
11035							}
11036							else
11037							{
11038								$credits = $credits_parent;
11039							}
11040
11041							// DESCRIPTION
11042							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11043							{
11044								$description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11045							}
11046							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11047							{
11048								$description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11049							}
11050							else
11051							{
11052								$description = $description_parent;
11053							}
11054
11055							// HASHES
11056							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11057							{
11058								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11059								{
11060									$value = null;
11061									$algo = null;
11062									if (isset($hash['data']))
11063									{
11064										$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11065									}
11066									if (isset($hash['attribs']['']['algo']))
11067									{
11068										$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11069									}
11070									else
11071									{
11072										$algo = 'md5';
11073									}
11074									$hashes[] = $algo.':'.$value;
11075								}
11076								if (is_array($hashes))
11077								{
11078									$hashes = array_values(array_unique($hashes));
11079								}
11080							}
11081							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11082							{
11083								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11084								{
11085									$value = null;
11086									$algo = null;
11087									if (isset($hash['data']))
11088									{
11089										$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11090									}
11091									if (isset($hash['attribs']['']['algo']))
11092									{
11093										$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11094									}
11095									else
11096									{
11097										$algo = 'md5';
11098									}
11099									$hashes[] = $algo.':'.$value;
11100								}
11101								if (is_array($hashes))
11102								{
11103									$hashes = array_values(array_unique($hashes));
11104								}
11105							}
11106							else
11107							{
11108								$hashes = $hashes_parent;
11109							}
11110
11111							// KEYWORDS
11112							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11113							{
11114								if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11115								{
11116									$temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11117									foreach ($temp as $word)
11118									{
11119										$keywords[] = trim($word);
11120									}
11121									unset($temp);
11122								}
11123								if (is_array($keywords))
11124								{
11125									$keywords = array_values(array_unique($keywords));
11126								}
11127							}
11128							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11129							{
11130								if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11131								{
11132									$temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11133									foreach ($temp as $word)
11134									{
11135										$keywords[] = trim($word);
11136									}
11137									unset($temp);
11138								}
11139								if (is_array($keywords))
11140								{
11141									$keywords = array_values(array_unique($keywords));
11142								}
11143							}
11144							else
11145							{
11146								$keywords = $keywords_parent;
11147							}
11148
11149							// PLAYER
11150							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11151							{
11152								$player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11153							}
11154							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11155							{
11156								$player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11157							}
11158							else
11159							{
11160								$player = $player_parent;
11161							}
11162
11163							// RATINGS
11164							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11165							{
11166								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11167								{
11168									$rating_scheme = null;
11169									$rating_value = null;
11170									if (isset($rating['attribs']['']['scheme']))
11171									{
11172										$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11173									}
11174									else
11175									{
11176										$rating_scheme = 'urn:simple';
11177									}
11178									if (isset($rating['data']))
11179									{
11180										$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11181									}
11182									$ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11183								}
11184								if (is_array($ratings))
11185								{
11186									$ratings = array_values(array_unique($ratings));
11187								}
11188							}
11189							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11190							{
11191								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11192								{
11193									$rating_scheme = null;
11194									$rating_value = null;
11195									if (isset($rating['attribs']['']['scheme']))
11196									{
11197										$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11198									}
11199									else
11200									{
11201										$rating_scheme = 'urn:simple';
11202									}
11203									if (isset($rating['data']))
11204									{
11205										$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11206									}
11207									$ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11208								}
11209								if (is_array($ratings))
11210								{
11211									$ratings = array_values(array_unique($ratings));
11212								}
11213							}
11214							else
11215							{
11216								$ratings = $ratings_parent;
11217							}
11218
11219							// RESTRICTIONS
11220							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11221							{
11222								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11223								{
11224									$restriction_relationship = null;
11225									$restriction_type = null;
11226									$restriction_value = null;
11227									if (isset($restriction['attribs']['']['relationship']))
11228									{
11229										$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11230									}
11231									if (isset($restriction['attribs']['']['type']))
11232									{
11233										$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11234									}
11235									if (isset($restriction['data']))
11236									{
11237										$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11238									}
11239									$restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11240								}
11241								if (is_array($restrictions))
11242								{
11243									$restrictions = array_values(array_unique($restrictions));
11244								}
11245							}
11246							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11247							{
11248								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11249								{
11250									$restriction_relationship = null;
11251									$restriction_type = null;
11252									$restriction_value = null;
11253									if (isset($restriction['attribs']['']['relationship']))
11254									{
11255										$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11256									}
11257									if (isset($restriction['attribs']['']['type']))
11258									{
11259										$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11260									}
11261									if (isset($restriction['data']))
11262									{
11263										$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11264									}
11265									$restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11266								}
11267								if (is_array($restrictions))
11268								{
11269									$restrictions = array_values(array_unique($restrictions));
11270								}
11271							}
11272							else
11273							{
11274								$restrictions = $restrictions_parent;
11275							}
11276
11277							// THUMBNAILS
11278							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11279							{
11280								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11281								{
11282									$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11283								}
11284								if (is_array($thumbnails))
11285								{
11286									$thumbnails = array_values(array_unique($thumbnails));
11287								}
11288							}
11289							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11290							{
11291								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11292								{
11293									$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11294								}
11295								if (is_array($thumbnails))
11296								{
11297									$thumbnails = array_values(array_unique($thumbnails));
11298								}
11299							}
11300							else
11301							{
11302								$thumbnails = $thumbnails_parent;
11303							}
11304
11305							// TITLES
11306							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11307							{
11308								$title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11309							}
11310							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11311							{
11312								$title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11313							}
11314							else
11315							{
11316								$title = $title_parent;
11317							}
11318
11319							$this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width));
11320						}
11321					}
11322				}
11323			}
11324
11325			// If we have standalone media:content tags, loop through them.
11326			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
11327			{
11328				foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
11329				{
11330					if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11331					{
11332						// Attributes
11333						$bitrate = null;
11334						$channels = null;
11335						$duration = null;
11336						$expression = null;
11337						$framerate = null;
11338						$height = null;
11339						$javascript = null;
11340						$lang = null;
11341						$length = null;
11342						$medium = null;
11343						$samplingrate = null;
11344						$type = null;
11345						$url = null;
11346						$width = null;
11347
11348						// Elements
11349						$captions = null;
11350						$categories = null;
11351						$copyrights = null;
11352						$credits = null;
11353						$description = null;
11354						$hashes = null;
11355						$keywords = null;
11356						$player = null;
11357						$ratings = null;
11358						$restrictions = null;
11359						$thumbnails = null;
11360						$title = null;
11361
11362						// Start checking the attributes of media:content
11363						if (isset($content['attribs']['']['bitrate']))
11364						{
11365							$bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11366						}
11367						if (isset($content['attribs']['']['channels']))
11368						{
11369							$channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
11370						}
11371						if (isset($content['attribs']['']['duration']))
11372						{
11373							$duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
11374						}
11375						else
11376						{
11377							$duration = $duration_parent;
11378						}
11379						if (isset($content['attribs']['']['expression']))
11380						{
11381							$expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
11382						}
11383						if (isset($content['attribs']['']['framerate']))
11384						{
11385							$framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
11386						}
11387						if (isset($content['attribs']['']['height']))
11388						{
11389							$height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
11390						}
11391						if (isset($content['attribs']['']['lang']))
11392						{
11393							$lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11394						}
11395						if (isset($content['attribs']['']['fileSize']))
11396						{
11397							$length = ceil($content['attribs']['']['fileSize']);
11398						}
11399						if (isset($content['attribs']['']['medium']))
11400						{
11401							$medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
11402						}
11403						if (isset($content['attribs']['']['samplingrate']))
11404						{
11405							$samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11406						}
11407						if (isset($content['attribs']['']['type']))
11408						{
11409							$type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11410						}
11411						if (isset($content['attribs']['']['width']))
11412						{
11413							$width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
11414						}
11415						if (isset($content['attribs']['']['url']))
11416						{
11417							$url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11418						}
11419						// Checking the other optional media: elements. Priority: media:content, media:group, item, channel
11420
11421						// CAPTIONS
11422						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
11423						{
11424							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
11425							{
11426								$caption_type = null;
11427								$caption_lang = null;
11428								$caption_startTime = null;
11429								$caption_endTime = null;
11430								$caption_text = null;
11431								if (isset($caption['attribs']['']['type']))
11432								{
11433									$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11434								}
11435								if (isset($caption['attribs']['']['lang']))
11436								{
11437									$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11438								}
11439								if (isset($caption['attribs']['']['start']))
11440								{
11441									$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
11442								}
11443								if (isset($caption['attribs']['']['end']))
11444								{
11445									$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
11446								}
11447								if (isset($caption['data']))
11448								{
11449									$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11450								}
11451								$captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
11452							}
11453							if (is_array($captions))
11454							{
11455								$captions = array_values(array_unique($captions));
11456							}
11457						}
11458						else
11459						{
11460							$captions = $captions_parent;
11461						}
11462
11463						// CATEGORIES
11464						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
11465						{
11466							foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
11467							{
11468								$term = null;
11469								$scheme = null;
11470								$label = null;
11471								if (isset($category['data']))
11472								{
11473									$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11474								}
11475								if (isset($category['attribs']['']['scheme']))
11476								{
11477									$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11478								}
11479								else
11480								{
11481									$scheme = 'http://search.yahoo.com/mrss/category_schema';
11482								}
11483								if (isset($category['attribs']['']['label']))
11484								{
11485									$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
11486								}
11487								$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
11488							}
11489						}
11490						if (is_array($categories) && is_array($categories_parent))
11491						{
11492							$categories = array_values(array_unique(array_merge($categories, $categories_parent)));
11493						}
11494						elseif (is_array($categories))
11495						{
11496							$categories = array_values(array_unique($categories));
11497						}
11498						elseif (is_array($categories_parent))
11499						{
11500							$categories = array_values(array_unique($categories_parent));
11501						}
11502						else
11503						{
11504							$categories = null;
11505						}
11506
11507						// COPYRIGHTS
11508						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
11509						{
11510							$copyright_url = null;
11511							$copyright_label = null;
11512							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
11513							{
11514								$copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
11515							}
11516							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
11517							{
11518								$copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11519							}
11520							$copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
11521						}
11522						else
11523						{
11524							$copyrights = $copyrights_parent;
11525						}
11526
11527						// CREDITS
11528						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11529						{
11530							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11531							{
11532								$credit_role = null;
11533								$credit_scheme = null;
11534								$credit_name = null;
11535								if (isset($credit['attribs']['']['role']))
11536								{
11537									$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11538								}
11539								if (isset($credit['attribs']['']['scheme']))
11540								{
11541									$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11542								}
11543								else
11544								{
11545									$credit_scheme = 'urn:ebu';
11546								}
11547								if (isset($credit['data']))
11548								{
11549									$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11550								}
11551								$credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11552							}
11553							if (is_array($credits))
11554							{
11555								$credits = array_values(array_unique($credits));
11556							}
11557						}
11558						else
11559						{
11560							$credits = $credits_parent;
11561						}
11562
11563						// DESCRIPTION
11564						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11565						{
11566							$description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11567						}
11568						else
11569						{
11570							$description = $description_parent;
11571						}
11572
11573						// HASHES
11574						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11575						{
11576							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11577							{
11578								$value = null;
11579								$algo = null;
11580								if (isset($hash['data']))
11581								{
11582									$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11583								}
11584								if (isset($hash['attribs']['']['algo']))
11585								{
11586									$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11587								}
11588								else
11589								{
11590									$algo = 'md5';
11591								}
11592								$hashes[] = $algo.':'.$value;
11593							}
11594							if (is_array($hashes))
11595							{
11596								$hashes = array_values(array_unique($hashes));
11597							}
11598						}
11599						else
11600						{
11601							$hashes = $hashes_parent;
11602						}
11603
11604						// KEYWORDS
11605						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11606						{
11607							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11608							{
11609								$temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11610								foreach ($temp as $word)
11611								{
11612									$keywords[] = trim($word);
11613								}
11614								unset($temp);
11615							}
11616							if (is_array($keywords))
11617							{
11618								$keywords = array_values(array_unique($keywords));
11619							}
11620						}
11621						else
11622						{
11623							$keywords = $keywords_parent;
11624						}
11625
11626						// PLAYER
11627						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11628						{
11629							$player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11630						}
11631						else
11632						{
11633							$player = $player_parent;
11634						}
11635
11636						// RATINGS
11637						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11638						{
11639							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11640							{
11641								$rating_scheme = null;
11642								$rating_value = null;
11643								if (isset($rating['attribs']['']['scheme']))
11644								{
11645									$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11646								}
11647								else
11648								{
11649									$rating_scheme = 'urn:simple';
11650								}
11651								if (isset($rating['data']))
11652								{
11653									$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11654								}
11655								$ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11656							}
11657							if (is_array($ratings))
11658							{
11659								$ratings = array_values(array_unique($ratings));
11660							}
11661						}
11662						else
11663						{
11664							$ratings = $ratings_parent;
11665						}
11666
11667						// RESTRICTIONS
11668						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11669						{
11670							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11671							{
11672								$restriction_relationship = null;
11673								$restriction_type = null;
11674								$restriction_value = null;
11675								if (isset($restriction['attribs']['']['relationship']))
11676								{
11677									$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11678								}
11679								if (isset($restriction['attribs']['']['type']))
11680								{
11681									$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11682								}
11683								if (isset($restriction['data']))
11684								{
11685									$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11686								}
11687								$restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11688							}
11689							if (is_array($restrictions))
11690							{
11691								$restrictions = array_values(array_unique($restrictions));
11692							}
11693						}
11694						else
11695						{
11696							$restrictions = $restrictions_parent;
11697						}
11698
11699						// THUMBNAILS
11700						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11701						{
11702							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11703							{
11704								$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11705							}
11706							if (is_array($thumbnails))
11707							{
11708								$thumbnails = array_values(array_unique($thumbnails));
11709							}
11710						}
11711						else
11712						{
11713							$thumbnails = $thumbnails_parent;
11714						}
11715
11716						// TITLES
11717						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11718						{
11719							$title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11720						}
11721						else
11722						{
11723							$title = $title_parent;
11724						}
11725
11726						$this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width));
11727					}
11728				}
11729			}
11730
11731			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
11732			{
11733				if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11734				{
11735					// Attributes
11736					$bitrate = null;
11737					$channels = null;
11738					$duration = null;
11739					$expression = null;
11740					$framerate = null;
11741					$height = null;
11742					$javascript = null;
11743					$lang = null;
11744					$length = null;
11745					$medium = null;
11746					$samplingrate = null;
11747					$type = null;
11748					$url = null;
11749					$width = null;
11750
11751					$url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
11752					if (isset($link['attribs']['']['type']))
11753					{
11754						$type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11755					}
11756					if (isset($link['attribs']['']['length']))
11757					{
11758						$length = ceil($link['attribs']['']['length']);
11759					}
11760
11761					// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11762					$this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11763				}
11764			}
11765
11766			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
11767			{
11768				if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11769				{
11770					// Attributes
11771					$bitrate = null;
11772					$channels = null;
11773					$duration = null;
11774					$expression = null;
11775					$framerate = null;
11776					$height = null;
11777					$javascript = null;
11778					$lang = null;
11779					$length = null;
11780					$medium = null;
11781					$samplingrate = null;
11782					$type = null;
11783					$url = null;
11784					$width = null;
11785
11786					$url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
11787					if (isset($link['attribs']['']['type']))
11788					{
11789						$type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11790					}
11791					if (isset($link['attribs']['']['length']))
11792					{
11793						$length = ceil($link['attribs']['']['length']);
11794					}
11795
11796					// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11797					$this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11798				}
11799			}
11800
11801			if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
11802			{
11803				if (isset($enclosure[0]['attribs']['']['url']))
11804				{
11805					// Attributes
11806					$bitrate = null;
11807					$channels = null;
11808					$duration = null;
11809					$expression = null;
11810					$framerate = null;
11811					$height = null;
11812					$javascript = null;
11813					$lang = null;
11814					$length = null;
11815					$medium = null;
11816					$samplingrate = null;
11817					$type = null;
11818					$url = null;
11819					$width = null;
11820
11821					$url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
11822					if (isset($enclosure[0]['attribs']['']['type']))
11823					{
11824						$type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11825					}
11826					if (isset($enclosure[0]['attribs']['']['length']))
11827					{
11828						$length = ceil($enclosure[0]['attribs']['']['length']);
11829					}
11830
11831					// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11832					$this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11833				}
11834			}
11835
11836			if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width))
11837			{
11838				// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11839				$this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11840			}
11841
11842			$this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
11843		}
11844		if (!empty($this->data['enclosures']))
11845		{
11846			return $this->data['enclosures'];
11847		}
11848		else
11849		{
11850			return null;
11851		}
11852	}
11853
11854	/**
11855	 * Get the latitude coordinates for the item
11856	 *
11857	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
11858	 *
11859	 * Uses `<geo:lat>` or `<georss:point>`
11860	 *
11861	 * @since 1.0
11862	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
11863	 * @link http://www.georss.org/ GeoRSS
11864	 * @return string|null
11865	 */
11866	public function get_latitude()
11867	{
11868		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
11869		{
11870			return (float) $return[0]['data'];
11871		}
11872		elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
11873		{
11874			return (float) $match[1];
11875		}
11876		else
11877		{
11878			return null;
11879		}
11880	}
11881
11882	/**
11883	 * Get the longitude coordinates for the item
11884	 *
11885	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
11886	 *
11887	 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
11888	 *
11889	 * @since 1.0
11890	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
11891	 * @link http://www.georss.org/ GeoRSS
11892	 * @return string|null
11893	 */
11894	public function get_longitude()
11895	{
11896		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
11897		{
11898			return (float) $return[0]['data'];
11899		}
11900		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
11901		{
11902			return (float) $return[0]['data'];
11903		}
11904		elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
11905		{
11906			return (float) $match[2];
11907		}
11908		else
11909		{
11910			return null;
11911		}
11912	}
11913
11914	/**
11915	 * Get the `<atom:source>` for the item
11916	 *
11917	 * @since 1.1
11918	 * @return SimplePie_Source|null
11919	 */
11920	public function get_source()
11921	{
11922		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
11923		{
11924			return $this->registry->create('Source', array($this, $return[0]));
11925		}
11926		else
11927		{
11928			return null;
11929		}
11930	}
11931}
11932
11933/**
11934 * Used for feed auto-discovery
11935 *
11936 *
11937 * This class can be overloaded with {@see SimplePie::set_locator_class()}
11938 *
11939 * @package SimplePie
11940 */
11941class SimplePie_Locator
11942{
11943	var $useragent;
11944	var $timeout;
11945	var $file;
11946	var $local = array();
11947	var $elsewhere = array();
11948	var $cached_entities = array();
11949	var $http_base;
11950	var $base;
11951	var $base_location = 0;
11952	var $checked_feeds = 0;
11953	var $max_checked_feeds = 10;
11954	protected $registry;
11955
11956	public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10)
11957	{
11958		$this->file = $file;
11959		$this->useragent = $useragent;
11960		$this->timeout = $timeout;
11961		$this->max_checked_feeds = $max_checked_feeds;
11962
11963		if (class_exists('DOMDocument'))
11964		{
11965			$this->dom = new DOMDocument();
11966
11967			set_error_handler(array('SimplePie_Misc', 'silence_errors'));
11968			$this->dom->loadHTML($this->file->body);
11969			restore_error_handler();
11970		}
11971		else
11972		{
11973			$this->dom = null;
11974		}
11975	}
11976
11977	public function set_registry(SimplePie_Registry $registry)
11978	{
11979		$this->registry = $registry;
11980	}
11981
11982	public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
11983	{
11984		if ($this->is_feed($this->file))
11985		{
11986			return $this->file;
11987		}
11988
11989		if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
11990		{
11991			$sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
11992			if ($sniffer->get_type() !== 'text/html')
11993			{
11994				return null;
11995			}
11996		}
11997
11998		if ($type & ~SIMPLEPIE_LOCATOR_NONE)
11999		{
12000			$this->get_base();
12001		}
12002
12003		if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
12004		{
12005			return $working[0];
12006		}
12007
12008		if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
12009		{
12010			if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
12011			{
12012				return $working;
12013			}
12014
12015			if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
12016			{
12017				return $working;
12018			}
12019
12020			if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
12021			{
12022				return $working;
12023			}
12024
12025			if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
12026			{
12027				return $working;
12028			}
12029		}
12030		return null;
12031	}
12032
12033	public function is_feed($file)
12034	{
12035		if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
12036		{
12037			$sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
12038			$sniffed = $sniffer->get_type();
12039			if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml')))
12040			{
12041				return true;
12042			}
12043			else
12044			{
12045				return false;
12046			}
12047		}
12048		elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
12049		{
12050			return true;
12051		}
12052		else
12053		{
12054			return false;
12055		}
12056	}
12057
12058	public function get_base()
12059	{
12060		if ($this->dom === null)
12061		{
12062			throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12063		}
12064		$this->http_base = $this->file->url;
12065		$this->base = $this->http_base;
12066		$elements = $this->dom->getElementsByTagName('base');
12067		foreach ($elements as $element)
12068		{
12069			if ($element->hasAttribute('href'))
12070			{
12071				$base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
12072				if ($base === false)
12073				{
12074					continue;
12075				}
12076				$this->base = $base;
12077				$this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
12078				break;
12079			}
12080		}
12081	}
12082
12083	public function autodiscovery()
12084	{
12085		$done = array();
12086		$feeds = array();
12087		$feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
12088		$feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
12089		$feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
12090
12091		if (!empty($feeds))
12092		{
12093			return array_values($feeds);
12094		}
12095		else
12096		{
12097			return null;
12098		}
12099	}
12100
12101	protected function search_elements_by_tag($name, &$done, $feeds)
12102	{
12103		if ($this->dom === null)
12104		{
12105			throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12106		}
12107
12108		$links = $this->dom->getElementsByTagName($name);
12109		foreach ($links as $link)
12110		{
12111			if ($this->checked_feeds === $this->max_checked_feeds)
12112			{
12113				break;
12114			}
12115			if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
12116			{
12117				$rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel')))));
12118				$line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
12119
12120				if ($this->base_location < $line)
12121				{
12122					$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12123				}
12124				else
12125				{
12126					$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12127				}
12128				if ($href === false)
12129				{
12130					continue;
12131				}
12132
12133				if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
12134				{
12135					$this->checked_feeds++;
12136					$headers = array(
12137						'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
12138					);
12139					$feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent));
12140					if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
12141					{
12142						$feeds[$href] = $feed;
12143					}
12144				}
12145				$done[] = $href;
12146			}
12147		}
12148
12149		return $feeds;
12150	}
12151
12152	public function get_links()
12153	{
12154		if ($this->dom === null)
12155		{
12156			throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12157		}
12158
12159		$links = $this->dom->getElementsByTagName('a');
12160		foreach ($links as $link)
12161		{
12162			if ($link->hasAttribute('href'))
12163			{
12164				$href = trim($link->getAttribute('href'));
12165				$parsed = $this->registry->call('Misc', 'parse_url', array($href));
12166				if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme']))
12167				{
12168					if ($this->base_location < $link->getLineNo())
12169					{
12170						$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12171					}
12172					else
12173					{
12174						$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12175					}
12176					if ($href === false)
12177					{
12178						continue;
12179					}
12180
12181					$current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
12182
12183					if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
12184					{
12185						$this->local[] = $href;
12186					}
12187					else
12188					{
12189						$this->elsewhere[] = $href;
12190					}
12191				}
12192			}
12193		}
12194		$this->local = array_unique($this->local);
12195		$this->elsewhere = array_unique($this->elsewhere);
12196		if (!empty($this->local) || !empty($this->elsewhere))
12197		{
12198			return true;
12199		}
12200		return null;
12201	}
12202
12203	public function extension(&$array)
12204	{
12205		foreach ($array as $key => $value)
12206		{
12207			if ($this->checked_feeds === $this->max_checked_feeds)
12208			{
12209				break;
12210			}
12211			if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
12212			{
12213				$this->checked_feeds++;
12214
12215				$headers = array(
12216					'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
12217				);
12218				$feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent));
12219				if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
12220				{
12221					return $feed;
12222				}
12223				else
12224				{
12225					unset($array[$key]);
12226				}
12227			}
12228		}
12229		return null;
12230	}
12231
12232	public function body(&$array)
12233	{
12234		foreach ($array as $key => $value)
12235		{
12236			if ($this->checked_feeds === $this->max_checked_feeds)
12237			{
12238				break;
12239			}
12240			if (preg_match('/(rss|rdf|atom|xml)/i', $value))
12241			{
12242				$this->checked_feeds++;
12243				$headers = array(
12244					'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
12245				);
12246				$feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent));
12247				if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
12248				{
12249					return $feed;
12250				}
12251				else
12252				{
12253					unset($array[$key]);
12254				}
12255			}
12256		}
12257		return null;
12258	}
12259}
12260
12261/**
12262 * Miscellanous utilities
12263 *
12264 * @package SimplePie
12265 */
12266class SimplePie_Misc
12267{
12268	public static function time_hms($seconds)
12269	{
12270		$time = '';
12271
12272		$hours = floor($seconds / 3600);
12273		$remainder = $seconds % 3600;
12274		if ($hours > 0)
12275		{
12276			$time .= $hours.':';
12277		}
12278
12279		$minutes = floor($remainder / 60);
12280		$seconds = $remainder % 60;
12281		if ($minutes < 10 && $hours > 0)
12282		{
12283			$minutes = '0' . $minutes;
12284		}
12285		if ($seconds < 10)
12286		{
12287			$seconds = '0' . $seconds;
12288		}
12289
12290		$time .= $minutes.':';
12291		$time .= $seconds;
12292
12293		return $time;
12294	}
12295
12296	public static function absolutize_url($relative, $base)
12297	{
12298		$iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
12299		if ($iri === false)
12300		{
12301			return false;
12302		}
12303		return $iri->get_uri();
12304	}
12305
12306	/**
12307	 * Get a HTML/XML element from a HTML string
12308	 *
12309	 * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!)
12310	 * @param string $realname Element name (including namespace prefix if applicable)
12311	 * @param string $string HTML document
12312	 * @return array
12313	 */
12314	public static function get_element($realname, $string)
12315	{
12316		$return = array();
12317		$name = preg_quote($realname, '/');
12318		if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
12319		{
12320			for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
12321			{
12322				$return[$i]['tag'] = $realname;
12323				$return[$i]['full'] = $matches[$i][0][0];
12324				$return[$i]['offset'] = $matches[$i][0][1];
12325				if (strlen($matches[$i][3][0]) <= 2)
12326				{
12327					$return[$i]['self_closing'] = true;
12328				}
12329				else
12330				{
12331					$return[$i]['self_closing'] = false;
12332					$return[$i]['content'] = $matches[$i][4][0];
12333				}
12334				$return[$i]['attribs'] = array();
12335				if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER))
12336				{
12337					for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
12338					{
12339						if (count($attribs[$j]) === 2)
12340						{
12341							$attribs[$j][2] = $attribs[$j][1];
12342						}
12343						$return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8');
12344					}
12345				}
12346			}
12347		}
12348		return $return;
12349	}
12350
12351	public static function element_implode($element)
12352	{
12353		$full = "<$element[tag]";
12354		foreach ($element['attribs'] as $key => $value)
12355		{
12356			$key = strtolower($key);
12357			$full .= " $key=\"" . htmlspecialchars($value['data']) . '"';
12358		}
12359		if ($element['self_closing'])
12360		{
12361			$full .= ' />';
12362		}
12363		else
12364		{
12365			$full .= ">$element[content]</$element[tag]>";
12366		}
12367		return $full;
12368	}
12369
12370	public static function error($message, $level, $file, $line)
12371	{
12372		if ((ini_get('error_reporting') & $level) > 0)
12373		{
12374			switch ($level)
12375			{
12376				case E_USER_ERROR:
12377					$note = 'PHP Error';
12378					break;
12379				case E_USER_WARNING:
12380					$note = 'PHP Warning';
12381					break;
12382				case E_USER_NOTICE:
12383					$note = 'PHP Notice';
12384					break;
12385				default:
12386					$note = 'Unknown Error';
12387					break;
12388			}
12389
12390			$log_error = true;
12391			if (!function_exists('error_log'))
12392			{
12393				$log_error = false;
12394			}
12395
12396			$log_file = @ini_get('error_log');
12397			if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file))
12398			{
12399				$log_error = false;
12400			}
12401
12402			if ($log_error)
12403			{
12404				@error_log("$note: $message in $file on line $line", 0);
12405			}
12406		}
12407
12408		return $message;
12409	}
12410
12411	public static function fix_protocol($url, $http = 1)
12412	{
12413		$url = SimplePie_Misc::normalize_url($url);
12414		$parsed = SimplePie_Misc::parse_url($url);
12415		if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
12416		{
12417			return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
12418		}
12419
12420		if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
12421		{
12422			return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
12423		}
12424
12425		if ($http === 2 && $parsed['scheme'] !== '')
12426		{
12427			return "feed:$url";
12428		}
12429		elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
12430		{
12431			return substr_replace($url, 'podcast', 0, 4);
12432		}
12433		elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
12434		{
12435			return substr_replace($url, 'itpc', 0, 4);
12436		}
12437		else
12438		{
12439			return $url;
12440		}
12441	}
12442
12443	public static function parse_url($url)
12444	{
12445		$iri = new SimplePie_IRI($url);
12446		return array(
12447			'scheme' => (string) $iri->scheme,
12448			'authority' => (string) $iri->authority,
12449			'path' => (string) $iri->path,
12450			'query' => (string) $iri->query,
12451			'fragment' => (string) $iri->fragment
12452		);
12453	}
12454
12455	public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
12456	{
12457		$iri = new SimplePie_IRI('');
12458		$iri->scheme = $scheme;
12459		$iri->authority = $authority;
12460		$iri->path = $path;
12461		$iri->query = $query;
12462		$iri->fragment = $fragment;
12463		return $iri->get_uri();
12464	}
12465
12466	public static function normalize_url($url)
12467	{
12468		$iri = new SimplePie_IRI($url);
12469		return $iri->get_uri();
12470	}
12471
12472	public static function percent_encoding_normalization($match)
12473	{
12474		$integer = hexdec($match[1]);
12475		if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
12476		{
12477			return chr($integer);
12478		}
12479		else
12480		{
12481			return strtoupper($match[0]);
12482		}
12483	}
12484
12485	/**
12486	 * Converts a Windows-1252 encoded string to a UTF-8 encoded string
12487	 *
12488	 * @static
12489	 * @param string $string Windows-1252 encoded string
12490	 * @return string UTF-8 encoded string
12491	 */
12492	public static function windows_1252_to_utf8($string)
12493	{
12494		static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF");
12495
12496		return strtr($string, $convert_table);
12497	}
12498
12499	/**
12500	 * Change a string from one encoding to another
12501	 *
12502	 * @param string $data Raw data in $input encoding
12503	 * @param string $input Encoding of $data
12504	 * @param string $output Encoding you want
12505	 * @return string|boolean False if we can't convert it
12506	 */
12507	public static function change_encoding($data, $input, $output)
12508	{
12509		$input = SimplePie_Misc::encoding($input);
12510		$output = SimplePie_Misc::encoding($output);
12511
12512		// We fail to fail on non US-ASCII bytes
12513		if ($input === 'US-ASCII')
12514		{
12515			static $non_ascii_octects = '';
12516			if (!$non_ascii_octects)
12517			{
12518				for ($i = 0x80; $i <= 0xFF; $i++)
12519				{
12520					$non_ascii_octects .= chr($i);
12521				}
12522			}
12523			$data = substr($data, 0, strcspn($data, $non_ascii_octects));
12524		}
12525
12526		// This is first, as behaviour of this is completely predictable
12527		if ($input === 'windows-1252' && $output === 'UTF-8')
12528		{
12529			return SimplePie_Misc::windows_1252_to_utf8($data);
12530		}
12531		// This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
12532		elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output)))
12533		{
12534			return $return;
12535 		}
12536		// This is last, as behaviour of this varies with OS userland and PHP version
12537		elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output)))
12538		{
12539			return $return;
12540		}
12541		// If we can't do anything, just fail
12542		else
12543		{
12544			return false;
12545		}
12546	}
12547
12548	protected static function change_encoding_mbstring($data, $input, $output)
12549	{
12550		if ($input === 'windows-949')
12551		{
12552			$input = 'EUC-KR';
12553		}
12554		if ($output === 'windows-949')
12555		{
12556			$output = 'EUC-KR';
12557		}
12558		if ($input === 'Windows-31J')
12559		{
12560			$input = 'SJIS';
12561		}
12562		if ($output === 'Windows-31J')
12563		{
12564			$output = 'SJIS';
12565		}
12566
12567		// Check that the encoding is supported
12568		if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
12569		{
12570			return false;
12571		}
12572		if (!in_array($input, mb_list_encodings()))
12573		{
12574			return false;
12575		}
12576
12577		// Let's do some conversion
12578		if ($return = @mb_convert_encoding($data, $output, $input))
12579		{
12580			return $return;
12581		}
12582
12583		return false;
12584	}
12585
12586	protected static function change_encoding_iconv($data, $input, $output)
12587	{
12588		return @iconv($input, $output, $data);
12589	}
12590
12591	/**
12592	 * Normalize an encoding name
12593	 *
12594	 * This is automatically generated by create.php
12595	 *
12596	 * To generate it, run `php create.php` on the command line, and copy the
12597	 * output to replace this function.
12598	 *
12599	 * @param string $charset Character set to standardise
12600	 * @return string Standardised name
12601	 */
12602	public static function encoding($charset)
12603	{
12604		// Normalization from UTS #22
12605		switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
12606		{
12607			case 'adobestandardencoding':
12608			case 'csadobestandardencoding':
12609				return 'Adobe-Standard-Encoding';
12610
12611			case 'adobesymbolencoding':
12612			case 'cshppsmath':
12613				return 'Adobe-Symbol-Encoding';
12614
12615			case 'ami1251':
12616			case 'amiga1251':
12617				return 'Amiga-1251';
12618
12619			case 'ansix31101983':
12620			case 'csat5001983':
12621			case 'csiso99naplps':
12622			case 'isoir99':
12623			case 'naplps':
12624				return 'ANSI_X3.110-1983';
12625
12626			case 'arabic7':
12627			case 'asmo449':
12628			case 'csiso89asmo449':
12629			case 'iso9036':
12630			case 'isoir89':
12631				return 'ASMO_449';
12632
12633			case 'big5':
12634			case 'csbig5':
12635				return 'Big5';
12636
12637			case 'big5hkscs':
12638				return 'Big5-HKSCS';
12639
12640			case 'bocu1':
12641			case 'csbocu1':
12642				return 'BOCU-1';
12643
12644			case 'brf':
12645			case 'csbrf':
12646				return 'BRF';
12647
12648			case 'bs4730':
12649			case 'csiso4unitedkingdom':
12650			case 'gb':
12651			case 'iso646gb':
12652			case 'isoir4':
12653			case 'uk':
12654				return 'BS_4730';
12655
12656			case 'bsviewdata':
12657			case 'csiso47bsviewdata':
12658			case 'isoir47':
12659				return 'BS_viewdata';
12660
12661			case 'cesu8':
12662			case 'cscesu8':
12663				return 'CESU-8';
12664
12665			case 'ca':
12666			case 'csa71':
12667			case 'csaz243419851':
12668			case 'csiso121canadian1':
12669			case 'iso646ca':
12670			case 'isoir121':
12671				return 'CSA_Z243.4-1985-1';
12672
12673			case 'csa72':
12674			case 'csaz243419852':
12675			case 'csiso122canadian2':
12676			case 'iso646ca2':
12677			case 'isoir122':
12678				return 'CSA_Z243.4-1985-2';
12679
12680			case 'csaz24341985gr':
12681			case 'csiso123csaz24341985gr':
12682			case 'isoir123':
12683				return 'CSA_Z243.4-1985-gr';
12684
12685			case 'csiso139csn369103':
12686			case 'csn369103':
12687			case 'isoir139':
12688				return 'CSN_369103';
12689
12690			case 'csdecmcs':
12691			case 'dec':
12692			case 'decmcs':
12693				return 'DEC-MCS';
12694
12695			case 'csiso21german':
12696			case 'de':
12697			case 'din66003':
12698			case 'iso646de':
12699			case 'isoir21':
12700				return 'DIN_66003';
12701
12702			case 'csdkus':
12703			case 'dkus':
12704				return 'dk-us';
12705
12706			case 'csiso646danish':
12707			case 'dk':
12708			case 'ds2089':
12709			case 'iso646dk':
12710				return 'DS_2089';
12711
12712			case 'csibmebcdicatde':
12713			case 'ebcdicatde':
12714				return 'EBCDIC-AT-DE';
12715
12716			case 'csebcdicatdea':
12717			case 'ebcdicatdea':
12718				return 'EBCDIC-AT-DE-A';
12719
12720			case 'csebcdiccafr':
12721			case 'ebcdiccafr':
12722				return 'EBCDIC-CA-FR';
12723
12724			case 'csebcdicdkno':
12725			case 'ebcdicdkno':
12726				return 'EBCDIC-DK-NO';
12727
12728			case 'csebcdicdknoa':
12729			case 'ebcdicdknoa':
12730				return 'EBCDIC-DK-NO-A';
12731
12732			case 'csebcdices':
12733			case 'ebcdices':
12734				return 'EBCDIC-ES';
12735
12736			case 'csebcdicesa':
12737			case 'ebcdicesa':
12738				return 'EBCDIC-ES-A';
12739
12740			case 'csebcdicess':
12741			case 'ebcdicess':
12742				return 'EBCDIC-ES-S';
12743
12744			case 'csebcdicfise':
12745			case 'ebcdicfise':
12746				return 'EBCDIC-FI-SE';
12747
12748			case 'csebcdicfisea':
12749			case 'ebcdicfisea':
12750				return 'EBCDIC-FI-SE-A';
12751
12752			case 'csebcdicfr':
12753			case 'ebcdicfr':
12754				return 'EBCDIC-FR';
12755
12756			case 'csebcdicit':
12757			case 'ebcdicit':
12758				return 'EBCDIC-IT';
12759
12760			case 'csebcdicpt':
12761			case 'ebcdicpt':
12762				return 'EBCDIC-PT';
12763
12764			case 'csebcdicuk':
12765			case 'ebcdicuk':
12766				return 'EBCDIC-UK';
12767
12768			case 'csebcdicus':
12769			case 'ebcdicus':
12770				return 'EBCDIC-US';
12771
12772			case 'csiso111ecmacyrillic':
12773			case 'ecmacyrillic':
12774			case 'isoir111':
12775			case 'koi8e':
12776				return 'ECMA-cyrillic';
12777
12778			case 'csiso17spanish':
12779			case 'es':
12780			case 'iso646es':
12781			case 'isoir17':
12782				return 'ES';
12783
12784			case 'csiso85spanish2':
12785			case 'es2':
12786			case 'iso646es2':
12787			case 'isoir85':
12788				return 'ES2';
12789
12790			case 'cseucpkdfmtjapanese':
12791			case 'eucjp':
12792			case 'extendedunixcodepackedformatforjapanese':
12793				return 'EUC-JP';
12794
12795			case 'cseucfixwidjapanese':
12796			case 'extendedunixcodefixedwidthforjapanese':
12797				return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
12798
12799			case 'gb18030':
12800				return 'GB18030';
12801
12802			case 'chinese':
12803			case 'cp936':
12804			case 'csgb2312':
12805			case 'csiso58gb231280':
12806			case 'gb2312':
12807			case 'gb231280':
12808			case 'gbk':
12809			case 'isoir58':
12810			case 'ms936':
12811			case 'windows936':
12812				return 'GBK';
12813
12814			case 'cn':
12815			case 'csiso57gb1988':
12816			case 'gb198880':
12817			case 'iso646cn':
12818			case 'isoir57':
12819				return 'GB_1988-80';
12820
12821			case 'csiso153gost1976874':
12822			case 'gost1976874':
12823			case 'isoir153':
12824			case 'stsev35888':
12825				return 'GOST_19768-74';
12826
12827			case 'csiso150':
12828			case 'csiso150greekccitt':
12829			case 'greekccitt':
12830			case 'isoir150':
12831				return 'greek-ccitt';
12832
12833			case 'csiso88greek7':
12834			case 'greek7':
12835			case 'isoir88':
12836				return 'greek7';
12837
12838			case 'csiso18greek7old':
12839			case 'greek7old':
12840			case 'isoir18':
12841				return 'greek7-old';
12842
12843			case 'cshpdesktop':
12844			case 'hpdesktop':
12845				return 'HP-DeskTop';
12846
12847			case 'cshplegal':
12848			case 'hplegal':
12849				return 'HP-Legal';
12850
12851			case 'cshpmath8':
12852			case 'hpmath8':
12853				return 'HP-Math8';
12854
12855			case 'cshppifont':
12856			case 'hppifont':
12857				return 'HP-Pi-font';
12858
12859			case 'cshproman8':
12860			case 'hproman8':
12861			case 'r8':
12862			case 'roman8':
12863				return 'hp-roman8';
12864
12865			case 'hzgb2312':
12866				return 'HZ-GB-2312';
12867
12868			case 'csibmsymbols':
12869			case 'ibmsymbols':
12870				return 'IBM-Symbols';
12871
12872			case 'csibmthai':
12873			case 'ibmthai':
12874				return 'IBM-Thai';
12875
12876			case 'cp37':
12877			case 'csibm37':
12878			case 'ebcdiccpca':
12879			case 'ebcdiccpnl':
12880			case 'ebcdiccpus':
12881			case 'ebcdiccpwt':
12882			case 'ibm37':
12883				return 'IBM037';
12884
12885			case 'cp38':
12886			case 'csibm38':
12887			case 'ebcdicint':
12888			case 'ibm38':
12889				return 'IBM038';
12890
12891			case 'cp273':
12892			case 'csibm273':
12893			case 'ibm273':
12894				return 'IBM273';
12895
12896			case 'cp274':
12897			case 'csibm274':
12898			case 'ebcdicbe':
12899			case 'ibm274':
12900				return 'IBM274';
12901
12902			case 'cp275':
12903			case 'csibm275':
12904			case 'ebcdicbr':
12905			case 'ibm275':
12906				return 'IBM275';
12907
12908			case 'csibm277':
12909			case 'ebcdiccpdk':
12910			case 'ebcdiccpno':
12911			case 'ibm277':
12912				return 'IBM277';
12913
12914			case 'cp278':
12915			case 'csibm278':
12916			case 'ebcdiccpfi':
12917			case 'ebcdiccpse':
12918			case 'ibm278':
12919				return 'IBM278';
12920
12921			case 'cp280':
12922			case 'csibm280':
12923			case 'ebcdiccpit':
12924			case 'ibm280':
12925				return 'IBM280';
12926
12927			case 'cp281':
12928			case 'csibm281':
12929			case 'ebcdicjpe':
12930			case 'ibm281':
12931				return 'IBM281';
12932
12933			case 'cp284':
12934			case 'csibm284':
12935			case 'ebcdiccpes':
12936			case 'ibm284':
12937				return 'IBM284';
12938
12939			case 'cp285':
12940			case 'csibm285':
12941			case 'ebcdiccpgb':
12942			case 'ibm285':
12943				return 'IBM285';
12944
12945			case 'cp290':
12946			case 'csibm290':
12947			case 'ebcdicjpkana':
12948			case 'ibm290':
12949				return 'IBM290';
12950
12951			case 'cp297':
12952			case 'csibm297':
12953			case 'ebcdiccpfr':
12954			case 'ibm297':
12955				return 'IBM297';
12956
12957			case 'cp420':
12958			case 'csibm420':
12959			case 'ebcdiccpar1':
12960			case 'ibm420':
12961				return 'IBM420';
12962
12963			case 'cp423':
12964			case 'csibm423':
12965			case 'ebcdiccpgr':
12966			case 'ibm423':
12967				return 'IBM423';
12968
12969			case 'cp424':
12970			case 'csibm424':
12971			case 'ebcdiccphe':
12972			case 'ibm424':
12973				return 'IBM424';
12974
12975			case '437':
12976			case 'cp437':
12977			case 'cspc8codepage437':
12978			case 'ibm437':
12979				return 'IBM437';
12980
12981			case 'cp500':
12982			case 'csibm500':
12983			case 'ebcdiccpbe':
12984			case 'ebcdiccpch':
12985			case 'ibm500':
12986				return 'IBM500';
12987
12988			case 'cp775':
12989			case 'cspc775baltic':
12990			case 'ibm775':
12991				return 'IBM775';
12992
12993			case '850':
12994			case 'cp850':
12995			case 'cspc850multilingual':
12996			case 'ibm850':
12997				return 'IBM850';
12998
12999			case '851':
13000			case 'cp851':
13001			case 'csibm851':
13002			case 'ibm851':
13003				return 'IBM851';
13004
13005			case '852':
13006			case 'cp852':
13007			case 'cspcp852':
13008			case 'ibm852':
13009				return 'IBM852';
13010
13011			case '855':
13012			case 'cp855':
13013			case 'csibm855':
13014			case 'ibm855':
13015				return 'IBM855';
13016
13017			case '857':
13018			case 'cp857':
13019			case 'csibm857':
13020			case 'ibm857':
13021				return 'IBM857';
13022
13023			case 'ccsid858':
13024			case 'cp858':
13025			case 'ibm858':
13026			case 'pcmultilingual850euro':
13027				return 'IBM00858';
13028
13029			case '860':
13030			case 'cp860':
13031			case 'csibm860':
13032			case 'ibm860':
13033				return 'IBM860';
13034
13035			case '861':
13036			case 'cp861':
13037			case 'cpis':
13038			case 'csibm861':
13039			case 'ibm861':
13040				return 'IBM861';
13041
13042			case '862':
13043			case 'cp862':
13044			case 'cspc862latinhebrew':
13045			case 'ibm862':
13046				return 'IBM862';
13047
13048			case '863':
13049			case 'cp863':
13050			case 'csibm863':
13051			case 'ibm863':
13052				return 'IBM863';
13053
13054			case 'cp864':
13055			case 'csibm864':
13056			case 'ibm864':
13057				return 'IBM864';
13058
13059			case '865':
13060			case 'cp865':
13061			case 'csibm865':
13062			case 'ibm865':
13063				return 'IBM865';
13064
13065			case '866':
13066			case 'cp866':
13067			case 'csibm866':
13068			case 'ibm866':
13069				return 'IBM866';
13070
13071			case 'cp868':
13072			case 'cpar':
13073			case 'csibm868':
13074			case 'ibm868':
13075				return 'IBM868';
13076
13077			case '869':
13078			case 'cp869':
13079			case 'cpgr':
13080			case 'csibm869':
13081			case 'ibm869':
13082				return 'IBM869';
13083
13084			case 'cp870':
13085			case 'csibm870':
13086			case 'ebcdiccproece':
13087			case 'ebcdiccpyu':
13088			case 'ibm870':
13089				return 'IBM870';
13090
13091			case 'cp871':
13092			case 'csibm871':
13093			case 'ebcdiccpis':
13094			case 'ibm871':
13095				return 'IBM871';
13096
13097			case 'cp880':
13098			case 'csibm880':
13099			case 'ebcdiccyrillic':
13100			case 'ibm880':
13101				return 'IBM880';
13102
13103			case 'cp891':
13104			case 'csibm891':
13105			case 'ibm891':
13106				return 'IBM891';
13107
13108			case 'cp903':
13109			case 'csibm903':
13110			case 'ibm903':
13111				return 'IBM903';
13112
13113			case '904':
13114			case 'cp904':
13115			case 'csibbm904':
13116			case 'ibm904':
13117				return 'IBM904';
13118
13119			case 'cp905':
13120			case 'csibm905':
13121			case 'ebcdiccptr':
13122			case 'ibm905':
13123				return 'IBM905';
13124
13125			case 'cp918':
13126			case 'csibm918':
13127			case 'ebcdiccpar2':
13128			case 'ibm918':
13129				return 'IBM918';
13130
13131			case 'ccsid924':
13132			case 'cp924':
13133			case 'ebcdiclatin9euro':
13134			case 'ibm924':
13135				return 'IBM00924';
13136
13137			case 'cp1026':
13138			case 'csibm1026':
13139			case 'ibm1026':
13140				return 'IBM1026';
13141
13142			case 'ibm1047':
13143				return 'IBM1047';
13144
13145			case 'ccsid1140':
13146			case 'cp1140':
13147			case 'ebcdicus37euro':
13148			case 'ibm1140':
13149				return 'IBM01140';
13150
13151			case 'ccsid1141':
13152			case 'cp1141':
13153			case 'ebcdicde273euro':
13154			case 'ibm1141':
13155				return 'IBM01141';
13156
13157			case 'ccsid1142':
13158			case 'cp1142':
13159			case 'ebcdicdk277euro':
13160			case 'ebcdicno277euro':
13161			case 'ibm1142':
13162				return 'IBM01142';
13163
13164			case 'ccsid1143':
13165			case 'cp1143':
13166			case 'ebcdicfi278euro':
13167			case 'ebcdicse278euro':
13168			case 'ibm1143':
13169				return 'IBM01143';
13170
13171			case 'ccsid1144':
13172			case 'cp1144':
13173			case 'ebcdicit280euro':
13174			case 'ibm1144':
13175				return 'IBM01144';
13176
13177			case 'ccsid1145':
13178			case 'cp1145':
13179			case 'ebcdices284euro':
13180			case 'ibm1145':
13181				return 'IBM01145';
13182
13183			case 'ccsid1146':
13184			case 'cp1146':
13185			case 'ebcdicgb285euro':
13186			case 'ibm1146':
13187				return 'IBM01146';
13188
13189			case 'ccsid1147':
13190			case 'cp1147':
13191			case 'ebcdicfr297euro':
13192			case 'ibm1147':
13193				return 'IBM01147';
13194
13195			case 'ccsid1148':
13196			case 'cp1148':
13197			case 'ebcdicinternational500euro':
13198			case 'ibm1148':
13199				return 'IBM01148';
13200
13201			case 'ccsid1149':
13202			case 'cp1149':
13203			case 'ebcdicis871euro':
13204			case 'ibm1149':
13205				return 'IBM01149';
13206
13207			case 'csiso143iecp271':
13208			case 'iecp271':
13209			case 'isoir143':
13210				return 'IEC_P27-1';
13211
13212			case 'csiso49inis':
13213			case 'inis':
13214			case 'isoir49':
13215				return 'INIS';
13216
13217			case 'csiso50inis8':
13218			case 'inis8':
13219			case 'isoir50':
13220				return 'INIS-8';
13221
13222			case 'csiso51iniscyrillic':
13223			case 'iniscyrillic':
13224			case 'isoir51':
13225				return 'INIS-cyrillic';
13226
13227			case 'csinvariant':
13228			case 'invariant':
13229				return 'INVARIANT';
13230
13231			case 'iso2022cn':
13232				return 'ISO-2022-CN';
13233
13234			case 'iso2022cnext':
13235				return 'ISO-2022-CN-EXT';
13236
13237			case 'csiso2022jp':
13238			case 'iso2022jp':
13239				return 'ISO-2022-JP';
13240
13241			case 'csiso2022jp2':
13242			case 'iso2022jp2':
13243				return 'ISO-2022-JP-2';
13244
13245			case 'csiso2022kr':
13246			case 'iso2022kr':
13247				return 'ISO-2022-KR';
13248
13249			case 'cswindows30latin1':
13250			case 'iso88591windows30latin1':
13251				return 'ISO-8859-1-Windows-3.0-Latin-1';
13252
13253			case 'cswindows31latin1':
13254			case 'iso88591windows31latin1':
13255				return 'ISO-8859-1-Windows-3.1-Latin-1';
13256
13257			case 'csisolatin2':
13258			case 'iso88592':
13259			case 'iso885921987':
13260			case 'isoir101':
13261			case 'l2':
13262			case 'latin2':
13263				return 'ISO-8859-2';
13264
13265			case 'cswindows31latin2':
13266			case 'iso88592windowslatin2':
13267				return 'ISO-8859-2-Windows-Latin-2';
13268
13269			case 'csisolatin3':
13270			case 'iso88593':
13271			case 'iso885931988':
13272			case 'isoir109':
13273			case 'l3':
13274			case 'latin3':
13275				return 'ISO-8859-3';
13276
13277			case 'csisolatin4':
13278			case 'iso88594':
13279			case 'iso885941988':
13280			case 'isoir110':
13281			case 'l4':
13282			case 'latin4':
13283				return 'ISO-8859-4';
13284
13285			case 'csisolatincyrillic':
13286			case 'cyrillic':
13287			case 'iso88595':
13288			case 'iso885951988':
13289			case 'isoir144':
13290				return 'ISO-8859-5';
13291
13292			case 'arabic':
13293			case 'asmo708':
13294			case 'csisolatinarabic':
13295			case 'ecma114':
13296			case 'iso88596':
13297			case 'iso885961987':
13298			case 'isoir127':
13299				return 'ISO-8859-6';
13300
13301			case 'csiso88596e':
13302			case 'iso88596e':
13303				return 'ISO-8859-6-E';
13304
13305			case 'csiso88596i':
13306			case 'iso88596i':
13307				return 'ISO-8859-6-I';
13308
13309			case 'csisolatingreek':
13310			case 'ecma118':
13311			case 'elot928':
13312			case 'greek':
13313			case 'greek8':
13314			case 'iso88597':
13315			case 'iso885971987':
13316			case 'isoir126':
13317				return 'ISO-8859-7';
13318
13319			case 'csisolatinhebrew':
13320			case 'hebrew':
13321			case 'iso88598':
13322			case 'iso885981988':
13323			case 'isoir138':
13324				return 'ISO-8859-8';
13325
13326			case 'csiso88598e':
13327			case 'iso88598e':
13328				return 'ISO-8859-8-E';
13329
13330			case 'csiso88598i':
13331			case 'iso88598i':
13332				return 'ISO-8859-8-I';
13333
13334			case 'cswindows31latin5':
13335			case 'iso88599windowslatin5':
13336				return 'ISO-8859-9-Windows-Latin-5';
13337
13338			case 'csisolatin6':
13339			case 'iso885910':
13340			case 'iso8859101992':
13341			case 'isoir157':
13342			case 'l6':
13343			case 'latin6':
13344				return 'ISO-8859-10';
13345
13346			case 'iso885913':
13347				return 'ISO-8859-13';
13348
13349			case 'iso885914':
13350			case 'iso8859141998':
13351			case 'isoceltic':
13352			case 'isoir199':
13353			case 'l8':
13354			case 'latin8':
13355				return 'ISO-8859-14';
13356
13357			case 'iso885915':
13358			case 'latin9':
13359				return 'ISO-8859-15';
13360
13361			case 'iso885916':
13362			case 'iso8859162001':
13363			case 'isoir226':
13364			case 'l10':
13365			case 'latin10':
13366				return 'ISO-8859-16';
13367
13368			case 'iso10646j1':
13369				return 'ISO-10646-J-1';
13370
13371			case 'csunicode':
13372			case 'iso10646ucs2':
13373				return 'ISO-10646-UCS-2';
13374
13375			case 'csucs4':
13376			case 'iso10646ucs4':
13377				return 'ISO-10646-UCS-4';
13378
13379			case 'csunicodeascii':
13380			case 'iso10646ucsbasic':
13381				return 'ISO-10646-UCS-Basic';
13382
13383			case 'csunicodelatin1':
13384			case 'iso10646':
13385			case 'iso10646unicodelatin1':
13386				return 'ISO-10646-Unicode-Latin1';
13387
13388			case 'csiso10646utf1':
13389			case 'iso10646utf1':
13390				return 'ISO-10646-UTF-1';
13391
13392			case 'csiso115481':
13393			case 'iso115481':
13394			case 'isotr115481':
13395				return 'ISO-11548-1';
13396
13397			case 'csiso90':
13398			case 'isoir90':
13399				return 'iso-ir-90';
13400
13401			case 'csunicodeibm1261':
13402			case 'isounicodeibm1261':
13403				return 'ISO-Unicode-IBM-1261';
13404
13405			case 'csunicodeibm1264':
13406			case 'isounicodeibm1264':
13407				return 'ISO-Unicode-IBM-1264';
13408
13409			case 'csunicodeibm1265':
13410			case 'isounicodeibm1265':
13411				return 'ISO-Unicode-IBM-1265';
13412
13413			case 'csunicodeibm1268':
13414			case 'isounicodeibm1268':
13415				return 'ISO-Unicode-IBM-1268';
13416
13417			case 'csunicodeibm1276':
13418			case 'isounicodeibm1276':
13419				return 'ISO-Unicode-IBM-1276';
13420
13421			case 'csiso646basic1983':
13422			case 'iso646basic1983':
13423			case 'ref':
13424				return 'ISO_646.basic:1983';
13425
13426			case 'csiso2intlrefversion':
13427			case 'irv':
13428			case 'iso646irv1983':
13429			case 'isoir2':
13430				return 'ISO_646.irv:1983';
13431
13432			case 'csiso2033':
13433			case 'e13b':
13434			case 'iso20331983':
13435			case 'isoir98':
13436				return 'ISO_2033-1983';
13437
13438			case 'csiso5427cyrillic':
13439			case 'iso5427':
13440			case 'isoir37':
13441				return 'ISO_5427';
13442
13443			case 'iso5427cyrillic1981':
13444			case 'iso54271981':
13445			case 'isoir54':
13446				return 'ISO_5427:1981';
13447
13448			case 'csiso5428greek':
13449			case 'iso54281980':
13450			case 'isoir55':
13451				return 'ISO_5428:1980';
13452
13453			case 'csiso6937add':
13454			case 'iso6937225':
13455			case 'isoir152':
13456				return 'ISO_6937-2-25';
13457
13458			case 'csisotextcomm':
13459			case 'iso69372add':
13460			case 'isoir142':
13461				return 'ISO_6937-2-add';
13462
13463			case 'csiso8859supp':
13464			case 'iso8859supp':
13465			case 'isoir154':
13466			case 'latin125':
13467				return 'ISO_8859-supp';
13468
13469			case 'csiso10367box':
13470			case 'iso10367box':
13471			case 'isoir155':
13472				return 'ISO_10367-box';
13473
13474			case 'csiso15italian':
13475			case 'iso646it':
13476			case 'isoir15':
13477			case 'it':
13478				return 'IT';
13479
13480			case 'csiso13jisc6220jp':
13481			case 'isoir13':
13482			case 'jisc62201969':
13483			case 'jisc62201969jp':
13484			case 'katakana':
13485			case 'x2017':
13486				return 'JIS_C6220-1969-jp';
13487
13488			case 'csiso14jisc6220ro':
13489			case 'iso646jp':
13490			case 'isoir14':
13491			case 'jisc62201969ro':
13492			case 'jp':
13493				return 'JIS_C6220-1969-ro';
13494
13495			case 'csiso42jisc62261978':
13496			case 'isoir42':
13497			case 'jisc62261978':
13498				return 'JIS_C6226-1978';
13499
13500			case 'csiso87jisx208':
13501			case 'isoir87':
13502			case 'jisc62261983':
13503			case 'jisx2081983':
13504			case 'x208':
13505				return 'JIS_C6226-1983';
13506
13507			case 'csiso91jisc62291984a':
13508			case 'isoir91':
13509			case 'jisc62291984a':
13510			case 'jpocra':
13511				return 'JIS_C6229-1984-a';
13512
13513			case 'csiso92jisc62991984b':
13514			case 'iso646jpocrb':
13515			case 'isoir92':
13516			case 'jisc62291984b':
13517			case 'jpocrb':
13518				return 'JIS_C6229-1984-b';
13519
13520			case 'csiso93jis62291984badd':
13521			case 'isoir93':
13522			case 'jisc62291984badd':
13523			case 'jpocrbadd':
13524				return 'JIS_C6229-1984-b-add';
13525
13526			case 'csiso94jis62291984hand':
13527			case 'isoir94':
13528			case 'jisc62291984hand':
13529			case 'jpocrhand':
13530				return 'JIS_C6229-1984-hand';
13531
13532			case 'csiso95jis62291984handadd':
13533			case 'isoir95':
13534			case 'jisc62291984handadd':
13535			case 'jpocrhandadd':
13536				return 'JIS_C6229-1984-hand-add';
13537
13538			case 'csiso96jisc62291984kana':
13539			case 'isoir96':
13540			case 'jisc62291984kana':
13541				return 'JIS_C6229-1984-kana';
13542
13543			case 'csjisencoding':
13544			case 'jisencoding':
13545				return 'JIS_Encoding';
13546
13547			case 'cshalfwidthkatakana':
13548			case 'jisx201':
13549			case 'x201':
13550				return 'JIS_X0201';
13551
13552			case 'csiso159jisx2121990':
13553			case 'isoir159':
13554			case 'jisx2121990':
13555			case 'x212':
13556				return 'JIS_X0212-1990';
13557
13558			case 'csiso141jusib1002':
13559			case 'iso646yu':
13560			case 'isoir141':
13561			case 'js':
13562			case 'jusib1002':
13563			case 'yu':
13564				return 'JUS_I.B1.002';
13565
13566			case 'csiso147macedonian':
13567			case 'isoir147':
13568			case 'jusib1003mac':
13569			case 'macedonian':
13570				return 'JUS_I.B1.003-mac';
13571
13572			case 'csiso146serbian':
13573			case 'isoir146':
13574			case 'jusib1003serb':
13575			case 'serbian':
13576				return 'JUS_I.B1.003-serb';
13577
13578			case 'koi7switched':
13579				return 'KOI7-switched';
13580
13581			case 'cskoi8r':
13582			case 'koi8r':
13583				return 'KOI8-R';
13584
13585			case 'koi8u':
13586				return 'KOI8-U';
13587
13588			case 'csksc5636':
13589			case 'iso646kr':
13590			case 'ksc5636':
13591				return 'KSC5636';
13592
13593			case 'cskz1048':
13594			case 'kz1048':
13595			case 'rk1048':
13596			case 'strk10482002':
13597				return 'KZ-1048';
13598
13599			case 'csiso19latingreek':
13600			case 'isoir19':
13601			case 'latingreek':
13602				return 'latin-greek';
13603
13604			case 'csiso27latingreek1':
13605			case 'isoir27':
13606			case 'latingreek1':
13607				return 'Latin-greek-1';
13608
13609			case 'csiso158lap':
13610			case 'isoir158':
13611			case 'lap':
13612			case 'latinlap':
13613				return 'latin-lap';
13614
13615			case 'csmacintosh':
13616			case 'mac':
13617			case 'macintosh':
13618				return 'macintosh';
13619
13620			case 'csmicrosoftpublishing':
13621			case 'microsoftpublishing':
13622				return 'Microsoft-Publishing';
13623
13624			case 'csmnem':
13625			case 'mnem':
13626				return 'MNEM';
13627
13628			case 'csmnemonic':
13629			case 'mnemonic':
13630				return 'MNEMONIC';
13631
13632			case 'csiso86hungarian':
13633			case 'hu':
13634			case 'iso646hu':
13635			case 'isoir86':
13636			case 'msz77953':
13637				return 'MSZ_7795.3';
13638
13639			case 'csnatsdano':
13640			case 'isoir91':
13641			case 'natsdano':
13642				return 'NATS-DANO';
13643
13644			case 'csnatsdanoadd':
13645			case 'isoir92':
13646			case 'natsdanoadd':
13647				return 'NATS-DANO-ADD';
13648
13649			case 'csnatssefi':
13650			case 'isoir81':
13651			case 'natssefi':
13652				return 'NATS-SEFI';
13653
13654			case 'csnatssefiadd':
13655			case 'isoir82':
13656			case 'natssefiadd':
13657				return 'NATS-SEFI-ADD';
13658
13659			case 'csiso151cuba':
13660			case 'cuba':
13661			case 'iso646cu':
13662			case 'isoir151':
13663			case 'ncnc1081':
13664				return 'NC_NC00-10:81';
13665
13666			case 'csiso69french':
13667			case 'fr':
13668			case 'iso646fr':
13669			case 'isoir69':
13670			case 'nfz62010':
13671				return 'NF_Z_62-010';
13672
13673			case 'csiso25french':
13674			case 'iso646fr1':
13675			case 'isoir25':
13676			case 'nfz620101973':
13677				return 'NF_Z_62-010_(1973)';
13678
13679			case 'csiso60danishnorwegian':
13680			case 'csiso60norwegian1':
13681			case 'iso646no':
13682			case 'isoir60':
13683			case 'no':
13684			case 'ns45511':
13685				return 'NS_4551-1';
13686
13687			case 'csiso61norwegian2':
13688			case 'iso646no2':
13689			case 'isoir61':
13690			case 'no2':
13691			case 'ns45512':
13692				return 'NS_4551-2';
13693
13694			case 'osdebcdicdf3irv':
13695				return 'OSD_EBCDIC_DF03_IRV';
13696
13697			case 'osdebcdicdf41':
13698				return 'OSD_EBCDIC_DF04_1';
13699
13700			case 'osdebcdicdf415':
13701				return 'OSD_EBCDIC_DF04_15';
13702
13703			case 'cspc8danishnorwegian':
13704			case 'pc8danishnorwegian':
13705				return 'PC8-Danish-Norwegian';
13706
13707			case 'cspc8turkish':
13708			case 'pc8turkish':
13709				return 'PC8-Turkish';
13710
13711			case 'csiso16portuguese':
13712			case 'iso646pt':
13713			case 'isoir16':
13714			case 'pt':
13715				return 'PT';
13716
13717			case 'csiso84portuguese2':
13718			case 'iso646pt2':
13719			case 'isoir84':
13720			case 'pt2':
13721				return 'PT2';
13722
13723			case 'cp154':
13724			case 'csptcp154':
13725			case 'cyrillicasian':
13726			case 'pt154':
13727			case 'ptcp154':
13728				return 'PTCP154';
13729
13730			case 'scsu':
13731				return 'SCSU';
13732
13733			case 'csiso10swedish':
13734			case 'fi':
13735			case 'iso646fi':
13736			case 'iso646se':
13737			case 'isoir10':
13738			case 'se':
13739			case 'sen850200b':
13740				return 'SEN_850200_B';
13741
13742			case 'csiso11swedishfornames':
13743			case 'iso646se2':
13744			case 'isoir11':
13745			case 'se2':
13746			case 'sen850200c':
13747				return 'SEN_850200_C';
13748
13749			case 'csiso102t617bit':
13750			case 'isoir102':
13751			case 't617bit':
13752				return 'T.61-7bit';
13753
13754			case 'csiso103t618bit':
13755			case 'isoir103':
13756			case 't61':
13757			case 't618bit':
13758				return 'T.61-8bit';
13759
13760			case 'csiso128t101g2':
13761			case 'isoir128':
13762			case 't101g2':
13763				return 'T.101-G2';
13764
13765			case 'cstscii':
13766			case 'tscii':
13767				return 'TSCII';
13768
13769			case 'csunicode11':
13770			case 'unicode11':
13771				return 'UNICODE-1-1';
13772
13773			case 'csunicode11utf7':
13774			case 'unicode11utf7':
13775				return 'UNICODE-1-1-UTF-7';
13776
13777			case 'csunknown8bit':
13778			case 'unknown8bit':
13779				return 'UNKNOWN-8BIT';
13780
13781			case 'ansix341968':
13782			case 'ansix341986':
13783			case 'ascii':
13784			case 'cp367':
13785			case 'csascii':
13786			case 'ibm367':
13787			case 'iso646irv1991':
13788			case 'iso646us':
13789			case 'isoir6':
13790			case 'us':
13791			case 'usascii':
13792				return 'US-ASCII';
13793
13794			case 'csusdk':
13795			case 'usdk':
13796				return 'us-dk';
13797
13798			case 'utf7':
13799				return 'UTF-7';
13800
13801			case 'utf8':
13802				return 'UTF-8';
13803
13804			case 'utf16':
13805				return 'UTF-16';
13806
13807			case 'utf16be':
13808				return 'UTF-16BE';
13809
13810			case 'utf16le':
13811				return 'UTF-16LE';
13812
13813			case 'utf32':
13814				return 'UTF-32';
13815
13816			case 'utf32be':
13817				return 'UTF-32BE';
13818
13819			case 'utf32le':
13820				return 'UTF-32LE';
13821
13822			case 'csventurainternational':
13823			case 'venturainternational':
13824				return 'Ventura-International';
13825
13826			case 'csventuramath':
13827			case 'venturamath':
13828				return 'Ventura-Math';
13829
13830			case 'csventuraus':
13831			case 'venturaus':
13832				return 'Ventura-US';
13833
13834			case 'csiso70videotexsupp1':
13835			case 'isoir70':
13836			case 'videotexsuppl':
13837				return 'videotex-suppl';
13838
13839			case 'csviqr':
13840			case 'viqr':
13841				return 'VIQR';
13842
13843			case 'csviscii':
13844			case 'viscii':
13845				return 'VISCII';
13846
13847			case 'csshiftjis':
13848			case 'cswindows31j':
13849			case 'mskanji':
13850			case 'shiftjis':
13851			case 'windows31j':
13852				return 'Windows-31J';
13853
13854			case 'iso885911':
13855			case 'tis620':
13856				return 'windows-874';
13857
13858			case 'cseuckr':
13859			case 'csksc56011987':
13860			case 'euckr':
13861			case 'isoir149':
13862			case 'korean':
13863			case 'ksc5601':
13864			case 'ksc56011987':
13865			case 'ksc56011989':
13866			case 'windows949':
13867				return 'windows-949';
13868
13869			case 'windows1250':
13870				return 'windows-1250';
13871
13872			case 'windows1251':
13873				return 'windows-1251';
13874
13875			case 'cp819':
13876			case 'csisolatin1':
13877			case 'ibm819':
13878			case 'iso88591':
13879			case 'iso885911987':
13880			case 'isoir100':
13881			case 'l1':
13882			case 'latin1':
13883			case 'windows1252':
13884				return 'windows-1252';
13885
13886			case 'windows1253':
13887				return 'windows-1253';
13888
13889			case 'csisolatin5':
13890			case 'iso88599':
13891			case 'iso885991989':
13892			case 'isoir148':
13893			case 'l5':
13894			case 'latin5':
13895			case 'windows1254':
13896				return 'windows-1254';
13897
13898			case 'windows1255':
13899				return 'windows-1255';
13900
13901			case 'windows1256':
13902				return 'windows-1256';
13903
13904			case 'windows1257':
13905				return 'windows-1257';
13906
13907			case 'windows1258':
13908				return 'windows-1258';
13909
13910			default:
13911				return $charset;
13912		}
13913	}
13914
13915	public static function get_curl_version()
13916	{
13917		if (is_array($curl = curl_version()))
13918		{
13919			$curl = $curl['version'];
13920		}
13921		elseif (substr($curl, 0, 5) === 'curl/')
13922		{
13923			$curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
13924		}
13925		elseif (substr($curl, 0, 8) === 'libcurl/')
13926		{
13927			$curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
13928		}
13929		else
13930		{
13931			$curl = 0;
13932		}
13933		return $curl;
13934	}
13935
13936	/**
13937	 * Strip HTML comments
13938	 *
13939	 * @param string $data Data to strip comments from
13940	 * @return string Comment stripped string
13941	 */
13942	public static function strip_comments($data)
13943	{
13944		$output = '';
13945		while (($start = strpos($data, '<!--')) !== false)
13946		{
13947			$output .= substr($data, 0, $start);
13948			if (($end = strpos($data, '-->', $start)) !== false)
13949			{
13950				$data = substr_replace($data, '', 0, $end + 3);
13951			}
13952			else
13953			{
13954				$data = '';
13955			}
13956		}
13957		return $output . $data;
13958	}
13959
13960	public static function parse_date($dt)
13961	{
13962		$parser = SimplePie_Parse_Date::get();
13963		return $parser->parse($dt);
13964	}
13965
13966	/**
13967	 * Decode HTML entities
13968	 *
13969	 * @deprecated Use DOMDocument instead
13970	 * @param string $data Input data
13971	 * @return string Output data
13972	 */
13973	public static function entities_decode($data)
13974	{
13975		$decoder = new SimplePie_Decode_HTML_Entities($data);
13976		return $decoder->parse();
13977	}
13978
13979	/**
13980	 * Remove RFC822 comments
13981	 *
13982	 * @param string $data Data to strip comments from
13983	 * @return string Comment stripped string
13984	 */
13985	public static function uncomment_rfc822($string)
13986	{
13987		$string = (string) $string;
13988		$position = 0;
13989		$length = strlen($string);
13990		$depth = 0;
13991
13992		$output = '';
13993
13994		while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
13995		{
13996			$output .= substr($string, $position, $pos - $position);
13997			$position = $pos + 1;
13998			if ($string[$pos - 1] !== '\\')
13999			{
14000				$depth++;
14001				while ($depth && $position < $length)
14002				{
14003					$position += strcspn($string, '()', $position);
14004					if ($string[$position - 1] === '\\')
14005					{
14006						$position++;
14007						continue;
14008					}
14009					elseif (isset($string[$position]))
14010					{
14011						switch ($string[$position])
14012						{
14013							case '(':
14014								$depth++;
14015								break;
14016
14017							case ')':
14018								$depth--;
14019								break;
14020						}
14021						$position++;
14022					}
14023					else
14024					{
14025						break;
14026					}
14027				}
14028			}
14029			else
14030			{
14031				$output .= '(';
14032			}
14033		}
14034		$output .= substr($string, $position);
14035
14036		return $output;
14037	}
14038
14039	public static function parse_mime($mime)
14040	{
14041		if (($pos = strpos($mime, ';')) === false)
14042		{
14043			return trim($mime);
14044		}
14045		else
14046		{
14047			return trim(substr($mime, 0, $pos));
14048		}
14049	}
14050
14051	public static function atom_03_construct_type($attribs)
14052	{
14053		if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
14054		{
14055			$mode = SIMPLEPIE_CONSTRUCT_BASE64;
14056		}
14057		else
14058		{
14059			$mode = SIMPLEPIE_CONSTRUCT_NONE;
14060		}
14061		if (isset($attribs['']['type']))
14062		{
14063			switch (strtolower(trim($attribs['']['type'])))
14064			{
14065				case 'text':
14066				case 'text/plain':
14067					return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14068
14069				case 'html':
14070				case 'text/html':
14071					return SIMPLEPIE_CONSTRUCT_HTML | $mode;
14072
14073				case 'xhtml':
14074				case 'application/xhtml+xml':
14075					return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
14076
14077				default:
14078					return SIMPLEPIE_CONSTRUCT_NONE | $mode;
14079			}
14080		}
14081		else
14082		{
14083			return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14084		}
14085	}
14086
14087	public static function atom_10_construct_type($attribs)
14088	{
14089		if (isset($attribs['']['type']))
14090		{
14091			switch (strtolower(trim($attribs['']['type'])))
14092			{
14093				case 'text':
14094					return SIMPLEPIE_CONSTRUCT_TEXT;
14095
14096				case 'html':
14097					return SIMPLEPIE_CONSTRUCT_HTML;
14098
14099				case 'xhtml':
14100					return SIMPLEPIE_CONSTRUCT_XHTML;
14101
14102				default:
14103					return SIMPLEPIE_CONSTRUCT_NONE;
14104			}
14105		}
14106		return SIMPLEPIE_CONSTRUCT_TEXT;
14107	}
14108
14109	public static function atom_10_content_construct_type($attribs)
14110	{
14111		if (isset($attribs['']['type']))
14112		{
14113			$type = strtolower(trim($attribs['']['type']));
14114			switch ($type)
14115			{
14116				case 'text':
14117					return SIMPLEPIE_CONSTRUCT_TEXT;
14118
14119				case 'html':
14120					return SIMPLEPIE_CONSTRUCT_HTML;
14121
14122				case 'xhtml':
14123					return SIMPLEPIE_CONSTRUCT_XHTML;
14124			}
14125			if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
14126			{
14127				return SIMPLEPIE_CONSTRUCT_NONE;
14128			}
14129			else
14130			{
14131				return SIMPLEPIE_CONSTRUCT_BASE64;
14132			}
14133		}
14134		else
14135		{
14136			return SIMPLEPIE_CONSTRUCT_TEXT;
14137		}
14138	}
14139
14140	public static function is_isegment_nz_nc($string)
14141	{
14142		return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
14143	}
14144
14145	public static function space_seperated_tokens($string)
14146	{
14147		$space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
14148		$string_length = strlen($string);
14149
14150		$position = strspn($string, $space_characters);
14151		$tokens = array();
14152
14153		while ($position < $string_length)
14154		{
14155			$len = strcspn($string, $space_characters, $position);
14156			$tokens[] = substr($string, $position, $len);
14157			$position += $len;
14158			$position += strspn($string, $space_characters, $position);
14159		}
14160
14161		return $tokens;
14162	}
14163
14164	/**
14165	 * Converts a unicode codepoint to a UTF-8 character
14166	 *
14167	 * @static
14168	 * @param int $codepoint Unicode codepoint
14169	 * @return string UTF-8 character
14170	 */
14171	public static function codepoint_to_utf8($codepoint)
14172	{
14173		$codepoint = (int) $codepoint;
14174		if ($codepoint < 0)
14175		{
14176			return false;
14177		}
14178		else if ($codepoint <= 0x7f)
14179		{
14180			return chr($codepoint);
14181		}
14182		else if ($codepoint <= 0x7ff)
14183		{
14184			return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
14185		}
14186		else if ($codepoint <= 0xffff)
14187		{
14188			return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14189		}
14190		else if ($codepoint <= 0x10ffff)
14191		{
14192			return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14193		}
14194		else
14195		{
14196			// U+FFFD REPLACEMENT CHARACTER
14197			return "\xEF\xBF\xBD";
14198		}
14199	}
14200
14201	/**
14202	 * Similar to parse_str()
14203	 *
14204	 * Returns an associative array of name/value pairs, where the value is an
14205	 * array of values that have used the same name
14206	 *
14207	 * @static
14208	 * @param string $str The input string.
14209	 * @return array
14210	 */
14211	public static function parse_str($str)
14212	{
14213		$return = array();
14214		$str = explode('&', $str);
14215
14216		foreach ($str as $section)
14217		{
14218			if (strpos($section, '=') !== false)
14219			{
14220				list($name, $value) = explode('=', $section, 2);
14221				$return[urldecode($name)][] = urldecode($value);
14222			}
14223			else
14224			{
14225				$return[urldecode($section)][] = null;
14226			}
14227		}
14228
14229		return $return;
14230	}
14231
14232	/**
14233	 * Detect XML encoding, as per XML 1.0 Appendix F.1
14234	 *
14235	 * @todo Add support for EBCDIC
14236	 * @param string $data XML data
14237	 * @param SimplePie_Registry $registry Class registry
14238	 * @return array Possible encodings
14239	 */
14240	public static function xml_encoding($data, $registry)
14241	{
14242		// UTF-32 Big Endian BOM
14243		if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
14244		{
14245			$encoding[] = 'UTF-32BE';
14246		}
14247		// UTF-32 Little Endian BOM
14248		elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
14249		{
14250			$encoding[] = 'UTF-32LE';
14251		}
14252		// UTF-16 Big Endian BOM
14253		elseif (substr($data, 0, 2) === "\xFE\xFF")
14254		{
14255			$encoding[] = 'UTF-16BE';
14256		}
14257		// UTF-16 Little Endian BOM
14258		elseif (substr($data, 0, 2) === "\xFF\xFE")
14259		{
14260			$encoding[] = 'UTF-16LE';
14261		}
14262		// UTF-8 BOM
14263		elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
14264		{
14265			$encoding[] = 'UTF-8';
14266		}
14267		// UTF-32 Big Endian Without BOM
14268		elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C")
14269		{
14270			if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
14271			{
14272				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')));
14273				if ($parser->parse())
14274				{
14275					$encoding[] = $parser->encoding;
14276				}
14277			}
14278			$encoding[] = 'UTF-32BE';
14279		}
14280		// UTF-32 Little Endian Without BOM
14281		elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00")
14282		{
14283			if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
14284			{
14285				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')));
14286				if ($parser->parse())
14287				{
14288					$encoding[] = $parser->encoding;
14289				}
14290			}
14291			$encoding[] = 'UTF-32LE';
14292		}
14293		// UTF-16 Big Endian Without BOM
14294		elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
14295		{
14296			if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
14297			{
14298				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')));
14299				if ($parser->parse())
14300				{
14301					$encoding[] = $parser->encoding;
14302				}
14303			}
14304			$encoding[] = 'UTF-16BE';
14305		}
14306		// UTF-16 Little Endian Without BOM
14307		elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
14308		{
14309			if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
14310			{
14311				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')));
14312				if ($parser->parse())
14313				{
14314					$encoding[] = $parser->encoding;
14315				}
14316			}
14317			$encoding[] = 'UTF-16LE';
14318		}
14319		// US-ASCII (or superset)
14320		elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
14321		{
14322			if ($pos = strpos($data, "\x3F\x3E"))
14323			{
14324				$parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
14325				if ($parser->parse())
14326				{
14327					$encoding[] = $parser->encoding;
14328				}
14329			}
14330			$encoding[] = 'UTF-8';
14331		}
14332		// Fallback to UTF-8
14333		else
14334		{
14335			$encoding[] = 'UTF-8';
14336		}
14337		return $encoding;
14338	}
14339
14340	public static function output_javascript()
14341	{
14342		if (function_exists('ob_gzhandler'))
14343		{
14344			ob_start('ob_gzhandler');
14345		}
14346		header('Content-type: text/javascript; charset: UTF-8');
14347		header('Cache-Control: must-revalidate');
14348		header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
14349		?>
14350function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
14351	if (placeholder != '') {
14352		document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
14353	}
14354	else {
14355		document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
14356	}
14357}
14358
14359function embed_flash(bgcolor, width, height, link, loop, type) {
14360	document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>');
14361}
14362
14363function embed_flv(width, height, link, placeholder, loop, player) {
14364	document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>');
14365}
14366
14367function embed_wmedia(width, height, link) {
14368	document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
14369}
14370		<?php
14371	}
14372
14373	/**
14374	 * Get the SimplePie build timestamp
14375	 *
14376	 * Uses the git index if it exists, otherwise uses the modification time
14377	 * of the newest file.
14378	 */
14379	public static function get_build()
14380	{
14381		$root = dirname(dirname(__FILE__));
14382		if (file_exists($root . '/.git/index'))
14383		{
14384			return filemtime($root . '/.git/index');
14385		}
14386		elseif (file_exists($root . '/SimplePie'))
14387		{
14388			$time = 0;
14389			foreach (glob($root . '/SimplePie/*.php') as $file)
14390			{
14391				if (($mtime = filemtime($file)) > $time)
14392				{
14393					$time = $mtime;
14394				}
14395			}
14396			return $time;
14397		}
14398		elseif (file_exists(dirname(__FILE__) . '/Core.php'))
14399		{
14400			return filemtime(dirname(__FILE__) . '/Core.php');
14401		}
14402		else
14403		{
14404			return filemtime(__FILE__);
14405		}
14406	}
14407
14408	/**
14409	 * Format debugging information
14410	 */
14411	public static function debug(&$sp)
14412	{
14413		$info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n";
14414		$info .= 'PHP ' . PHP_VERSION . "\n";
14415		if ($sp->error() !== null)
14416		{
14417			$info .= 'Error occurred: ' . $sp->error() . "\n";
14418		}
14419		else
14420		{
14421			$info .= "No error found.\n";
14422		}
14423		$info .= "Extensions:\n";
14424		$extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml');
14425		foreach ($extensions as $ext)
14426		{
14427			if (extension_loaded($ext))
14428			{
14429				$info .= "    $ext loaded\n";
14430				switch ($ext)
14431				{
14432					case 'pcre':
14433						$info .= '      Version ' . PCRE_VERSION . "\n";
14434						break;
14435					case 'curl':
14436						$version = curl_version();
14437						$info .= '      Version ' . $version['version'] . "\n";
14438						break;
14439					case 'mbstring':
14440						$info .= '      Overloading: ' . mb_get_info('func_overload') . "\n";
14441						break;
14442					case 'iconv':
14443						$info .= '      Version ' . ICONV_VERSION . "\n";
14444						break;
14445					case 'xml':
14446						$info .= '      Version ' . LIBXML_DOTTED_VERSION . "\n";
14447						break;
14448				}
14449			}
14450			else
14451			{
14452				$info .= "    $ext not loaded\n";
14453			}
14454		}
14455		return $info;
14456	}
14457
14458	public static function silence_errors($num, $str)
14459	{
14460		// No-op
14461	}
14462}
14463
14464/**
14465 * Class to validate and to work with IPv6 addresses.
14466 *
14467 * @package SimplePie
14468 * @subpackage HTTP
14469 * @copyright 2003-2005 The PHP Group
14470 * @license http://www.opensource.org/licenses/bsd-license.php
14471 * @link http://pear.php.net/package/Net_IPv6
14472 * @author Alexander Merz <alexander.merz@web.de>
14473 * @author elfrink at introweb dot nl
14474 * @author Josh Peck <jmp at joshpeck dot org>
14475 * @author Geoffrey Sneddon <geoffers@gmail.com>
14476 */
14477class SimplePie_Net_IPv6
14478{
14479	/**
14480	 * Uncompresses an IPv6 address
14481	 *
14482	 * RFC 4291 allows you to compress concecutive zero pieces in an address to
14483	 * '::'. This method expects a valid IPv6 address and expands the '::' to
14484	 * the required number of zero pieces.
14485	 *
14486	 * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
14487	 *           ::1         ->  0:0:0:0:0:0:0:1
14488	 *
14489	 * @author Alexander Merz <alexander.merz@web.de>
14490	 * @author elfrink at introweb dot nl
14491	 * @author Josh Peck <jmp at joshpeck dot org>
14492	 * @copyright 2003-2005 The PHP Group
14493	 * @license http://www.opensource.org/licenses/bsd-license.php
14494	 * @param string $ip An IPv6 address
14495	 * @return string The uncompressed IPv6 address
14496	 */
14497	public static function uncompress($ip)
14498	{
14499		$c1 = -1;
14500		$c2 = -1;
14501		if (substr_count($ip, '::') === 1)
14502		{
14503			list($ip1, $ip2) = explode('::', $ip);
14504			if ($ip1 === '')
14505			{
14506				$c1 = -1;
14507			}
14508			else
14509			{
14510				$c1 = substr_count($ip1, ':');
14511			}
14512			if ($ip2 === '')
14513			{
14514				$c2 = -1;
14515			}
14516			else
14517			{
14518				$c2 = substr_count($ip2, ':');
14519			}
14520			if (strpos($ip2, '.') !== false)
14521			{
14522				$c2++;
14523			}
14524			// ::
14525			if ($c1 === -1 && $c2 === -1)
14526			{
14527				$ip = '0:0:0:0:0:0:0:0';
14528			}
14529			// ::xxx
14530			else if ($c1 === -1)
14531			{
14532				$fill = str_repeat('0:', 7 - $c2);
14533				$ip = str_replace('::', $fill, $ip);
14534			}
14535			// xxx::
14536			else if ($c2 === -1)
14537			{
14538				$fill = str_repeat(':0', 7 - $c1);
14539				$ip = str_replace('::', $fill, $ip);
14540			}
14541			// xxx::xxx
14542			else
14543			{
14544				$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
14545				$ip = str_replace('::', $fill, $ip);
14546			}
14547		}
14548		return $ip;
14549	}
14550
14551	/**
14552	 * Compresses an IPv6 address
14553	 *
14554	 * RFC 4291 allows you to compress concecutive zero pieces in an address to
14555	 * '::'. This method expects a valid IPv6 address and compresses consecutive
14556	 * zero pieces to '::'.
14557	 *
14558	 * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
14559	 *           0:0:0:0:0:0:0:1        ->  ::1
14560	 *
14561	 * @see uncompress()
14562	 * @param string $ip An IPv6 address
14563	 * @return string The compressed IPv6 address
14564	 */
14565	public static function compress($ip)
14566	{
14567		// Prepare the IP to be compressed
14568		$ip = self::uncompress($ip);
14569		$ip_parts = self::split_v6_v4($ip);
14570
14571		// Replace all leading zeros
14572		$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
14573
14574		// Find bunches of zeros
14575		if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE))
14576		{
14577			$max = 0;
14578			$pos = null;
14579			foreach ($matches[0] as $match)
14580			{
14581				if (strlen($match[0]) > $max)
14582				{
14583					$max = strlen($match[0]);
14584					$pos = $match[1];
14585				}
14586			}
14587
14588			$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
14589		}
14590
14591		if ($ip_parts[1] !== '')
14592		{
14593			return implode(':', $ip_parts);
14594		}
14595		else
14596		{
14597			return $ip_parts[0];
14598		}
14599	}
14600
14601	/**
14602	 * Splits an IPv6 address into the IPv6 and IPv4 representation parts
14603	 *
14604	 * RFC 4291 allows you to represent the last two parts of an IPv6 address
14605	 * using the standard IPv4 representation
14606	 *
14607	 * Example:  0:0:0:0:0:0:13.1.68.3
14608	 *           0:0:0:0:0:FFFF:129.144.52.38
14609	 *
14610	 * @param string $ip An IPv6 address
14611	 * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
14612	 */
14613	private static function split_v6_v4($ip)
14614	{
14615		if (strpos($ip, '.') !== false)
14616		{
14617			$pos = strrpos($ip, ':');
14618			$ipv6_part = substr($ip, 0, $pos);
14619			$ipv4_part = substr($ip, $pos + 1);
14620			return array($ipv6_part, $ipv4_part);
14621		}
14622		else
14623		{
14624			return array($ip, '');
14625		}
14626	}
14627
14628	/**
14629	 * Checks an IPv6 address
14630	 *
14631	 * Checks if the given IP is a valid IPv6 address
14632	 *
14633	 * @param string $ip An IPv6 address
14634	 * @return bool true if $ip is a valid IPv6 address
14635	 */
14636	public static function check_ipv6($ip)
14637	{
14638		$ip = self::uncompress($ip);
14639		list($ipv6, $ipv4) = self::split_v6_v4($ip);
14640		$ipv6 = explode(':', $ipv6);
14641		$ipv4 = explode('.', $ipv4);
14642		if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4)
14643		{
14644			foreach ($ipv6 as $ipv6_part)
14645			{
14646				// The section can't be empty
14647				if ($ipv6_part === '')
14648					return false;
14649
14650				// Nor can it be over four characters
14651				if (strlen($ipv6_part) > 4)
14652					return false;
14653
14654				// Remove leading zeros (this is safe because of the above)
14655				$ipv6_part = ltrim($ipv6_part, '0');
14656				if ($ipv6_part === '')
14657					$ipv6_part = '0';
14658
14659				// Check the value is valid
14660				$value = hexdec($ipv6_part);
14661				if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF)
14662					return false;
14663			}
14664			if (count($ipv4) === 4)
14665			{
14666				foreach ($ipv4 as $ipv4_part)
14667				{
14668					$value = (int) $ipv4_part;
14669					if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF)
14670						return false;
14671				}
14672			}
14673			return true;
14674		}
14675		else
14676		{
14677			return false;
14678		}
14679	}
14680
14681	/**
14682	 * Checks if the given IP is a valid IPv6 address
14683	 *
14684	 * @codeCoverageIgnore
14685	 * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead
14686	 * @see check_ipv6
14687	 * @param string $ip An IPv6 address
14688	 * @return bool true if $ip is a valid IPv6 address
14689	 */
14690	public static function checkIPv6($ip)
14691	{
14692		return self::check_ipv6($ip);
14693	}
14694}
14695
14696/**
14697 * Date Parser
14698 *
14699 * @package SimplePie
14700 * @subpackage Parsing
14701 */
14702class SimplePie_Parse_Date
14703{
14704	/**
14705	 * Input data
14706	 *
14707	 * @access protected
14708	 * @var string
14709	 */
14710	var $date;
14711
14712	/**
14713	 * List of days, calendar day name => ordinal day number in the week
14714	 *
14715	 * @access protected
14716	 * @var array
14717	 */
14718	var $day = array(
14719		// English
14720		'mon' => 1,
14721		'monday' => 1,
14722		'tue' => 2,
14723		'tuesday' => 2,
14724		'wed' => 3,
14725		'wednesday' => 3,
14726		'thu' => 4,
14727		'thursday' => 4,
14728		'fri' => 5,
14729		'friday' => 5,
14730		'sat' => 6,
14731		'saturday' => 6,
14732		'sun' => 7,
14733		'sunday' => 7,
14734		// Dutch
14735		'maandag' => 1,
14736		'dinsdag' => 2,
14737		'woensdag' => 3,
14738		'donderdag' => 4,
14739		'vrijdag' => 5,
14740		'zaterdag' => 6,
14741		'zondag' => 7,
14742		// French
14743		'lundi' => 1,
14744		'mardi' => 2,
14745		'mercredi' => 3,
14746		'jeudi' => 4,
14747		'vendredi' => 5,
14748		'samedi' => 6,
14749		'dimanche' => 7,
14750		// German
14751		'montag' => 1,
14752		'dienstag' => 2,
14753		'mittwoch' => 3,
14754		'donnerstag' => 4,
14755		'freitag' => 5,
14756		'samstag' => 6,
14757		'sonnabend' => 6,
14758		'sonntag' => 7,
14759		// Italian
14760		'lunedì' => 1,
14761		'martedì' => 2,
14762		'mercoledì' => 3,
14763		'giovedì' => 4,
14764		'venerdì' => 5,
14765		'sabato' => 6,
14766		'domenica' => 7,
14767		// Spanish
14768		'lunes' => 1,
14769		'martes' => 2,
14770		'miércoles' => 3,
14771		'jueves' => 4,
14772		'viernes' => 5,
14773		'sábado' => 6,
14774		'domingo' => 7,
14775		// Finnish
14776		'maanantai' => 1,
14777		'tiistai' => 2,
14778		'keskiviikko' => 3,
14779		'torstai' => 4,
14780		'perjantai' => 5,
14781		'lauantai' => 6,
14782		'sunnuntai' => 7,
14783		// Hungarian
14784		'hétfő' => 1,
14785		'kedd' => 2,
14786		'szerda' => 3,
14787		'csütörtok' => 4,
14788		'péntek' => 5,
14789		'szombat' => 6,
14790		'vasárnap' => 7,
14791		// Greek
14792		'Δευ' => 1,
14793		'Τρι' => 2,
14794		'Τετ' => 3,
14795		'Πεμ' => 4,
14796		'Παρ' => 5,
14797		'Σαβ' => 6,
14798		'Κυρ' => 7,
14799	);
14800
14801	/**
14802	 * List of months, calendar month name => calendar month number
14803	 *
14804	 * @access protected
14805	 * @var array
14806	 */
14807	var $month = array(
14808		// English
14809		'jan' => 1,
14810		'january' => 1,
14811		'feb' => 2,
14812		'february' => 2,
14813		'mar' => 3,
14814		'march' => 3,
14815		'apr' => 4,
14816		'april' => 4,
14817		'may' => 5,
14818		// No long form of May
14819		'jun' => 6,
14820		'june' => 6,
14821		'jul' => 7,
14822		'july' => 7,
14823		'aug' => 8,
14824		'august' => 8,
14825		'sep' => 9,
14826		'september' => 8,
14827		'oct' => 10,
14828		'october' => 10,
14829		'nov' => 11,
14830		'november' => 11,
14831		'dec' => 12,
14832		'december' => 12,
14833		// Dutch
14834		'januari' => 1,
14835		'februari' => 2,
14836		'maart' => 3,
14837		'april' => 4,
14838		'mei' => 5,
14839		'juni' => 6,
14840		'juli' => 7,
14841		'augustus' => 8,
14842		'september' => 9,
14843		'oktober' => 10,
14844		'november' => 11,
14845		'december' => 12,
14846		// French
14847		'janvier' => 1,
14848		'février' => 2,
14849		'mars' => 3,
14850		'avril' => 4,
14851		'mai' => 5,
14852		'juin' => 6,
14853		'juillet' => 7,
14854		'août' => 8,
14855		'septembre' => 9,
14856		'octobre' => 10,
14857		'novembre' => 11,
14858		'décembre' => 12,
14859		// German
14860		'januar' => 1,
14861		'februar' => 2,
14862		'märz' => 3,
14863		'april' => 4,
14864		'mai' => 5,
14865		'juni' => 6,
14866		'juli' => 7,
14867		'august' => 8,
14868		'september' => 9,
14869		'oktober' => 10,
14870		'november' => 11,
14871		'dezember' => 12,
14872		// Italian
14873		'gennaio' => 1,
14874		'febbraio' => 2,
14875		'marzo' => 3,
14876		'aprile' => 4,
14877		'maggio' => 5,
14878		'giugno' => 6,
14879		'luglio' => 7,
14880		'agosto' => 8,
14881		'settembre' => 9,
14882		'ottobre' => 10,
14883		'novembre' => 11,
14884		'dicembre' => 12,
14885		// Spanish
14886		'enero' => 1,
14887		'febrero' => 2,
14888		'marzo' => 3,
14889		'abril' => 4,
14890		'mayo' => 5,
14891		'junio' => 6,
14892		'julio' => 7,
14893		'agosto' => 8,
14894		'septiembre' => 9,
14895		'setiembre' => 9,
14896		'octubre' => 10,
14897		'noviembre' => 11,
14898		'diciembre' => 12,
14899		// Finnish
14900		'tammikuu' => 1,
14901		'helmikuu' => 2,
14902		'maaliskuu' => 3,
14903		'huhtikuu' => 4,
14904		'toukokuu' => 5,
14905		'kesäkuu' => 6,
14906		'heinäkuu' => 7,
14907		'elokuu' => 8,
14908		'suuskuu' => 9,
14909		'lokakuu' => 10,
14910		'marras' => 11,
14911		'joulukuu' => 12,
14912		// Hungarian
14913		'január' => 1,
14914		'február' => 2,
14915		'március' => 3,
14916		'április' => 4,
14917		'május' => 5,
14918		'június' => 6,
14919		'július' => 7,
14920		'augusztus' => 8,
14921		'szeptember' => 9,
14922		'október' => 10,
14923		'november' => 11,
14924		'december' => 12,
14925		// Greek
14926		'Ιαν' => 1,
14927		'Φεβ' => 2,
14928		'Μάώ' => 3,
14929		'Μαώ' => 3,
14930		'Απρ' => 4,
14931		'Μάι' => 5,
14932		'Μαϊ' => 5,
14933		'Μαι' => 5,
14934		'Ιούν' => 6,
14935		'Ιον' => 6,
14936		'Ιούλ' => 7,
14937		'Ιολ' => 7,
14938		'Αύγ' => 8,
14939		'Αυγ' => 8,
14940		'Σεπ' => 9,
14941		'Οκτ' => 10,
14942		'Νοέ' => 11,
14943		'Δεκ' => 12,
14944	);
14945
14946	/**
14947	 * List of timezones, abbreviation => offset from UTC
14948	 *
14949	 * @access protected
14950	 * @var array
14951	 */
14952	var $timezone = array(
14953		'ACDT' => 37800,
14954		'ACIT' => 28800,
14955		'ACST' => 34200,
14956		'ACT' => -18000,
14957		'ACWDT' => 35100,
14958		'ACWST' => 31500,
14959		'AEDT' => 39600,
14960		'AEST' => 36000,
14961		'AFT' => 16200,
14962		'AKDT' => -28800,
14963		'AKST' => -32400,
14964		'AMDT' => 18000,
14965		'AMT' => -14400,
14966		'ANAST' => 46800,
14967		'ANAT' => 43200,
14968		'ART' => -10800,
14969		'AZOST' => -3600,
14970		'AZST' => 18000,
14971		'AZT' => 14400,
14972		'BIOT' => 21600,
14973		'BIT' => -43200,
14974		'BOT' => -14400,
14975		'BRST' => -7200,
14976		'BRT' => -10800,
14977		'BST' => 3600,
14978		'BTT' => 21600,
14979		'CAST' => 18000,
14980		'CAT' => 7200,
14981		'CCT' => 23400,
14982		'CDT' => -18000,
14983		'CEDT' => 7200,
14984		'CET' => 3600,
14985		'CGST' => -7200,
14986		'CGT' => -10800,
14987		'CHADT' => 49500,
14988		'CHAST' => 45900,
14989		'CIST' => -28800,
14990		'CKT' => -36000,
14991		'CLDT' => -10800,
14992		'CLST' => -14400,
14993		'COT' => -18000,
14994		'CST' => -21600,
14995		'CVT' => -3600,
14996		'CXT' => 25200,
14997		'DAVT' => 25200,
14998		'DTAT' => 36000,
14999		'EADT' => -18000,
15000		'EAST' => -21600,
15001		'EAT' => 10800,
15002		'ECT' => -18000,
15003		'EDT' => -14400,
15004		'EEST' => 10800,
15005		'EET' => 7200,
15006		'EGT' => -3600,
15007		'EKST' => 21600,
15008		'EST' => -18000,
15009		'FJT' => 43200,
15010		'FKDT' => -10800,
15011		'FKST' => -14400,
15012		'FNT' => -7200,
15013		'GALT' => -21600,
15014		'GEDT' => 14400,
15015		'GEST' => 10800,
15016		'GFT' => -10800,
15017		'GILT' => 43200,
15018		'GIT' => -32400,
15019		'GST' => 14400,
15020		'GST' => -7200,
15021		'GYT' => -14400,
15022		'HAA' => -10800,
15023		'HAC' => -18000,
15024		'HADT' => -32400,
15025		'HAE' => -14400,
15026		'HAP' => -25200,
15027		'HAR' => -21600,
15028		'HAST' => -36000,
15029		'HAT' => -9000,
15030		'HAY' => -28800,
15031		'HKST' => 28800,
15032		'HMT' => 18000,
15033		'HNA' => -14400,
15034		'HNC' => -21600,
15035		'HNE' => -18000,
15036		'HNP' => -28800,
15037		'HNR' => -25200,
15038		'HNT' => -12600,
15039		'HNY' => -32400,
15040		'IRDT' => 16200,
15041		'IRKST' => 32400,
15042		'IRKT' => 28800,
15043		'IRST' => 12600,
15044		'JFDT' => -10800,
15045		'JFST' => -14400,
15046		'JST' => 32400,
15047		'KGST' => 21600,
15048		'KGT' => 18000,
15049		'KOST' => 39600,
15050		'KOVST' => 28800,
15051		'KOVT' => 25200,
15052		'KRAST' => 28800,
15053		'KRAT' => 25200,
15054		'KST' => 32400,
15055		'LHDT' => 39600,
15056		'LHST' => 37800,
15057		'LINT' => 50400,
15058		'LKT' => 21600,
15059		'MAGST' => 43200,
15060		'MAGT' => 39600,
15061		'MAWT' => 21600,
15062		'MDT' => -21600,
15063		'MESZ' => 7200,
15064		'MEZ' => 3600,
15065		'MHT' => 43200,
15066		'MIT' => -34200,
15067		'MNST' => 32400,
15068		'MSDT' => 14400,
15069		'MSST' => 10800,
15070		'MST' => -25200,
15071		'MUT' => 14400,
15072		'MVT' => 18000,
15073		'MYT' => 28800,
15074		'NCT' => 39600,
15075		'NDT' => -9000,
15076		'NFT' => 41400,
15077		'NMIT' => 36000,
15078		'NOVST' => 25200,
15079		'NOVT' => 21600,
15080		'NPT' => 20700,
15081		'NRT' => 43200,
15082		'NST' => -12600,
15083		'NUT' => -39600,
15084		'NZDT' => 46800,
15085		'NZST' => 43200,
15086		'OMSST' => 25200,
15087		'OMST' => 21600,
15088		'PDT' => -25200,
15089		'PET' => -18000,
15090		'PETST' => 46800,
15091		'PETT' => 43200,
15092		'PGT' => 36000,
15093		'PHOT' => 46800,
15094		'PHT' => 28800,
15095		'PKT' => 18000,
15096		'PMDT' => -7200,
15097		'PMST' => -10800,
15098		'PONT' => 39600,
15099		'PST' => -28800,
15100		'PWT' => 32400,
15101		'PYST' => -10800,
15102		'PYT' => -14400,
15103		'RET' => 14400,
15104		'ROTT' => -10800,
15105		'SAMST' => 18000,
15106		'SAMT' => 14400,
15107		'SAST' => 7200,
15108		'SBT' => 39600,
15109		'SCDT' => 46800,
15110		'SCST' => 43200,
15111		'SCT' => 14400,
15112		'SEST' => 3600,
15113		'SGT' => 28800,
15114		'SIT' => 28800,
15115		'SRT' => -10800,
15116		'SST' => -39600,
15117		'SYST' => 10800,
15118		'SYT' => 7200,
15119		'TFT' => 18000,
15120		'THAT' => -36000,
15121		'TJT' => 18000,
15122		'TKT' => -36000,
15123		'TMT' => 18000,
15124		'TOT' => 46800,
15125		'TPT' => 32400,
15126		'TRUT' => 36000,
15127		'TVT' => 43200,
15128		'TWT' => 28800,
15129		'UYST' => -7200,
15130		'UYT' => -10800,
15131		'UZT' => 18000,
15132		'VET' => -14400,
15133		'VLAST' => 39600,
15134		'VLAT' => 36000,
15135		'VOST' => 21600,
15136		'VUT' => 39600,
15137		'WAST' => 7200,
15138		'WAT' => 3600,
15139		'WDT' => 32400,
15140		'WEST' => 3600,
15141		'WFT' => 43200,
15142		'WIB' => 25200,
15143		'WIT' => 32400,
15144		'WITA' => 28800,
15145		'WKST' => 18000,
15146		'WST' => 28800,
15147		'YAKST' => 36000,
15148		'YAKT' => 32400,
15149		'YAPT' => 36000,
15150		'YEKST' => 21600,
15151		'YEKT' => 18000,
15152	);
15153
15154	/**
15155	 * Cached PCRE for SimplePie_Parse_Date::$day
15156	 *
15157	 * @access protected
15158	 * @var string
15159	 */
15160	var $day_pcre;
15161
15162	/**
15163	 * Cached PCRE for SimplePie_Parse_Date::$month
15164	 *
15165	 * @access protected
15166	 * @var string
15167	 */
15168	var $month_pcre;
15169
15170	/**
15171	 * Array of user-added callback methods
15172	 *
15173	 * @access private
15174	 * @var array
15175	 */
15176	var $built_in = array();
15177
15178	/**
15179	 * Array of user-added callback methods
15180	 *
15181	 * @access private
15182	 * @var array
15183	 */
15184	var $user = array();
15185
15186	/**
15187	 * Create new SimplePie_Parse_Date object, and set self::day_pcre,
15188	 * self::month_pcre, and self::built_in
15189	 *
15190	 * @access private
15191	 */
15192	public function __construct()
15193	{
15194		$this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
15195		$this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
15196
15197		static $cache;
15198		if (!isset($cache[get_class($this)]))
15199		{
15200			$all_methods = get_class_methods($this);
15201
15202			foreach ($all_methods as $method)
15203			{
15204				if (strtolower(substr($method, 0, 5)) === 'date_')
15205				{
15206					$cache[get_class($this)][] = $method;
15207				}
15208			}
15209		}
15210
15211		foreach ($cache[get_class($this)] as $method)
15212		{
15213			$this->built_in[] = $method;
15214		}
15215	}
15216
15217	/**
15218	 * Get the object
15219	 *
15220	 * @access public
15221	 */
15222	public static function get()
15223	{
15224		static $object;
15225		if (!$object)
15226		{
15227			$object = new SimplePie_Parse_Date;
15228		}
15229		return $object;
15230	}
15231
15232	/**
15233	 * Parse a date
15234	 *
15235	 * @final
15236	 * @access public
15237	 * @param string $date Date to parse
15238	 * @return int Timestamp corresponding to date string, or false on failure
15239	 */
15240	public function parse($date)
15241	{
15242		foreach ($this->user as $method)
15243		{
15244			if (($returned = call_user_func($method, $date)) !== false)
15245			{
15246				return $returned;
15247			}
15248		}
15249
15250		foreach ($this->built_in as $method)
15251		{
15252			if (($returned = call_user_func(array($this, $method), $date)) !== false)
15253			{
15254				return $returned;
15255			}
15256		}
15257
15258		return false;
15259	}
15260
15261	/**
15262	 * Add a callback method to parse a date
15263	 *
15264	 * @final
15265	 * @access public
15266	 * @param callback $callback
15267	 */
15268	public function add_callback($callback)
15269	{
15270		if (is_callable($callback))
15271		{
15272			$this->user[] = $callback;
15273		}
15274		else
15275		{
15276			trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
15277		}
15278	}
15279
15280	/**
15281	 * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
15282	 * well as allowing any of upper or lower case "T", horizontal tabs, or
15283	 * spaces to be used as the time seperator (including more than one))
15284	 *
15285	 * @access protected
15286	 * @return int Timestamp
15287	 */
15288	public function date_w3cdtf($date)
15289	{
15290		static $pcre;
15291		if (!$pcre)
15292		{
15293			$year = '([0-9]{4})';
15294			$month = $day = $hour = $minute = $second = '([0-9]{2})';
15295			$decimal = '([0-9]*)';
15296			$zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
15297			$pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
15298		}
15299		if (preg_match($pcre, $date, $match))
15300		{
15301			/*
15302			Capturing subpatterns:
15303			1: Year
15304			2: Month
15305			3: Day
15306			4: Hour
15307			5: Minute
15308			6: Second
15309			7: Decimal fraction of a second
15310			8: Zulu
15311			9: Timezone ±
15312			10: Timezone hours
15313			11: Timezone minutes
15314			*/
15315
15316			// Fill in empty matches
15317			for ($i = count($match); $i <= 3; $i++)
15318			{
15319				$match[$i] = '1';
15320			}
15321
15322			for ($i = count($match); $i <= 7; $i++)
15323			{
15324				$match[$i] = '0';
15325			}
15326
15327			// Numeric timezone
15328			if (isset($match[9]) && $match[9] !== '')
15329			{
15330				$timezone = $match[10] * 3600;
15331				$timezone += $match[11] * 60;
15332				if ($match[9] === '-')
15333				{
15334					$timezone = 0 - $timezone;
15335				}
15336			}
15337			else
15338			{
15339				$timezone = 0;
15340			}
15341
15342			// Convert the number of seconds to an integer, taking decimals into account
15343			$second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
15344
15345			return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
15346		}
15347		else
15348		{
15349			return false;
15350		}
15351	}
15352
15353	/**
15354	 * Remove RFC822 comments
15355	 *
15356	 * @access protected
15357	 * @param string $data Data to strip comments from
15358	 * @return string Comment stripped string
15359	 */
15360	public function remove_rfc2822_comments($string)
15361	{
15362		$string = (string) $string;
15363		$position = 0;
15364		$length = strlen($string);
15365		$depth = 0;
15366
15367		$output = '';
15368
15369		while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
15370		{
15371			$output .= substr($string, $position, $pos - $position);
15372			$position = $pos + 1;
15373			if ($string[$pos - 1] !== '\\')
15374			{
15375				$depth++;
15376				while ($depth && $position < $length)
15377				{
15378					$position += strcspn($string, '()', $position);
15379					if ($string[$position - 1] === '\\')
15380					{
15381						$position++;
15382						continue;
15383					}
15384					elseif (isset($string[$position]))
15385					{
15386						switch ($string[$position])
15387						{
15388							case '(':
15389								$depth++;
15390								break;
15391
15392							case ')':
15393								$depth--;
15394								break;
15395						}
15396						$position++;
15397					}
15398					else
15399					{
15400						break;
15401					}
15402				}
15403			}
15404			else
15405			{
15406				$output .= '(';
15407			}
15408		}
15409		$output .= substr($string, $position);
15410
15411		return $output;
15412	}
15413
15414	/**
15415	 * Parse RFC2822's date format
15416	 *
15417	 * @access protected
15418	 * @return int Timestamp
15419	 */
15420	public function date_rfc2822($date)
15421	{
15422		static $pcre;
15423		if (!$pcre)
15424		{
15425			$wsp = '[\x09\x20]';
15426			$fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
15427			$optional_fws = $fws . '?';
15428			$day_name = $this->day_pcre;
15429			$month = $this->month_pcre;
15430			$day = '([0-9]{1,2})';
15431			$hour = $minute = $second = '([0-9]{2})';
15432			$year = '([0-9]{2,4})';
15433			$num_zone = '([+\-])([0-9]{2})([0-9]{2})';
15434			$character_zone = '([A-Z]{1,5})';
15435			$zone = '(?:' . $num_zone . '|' . $character_zone . ')';
15436			$pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
15437		}
15438		if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
15439		{
15440			/*
15441			Capturing subpatterns:
15442			1: Day name
15443			2: Day
15444			3: Month
15445			4: Year
15446			5: Hour
15447			6: Minute
15448			7: Second
15449			8: Timezone ±
15450			9: Timezone hours
15451			10: Timezone minutes
15452			11: Alphabetic timezone
15453			*/
15454
15455			// Find the month number
15456			$month = $this->month[strtolower($match[3])];
15457
15458			// Numeric timezone
15459			if ($match[8] !== '')
15460			{
15461				$timezone = $match[9] * 3600;
15462				$timezone += $match[10] * 60;
15463				if ($match[8] === '-')
15464				{
15465					$timezone = 0 - $timezone;
15466				}
15467			}
15468			// Character timezone
15469			elseif (isset($this->timezone[strtoupper($match[11])]))
15470			{
15471				$timezone = $this->timezone[strtoupper($match[11])];
15472			}
15473			// Assume everything else to be -0000
15474			else
15475			{
15476				$timezone = 0;
15477			}
15478
15479			// Deal with 2/3 digit years
15480			if ($match[4] < 50)
15481			{
15482				$match[4] += 2000;
15483			}
15484			elseif ($match[4] < 1000)
15485			{
15486				$match[4] += 1900;
15487			}
15488
15489			// Second is optional, if it is empty set it to zero
15490			if ($match[7] !== '')
15491			{
15492				$second = $match[7];
15493			}
15494			else
15495			{
15496				$second = 0;
15497			}
15498
15499			return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
15500		}
15501		else
15502		{
15503			return false;
15504		}
15505	}
15506
15507	/**
15508	 * Parse RFC850's date format
15509	 *
15510	 * @access protected
15511	 * @return int Timestamp
15512	 */
15513	public function date_rfc850($date)
15514	{
15515		static $pcre;
15516		if (!$pcre)
15517		{
15518			$space = '[\x09\x20]+';
15519			$day_name = $this->day_pcre;
15520			$month = $this->month_pcre;
15521			$day = '([0-9]{1,2})';
15522			$year = $hour = $minute = $second = '([0-9]{2})';
15523			$zone = '([A-Z]{1,5})';
15524			$pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
15525		}
15526		if (preg_match($pcre, $date, $match))
15527		{
15528			/*
15529			Capturing subpatterns:
15530			1: Day name
15531			2: Day
15532			3: Month
15533			4: Year
15534			5: Hour
15535			6: Minute
15536			7: Second
15537			8: Timezone
15538			*/
15539
15540			// Month
15541			$month = $this->month[strtolower($match[3])];
15542
15543			// Character timezone
15544			if (isset($this->timezone[strtoupper($match[8])]))
15545			{
15546				$timezone = $this->timezone[strtoupper($match[8])];
15547			}
15548			// Assume everything else to be -0000
15549			else
15550			{
15551				$timezone = 0;
15552			}
15553
15554			// Deal with 2 digit year
15555			if ($match[4] < 50)
15556			{
15557				$match[4] += 2000;
15558			}
15559			else
15560			{
15561				$match[4] += 1900;
15562			}
15563
15564			return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
15565		}
15566		else
15567		{
15568			return false;
15569		}
15570	}
15571
15572	/**
15573	 * Parse C99's asctime()'s date format
15574	 *
15575	 * @access protected
15576	 * @return int Timestamp
15577	 */
15578	public function date_asctime($date)
15579	{
15580		static $pcre;
15581		if (!$pcre)
15582		{
15583			$space = '[\x09\x20]+';
15584			$wday_name = $this->day_pcre;
15585			$mon_name = $this->month_pcre;
15586			$day = '([0-9]{1,2})';
15587			$hour = $sec = $min = '([0-9]{2})';
15588			$year = '([0-9]{4})';
15589			$terminator = '\x0A?\x00?';
15590			$pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
15591		}
15592		if (preg_match($pcre, $date, $match))
15593		{
15594			/*
15595			Capturing subpatterns:
15596			1: Day name
15597			2: Month
15598			3: Day
15599			4: Hour
15600			5: Minute
15601			6: Second
15602			7: Year
15603			*/
15604
15605			$month = $this->month[strtolower($match[2])];
15606			return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
15607		}
15608		else
15609		{
15610			return false;
15611		}
15612	}
15613
15614	/**
15615	 * Parse dates using strtotime()
15616	 *
15617	 * @access protected
15618	 * @return int Timestamp
15619	 */
15620	public function date_strtotime($date)
15621	{
15622		$strtotime = strtotime($date);
15623		if ($strtotime === -1 || $strtotime === false)
15624		{
15625			return false;
15626		}
15627		else
15628		{
15629			return $strtotime;
15630		}
15631	}
15632}
15633
15634/**
15635 * Parses XML into something sane
15636 *
15637 *
15638 * This class can be overloaded with {@see SimplePie::set_parser_class()}
15639 *
15640 * @package SimplePie
15641 * @subpackage Parsing
15642 */
15643class SimplePie_Parser
15644{
15645	var $error_code;
15646	var $error_string;
15647	var $current_line;
15648	var $current_column;
15649	var $current_byte;
15650	var $separator = ' ';
15651	var $namespace = array('');
15652	var $element = array('');
15653	var $xml_base = array('');
15654	var $xml_base_explicit = array(false);
15655	var $xml_lang = array('');
15656	var $data = array();
15657	var $datas = array(array());
15658	var $current_xhtml_construct = -1;
15659	var $encoding;
15660	protected $registry;
15661
15662	public function set_registry(SimplePie_Registry $registry)
15663	{
15664		$this->registry = $registry;
15665	}
15666
15667	public function parse(&$data, $encoding)
15668	{
15669		// Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
15670		if (strtoupper($encoding) === 'US-ASCII')
15671		{
15672			$this->encoding = 'UTF-8';
15673		}
15674		else
15675		{
15676			$this->encoding = $encoding;
15677		}
15678
15679		// Strip BOM:
15680		// UTF-32 Big Endian BOM
15681		if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
15682		{
15683			$data = substr($data, 4);
15684		}
15685		// UTF-32 Little Endian BOM
15686		elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
15687		{
15688			$data = substr($data, 4);
15689		}
15690		// UTF-16 Big Endian BOM
15691		elseif (substr($data, 0, 2) === "\xFE\xFF")
15692		{
15693			$data = substr($data, 2);
15694		}
15695		// UTF-16 Little Endian BOM
15696		elseif (substr($data, 0, 2) === "\xFF\xFE")
15697		{
15698			$data = substr($data, 2);
15699		}
15700		// UTF-8 BOM
15701		elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
15702		{
15703			$data = substr($data, 3);
15704		}
15705
15706		if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
15707		{
15708			$declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
15709			if ($declaration->parse())
15710			{
15711				$data = substr($data, $pos + 2);
15712				$data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
15713			}
15714			else
15715			{
15716				$this->error_string = 'SimplePie bug! Please report this!';
15717				return false;
15718			}
15719		}
15720
15721		$return = true;
15722
15723		static $xml_is_sane = null;
15724		if ($xml_is_sane === null)
15725		{
15726			$parser_check = xml_parser_create();
15727			xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
15728			xml_parser_free($parser_check);
15729			$xml_is_sane = isset($values[0]['value']);
15730		}
15731
15732		// Create the parser
15733		if ($xml_is_sane)
15734		{
15735			$xml = xml_parser_create_ns($this->encoding, $this->separator);
15736			xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
15737			xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
15738			xml_set_object($xml, $this);
15739			xml_set_character_data_handler($xml, 'cdata');
15740			xml_set_element_handler($xml, 'tag_open', 'tag_close');
15741
15742			// Parse!
15743			if (!xml_parse($xml, $data, true))
15744			{
15745				$this->error_code = xml_get_error_code($xml);
15746				$this->error_string = xml_error_string($this->error_code);
15747				$return = false;
15748			}
15749			$this->current_line = xml_get_current_line_number($xml);
15750			$this->current_column = xml_get_current_column_number($xml);
15751			$this->current_byte = xml_get_current_byte_index($xml);
15752			xml_parser_free($xml);
15753			return $return;
15754		}
15755		else
15756		{
15757			libxml_clear_errors();
15758			$xml = new XMLReader();
15759			$xml->xml($data);
15760			while (@$xml->read())
15761			{
15762				switch ($xml->nodeType)
15763				{
15764
15765					case constant('XMLReader::END_ELEMENT'):
15766						if ($xml->namespaceURI !== '')
15767						{
15768							$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
15769						}
15770						else
15771						{
15772							$tagName = $xml->localName;
15773						}
15774						$this->tag_close(null, $tagName);
15775						break;
15776					case constant('XMLReader::ELEMENT'):
15777						$empty = $xml->isEmptyElement;
15778						if ($xml->namespaceURI !== '')
15779						{
15780							$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
15781						}
15782						else
15783						{
15784							$tagName = $xml->localName;
15785						}
15786						$attributes = array();
15787						while ($xml->moveToNextAttribute())
15788						{
15789							if ($xml->namespaceURI !== '')
15790							{
15791								$attrName = $xml->namespaceURI . $this->separator . $xml->localName;
15792							}
15793							else
15794							{
15795								$attrName = $xml->localName;
15796							}
15797							$attributes[$attrName] = $xml->value;
15798						}
15799						$this->tag_open(null, $tagName, $attributes);
15800						if ($empty)
15801						{
15802							$this->tag_close(null, $tagName);
15803						}
15804						break;
15805					case constant('XMLReader::TEXT'):
15806
15807					case constant('XMLReader::CDATA'):
15808						$this->cdata(null, $xml->value);
15809						break;
15810				}
15811			}
15812			if ($error = libxml_get_last_error())
15813			{
15814				$this->error_code = $error->code;
15815				$this->error_string = $error->message;
15816				$this->current_line = $error->line;
15817				$this->current_column = $error->column;
15818				return false;
15819			}
15820			else
15821			{
15822				return true;
15823			}
15824		}
15825	}
15826
15827	public function get_error_code()
15828	{
15829		return $this->error_code;
15830	}
15831
15832	public function get_error_string()
15833	{
15834		return $this->error_string;
15835	}
15836
15837	public function get_current_line()
15838	{
15839		return $this->current_line;
15840	}
15841
15842	public function get_current_column()
15843	{
15844		return $this->current_column;
15845	}
15846
15847	public function get_current_byte()
15848	{
15849		return $this->current_byte;
15850	}
15851
15852	public function get_data()
15853	{
15854		return $this->data;
15855	}
15856
15857	public function tag_open($parser, $tag, $attributes)
15858	{
15859		list($this->namespace[], $this->element[]) = $this->split_ns($tag);
15860
15861		$attribs = array();
15862		foreach ($attributes as $name => $value)
15863		{
15864			list($attrib_namespace, $attribute) = $this->split_ns($name);
15865			$attribs[$attrib_namespace][$attribute] = $value;
15866		}
15867
15868		if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
15869		{
15870			$base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)));
15871			if ($base !== false)
15872			{
15873				$this->xml_base[] = $base;
15874				$this->xml_base_explicit[] = true;
15875			}
15876		}
15877		else
15878		{
15879			$this->xml_base[] = end($this->xml_base);
15880			$this->xml_base_explicit[] = end($this->xml_base_explicit);
15881		}
15882
15883		if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
15884		{
15885			$this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
15886		}
15887		else
15888		{
15889			$this->xml_lang[] = end($this->xml_lang);
15890		}
15891
15892		if ($this->current_xhtml_construct >= 0)
15893		{
15894			$this->current_xhtml_construct++;
15895			if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
15896			{
15897				$this->data['data'] .= '<' . end($this->element);
15898				if (isset($attribs['']))
15899				{
15900					foreach ($attribs[''] as $name => $value)
15901					{
15902						$this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
15903					}
15904				}
15905				$this->data['data'] .= '>';
15906			}
15907		}
15908		else
15909		{
15910			$this->datas[] =& $this->data;
15911			$this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
15912			$this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang));
15913			if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
15914			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')
15915			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title')))
15916			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title')))
15917			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title'))))
15918			{
15919				$this->current_xhtml_construct = 0;
15920			}
15921		}
15922	}
15923
15924	public function cdata($parser, $cdata)
15925	{
15926		if ($this->current_xhtml_construct >= 0)
15927		{
15928			$this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
15929		}
15930		else
15931		{
15932			$this->data['data'] .= $cdata;
15933		}
15934	}
15935
15936	public function tag_close($parser, $tag)
15937	{
15938		if ($this->current_xhtml_construct >= 0)
15939		{
15940			$this->current_xhtml_construct--;
15941			if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param')))
15942			{
15943				$this->data['data'] .= '</' . end($this->element) . '>';
15944			}
15945		}
15946		if ($this->current_xhtml_construct === -1)
15947		{
15948			$this->data =& $this->datas[count($this->datas) - 1];
15949			array_pop($this->datas);
15950		}
15951
15952		array_pop($this->element);
15953		array_pop($this->namespace);
15954		array_pop($this->xml_base);
15955		array_pop($this->xml_base_explicit);
15956		array_pop($this->xml_lang);
15957	}
15958
15959	public function split_ns($string)
15960	{
15961		static $cache = array();
15962		if (!isset($cache[$string]))
15963		{
15964			if ($pos = strpos($string, $this->separator))
15965			{
15966				static $separator_length;
15967				if (!$separator_length)
15968				{
15969					$separator_length = strlen($this->separator);
15970				}
15971				$namespace = substr($string, 0, $pos);
15972				$local_name = substr($string, $pos + $separator_length);
15973				if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
15974				{
15975					$namespace = SIMPLEPIE_NAMESPACE_ITUNES;
15976				}
15977
15978				// Normalize the Media RSS namespaces
15979				if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
15980					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
15981					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
15982					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
15983					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
15984				{
15985					$namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
15986				}
15987				$cache[$string] = array($namespace, $local_name);
15988			}
15989			else
15990			{
15991				$cache[$string] = array('', $string);
15992			}
15993		}
15994		return $cache[$string];
15995	}
15996}
15997
15998/**
15999 * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
16000 *
16001 * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()}
16002 *
16003 * This class can be overloaded with {@see SimplePie::set_rating_class()}
16004 *
16005 * @package SimplePie
16006 * @subpackage API
16007 */
16008class SimplePie_Rating
16009{
16010	/**
16011	 * Rating scheme
16012	 *
16013	 * @var string
16014	 * @see get_scheme()
16015	 */
16016	var $scheme;
16017
16018	/**
16019	 * Rating value
16020	 *
16021	 * @var string
16022	 * @see get_value()
16023	 */
16024	var $value;
16025
16026	/**
16027	 * Constructor, used to input the data
16028	 *
16029	 * For documentation on all the parameters, see the corresponding
16030	 * properties and their accessors
16031	 */
16032	public function __construct($scheme = null, $value = null)
16033	{
16034		$this->scheme = $scheme;
16035		$this->value = $value;
16036	}
16037
16038	/**
16039	 * String-ified version
16040	 *
16041	 * @return string
16042	 */
16043	public function __toString()
16044	{
16045		// There is no $this->data here
16046		return md5(serialize($this));
16047	}
16048
16049	/**
16050	 * Get the organizational scheme for the rating
16051	 *
16052	 * @return string|null
16053	 */
16054	public function get_scheme()
16055	{
16056		if ($this->scheme !== null)
16057		{
16058			return $this->scheme;
16059		}
16060		else
16061		{
16062			return null;
16063		}
16064	}
16065
16066	/**
16067	 * Get the value of the rating
16068	 *
16069	 * @return string|null
16070	 */
16071	public function get_value()
16072	{
16073		if ($this->value !== null)
16074		{
16075			return $this->value;
16076		}
16077		else
16078		{
16079			return null;
16080		}
16081	}
16082}
16083
16084/**
16085 * Handles creating objects and calling methods
16086 *
16087 * Access this via {@see SimplePie::get_registry()}
16088 *
16089 * @package SimplePie
16090 */
16091class SimplePie_Registry
16092{
16093	/**
16094	 * Default class mapping
16095	 *
16096	 * Overriding classes *must* subclass these.
16097	 *
16098	 * @var array
16099	 */
16100	protected $default = array(
16101		'Cache' => 'SimplePie_Cache',
16102		'Locator' => 'SimplePie_Locator',
16103		'Parser' => 'SimplePie_Parser',
16104		'File' => 'SimplePie_File',
16105		'Sanitize' => 'SimplePie_Sanitize',
16106		'Item' => 'SimplePie_Item',
16107		'Author' => 'SimplePie_Author',
16108		'Category' => 'SimplePie_Category',
16109		'Enclosure' => 'SimplePie_Enclosure',
16110		'Caption' => 'SimplePie_Caption',
16111		'Copyright' => 'SimplePie_Copyright',
16112		'Credit' => 'SimplePie_Credit',
16113		'Rating' => 'SimplePie_Rating',
16114		'Restriction' => 'SimplePie_Restriction',
16115		'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer',
16116		'Source' => 'SimplePie_Source',
16117		'Misc' => 'SimplePie_Misc',
16118		'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser',
16119		'Parse_Date' => 'SimplePie_Parse_Date',
16120	);
16121
16122	/**
16123	 * Class mapping
16124	 *
16125	 * @see register()
16126	 * @var array
16127	 */
16128	protected $classes = array();
16129
16130	/**
16131	 * Legacy classes
16132	 *
16133	 * @see register()
16134	 * @var array
16135	 */
16136	protected $legacy = array();
16137
16138	/**
16139	 * Constructor
16140	 *
16141	 * No-op
16142	 */
16143	public function __construct() { }
16144
16145	/**
16146	 * Register a class
16147	 *
16148	 * @param string $type See {@see $default} for names
16149	 * @param string $class Class name, must subclass the corresponding default
16150	 * @param bool $legacy Whether to enable legacy support for this class
16151	 * @return bool Successfulness
16152	 */
16153	public function register($type, $class, $legacy = false)
16154	{
16155		if (!is_subclass_of($class, $this->default[$type]))
16156		{
16157			return false;
16158		}
16159
16160		$this->classes[$type] = $class;
16161
16162		if ($legacy)
16163		{
16164			$this->legacy[] = $class;
16165		}
16166
16167		return true;
16168	}
16169
16170	/**
16171	 * Get the class registered for a type
16172	 *
16173	 * Where possible, use {@see create()} or {@see call()} instead
16174	 *
16175	 * @param string $type
16176	 * @return string|null
16177	 */
16178	public function get_class($type)
16179	{
16180		if (!empty($this->classes[$type]))
16181		{
16182			return $this->classes[$type];
16183		}
16184		if (!empty($this->default[$type]))
16185		{
16186			return $this->default[$type];
16187		}
16188
16189		return null;
16190	}
16191
16192	/**
16193	 * Create a new instance of a given type
16194	 *
16195	 * @param string $type
16196	 * @param array $parameters Parameters to pass to the constructor
16197	 * @return object Instance of class
16198	 */
16199	public function &create($type, $parameters = array())
16200	{
16201		$class = $this->get_class($type);
16202
16203		if (in_array($class, $this->legacy))
16204		{
16205			switch ($type)
16206			{
16207				case 'locator':
16208					// Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class
16209					// Specified: file, timeout, useragent, max_checked_feeds
16210					$replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer'));
16211					array_splice($parameters, 3, 1, $replacement);
16212					break;
16213			}
16214		}
16215
16216		if (!method_exists($class, '__construct'))
16217		{
16218			$instance = new $class;
16219		}
16220		else
16221		{
16222			$reflector = new ReflectionClass($class);
16223			$instance = $reflector->newInstanceArgs($parameters);
16224		}
16225
16226		if (method_exists($instance, 'set_registry'))
16227		{
16228			$instance->set_registry($this);
16229		}
16230		return $instance;
16231	}
16232
16233	/**
16234	 * Call a static method for a type
16235	 *
16236	 * @param string $type
16237	 * @param string $method
16238	 * @param array $parameters
16239	 * @return mixed
16240	 */
16241	public function &call($type, $method, $parameters = array())
16242	{
16243		$class = $this->get_class($type);
16244
16245		if (in_array($class, $this->legacy))
16246		{
16247			switch ($type)
16248			{
16249				case 'Cache':
16250					// For backwards compatibility with old non-static
16251					// Cache::create() methods
16252					if ($method === 'get_handler')
16253					{
16254						$result = @call_user_func_array(array($class, 'create'), $parameters);
16255						return $result;
16256					}
16257					break;
16258			}
16259		}
16260
16261		$result = call_user_func_array(array($class, $method), $parameters);
16262		return $result;
16263	}
16264}
16265
16266/**
16267 * Handles `<media:restriction>` as defined in Media RSS
16268 *
16269 * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()}
16270 *
16271 * This class can be overloaded with {@see SimplePie::set_restriction_class()}
16272 *
16273 * @package SimplePie
16274 * @subpackage API
16275 */
16276class SimplePie_Restriction
16277{
16278	/**
16279	 * Relationship ('allow'/'deny')
16280	 *
16281	 * @var string
16282	 * @see get_relationship()
16283	 */
16284	var $relationship;
16285
16286	/**
16287	 * Type of restriction
16288	 *
16289	 * @var string
16290	 * @see get_type()
16291	 */
16292	var $type;
16293
16294	/**
16295	 * Restricted values
16296	 *
16297	 * @var string
16298	 * @see get_value()
16299	 */
16300	var $value;
16301
16302	/**
16303	 * Constructor, used to input the data
16304	 *
16305	 * For documentation on all the parameters, see the corresponding
16306	 * properties and their accessors
16307	 */
16308	public function __construct($relationship = null, $type = null, $value = null)
16309	{
16310		$this->relationship = $relationship;
16311		$this->type = $type;
16312		$this->value = $value;
16313	}
16314
16315	/**
16316	 * String-ified version
16317	 *
16318	 * @return string
16319	 */
16320	public function __toString()
16321	{
16322		// There is no $this->data here
16323		return md5(serialize($this));
16324	}
16325
16326	/**
16327	 * Get the relationship
16328	 *
16329	 * @return string|null Either 'allow' or 'deny'
16330	 */
16331	public function get_relationship()
16332	{
16333		if ($this->relationship !== null)
16334		{
16335			return $this->relationship;
16336		}
16337		else
16338		{
16339			return null;
16340		}
16341	}
16342
16343	/**
16344	 * Get the type
16345	 *
16346	 * @return string|null
16347	 */
16348	public function get_type()
16349	{
16350		if ($this->type !== null)
16351		{
16352			return $this->type;
16353		}
16354		else
16355		{
16356			return null;
16357		}
16358	}
16359
16360	/**
16361	 * Get the list of restricted things
16362	 *
16363	 * @return string|null
16364	 */
16365	public function get_value()
16366	{
16367		if ($this->value !== null)
16368		{
16369			return $this->value;
16370		}
16371		else
16372		{
16373			return null;
16374		}
16375	}
16376}
16377
16378/**
16379 * Used for data cleanup and post-processing
16380 *
16381 *
16382 * This class can be overloaded with {@see SimplePie::set_sanitize_class()}
16383 *
16384 * @package SimplePie
16385 * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
16386 */
16387class SimplePie_Sanitize
16388{
16389	// Private vars
16390	var $base;
16391
16392	// Options
16393	var $remove_div = true;
16394	var $image_handler = '';
16395	var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
16396	var $encode_instead_of_strip = false;
16397	var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
16398	var $strip_comments = false;
16399	var $output_encoding = 'UTF-8';
16400	var $enable_cache = true;
16401	var $cache_location = './cache';
16402	var $cache_name_function = 'md5';
16403	var $timeout = 10;
16404	var $useragent = '';
16405	var $force_fsockopen = false;
16406	var $replace_url_attributes = null;
16407
16408	public function __construct()
16409	{
16410		// Set defaults
16411		$this->set_url_replacements(null);
16412	}
16413
16414	public function remove_div($enable = true)
16415	{
16416		$this->remove_div = (bool) $enable;
16417	}
16418
16419	public function set_image_handler($page = false)
16420	{
16421		if ($page)
16422		{
16423			$this->image_handler = (string) $page;
16424		}
16425		else
16426		{
16427			$this->image_handler = false;
16428		}
16429	}
16430
16431	public function set_registry(SimplePie_Registry $registry)
16432	{
16433		$this->registry = $registry;
16434	}
16435
16436	public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
16437	{
16438		if (isset($enable_cache))
16439		{
16440			$this->enable_cache = (bool) $enable_cache;
16441		}
16442
16443		if ($cache_location)
16444		{
16445			$this->cache_location = (string) $cache_location;
16446		}
16447
16448		if ($cache_name_function)
16449		{
16450			$this->cache_name_function = (string) $cache_name_function;
16451		}
16452	}
16453
16454	public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
16455	{
16456		if ($timeout)
16457		{
16458			$this->timeout = (string) $timeout;
16459		}
16460
16461		if ($useragent)
16462		{
16463			$this->useragent = (string) $useragent;
16464		}
16465
16466		if ($force_fsockopen)
16467		{
16468			$this->force_fsockopen = (string) $force_fsockopen;
16469		}
16470	}
16471
16472	public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
16473	{
16474		if ($tags)
16475		{
16476			if (is_array($tags))
16477			{
16478				$this->strip_htmltags = $tags;
16479			}
16480			else
16481			{
16482				$this->strip_htmltags = explode(',', $tags);
16483			}
16484		}
16485		else
16486		{
16487			$this->strip_htmltags = false;
16488		}
16489	}
16490
16491	public function encode_instead_of_strip($encode = false)
16492	{
16493		$this->encode_instead_of_strip = (bool) $encode;
16494	}
16495
16496	public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
16497	{
16498		if ($attribs)
16499		{
16500			if (is_array($attribs))
16501			{
16502				$this->strip_attributes = $attribs;
16503			}
16504			else
16505			{
16506				$this->strip_attributes = explode(',', $attribs);
16507			}
16508		}
16509		else
16510		{
16511			$this->strip_attributes = false;
16512		}
16513	}
16514
16515	public function strip_comments($strip = false)
16516	{
16517		$this->strip_comments = (bool) $strip;
16518	}
16519
16520	public function set_output_encoding($encoding = 'UTF-8')
16521	{
16522		$this->output_encoding = (string) $encoding;
16523	}
16524
16525	/**
16526	 * Set element/attribute key/value pairs of HTML attributes
16527	 * containing URLs that need to be resolved relative to the feed
16528	 *
16529	 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
16530	 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
16531	 * |q|@cite
16532	 *
16533	 * @since 1.0
16534	 * @param array|null $element_attribute Element/attribute key/value pairs, null for default
16535	 */
16536	public function set_url_replacements($element_attribute = null)
16537	{
16538		if ($element_attribute === null)
16539		{
16540			$element_attribute = array(
16541				'a' => 'href',
16542				'area' => 'href',
16543				'blockquote' => 'cite',
16544				'del' => 'cite',
16545				'form' => 'action',
16546				'img' => array(
16547					'longdesc',
16548					'src'
16549				),
16550				'input' => 'src',
16551				'ins' => 'cite',
16552				'q' => 'cite'
16553			);
16554		}
16555		$this->replace_url_attributes = (array) $element_attribute;
16556	}
16557
16558	public function sanitize($data, $type, $base = '')
16559	{
16560		$data = trim($data);
16561		if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
16562		{
16563			if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
16564			{
16565				if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data))
16566				{
16567					$type |= SIMPLEPIE_CONSTRUCT_HTML;
16568				}
16569				else
16570				{
16571					$type |= SIMPLEPIE_CONSTRUCT_TEXT;
16572				}
16573			}
16574
16575			if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
16576			{
16577				$data = base64_decode($data);
16578			}
16579
16580			if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
16581			{
16582
16583				$document = new DOMDocument();
16584				$document->encoding = 'UTF-8';
16585				$data = $this->preprocess($data, $type);
16586
16587				set_error_handler(array('SimplePie_Misc', 'silence_errors'));
16588				$document->loadHTML($data);
16589				restore_error_handler();
16590
16591				// Strip comments
16592				if ($this->strip_comments)
16593				{
16594					$xpath = new DOMXPath($document);
16595					$comments = $xpath->query('//comment()');
16596
16597					foreach ($comments as $comment)
16598					{
16599						$comment->parentNode->removeChild($comment);
16600					}
16601				}
16602
16603				// Strip out HTML tags and attributes that might cause various security problems.
16604				// Based on recommendations by Mark Pilgrim at:
16605				// http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
16606				if ($this->strip_htmltags)
16607				{
16608					foreach ($this->strip_htmltags as $tag)
16609					{
16610						$this->strip_tag($tag, $document, $type);
16611					}
16612				}
16613
16614				if ($this->strip_attributes)
16615				{
16616					foreach ($this->strip_attributes as $attrib)
16617					{
16618						$this->strip_attr($attrib, $document);
16619					}
16620				}
16621
16622				// Replace relative URLs
16623				$this->base = $base;
16624				foreach ($this->replace_url_attributes as $element => $attributes)
16625				{
16626					$this->replace_urls($document, $element, $attributes);
16627				}
16628
16629				// If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
16630				if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
16631				{
16632					$images = $document->getElementsByTagName('img');
16633					foreach ($images as $img)
16634					{
16635						if ($img->hasAttribute('src'))
16636						{
16637							$image_url = call_user_func($this->cache_name_function, $img->getAttribute('src'));
16638							$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi'));
16639
16640							if ($cache->load())
16641							{
16642								$img->setAttribute('src', $this->image_handler . $image_url);
16643							}
16644							else
16645							{
16646								$file = $this->registry->create('File', array($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen));
16647								$headers = $file->headers;
16648
16649								if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
16650								{
16651									if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
16652									{
16653										$img->setAttribute('src', $this->image_handler . $image_url);
16654									}
16655									else
16656									{
16657										trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
16658									}
16659								}
16660							}
16661						}
16662					}
16663				}
16664
16665				// Remove the DOCTYPE
16666				// Seems to cause segfaulting if we don't do this
16667				if ($document->firstChild instanceof DOMDocumentType)
16668				{
16669					$document->removeChild($document->firstChild);
16670				}
16671
16672				// Move everything from the body to the root
16673				$real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0);
16674				$document->replaceChild($real_body, $document->firstChild);
16675
16676				// Finally, convert to a HTML string
16677				$data = trim($document->saveHTML());
16678
16679				if ($this->remove_div)
16680				{
16681					$data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
16682					$data = preg_replace('/<\/div>$/', '', $data);
16683				}
16684				else
16685				{
16686					$data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
16687				}
16688			}
16689
16690			if ($type & SIMPLEPIE_CONSTRUCT_IRI)
16691			{
16692				$absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
16693				if ($absolute !== false)
16694				{
16695					$data = $absolute;
16696				}
16697			}
16698
16699			if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
16700			{
16701				$data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
16702			}
16703
16704			if ($this->output_encoding !== 'UTF-8')
16705			{
16706				$data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding));
16707			}
16708		}
16709		return $data;
16710	}
16711
16712	protected function preprocess($html, $type)
16713	{
16714		$ret = '';
16715		if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML)
16716		{
16717			// Atom XHTML constructs are wrapped with a div by default
16718			// Note: No protection if $html contains a stray </div>!
16719			$html = '<div>' . $html . '</div>';
16720			$ret .= '<!DOCTYPE html>';
16721			$content_type = 'text/html';
16722		}
16723		else
16724		{
16725			$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
16726			$content_type = 'application/xhtml+xml';
16727		}
16728
16729		$ret .= '<html><head>';
16730		$ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
16731		$ret .= '</head><body>' . $html . '</body></html>';
16732		return $ret;
16733	}
16734
16735	public function replace_urls($document, $tag, $attributes)
16736	{
16737		if (!is_array($attributes))
16738		{
16739			$attributes = array($attributes);
16740		}
16741
16742		if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
16743		{
16744			$elements = $document->getElementsByTagName($tag);
16745			foreach ($elements as $element)
16746			{
16747				foreach ($attributes as $attribute)
16748				{
16749					if ($element->hasAttribute($attribute))
16750					{
16751						$value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base));
16752						if ($value !== false)
16753						{
16754							$element->setAttribute($attribute, $value);
16755						}
16756					}
16757				}
16758			}
16759		}
16760	}
16761
16762	public function do_strip_htmltags($match)
16763	{
16764		if ($this->encode_instead_of_strip)
16765		{
16766			if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
16767			{
16768				$match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
16769				$match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
16770				return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
16771			}
16772			else
16773			{
16774				return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
16775			}
16776		}
16777		elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
16778		{
16779			return $match[4];
16780		}
16781		else
16782		{
16783			return '';
16784		}
16785	}
16786
16787	protected function strip_tag($tag, $document, $type)
16788	{
16789		$xpath = new DOMXPath($document);
16790		$elements = $xpath->query('body//' . $tag);
16791		if ($this->encode_instead_of_strip)
16792		{
16793			foreach ($elements as $element)
16794			{
16795				$fragment = $document->createDocumentFragment();
16796
16797				// For elements which aren't script or style, include the tag itself
16798				if (!in_array($tag, array('script', 'style')))
16799				{
16800					$text = '<' . $tag;
16801					if ($element->hasAttributes())
16802					{
16803						$attrs = array();
16804						foreach ($element->attributes as $name => $attr)
16805						{
16806							$value = $attr->value;
16807
16808							// In XHTML, empty values should never exist, so we repeat the value
16809							if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML))
16810							{
16811								$value = $name;
16812							}
16813							// For HTML, empty is fine
16814							elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML))
16815							{
16816								$attrs[] = $name;
16817								continue;
16818							}
16819
16820							// Standard attribute text
16821							$attrs[] = $name . '="' . $attr->value . '"';
16822						}
16823						$text .= ' ' . implode(' ', $attrs);
16824					}
16825					$text .= '>';
16826					$fragment->appendChild(new DOMText($text));
16827				}
16828
16829				$number = $element->childNodes->length;
16830				for ($i = $number; $i > 0; $i--)
16831				{
16832					$child = $element->childNodes->item(0);
16833					$fragment->appendChild($child);
16834				}
16835
16836				if (!in_array($tag, array('script', 'style')))
16837				{
16838					$fragment->appendChild(new DOMText('</' . $tag . '>'));
16839				}
16840
16841				$element->parentNode->replaceChild($fragment, $element);
16842			}
16843
16844			return;
16845		}
16846		elseif (in_array($tag, array('script', 'style')))
16847		{
16848			foreach ($elements as $element)
16849			{
16850				$element->parentNode->removeChild($element);
16851			}
16852
16853			return;
16854		}
16855		else
16856		{
16857			foreach ($elements as $element)
16858			{
16859				$fragment = $document->createDocumentFragment();
16860				$number = $element->childNodes->length;
16861				for ($i = $number; $i > 0; $i--)
16862				{
16863					$child = $element->childNodes->item(0);
16864					$fragment->appendChild($child);
16865				}
16866
16867				$element->parentNode->replaceChild($fragment, $element);
16868			}
16869		}
16870	}
16871
16872	protected function strip_attr($attrib, $document)
16873	{
16874		$xpath = new DOMXPath($document);
16875		$elements = $xpath->query('//*[@' . $attrib . ']');
16876
16877		foreach ($elements as $element)
16878		{
16879			$element->removeAttribute($attrib);
16880		}
16881	}
16882}
16883
16884/**
16885 * Handles `<atom:source>`
16886 *
16887 * Used by {@see SimplePie_Item::get_source()}
16888 *
16889 * This class can be overloaded with {@see SimplePie::set_source_class()}
16890 *
16891 * @package SimplePie
16892 * @subpackage API
16893 */
16894class SimplePie_Source
16895{
16896	var $item;
16897	var $data = array();
16898	protected $registry;
16899
16900	public function __construct($item, $data)
16901	{
16902		$this->item = $item;
16903		$this->data = $data;
16904	}
16905
16906	public function set_registry(SimplePie_Registry $registry)
16907	{
16908		$this->registry = $registry;
16909	}
16910
16911	public function __toString()
16912	{
16913		return md5(serialize($this->data));
16914	}
16915
16916	public function get_source_tags($namespace, $tag)
16917	{
16918		if (isset($this->data['child'][$namespace][$tag]))
16919		{
16920			return $this->data['child'][$namespace][$tag];
16921		}
16922		else
16923		{
16924			return null;
16925		}
16926	}
16927
16928	public function get_base($element = array())
16929	{
16930		return $this->item->get_base($element);
16931	}
16932
16933	public function sanitize($data, $type, $base = '')
16934	{
16935		return $this->item->sanitize($data, $type, $base);
16936	}
16937
16938	public function get_item()
16939	{
16940		return $this->item;
16941	}
16942
16943	public function get_title()
16944	{
16945		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
16946		{
16947			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
16948		}
16949		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
16950		{
16951			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
16952		}
16953		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
16954		{
16955			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16956		}
16957		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
16958		{
16959			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16960		}
16961		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
16962		{
16963			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16964		}
16965		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
16966		{
16967			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
16968		}
16969		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
16970		{
16971			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
16972		}
16973		else
16974		{
16975			return null;
16976		}
16977	}
16978
16979	public function get_category($key = 0)
16980	{
16981		$categories = $this->get_categories();
16982		if (isset($categories[$key]))
16983		{
16984			return $categories[$key];
16985		}
16986		else
16987		{
16988			return null;
16989		}
16990	}
16991
16992	public function get_categories()
16993	{
16994		$categories = array();
16995
16996		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
16997		{
16998			$term = null;
16999			$scheme = null;
17000			$label = null;
17001			if (isset($category['attribs']['']['term']))
17002			{
17003				$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
17004			}
17005			if (isset($category['attribs']['']['scheme']))
17006			{
17007				$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
17008			}
17009			if (isset($category['attribs']['']['label']))
17010			{
17011				$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
17012			}
17013			$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
17014		}
17015		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
17016		{
17017			// This is really the label, but keep this as the term also for BC.
17018			// Label will also work on retrieving because that falls back to term.
17019			$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17020			if (isset($category['attribs']['']['domain']))
17021			{
17022				$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
17023			}
17024			else
17025			{
17026				$scheme = null;
17027			}
17028			$categories[] = $this->registry->create('Category', array($term, $scheme, null));
17029		}
17030		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
17031		{
17032			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17033		}
17034		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
17035		{
17036			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17037		}
17038
17039		if (!empty($categories))
17040		{
17041			return array_unique($categories);
17042		}
17043		else
17044		{
17045			return null;
17046		}
17047	}
17048
17049	public function get_author($key = 0)
17050	{
17051		$authors = $this->get_authors();
17052		if (isset($authors[$key]))
17053		{
17054			return $authors[$key];
17055		}
17056		else
17057		{
17058			return null;
17059		}
17060	}
17061
17062	public function get_authors()
17063	{
17064		$authors = array();
17065		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
17066		{
17067			$name = null;
17068			$uri = null;
17069			$email = null;
17070			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17071			{
17072				$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17073			}
17074			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17075			{
17076				$uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
17077			}
17078			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17079			{
17080				$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17081			}
17082			if ($name !== null || $email !== null || $uri !== null)
17083			{
17084				$authors[] = $this->registry->create('Author', array($name, $uri, $email));
17085			}
17086		}
17087		if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
17088		{
17089			$name = null;
17090			$url = null;
17091			$email = null;
17092			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17093			{
17094				$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17095			}
17096			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17097			{
17098				$url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
17099			}
17100			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17101			{
17102				$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17103			}
17104			if ($name !== null || $email !== null || $url !== null)
17105			{
17106				$authors[] = $this->registry->create('Author', array($name, $url, $email));
17107			}
17108		}
17109		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
17110		{
17111			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17112		}
17113		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
17114		{
17115			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17116		}
17117		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
17118		{
17119			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17120		}
17121
17122		if (!empty($authors))
17123		{
17124			return array_unique($authors);
17125		}
17126		else
17127		{
17128			return null;
17129		}
17130	}
17131
17132	public function get_contributor($key = 0)
17133	{
17134		$contributors = $this->get_contributors();
17135		if (isset($contributors[$key]))
17136		{
17137			return $contributors[$key];
17138		}
17139		else
17140		{
17141			return null;
17142		}
17143	}
17144
17145	public function get_contributors()
17146	{
17147		$contributors = array();
17148		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
17149		{
17150			$name = null;
17151			$uri = null;
17152			$email = null;
17153			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17154			{
17155				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17156			}
17157			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17158			{
17159				$uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
17160			}
17161			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17162			{
17163				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17164			}
17165			if ($name !== null || $email !== null || $uri !== null)
17166			{
17167				$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
17168			}
17169		}
17170		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
17171		{
17172			$name = null;
17173			$url = null;
17174			$email = null;
17175			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17176			{
17177				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17178			}
17179			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17180			{
17181				$url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
17182			}
17183			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17184			{
17185				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17186			}
17187			if ($name !== null || $email !== null || $url !== null)
17188			{
17189				$contributors[] = $this->registry->create('Author', array($name, $url, $email));
17190			}
17191		}
17192
17193		if (!empty($contributors))
17194		{
17195			return array_unique($contributors);
17196		}
17197		else
17198		{
17199			return null;
17200		}
17201	}
17202
17203	public function get_link($key = 0, $rel = 'alternate')
17204	{
17205		$links = $this->get_links($rel);
17206		if (isset($links[$key]))
17207		{
17208			return $links[$key];
17209		}
17210		else
17211		{
17212			return null;
17213		}
17214	}
17215
17216	/**
17217	 * Added for parity between the parent-level and the item/entry-level.
17218	 */
17219	public function get_permalink()
17220	{
17221		return $this->get_link(0);
17222	}
17223
17224	public function get_links($rel = 'alternate')
17225	{
17226		if (!isset($this->data['links']))
17227		{
17228			$this->data['links'] = array();
17229			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
17230			{
17231				foreach ($links as $link)
17232				{
17233					if (isset($link['attribs']['']['href']))
17234					{
17235						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17236						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17237					}
17238				}
17239			}
17240			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
17241			{
17242				foreach ($links as $link)
17243				{
17244					if (isset($link['attribs']['']['href']))
17245					{
17246						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17247						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17248
17249					}
17250				}
17251			}
17252			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
17253			{
17254				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17255			}
17256			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
17257			{
17258				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17259			}
17260			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
17261			{
17262				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17263			}
17264
17265			$keys = array_keys($this->data['links']);
17266			foreach ($keys as $key)
17267			{
17268				if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
17269				{
17270					if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
17271					{
17272						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
17273						$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
17274					}
17275					else
17276					{
17277						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
17278					}
17279				}
17280				elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
17281				{
17282					$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
17283				}
17284				$this->data['links'][$key] = array_unique($this->data['links'][$key]);
17285			}
17286		}
17287
17288		if (isset($this->data['links'][$rel]))
17289		{
17290			return $this->data['links'][$rel];
17291		}
17292		else
17293		{
17294			return null;
17295		}
17296	}
17297
17298	public function get_description()
17299	{
17300		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
17301		{
17302			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17303		}
17304		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
17305		{
17306			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17307		}
17308		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
17309		{
17310			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17311		}
17312		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
17313		{
17314			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17315		}
17316		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
17317		{
17318			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17319		}
17320		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
17321		{
17322			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17323		}
17324		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
17325		{
17326			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17327		}
17328		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
17329		{
17330			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17331		}
17332		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
17333		{
17334			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17335		}
17336		else
17337		{
17338			return null;
17339		}
17340	}
17341
17342	public function get_copyright()
17343	{
17344		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
17345		{
17346			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17347		}
17348		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
17349		{
17350			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17351		}
17352		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
17353		{
17354			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17355		}
17356		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
17357		{
17358			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17359		}
17360		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
17361		{
17362			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17363		}
17364		else
17365		{
17366			return null;
17367		}
17368	}
17369
17370	public function get_language()
17371	{
17372		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
17373		{
17374			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17375		}
17376		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
17377		{
17378			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17379		}
17380		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
17381		{
17382			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17383		}
17384		elseif (isset($this->data['xml_lang']))
17385		{
17386			return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
17387		}
17388		else
17389		{
17390			return null;
17391		}
17392	}
17393
17394	public function get_latitude()
17395	{
17396		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
17397		{
17398			return (float) $return[0]['data'];
17399		}
17400		elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
17401		{
17402			return (float) $match[1];
17403		}
17404		else
17405		{
17406			return null;
17407		}
17408	}
17409
17410	public function get_longitude()
17411	{
17412		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
17413		{
17414			return (float) $return[0]['data'];
17415		}
17416		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
17417		{
17418			return (float) $return[0]['data'];
17419		}
17420		elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
17421		{
17422			return (float) $match[2];
17423		}
17424		else
17425		{
17426			return null;
17427		}
17428	}
17429
17430	public function get_image_url()
17431	{
17432		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
17433		{
17434			return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
17435		}
17436		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
17437		{
17438			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
17439		}
17440		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
17441		{
17442			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
17443		}
17444		else
17445		{
17446			return null;
17447		}
17448	}
17449}
17450
17451/**
17452 * Parses the XML Declaration
17453 *
17454 * @package SimplePie
17455 * @subpackage Parsing
17456 */
17457class SimplePie_XML_Declaration_Parser
17458{
17459	/**
17460	 * XML Version
17461	 *
17462	 * @access public
17463	 * @var string
17464	 */
17465	var $version = '1.0';
17466
17467	/**
17468	 * Encoding
17469	 *
17470	 * @access public
17471	 * @var string
17472	 */
17473	var $encoding = 'UTF-8';
17474
17475	/**
17476	 * Standalone
17477	 *
17478	 * @access public
17479	 * @var bool
17480	 */
17481	var $standalone = false;
17482
17483	/**
17484	 * Current state of the state machine
17485	 *
17486	 * @access private
17487	 * @var string
17488	 */
17489	var $state = 'before_version_name';
17490
17491	/**
17492	 * Input data
17493	 *
17494	 * @access private
17495	 * @var string
17496	 */
17497	var $data = '';
17498
17499	/**
17500	 * Input data length (to avoid calling strlen() everytime this is needed)
17501	 *
17502	 * @access private
17503	 * @var int
17504	 */
17505	var $data_length = 0;
17506
17507	/**
17508	 * Current position of the pointer
17509	 *
17510	 * @var int
17511	 * @access private
17512	 */
17513	var $position = 0;
17514
17515	/**
17516	 * Create an instance of the class with the input data
17517	 *
17518	 * @access public
17519	 * @param string $data Input data
17520	 */
17521	public function __construct($data)
17522	{
17523		$this->data = $data;
17524		$this->data_length = strlen($this->data);
17525	}
17526
17527	/**
17528	 * Parse the input data
17529	 *
17530	 * @access public
17531	 * @return bool true on success, false on failure
17532	 */
17533	public function parse()
17534	{
17535		while ($this->state && $this->state !== 'emit' && $this->has_data())
17536		{
17537			$state = $this->state;
17538			$this->$state();
17539		}
17540		$this->data = '';
17541		if ($this->state === 'emit')
17542		{
17543			return true;
17544		}
17545		else
17546		{
17547			$this->version = '';
17548			$this->encoding = '';
17549			$this->standalone = '';
17550			return false;
17551		}
17552	}
17553
17554	/**
17555	 * Check whether there is data beyond the pointer
17556	 *
17557	 * @access private
17558	 * @return bool true if there is further data, false if not
17559	 */
17560	public function has_data()
17561	{
17562		return (bool) ($this->position < $this->data_length);
17563	}
17564
17565	/**
17566	 * Advance past any whitespace
17567	 *
17568	 * @return int Number of whitespace characters passed
17569	 */
17570	public function skip_whitespace()
17571	{
17572		$whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
17573		$this->position += $whitespace;
17574		return $whitespace;
17575	}
17576
17577	/**
17578	 * Read value
17579	 */
17580	public function get_value()
17581	{
17582		$quote = substr($this->data, $this->position, 1);
17583		if ($quote === '"' || $quote === "'")
17584		{
17585			$this->position++;
17586			$len = strcspn($this->data, $quote, $this->position);
17587			if ($this->has_data())
17588			{
17589				$value = substr($this->data, $this->position, $len);
17590				$this->position += $len + 1;
17591				return $value;
17592			}
17593		}
17594		return false;
17595	}
17596
17597	public function before_version_name()
17598	{
17599		if ($this->skip_whitespace())
17600		{
17601			$this->state = 'version_name';
17602		}
17603		else
17604		{
17605			$this->state = false;
17606		}
17607	}
17608
17609	public function version_name()
17610	{
17611		if (substr($this->data, $this->position, 7) === 'version')
17612		{
17613			$this->position += 7;
17614			$this->skip_whitespace();
17615			$this->state = 'version_equals';
17616		}
17617		else
17618		{
17619			$this->state = false;
17620		}
17621	}
17622
17623	public function version_equals()
17624	{
17625		if (substr($this->data, $this->position, 1) === '=')
17626		{
17627			$this->position++;
17628			$this->skip_whitespace();
17629			$this->state = 'version_value';
17630		}
17631		else
17632		{
17633			$this->state = false;
17634		}
17635	}
17636
17637	public function version_value()
17638	{
17639		if ($this->version = $this->get_value())
17640		{
17641			$this->skip_whitespace();
17642			if ($this->has_data())
17643			{
17644				$this->state = 'encoding_name';
17645			}
17646			else
17647			{
17648				$this->state = 'emit';
17649			}
17650		}
17651		else
17652		{
17653			$this->state = false;
17654		}
17655	}
17656
17657	public function encoding_name()
17658	{
17659		if (substr($this->data, $this->position, 8) === 'encoding')
17660		{
17661			$this->position += 8;
17662			$this->skip_whitespace();
17663			$this->state = 'encoding_equals';
17664		}
17665		else
17666		{
17667			$this->state = 'standalone_name';
17668		}
17669	}
17670
17671	public function encoding_equals()
17672	{
17673		if (substr($this->data, $this->position, 1) === '=')
17674		{
17675			$this->position++;
17676			$this->skip_whitespace();
17677			$this->state = 'encoding_value';
17678		}
17679		else
17680		{
17681			$this->state = false;
17682		}
17683	}
17684
17685	public function encoding_value()
17686	{
17687		if ($this->encoding = $this->get_value())
17688		{
17689			$this->skip_whitespace();
17690			if ($this->has_data())
17691			{
17692				$this->state = 'standalone_name';
17693			}
17694			else
17695			{
17696				$this->state = 'emit';
17697			}
17698		}
17699		else
17700		{
17701			$this->state = false;
17702		}
17703	}
17704
17705	public function standalone_name()
17706	{
17707		if (substr($this->data, $this->position, 10) === 'standalone')
17708		{
17709			$this->position += 10;
17710			$this->skip_whitespace();
17711			$this->state = 'standalone_equals';
17712		}
17713		else
17714		{
17715			$this->state = false;
17716		}
17717	}
17718
17719	public function standalone_equals()
17720	{
17721		if (substr($this->data, $this->position, 1) === '=')
17722		{
17723			$this->position++;
17724			$this->skip_whitespace();
17725			$this->state = 'standalone_value';
17726		}
17727		else
17728		{
17729			$this->state = false;
17730		}
17731	}
17732
17733	public function standalone_value()
17734	{
17735		if ($standalone = $this->get_value())
17736		{
17737			switch ($standalone)
17738			{
17739				case 'yes':
17740					$this->standalone = true;
17741					break;
17742
17743				case 'no':
17744					$this->standalone = false;
17745					break;
17746
17747				default:
17748					$this->state = false;
17749					return;
17750			}
17751
17752			$this->skip_whitespace();
17753			if ($this->has_data())
17754			{
17755				$this->state = false;
17756			}
17757			else
17758			{
17759				$this->state = 'emit';
17760			}
17761		}
17762		else
17763		{
17764			$this->state = false;
17765		}
17766	}
17767}
17768
17769