]> git.wh0rd.org Git - tt-rss.git/blob - lib/simplepie/simplepie.inc
Merge pull request #61 from hydrian/master
[tt-rss.git] / lib / simplepie / simplepie.inc
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  */
51 define('SIMPLEPIE_NAME', 'SimplePie');
52
53 /**
54  * SimplePie Version
55  */
56 define('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  */
62 define('SIMPLEPIE_BUILD', '20121030175911');
63
64 /**
65  * SimplePie Website URL
66  */
67 define('SIMPLEPIE_URL', 'http://simplepie.org');
68
69 /**
70  * SimplePie Useragent
71  * @see SimplePie::set_useragent()
72  */
73 define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
74
75 /**
76  * SimplePie Linkback
77  */
78 define('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  */
84 define('SIMPLEPIE_LOCATOR_NONE', 0);
85
86 /**
87  * Feed Link Element Autodiscovery
88  * @see SimplePie::set_autodiscovery_level()
89  */
90 define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
91
92 /**
93  * Local Feed Extension Autodiscovery
94  * @see SimplePie::set_autodiscovery_level()
95  */
96 define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
97
98 /**
99  * Local Feed Body Autodiscovery
100  * @see SimplePie::set_autodiscovery_level()
101  */
102 define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
103
104 /**
105  * Remote Feed Extension Autodiscovery
106  * @see SimplePie::set_autodiscovery_level()
107  */
108 define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
109
110 /**
111  * Remote Feed Body Autodiscovery
112  * @see SimplePie::set_autodiscovery_level()
113  */
114 define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
115
116 /**
117  * All Feed Autodiscovery
118  * @see SimplePie::set_autodiscovery_level()
119  */
120 define('SIMPLEPIE_LOCATOR_ALL', 31);
121
122 /**
123  * No known feed type
124  */
125 define('SIMPLEPIE_TYPE_NONE', 0);
126
127 /**
128  * RSS 0.90
129  */
130 define('SIMPLEPIE_TYPE_RSS_090', 1);
131
132 /**
133  * RSS 0.91 (Netscape)
134  */
135 define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
136
137 /**
138  * RSS 0.91 (Userland)
139  */
140 define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
141
142 /**
143  * RSS 0.91 (both Netscape and Userland)
144  */
145 define('SIMPLEPIE_TYPE_RSS_091', 6);
146
147 /**
148  * RSS 0.92
149  */
150 define('SIMPLEPIE_TYPE_RSS_092', 8);
151
152 /**
153  * RSS 0.93
154  */
155 define('SIMPLEPIE_TYPE_RSS_093', 16);
156
157 /**
158  * RSS 0.94
159  */
160 define('SIMPLEPIE_TYPE_RSS_094', 32);
161
162 /**
163  * RSS 1.0
164  */
165 define('SIMPLEPIE_TYPE_RSS_10', 64);
166
167 /**
168  * RSS 2.0
169  */
170 define('SIMPLEPIE_TYPE_RSS_20', 128);
171
172 /**
173  * RDF-based RSS
174  */
175 define('SIMPLEPIE_TYPE_RSS_RDF', 65);
176
177 /**
178  * Non-RDF-based RSS (truly intended as syndication format)
179  */
180 define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
181
182 /**
183  * All RSS
184  */
185 define('SIMPLEPIE_TYPE_RSS_ALL', 255);
186
187 /**
188  * Atom 0.3
189  */
190 define('SIMPLEPIE_TYPE_ATOM_03', 256);
191
192 /**
193  * Atom 1.0
194  */
195 define('SIMPLEPIE_TYPE_ATOM_10', 512);
196
197 /**
198  * All Atom
199  */
200 define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
201
202 /**
203  * All feed types
204  */
205 define('SIMPLEPIE_TYPE_ALL', 1023);
206
207 /**
208  * No construct
209  */
210 define('SIMPLEPIE_CONSTRUCT_NONE', 0);
211
212 /**
213  * Text construct
214  */
215 define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
216
217 /**
218  * HTML construct
219  */
220 define('SIMPLEPIE_CONSTRUCT_HTML', 2);
221
222 /**
223  * XHTML construct
224  */
225 define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
226
227 /**
228  * base64-encoded construct
229  */
230 define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
231
232 /**
233  * IRI construct
234  */
235 define('SIMPLEPIE_CONSTRUCT_IRI', 16);
236
237 /**
238  * A construct that might be HTML
239  */
240 define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
241
242 /**
243  * All constructs
244  */
245 define('SIMPLEPIE_CONSTRUCT_ALL', 63);
246
247 /**
248  * Don't change case
249  */
250 define('SIMPLEPIE_SAME_CASE', 1);
251
252 /**
253  * Change to lowercase
254  */
255 define('SIMPLEPIE_LOWERCASE', 2);
256
257 /**
258  * Change to uppercase
259  */
260 define('SIMPLEPIE_UPPERCASE', 4);
261
262 /**
263  * PCRE for HTML attributes
264  */
265 define('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  */
270 define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
271
272 /**
273  * XML Namespace
274  */
275 define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
276
277 /**
278  * Atom 1.0 Namespace
279  */
280 define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
281
282 /**
283  * Atom 0.3 Namespace
284  */
285 define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
286
287 /**
288  * RDF Namespace
289  */
290 define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
291
292 /**
293  * RSS 0.90 Namespace
294  */
295 define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
296
297 /**
298  * RSS 1.0 Namespace
299  */
300 define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
301
302 /**
303  * RSS 1.0 Content Module Namespace
304  */
305 define('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  */
311 define('SIMPLEPIE_NAMESPACE_RSS_20', '');
312
313 /**
314  * DC 1.0 Namespace
315  */
316 define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
317
318 /**
319  * DC 1.1 Namespace
320  */
321 define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
322
323 /**
324  * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
325  */
326 define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
327
328 /**
329  * GeoRSS Namespace
330  */
331 define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
332
333 /**
334  * Media RSS Namespace
335  */
336 define('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  */
341 define('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  */
346 define('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  */
351 define('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  */
356 define('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  */
361 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/');
362
363 /**
364  * iTunes RSS Namespace
365  */
366 define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
367
368 /**
369  * XHTML Namespace
370  */
371 define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
372
373 /**
374  * IANA Link Relations Registry
375  */
376 define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
377
378 /**
379  * No file source
380  */
381 define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
382
383 /**
384  * Remote file source
385  */
386 define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
387
388 /**
389  * Local file source
390  */
391 define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
392
393 /**
394  * fsockopen() file source
395  */
396 define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
397
398 /**
399  * cURL file source
400  */
401 define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
402
403 /**
404  * file_get_contents() file source
405  */
406 define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
407
408 /**
409  * SimplePie
410  *
411  * @package SimplePie
412  * @subpackage API
413  */
414 class 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  */
3070 class 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  */
3182 interface 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  */
3250 abstract 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  */
3344 class 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  */
3481 class 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  */
3620 class 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  */
4013 class 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  */
4103 class 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  */
4268 class 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  */
4385 class 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  */
4668 class 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  */
4753 class 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  */
4768 class 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  */
4878 class 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  */
5452 class 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  */
6783 class 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  */
6798 class 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                                 /* Enable Digest authentication and SSL -fox */
6859                                 curl_setopt($fp, CURLOPT_SSL_VERIFYPEER, false); 
6860                                 curl_setopt($fp, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
6861
6862                                 $this->headers = curl_exec($fp);
6863                                 if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
6864                                 {
6865                                         curl_setopt($fp, CURLOPT_ENCODING, 'none');
6866                                         $this->headers = curl_exec($fp);
6867                                 }
6868                                 if (curl_errno($fp))
6869                                 {
6870                                         $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
6871                                         $this->success = false;
6872                                 }
6873                                 else
6874                                 {
6875                                         $info = curl_getinfo($fp);
6876                                         curl_close($fp);
6877                                         $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
6878                                         $this->headers = array_pop($this->headers);
6879                                         $parser = new SimplePie_HTTP_Parser($this->headers);
6880                                         if ($parser->parse())
6881                                         {
6882                                                 $this->headers = $parser->headers;
6883                                                 $this->body = $parser->body;
6884                                                 $this->status_code = $parser->status_code;
6885                                                 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)
6886                                                 {
6887                                                         $this->redirects++;
6888                                                         $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
6889                                                         return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
6890                                                 }
6891                                         }
6892                                 }
6893                         }
6894                         else
6895                         {
6896                                 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
6897                                 $url_parts = parse_url($url);
6898                                 $socket_host = $url_parts['host'];
6899                                 if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
6900                                 {
6901                                         $socket_host = "ssl://$url_parts[host]";
6902                                         $url_parts['port'] = 443;
6903                                 }
6904                                 if (!isset($url_parts['port']))
6905                                 {
6906                                         $url_parts['port'] = 80;
6907                                 }
6908                                 $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
6909                                 if (!$fp)
6910                                 {
6911                                         $this->error = 'fsockopen error: ' . $errstr;
6912                                         $this->success = false;
6913                                 }
6914                                 else
6915                                 {
6916                                         stream_set_timeout($fp, $timeout);
6917                                         if (isset($url_parts['path']))
6918                                         {
6919                                                 if (isset($url_parts['query']))
6920                                                 {
6921                                                         $get = "$url_parts[path]?$url_parts[query]";
6922                                                 }
6923                                                 else
6924                                                 {
6925                                                         $get = $url_parts['path'];
6926                                                 }
6927                                         }
6928                                         else
6929                                         {
6930                                                 $get = '/';
6931                                         }
6932                                         $out = "GET $get HTTP/1.1\r\n";
6933                                         $out .= "Host: $url_parts[host]\r\n";
6934                                         $out .= "User-Agent: $useragent\r\n";
6935                                         if (extension_loaded('zlib'))
6936                                         {
6937                                                 $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
6938                                         }
6939
6940                                         if (isset($url_parts['user']) && isset($url_parts['pass']))
6941                                         {
6942                                                 $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
6943                                         }
6944                                         foreach ($headers as $key => $value)
6945                                         {
6946                                                 $out .= "$key: $value\r\n";
6947                                         }
6948                                         $out .= "Connection: Close\r\n\r\n";
6949                                         fwrite($fp, $out);
6950
6951                                         $info = stream_get_meta_data($fp);
6952
6953                                         $this->headers = '';
6954                                         while (!$info['eof'] && !$info['timed_out'])
6955                                         {
6956                                                 $this->headers .= fread($fp, 1160);
6957                                                 $info = stream_get_meta_data($fp);
6958                                         }
6959                                         if (!$info['timed_out'])
6960                                         {
6961                                                 $parser = new SimplePie_HTTP_Parser($this->headers);
6962                                                 if ($parser->parse())
6963                                                 {
6964                                                         $this->headers = $parser->headers;
6965                                                         $this->body = $parser->body;
6966                                                         $this->status_code = $parser->status_code;
6967                                                         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)
6968                                                         {
6969                                                                 $this->redirects++;
6970                                                                 $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
6971                                                                 return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
6972                                                         }
6973                                                         if (isset($this->headers['content-encoding']))
6974                                                         {
6975                                                                 // Hey, we act dumb elsewhere, so let's do that here too
6976                                                                 switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
6977                                                                 {
6978                                                                         case 'gzip':
6979                                                                         case 'x-gzip':
6980                                                                                 $decoder = new SimplePie_gzdecode($this->body);
6981                                                                                 if (!$decoder->parse())
6982                                                                                 {
6983                                                                                         $this->error = 'Unable to decode HTTP "gzip" stream';
6984                                                                                         $this->success = false;
6985                                                                                 }
6986                                                                                 else
6987                                                                                 {
6988                                                                                         $this->body = $decoder->data;
6989                                                                                 }
6990                                                                                 break;
6991
6992                                                                         case 'deflate':
6993                                                                                 if (($decompressed = gzinflate($this->body)) !== false)
6994                                                                                 {
6995                                                                                         $this->body = $decompressed;
6996                                                                                 }
6997                                                                                 else if (($decompressed = gzuncompress($this->body)) !== false)
6998                                                                                 {
6999                                                                                         $this->body = $decompressed;
7000                                                                                 }
7001                                                                                 else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false)
7002                                                                                 {
7003                                                                                         $this->body = $decompressed;
7004                                                                                 }
7005                                                                                 else
7006                                                                                 {
7007                                                                                         $this->error = 'Unable to decode HTTP "deflate" stream';
7008                                                                                         $this->success = false;
7009                                                                                 }
7010                                                                                 break;
7011
7012                                                                         default:
7013                                                                                 $this->error = 'Unknown content coding';
7014                                                                                 $this->success = false;
7015                                                                 }
7016                                                         }
7017                                                 }
7018                                         }
7019                                         else
7020                                         {
7021                                                 $this->error = 'fsocket timed out';
7022                                                 $this->success = false;
7023                                         }
7024                                         fclose($fp);
7025                                 }
7026                         }
7027                 }
7028                 else
7029                 {
7030                         $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
7031                         if (!$this->body = file_get_contents($url))
7032                         {
7033                                 $this->error = 'file_get_contents could not read the file';
7034                                 $this->success = false;
7035                         }
7036                 }
7037         }
7038 }
7039
7040 /**
7041  * Decode 'gzip' encoded HTTP data
7042  *
7043  * @package SimplePie
7044  * @subpackage HTTP
7045  * @link http://www.gzip.org/format.txt
7046  */
7047 class SimplePie_gzdecode
7048 {
7049         /**
7050          * Compressed data
7051          *
7052          * @access private
7053          * @var string
7054          * @see gzdecode::$data
7055          */
7056         var $compressed_data;
7057
7058         /**
7059          * Size of compressed data
7060          *
7061          * @access private
7062          * @var int
7063          */
7064         var $compressed_size;
7065
7066         /**
7067          * Minimum size of a valid gzip string
7068          *
7069          * @access private
7070          * @var int
7071          */
7072         var $min_compressed_size = 18;
7073
7074         /**
7075          * Current position of pointer
7076          *
7077          * @access private
7078          * @var int
7079          */
7080         var $position = 0;
7081
7082         /**
7083          * Flags (FLG)
7084          *
7085          * @access private
7086          * @var int
7087          */
7088         var $flags;
7089
7090         /**
7091          * Uncompressed data
7092          *
7093          * @access public
7094          * @see gzdecode::$compressed_data
7095          * @var string
7096          */
7097         var $data;
7098
7099         /**
7100          * Modified time
7101          *
7102          * @access public
7103          * @var int
7104          */
7105         var $MTIME;
7106
7107         /**
7108          * Extra Flags
7109          *
7110          * @access public
7111          * @var int
7112          */
7113         var $XFL;
7114
7115         /**
7116          * Operating System
7117          *
7118          * @access public
7119          * @var int
7120          */
7121         var $OS;
7122
7123         /**
7124          * Subfield ID 1
7125          *
7126          * @access public
7127          * @see gzdecode::$extra_field
7128          * @see gzdecode::$SI2
7129          * @var string
7130          */
7131         var $SI1;
7132
7133         /**
7134          * Subfield ID 2
7135          *
7136          * @access public
7137          * @see gzdecode::$extra_field
7138          * @see gzdecode::$SI1
7139          * @var string
7140          */
7141         var $SI2;
7142
7143         /**
7144          * Extra field content
7145          *
7146          * @access public
7147          * @see gzdecode::$SI1
7148          * @see gzdecode::$SI2
7149          * @var string
7150          */
7151         var $extra_field;
7152
7153         /**
7154          * Original filename
7155          *
7156          * @access public
7157          * @var string
7158          */
7159         var $filename;
7160
7161         /**
7162          * Human readable comment
7163          *
7164          * @access public
7165          * @var string
7166          */
7167         var $comment;
7168
7169         /**
7170          * Don't allow anything to be set
7171          *
7172          * @param string $name
7173          * @param mixed $value
7174          */
7175         public function __set($name, $value)
7176         {
7177                 trigger_error("Cannot write property $name", E_USER_ERROR);
7178         }
7179
7180         /**
7181          * Set the compressed string and related properties
7182          *
7183          * @param string $data
7184          */
7185         public function __construct($data)
7186         {
7187                 $this->compressed_data = $data;
7188                 $this->compressed_size = strlen($data);
7189         }
7190
7191         /**
7192          * Decode the GZIP stream
7193          *
7194          * @return bool Successfulness
7195          */
7196         public function parse()
7197         {
7198                 if ($this->compressed_size >= $this->min_compressed_size)
7199                 {
7200                         // Check ID1, ID2, and CM
7201                         if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
7202                         {
7203                                 return false;
7204                         }
7205
7206                         // Get the FLG (FLaGs)
7207                         $this->flags = ord($this->compressed_data[3]);
7208
7209                         // FLG bits above (1 << 4) are reserved
7210                         if ($this->flags > 0x1F)
7211                         {
7212                                 return false;
7213                         }
7214
7215                         // Advance the pointer after the above
7216                         $this->position += 4;
7217
7218                         // MTIME
7219                         $mtime = substr($this->compressed_data, $this->position, 4);
7220                         // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
7221                         if (current(unpack('S', "\x00\x01")) === 1)
7222                         {
7223                                 $mtime = strrev($mtime);
7224                         }
7225                         $this->MTIME = current(unpack('l', $mtime));
7226                         $this->position += 4;
7227
7228                         // Get the XFL (eXtra FLags)
7229                         $this->XFL = ord($this->compressed_data[$this->position++]);
7230
7231                         // Get the OS (Operating System)
7232                         $this->OS = ord($this->compressed_data[$this->position++]);
7233
7234                         // Parse the FEXTRA
7235                         if ($this->flags & 4)
7236                         {
7237                                 // Read subfield IDs
7238                                 $this->SI1 = $this->compressed_data[$this->position++];
7239                                 $this->SI2 = $this->compressed_data[$this->position++];
7240
7241                                 // SI2 set to zero is reserved for future use
7242                                 if ($this->SI2 === "\x00")
7243                                 {
7244                                         return false;
7245                                 }
7246
7247                                 // Get the length of the extra field
7248                                 $len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
7249                                 $this->position += 2;
7250
7251                                 // Check the length of the string is still valid
7252                                 $this->min_compressed_size += $len + 4;
7253                                 if ($this->compressed_size >= $this->min_compressed_size)
7254                                 {
7255                                         // Set the extra field to the given data
7256                                         $this->extra_field = substr($this->compressed_data, $this->position, $len);
7257                                         $this->position += $len;
7258                                 }
7259                                 else
7260                                 {
7261                                         return false;
7262                                 }
7263                         }
7264
7265                         // Parse the FNAME
7266                         if ($this->flags & 8)
7267                         {
7268                                 // Get the length of the filename
7269                                 $len = strcspn($this->compressed_data, "\x00", $this->position);
7270
7271                                 // Check the length of the string is still valid
7272                                 $this->min_compressed_size += $len + 1;
7273                                 if ($this->compressed_size >= $this->min_compressed_size)
7274                                 {
7275                                         // Set the original filename to the given string
7276                                         $this->filename = substr($this->compressed_data, $this->position, $len);
7277                                         $this->position += $len + 1;
7278                                 }
7279                                 else
7280                                 {
7281                                         return false;
7282                                 }
7283                         }
7284
7285                         // Parse the FCOMMENT
7286                         if ($this->flags & 16)
7287                         {
7288                                 // Get the length of the comment
7289                                 $len = strcspn($this->compressed_data, "\x00", $this->position);
7290
7291                                 // Check the length of the string is still valid
7292                                 $this->min_compressed_size += $len + 1;
7293                                 if ($this->compressed_size >= $this->min_compressed_size)
7294                                 {
7295                                         // Set the original comment to the given string
7296                                         $this->comment = substr($this->compressed_data, $this->position, $len);
7297                                         $this->position += $len + 1;
7298                                 }
7299                                 else
7300                                 {
7301                                         return false;
7302                                 }
7303                         }
7304
7305                         // Parse the FHCRC
7306                         if ($this->flags & 2)
7307                         {
7308                                 // Check the length of the string is still valid
7309                                 $this->min_compressed_size += $len + 2;
7310                                 if ($this->compressed_size >= $this->min_compressed_size)
7311                                 {
7312                                         // Read the CRC
7313                                         $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
7314
7315                                         // Check the CRC matches
7316                                         if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
7317                                         {
7318                                                 $this->position += 2;
7319                                         }
7320                                         else
7321                                         {
7322                                                 return false;
7323                                         }
7324                                 }
7325                                 else
7326                                 {
7327                                         return false;
7328                                 }
7329                         }
7330
7331                         // Decompress the actual data
7332                         if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
7333                         {
7334                                 return false;
7335                         }
7336                         else
7337                         {
7338                                 $this->position = $this->compressed_size - 8;
7339                         }
7340
7341                         // Check CRC of data
7342                         $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
7343                         $this->position += 4;
7344                         /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
7345                         {
7346                                 return false;
7347                         }*/
7348
7349                         // Check ISIZE of data
7350                         $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
7351                         $this->position += 4;
7352                         if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
7353                         {
7354                                 return false;
7355                         }
7356
7357                         // Wow, against all odds, we've actually got a valid gzip string
7358                         return true;
7359                 }
7360                 else
7361                 {
7362                         return false;
7363                 }
7364         }
7365 }
7366
7367 /**
7368  * HTTP Response Parser
7369  *
7370  * @package SimplePie
7371  * @subpackage HTTP
7372  */
7373 class SimplePie_HTTP_Parser
7374 {
7375         /**
7376          * HTTP Version
7377          *
7378          * @var float
7379          */
7380         public $http_version = 0.0;
7381
7382         /**
7383          * Status code
7384          *
7385          * @var int
7386          */
7387         public $status_code = 0;
7388
7389         /**
7390          * Reason phrase
7391          *
7392          * @var string
7393          */
7394         public $reason = '';
7395
7396         /**
7397          * Key/value pairs of the headers
7398          *
7399          * @var array
7400          */
7401         public $headers = array();
7402
7403         /**
7404          * Body of the response
7405          *
7406          * @var string
7407          */
7408         public $body = '';
7409
7410         /**
7411          * Current state of the state machine
7412          *
7413          * @var string
7414          */
7415         protected $state = 'http_version';
7416
7417         /**
7418          * Input data
7419          *
7420          * @var string
7421          */
7422         protected $data = '';
7423
7424         /**
7425          * Input data length (to avoid calling strlen() everytime this is needed)
7426          *
7427          * @var int
7428          */
7429         protected $data_length = 0;
7430
7431         /**
7432          * Current position of the pointer
7433          *
7434          * @var int
7435          */
7436         protected $position = 0;
7437
7438         /**
7439          * Name of the hedaer currently being parsed
7440          *
7441          * @var string
7442          */
7443         protected $name = '';
7444
7445         /**
7446          * Value of the hedaer currently being parsed
7447          *
7448          * @var string
7449          */
7450         protected $value = '';
7451
7452         /**
7453          * Create an instance of the class with the input data
7454          *
7455          * @param string $data Input data
7456          */
7457         public function __construct($data)
7458         {
7459                 $this->data = $data;
7460                 $this->data_length = strlen($this->data);
7461         }
7462
7463         /**
7464          * Parse the input data
7465          *
7466          * @return bool true on success, false on failure
7467          */
7468         public function parse()
7469         {
7470                 while ($this->state && $this->state !== 'emit' && $this->has_data())
7471                 {
7472                         $state = $this->state;
7473                         $this->$state();
7474                 }
7475                 $this->data = '';
7476                 if ($this->state === 'emit' || $this->state === 'body')
7477                 {
7478                         return true;
7479                 }
7480                 else
7481                 {
7482                         $this->http_version = '';
7483                         $this->status_code = '';
7484                         $this->reason = '';
7485                         $this->headers = array();
7486                         $this->body = '';
7487                         return false;
7488                 }
7489         }
7490
7491         /**
7492          * Check whether there is data beyond the pointer
7493          *
7494          * @return bool true if there is further data, false if not
7495          */
7496         protected function has_data()
7497         {
7498                 return (bool) ($this->position < $this->data_length);
7499         }
7500
7501         /**
7502          * See if the next character is LWS
7503          *
7504          * @return bool true if the next character is LWS, false if not
7505          */
7506         protected function is_linear_whitespace()
7507         {
7508                 return (bool) ($this->data[$this->position] === "\x09"
7509                         || $this->data[$this->position] === "\x20"
7510                         || ($this->data[$this->position] === "\x0A"
7511                                 && isset($this->data[$this->position + 1])
7512                                 && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
7513         }
7514
7515         /**
7516          * Parse the HTTP version
7517          */
7518         protected function http_version()
7519         {
7520                 if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
7521                 {
7522                         $len = strspn($this->data, '0123456789.', 5);
7523                         $this->http_version = substr($this->data, 5, $len);
7524                         $this->position += 5 + $len;
7525                         if (substr_count($this->http_version, '.') <= 1)
7526                         {
7527                                 $this->http_version = (float) $this->http_version;
7528                                 $this->position += strspn($this->data, "\x09\x20", $this->position);
7529                                 $this->state = 'status';
7530                         }
7531                         else
7532                         {
7533                                 $this->state = false;
7534                         }
7535                 }
7536                 else
7537                 {
7538                         $this->state = false;
7539                 }
7540         }
7541
7542         /**
7543          * Parse the status code
7544          */
7545         protected function status()
7546         {
7547                 if ($len = strspn($this->data, '0123456789', $this->position))
7548                 {
7549                         $this->status_code = (int) substr($this->data, $this->position, $len);
7550                         $this->position += $len;
7551                         $this->state = 'reason';
7552                 }
7553                 else
7554                 {
7555                         $this->state = false;
7556                 }
7557         }
7558
7559         /**
7560          * Parse the reason phrase
7561          */
7562         protected function reason()
7563         {
7564                 $len = strcspn($this->data, "\x0A", $this->position);
7565                 $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
7566                 $this->position += $len + 1;
7567                 $this->state = 'new_line';
7568         }
7569
7570         /**
7571          * Deal with a new line, shifting data around as needed
7572          */
7573         protected function new_line()
7574         {
7575                 $this->value = trim($this->value, "\x0D\x20");
7576                 if ($this->name !== '' && $this->value !== '')
7577                 {
7578                         $this->name = strtolower($this->name);
7579                         // We should only use the last Content-Type header. c.f. issue #1
7580                         if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
7581                         {
7582                                 $this->headers[$this->name] .= ', ' . $this->value;
7583                         }
7584                         else
7585                         {
7586                                 $this->headers[$this->name] = $this->value;
7587                         }
7588                 }
7589                 $this->name = '';
7590                 $this->value = '';
7591                 if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
7592                 {
7593                         $this->position += 2;
7594                         $this->state = 'body';
7595                 }
7596                 elseif ($this->data[$this->position] === "\x0A")
7597                 {
7598                         $this->position++;
7599                         $this->state = 'body';
7600                 }
7601                 else
7602                 {
7603                         $this->state = 'name';
7604                 }
7605         }
7606
7607         /**
7608          * Parse a header name
7609          */
7610         protected function name()
7611         {
7612                 $len = strcspn($this->data, "\x0A:", $this->position);
7613                 if (isset($this->data[$this->position + $len]))
7614                 {
7615                         if ($this->data[$this->position + $len] === "\x0A")
7616                         {
7617                                 $this->position += $len;
7618                                 $this->state = 'new_line';
7619                         }
7620                         else
7621                         {
7622                                 $this->name = substr($this->data, $this->position, $len);
7623                                 $this->position += $len + 1;
7624                                 $this->state = 'value';
7625                         }
7626                 }
7627                 else
7628                 {
7629                         $this->state = false;
7630                 }
7631         }
7632
7633         /**
7634          * Parse LWS, replacing consecutive LWS characters with a single space
7635          */
7636         protected function linear_whitespace()
7637         {
7638                 do
7639                 {
7640                         if (substr($this->data, $this->position, 2) === "\x0D\x0A")
7641                         {
7642                                 $this->position += 2;
7643                         }
7644                         elseif ($this->data[$this->position] === "\x0A")
7645                         {
7646                                 $this->position++;
7647                         }
7648                         $this->position += strspn($this->data, "\x09\x20", $this->position);
7649                 } while ($this->has_data() && $this->is_linear_whitespace());
7650                 $this->value .= "\x20";
7651         }
7652
7653         /**
7654          * See what state to move to while within non-quoted header values
7655          */
7656         protected function value()
7657         {
7658                 if ($this->is_linear_whitespace())
7659                 {
7660                         $this->linear_whitespace();
7661                 }
7662                 else
7663                 {
7664                         switch ($this->data[$this->position])
7665                         {
7666                                 case '"':
7667                                         // Workaround for ETags: we have to include the quotes as
7668                                         // part of the tag.
7669                                         if (strtolower($this->name) === 'etag')
7670                                         {
7671                                                 $this->value .= '"';
7672                                                 $this->position++;
7673                                                 $this->state = 'value_char';
7674                                                 break;
7675                                         }
7676                                         $this->position++;
7677                                         $this->state = 'quote';
7678                                         break;
7679
7680                                 case "\x0A":
7681                                         $this->position++;
7682                                         $this->state = 'new_line';
7683                                         break;
7684
7685                                 default:
7686                                         $this->state = 'value_char';
7687                                         break;
7688                         }
7689                 }
7690         }
7691
7692         /**
7693          * Parse a header value while outside quotes
7694          */
7695         protected function value_char()
7696         {
7697                 $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
7698                 $this->value .= substr($this->data, $this->position, $len);
7699                 $this->position += $len;
7700                 $this->state = 'value';
7701         }
7702
7703         /**
7704          * See what state to move to while within quoted header values
7705          */
7706         protected function quote()
7707         {
7708                 if ($this->is_linear_whitespace())
7709                 {
7710                         $this->linear_whitespace();
7711                 }
7712                 else
7713                 {
7714                         switch ($this->data[$this->position])
7715                         {
7716                                 case '"':
7717                                         $this->position++;
7718                                         $this->state = 'value';
7719                                         break;
7720
7721                                 case "\x0A":
7722                                         $this->position++;
7723                                         $this->state = 'new_line';
7724                                         break;
7725
7726                                 case '\\':
7727                                         $this->position++;
7728                                         $this->state = 'quote_escaped';
7729                                         break;
7730
7731                                 default:
7732                                         $this->state = 'quote_char';
7733                                         break;
7734                         }
7735                 }
7736         }
7737
7738         /**
7739          * Parse a header value while within quotes
7740          */
7741         protected function quote_char()
7742         {
7743                 $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
7744                 $this->value .= substr($this->data, $this->position, $len);
7745                 $this->position += $len;
7746                 $this->state = 'value';
7747         }
7748
7749         /**
7750          * Parse an escaped character within quotes
7751          */
7752         protected function quote_escaped()
7753         {
7754                 $this->value .= $this->data[$this->position];
7755                 $this->position++;
7756                 $this->state = 'quote';
7757         }
7758
7759         /**
7760          * Parse the body
7761          */
7762         protected function body()
7763         {
7764                 $this->body = substr($this->data, $this->position);
7765                 if (!empty($this->headers['transfer-encoding']))
7766                 {
7767                         unset($this->headers['transfer-encoding']);
7768                         $this->state = 'chunked';
7769                 }
7770                 else
7771                 {
7772                         $this->state = 'emit';
7773                 }
7774         }
7775
7776         /**
7777          * Parsed a "Transfer-Encoding: chunked" body
7778          */
7779         protected function chunked()
7780         {
7781                 if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
7782                 {
7783                         $this->state = 'emit';
7784                         return;
7785                 }
7786
7787                 $decoded = '';
7788                 $encoded = $this->body;
7789
7790                 while (true)
7791                 {
7792                         $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
7793                         if (!$is_chunked)
7794                         {
7795                                 // Looks like it's not chunked after all
7796                                 $this->state = 'emit';
7797                                 return;
7798                         }
7799
7800                         $length = hexdec(trim($matches[1]));
7801                         if ($length === 0)
7802                         {
7803                                 // Ignore trailer headers
7804                                 $this->state = 'emit';
7805                                 $this->body = $decoded;
7806                                 return;
7807                         }
7808
7809                         $chunk_length = strlen($matches[0]);
7810                         $decoded .= $part = substr($encoded, $chunk_length, $length);
7811                         $encoded = substr($encoded, $chunk_length + $length + 2);
7812
7813                         if (trim($encoded) === '0' || empty($encoded))
7814                         {
7815                                 $this->state = 'emit';
7816                                 $this->body = $decoded;
7817                                 return;
7818                         }
7819                 }
7820         }
7821 }
7822
7823 /**
7824  * IRI parser/serialiser/normaliser
7825  *
7826  * @package SimplePie
7827  * @subpackage HTTP
7828  * @author Geoffrey Sneddon
7829  * @author Steve Minutillo
7830  * @author Ryan McCue
7831  * @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue
7832  * @license http://www.opensource.org/licenses/bsd-license.php
7833  */
7834 class SimplePie_IRI
7835 {
7836         /**
7837          * Scheme
7838          *
7839          * @var string
7840          */
7841         protected $scheme = null;
7842
7843         /**
7844          * User Information
7845          *
7846          * @var string
7847          */
7848         protected $iuserinfo = null;
7849
7850         /**
7851          * ihost
7852          *
7853          * @var string
7854          */
7855         protected $ihost = null;
7856
7857         /**
7858          * Port
7859          *
7860          * @var string
7861          */
7862         protected $port = null;
7863
7864         /**
7865          * ipath
7866          *
7867          * @var string
7868          */
7869         protected $ipath = '';
7870
7871         /**
7872          * iquery
7873          *
7874          * @var string
7875          */
7876         protected $iquery = null;
7877
7878         /**
7879          * ifragment
7880          *
7881          * @var string
7882          */
7883         protected $ifragment = null;
7884
7885         /**
7886          * Normalization database
7887          *
7888          * Each key is the scheme, each value is an array with each key as the IRI
7889          * part and value as the default value for that part.
7890          */
7891         protected $normalization = array(
7892                 'acap' => array(
7893                         'port' => 674
7894                 ),
7895                 'dict' => array(
7896                         'port' => 2628
7897                 ),
7898                 'file' => array(
7899                         'ihost' => 'localhost'
7900                 ),
7901                 'http' => array(
7902                         'port' => 80,
7903                         'ipath' => '/'
7904                 ),
7905                 'https' => array(
7906                         'port' => 443,
7907                         'ipath' => '/'
7908                 ),
7909         );
7910
7911         /**
7912          * Return the entire IRI when you try and read the object as a string
7913          *
7914          * @return string
7915          */
7916         public function __toString()
7917         {
7918                 return $this->get_iri();
7919         }
7920
7921         /**
7922          * Overload __set() to provide access via properties
7923          *
7924          * @param string $name Property name
7925          * @param mixed $value Property value
7926          */
7927         public function __set($name, $value)
7928         {
7929                 if (method_exists($this, 'set_' . $name))
7930                 {
7931                         call_user_func(array($this, 'set_' . $name), $value);
7932                 }
7933                 elseif (
7934                            $name === 'iauthority'
7935                         || $name === 'iuserinfo'
7936                         || $name === 'ihost'
7937                         || $name === 'ipath'
7938                         || $name === 'iquery'
7939                         || $name === 'ifragment'
7940                 )
7941                 {
7942                         call_user_func(array($this, 'set_' . substr($name, 1)), $value);
7943                 }
7944         }
7945
7946         /**
7947          * Overload __get() to provide access via properties
7948          *
7949          * @param string $name Property name
7950          * @return mixed
7951          */
7952         public function __get($name)
7953         {
7954                 // isset() returns false for null, we don't want to do that
7955                 // Also why we use array_key_exists below instead of isset()
7956                 $props = get_object_vars($this);
7957
7958                 if (
7959                         $name === 'iri' ||
7960                         $name === 'uri' ||
7961                         $name === 'iauthority' ||
7962                         $name === 'authority'
7963                 )
7964                 {
7965                         $return = $this->{"get_$name"}();
7966                 }
7967                 elseif (array_key_exists($name, $props))
7968                 {
7969                         $return = $this->$name;
7970                 }
7971                 // host -> ihost
7972                 elseif (($prop = 'i' . $name) && array_key_exists($prop, $props))
7973                 {
7974                         $name = $prop;
7975                         $return = $this->$prop;
7976                 }
7977                 // ischeme -> scheme
7978                 elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props))
7979                 {
7980                         $name = $prop;
7981                         $return = $this->$prop;
7982                 }
7983                 else
7984                 {
7985                         trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
7986                         $return = null;
7987                 }
7988
7989                 if ($return === null && isset($this->normalization[$this->scheme][$name]))
7990                 {
7991                         return $this->normalization[$this->scheme][$name];
7992                 }
7993                 else
7994                 {
7995                         return $return;
7996                 }
7997         }
7998
7999         /**
8000          * Overload __isset() to provide access via properties
8001          *
8002          * @param string $name Property name
8003          * @return bool
8004          */
8005         public function __isset($name)
8006         {
8007                 if (method_exists($this, 'get_' . $name) || isset($this->$name))
8008                 {
8009                         return true;
8010                 }
8011                 else
8012                 {
8013                         return false;
8014                 }
8015         }
8016
8017         /**
8018          * Overload __unset() to provide access via properties
8019          *
8020          * @param string $name Property name
8021          */
8022         public function __unset($name)
8023         {
8024                 if (method_exists($this, 'set_' . $name))
8025                 {
8026                         call_user_func(array($this, 'set_' . $name), '');
8027                 }
8028         }
8029
8030         /**
8031          * Create a new IRI object, from a specified string
8032          *
8033          * @param string $iri
8034          */
8035         public function __construct($iri = null)
8036         {
8037                 $this->set_iri($iri);
8038         }
8039
8040         /**
8041          * Create a new IRI object by resolving a relative IRI
8042          *
8043          * Returns false if $base is not absolute, otherwise an IRI.
8044          *
8045          * @param IRI|string $base (Absolute) Base IRI
8046          * @param IRI|string $relative Relative IRI
8047          * @return IRI|false
8048          */
8049         public static function absolutize($base, $relative)
8050         {
8051                 if (!($relative instanceof SimplePie_IRI))
8052                 {
8053                         $relative = new SimplePie_IRI($relative);
8054                 }
8055                 if (!$relative->is_valid())
8056                 {
8057                         return false;
8058                 }
8059                 elseif ($relative->scheme !== null)
8060                 {
8061                         return clone $relative;
8062                 }
8063                 else
8064                 {
8065                         if (!($base instanceof SimplePie_IRI))
8066                         {
8067                                 $base = new SimplePie_IRI($base);
8068                         }
8069                         if ($base->scheme !== null && $base->is_valid())
8070                         {
8071                                 if ($relative->get_iri() !== '')
8072                                 {
8073                                         if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null)
8074                                         {
8075                                                 $target = clone $relative;
8076                                                 $target->scheme = $base->scheme;
8077                                         }
8078                                         else
8079                                         {
8080                                                 $target = new SimplePie_IRI;
8081                                                 $target->scheme = $base->scheme;
8082                                                 $target->iuserinfo = $base->iuserinfo;
8083                                                 $target->ihost = $base->ihost;
8084                                                 $target->port = $base->port;
8085                                                 if ($relative->ipath !== '')
8086                                                 {
8087                                                         if ($relative->ipath[0] === '/')
8088                                                         {
8089                                                                 $target->ipath = $relative->ipath;
8090                                                         }
8091                                                         elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '')
8092                                                         {
8093                                                                 $target->ipath = '/' . $relative->ipath;
8094                                                         }
8095                                                         elseif (($last_segment = strrpos($base->ipath, '/')) !== false)
8096                                                         {
8097                                                                 $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
8098                                                         }
8099                                                         else
8100                                                         {
8101                                                                 $target->ipath = $relative->ipath;
8102                                                         }
8103                                                         $target->ipath = $target->remove_dot_segments($target->ipath);
8104                                                         $target->iquery = $relative->iquery;
8105                                                 }
8106                                                 else
8107                                                 {
8108                                                         $target->ipath = $base->ipath;
8109                                                         if ($relative->iquery !== null)
8110                                                         {
8111                                                                 $target->iquery = $relative->iquery;
8112                                                         }
8113                                                         elseif ($base->iquery !== null)
8114                                                         {
8115                                                                 $target->iquery = $base->iquery;
8116                                                         }
8117                                                 }
8118                                                 $target->ifragment = $relative->ifragment;
8119                                         }
8120                                 }
8121                                 else
8122                                 {
8123                                         $target = clone $base;
8124                                         $target->ifragment = null;
8125                                 }
8126                                 $target->scheme_normalization();
8127                                 return $target;
8128                         }
8129                         else
8130                         {
8131                                 return false;
8132                         }
8133                 }
8134         }
8135
8136         /**
8137          * Parse an IRI into scheme/authority/path/query/fragment segments
8138          *
8139          * @param string $iri
8140          * @return array
8141          */
8142         protected function parse_iri($iri)
8143         {
8144                 $iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
8145                 if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match))
8146                 {
8147                         if ($match[1] === '')
8148                         {
8149                                 $match['scheme'] = null;
8150                         }
8151                         if (!isset($match[3]) || $match[3] === '')
8152                         {
8153                                 $match['authority'] = null;
8154                         }
8155                         if (!isset($match[5]))
8156                         {
8157                                 $match['path'] = '';
8158                         }
8159                         if (!isset($match[6]) || $match[6] === '')
8160                         {
8161                                 $match['query'] = null;
8162                         }
8163                         if (!isset($match[8]) || $match[8] === '')
8164                         {
8165                                 $match['fragment'] = null;
8166                         }
8167                         return $match;
8168                 }
8169                 else
8170                 {
8171                         // This can occur when a paragraph is accidentally parsed as a URI
8172                         return false;
8173                 }
8174         }
8175
8176         /**
8177          * Remove dot segments from a path
8178          *
8179          * @param string $input
8180          * @return string
8181          */
8182         protected function remove_dot_segments($input)
8183         {
8184                 $output = '';
8185                 while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
8186                 {
8187                         // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
8188                         if (strpos($input, '../') === 0)
8189                         {
8190                                 $input = substr($input, 3);
8191                         }
8192                         elseif (strpos($input, './') === 0)
8193                         {
8194                                 $input = substr($input, 2);
8195                         }
8196                         // 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,
8197                         elseif (strpos($input, '/./') === 0)
8198                         {
8199                                 $input = substr($input, 2);
8200                         }
8201                         elseif ($input === '/.')
8202                         {
8203                                 $input = '/';
8204                         }
8205                         // 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,
8206                         elseif (strpos($input, '/../') === 0)
8207                         {
8208                                 $input = substr($input, 3);
8209                                 $output = substr_replace($output, '', strrpos($output, '/'));
8210                         }
8211                         elseif ($input === '/..')
8212                         {
8213                                 $input = '/';
8214                                 $output = substr_replace($output, '', strrpos($output, '/'));
8215                         }
8216                         // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
8217                         elseif ($input === '.' || $input === '..')
8218                         {
8219                                 $input = '';
8220                         }
8221                         // 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
8222                         elseif (($pos = strpos($input, '/', 1)) !== false)
8223                         {
8224                                 $output .= substr($input, 0, $pos);
8225                                 $input = substr_replace($input, '', 0, $pos);
8226                         }
8227                         else
8228                         {
8229                                 $output .= $input;
8230                                 $input = '';
8231                         }
8232                 }
8233                 return $output . $input;
8234         }
8235
8236         /**
8237          * Replace invalid character with percent encoding
8238          *
8239          * @param string $string Input string
8240          * @param string $extra_chars Valid characters not in iunreserved or
8241          *                            iprivate (this is ASCII-only)
8242          * @param bool $iprivate Allow iprivate
8243          * @return string
8244          */
8245         protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
8246         {
8247                 // Normalize as many pct-encoded sections as possible
8248                 $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
8249
8250                 // Replace invalid percent characters
8251                 $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
8252
8253                 // Add unreserved and % to $extra_chars (the latter is safe because all
8254                 // pct-encoded sections are now valid).
8255                 $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
8256
8257                 // Now replace any bytes that aren't allowed with their pct-encoded versions
8258                 $position = 0;
8259                 $strlen = strlen($string);
8260                 while (($position += strspn($string, $extra_chars, $position)) < $strlen)
8261                 {
8262                         $value = ord($string[$position]);
8263
8264                         // Start position
8265                         $start = $position;
8266
8267                         // By default we are valid
8268                         $valid = true;
8269
8270                         // No one byte sequences are valid due to the while.
8271                         // Two byte sequence:
8272                         if (($value & 0xE0) === 0xC0)
8273                         {
8274                                 $character = ($value & 0x1F) << 6;
8275                                 $length = 2;
8276                                 $remaining = 1;
8277                         }
8278                         // Three byte sequence:
8279                         elseif (($value & 0xF0) === 0xE0)
8280                         {
8281                                 $character = ($value & 0x0F) << 12;
8282                                 $length = 3;
8283                                 $remaining = 2;
8284                         }
8285                         // Four byte sequence:
8286                         elseif (($value & 0xF8) === 0xF0)
8287                         {
8288                                 $character = ($value & 0x07) << 18;
8289                                 $length = 4;
8290                                 $remaining = 3;
8291                         }
8292                         // Invalid byte:
8293                         else
8294                         {
8295                                 $valid = false;
8296                                 $length = 1;
8297                                 $remaining = 0;
8298                         }
8299
8300                         if ($remaining)
8301                         {
8302                                 if ($position + $length <= $strlen)
8303                                 {
8304                                         for ($position++; $remaining; $position++)
8305                                         {
8306                                                 $value = ord($string[$position]);
8307
8308                                                 // Check that the byte is valid, then add it to the character:
8309                                                 if (($value & 0xC0) === 0x80)
8310                                                 {
8311                                                         $character |= ($value & 0x3F) << (--$remaining * 6);
8312                                                 }
8313                                                 // If it is invalid, count the sequence as invalid and reprocess the current byte:
8314                                                 else
8315                                                 {
8316                                                         $valid = false;
8317                                                         $position--;
8318                                                         break;
8319                                                 }
8320                                         }
8321                                 }
8322                                 else
8323                                 {
8324                                         $position = $strlen - 1;
8325                                         $valid = false;
8326                                 }
8327                         }
8328
8329                         // Percent encode anything invalid or not in ucschar
8330                         if (
8331                                 // Invalid sequences
8332                                 !$valid
8333                                 // Non-shortest form sequences are invalid
8334                                 || $length > 1 && $character <= 0x7F
8335                                 || $length > 2 && $character <= 0x7FF
8336                                 || $length > 3 && $character <= 0xFFFF
8337                                 // Outside of range of ucschar codepoints
8338                                 // Noncharacters
8339                                 || ($character & 0xFFFE) === 0xFFFE
8340                                 || $character >= 0xFDD0 && $character <= 0xFDEF
8341                                 || (
8342                                         // Everything else not in ucschar
8343                                            $character > 0xD7FF && $character < 0xF900
8344                                         || $character < 0xA0
8345                                         || $character > 0xEFFFD
8346                                 )
8347                                 && (
8348                                         // Everything not in iprivate, if it applies
8349                                            !$iprivate
8350                                         || $character < 0xE000
8351                                         || $character > 0x10FFFD
8352                                 )
8353                         )
8354                         {
8355                                 // If we were a character, pretend we weren't, but rather an error.
8356                                 if ($valid)
8357                                         $position--;
8358
8359                                 for ($j = $start; $j <= $position; $j++)
8360                                 {
8361                                         $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
8362                                         $j += 2;
8363                                         $position += 2;
8364                                         $strlen += 2;
8365                                 }
8366                         }
8367                 }
8368
8369                 return $string;
8370         }
8371
8372         /**
8373          * Callback function for preg_replace_callback.
8374          *
8375          * Removes sequences of percent encoded bytes that represent UTF-8
8376          * encoded characters in iunreserved
8377          *
8378          * @param array $match PCRE match
8379          * @return string Replacement
8380          */
8381         protected function remove_iunreserved_percent_encoded($match)
8382         {
8383                 // As we just have valid percent encoded sequences we can just explode
8384                 // and ignore the first member of the returned array (an empty string).
8385                 $bytes = explode('%', $match[0]);
8386
8387                 // Initialize the new string (this is what will be returned) and that
8388                 // there are no bytes remaining in the current sequence (unsurprising
8389                 // at the first byte!).
8390                 $string = '';
8391                 $remaining = 0;
8392
8393                 // Loop over each and every byte, and set $value to its value
8394                 for ($i = 1, $len = count($bytes); $i < $len; $i++)
8395                 {
8396                         $value = hexdec($bytes[$i]);
8397
8398                         // If we're the first byte of sequence:
8399                         if (!$remaining)
8400                         {
8401                                 // Start position
8402                                 $start = $i;
8403
8404                                 // By default we are valid
8405                                 $valid = true;
8406
8407                                 // One byte sequence:
8408                                 if ($value <= 0x7F)
8409                                 {
8410                                         $character = $value;
8411                                         $length = 1;
8412                                 }
8413                                 // Two byte sequence:
8414                                 elseif (($value & 0xE0) === 0xC0)
8415                                 {
8416                                         $character = ($value & 0x1F) << 6;
8417                                         $length = 2;
8418                                         $remaining = 1;
8419                                 }
8420                                 // Three byte sequence:
8421                                 elseif (($value & 0xF0) === 0xE0)
8422                                 {
8423                                         $character = ($value & 0x0F) << 12;
8424                                         $length = 3;
8425                                         $remaining = 2;
8426                                 }
8427                                 // Four byte sequence:
8428                                 elseif (($value & 0xF8) === 0xF0)
8429                                 {
8430                                         $character = ($value & 0x07) << 18;
8431                                         $length = 4;
8432                                         $remaining = 3;
8433                                 }
8434                                 // Invalid byte:
8435                                 else
8436                                 {
8437                                         $valid = false;
8438                                         $remaining = 0;
8439                                 }
8440                         }
8441                         // Continuation byte:
8442                         else
8443                         {
8444                                 // Check that the byte is valid, then add it to the character:
8445                                 if (($value & 0xC0) === 0x80)
8446                                 {
8447                                         $remaining--;
8448                                         $character |= ($value & 0x3F) << ($remaining * 6);
8449                                 }
8450                                 // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
8451                                 else
8452                                 {
8453                                         $valid = false;
8454                                         $remaining = 0;
8455                                         $i--;
8456                                 }
8457                         }
8458
8459                         // If we've reached the end of the current byte sequence, append it to Unicode::$data
8460                         if (!$remaining)
8461                         {
8462                                 // Percent encode anything invalid or not in iunreserved
8463                                 if (
8464                                         // Invalid sequences
8465                                         !$valid
8466                                         // Non-shortest form sequences are invalid
8467                                         || $length > 1 && $character <= 0x7F
8468                                         || $length > 2 && $character <= 0x7FF
8469                                         || $length > 3 && $character <= 0xFFFF
8470                                         // Outside of range of iunreserved codepoints
8471                                         || $character < 0x2D
8472                                         || $character > 0xEFFFD
8473                                         // Noncharacters
8474                                         || ($character & 0xFFFE) === 0xFFFE
8475                                         || $character >= 0xFDD0 && $character <= 0xFDEF
8476                                         // Everything else not in iunreserved (this is all BMP)
8477                                         || $character === 0x2F
8478                                         || $character > 0x39 && $character < 0x41
8479                                         || $character > 0x5A && $character < 0x61
8480                                         || $character > 0x7A && $character < 0x7E
8481                                         || $character > 0x7E && $character < 0xA0
8482                                         || $character > 0xD7FF && $character < 0xF900
8483                                 )
8484                                 {
8485                                         for ($j = $start; $j <= $i; $j++)
8486                                         {
8487                                                 $string .= '%' . strtoupper($bytes[$j]);
8488                                         }
8489                                 }
8490                                 else
8491                                 {
8492                                         for ($j = $start; $j <= $i; $j++)
8493                                         {
8494                                                 $string .= chr(hexdec($bytes[$j]));
8495                                         }
8496                                 }
8497                         }
8498                 }
8499
8500                 // If we have any bytes left over they are invalid (i.e., we are
8501                 // mid-way through a multi-byte sequence)
8502                 if ($remaining)
8503                 {
8504                         for ($j = $start; $j < $len; $j++)
8505                         {
8506                                 $string .= '%' . strtoupper($bytes[$j]);
8507                         }
8508                 }
8509
8510                 return $string;
8511         }
8512
8513         protected function scheme_normalization()
8514         {
8515                 if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo'])
8516                 {
8517                         $this->iuserinfo = null;
8518                 }
8519                 if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost'])
8520                 {
8521                         $this->ihost = null;
8522                 }
8523                 if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port'])
8524                 {
8525                         $this->port = null;
8526                 }
8527                 if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath'])
8528                 {
8529                         $this->ipath = '';
8530                 }
8531                 if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery'])
8532                 {
8533                         $this->iquery = null;
8534                 }
8535                 if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment'])
8536                 {
8537                         $this->ifragment = null;
8538                 }
8539         }
8540
8541         /**
8542          * Check if the object represents a valid IRI. This needs to be done on each
8543          * call as some things change depending on another part of the IRI.
8544          *
8545          * @return bool
8546          */
8547         public function is_valid()
8548         {
8549                 $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null;
8550                 if ($this->ipath !== '' &&
8551                         (
8552                                 $isauthority && (
8553                                         $this->ipath[0] !== '/' ||
8554                                         substr($this->ipath, 0, 2) === '//'
8555                                 ) ||
8556                                 (
8557                                         $this->scheme === null &&
8558                                         !$isauthority &&
8559                                         strpos($this->ipath, ':') !== false &&
8560                                         (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/'))
8561                                 )
8562                         )
8563                 )
8564                 {
8565                         return false;
8566                 }
8567
8568                 return true;
8569         }
8570
8571         /**
8572          * Set the entire IRI. Returns true on success, false on failure (if there
8573          * are any invalid characters).
8574          *
8575          * @param string $iri
8576          * @return bool
8577          */
8578         public function set_iri($iri)
8579         {
8580                 static $cache;
8581                 if (!$cache)
8582                 {
8583                         $cache = array();
8584                 }
8585
8586                 if ($iri === null)
8587                 {
8588                         return true;
8589                 }
8590                 elseif (isset($cache[$iri]))
8591                 {
8592                         list($this->scheme,
8593                                  $this->iuserinfo,
8594                                  $this->ihost,
8595                                  $this->port,
8596                                  $this->ipath,
8597                                  $this->iquery,
8598                                  $this->ifragment,
8599                                  $return) = $cache[$iri];
8600                         return $return;
8601                 }
8602                 else
8603                 {
8604                         $parsed = $this->parse_iri((string) $iri);
8605                         if (!$parsed)
8606                         {
8607                                 return false;
8608                         }
8609
8610                         $return = $this->set_scheme($parsed['scheme'])
8611                                 && $this->set_authority($parsed['authority'])
8612                                 && $this->set_path($parsed['path'])
8613                                 && $this->set_query($parsed['query'])
8614                                 && $this->set_fragment($parsed['fragment']);
8615
8616                         $cache[$iri] = array($this->scheme,
8617                                                                  $this->iuserinfo,
8618                                                                  $this->ihost,
8619                                                                  $this->port,
8620                                                                  $this->ipath,
8621                                                                  $this->iquery,
8622                                                                  $this->ifragment,
8623                                                                  $return);
8624                         return $return;
8625                 }
8626         }
8627
8628         /**
8629          * Set the scheme. Returns true on success, false on failure (if there are
8630          * any invalid characters).
8631          *
8632          * @param string $scheme
8633          * @return bool
8634          */
8635         public function set_scheme($scheme)
8636         {
8637                 if ($scheme === null)
8638                 {
8639                         $this->scheme = null;
8640                 }
8641                 elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme))
8642                 {
8643                         $this->scheme = null;
8644                         return false;
8645                 }
8646                 else
8647                 {
8648                         $this->scheme = strtolower($scheme);
8649                 }
8650                 return true;
8651         }
8652
8653         /**
8654          * Set the authority. Returns true on success, false on failure (if there are
8655          * any invalid characters).
8656          *
8657          * @param string $authority
8658          * @return bool
8659          */
8660         public function set_authority($authority)
8661         {
8662                 static $cache;
8663                 if (!$cache)
8664                         $cache = array();
8665
8666                 if ($authority === null)
8667                 {
8668                         $this->iuserinfo = null;
8669                         $this->ihost = null;
8670                         $this->port = null;
8671                         return true;
8672                 }
8673                 elseif (isset($cache[$authority]))
8674                 {
8675                         list($this->iuserinfo,
8676                                  $this->ihost,
8677                                  $this->port,
8678                                  $return) = $cache[$authority];
8679
8680                         return $return;
8681                 }
8682                 else
8683                 {
8684                         $remaining = $authority;
8685                         if (($iuserinfo_end = strrpos($remaining, '@')) !== false)
8686                         {
8687                                 $iuserinfo = substr($remaining, 0, $iuserinfo_end);
8688                                 $remaining = substr($remaining, $iuserinfo_end + 1);
8689                         }
8690                         else
8691                         {
8692                                 $iuserinfo = null;
8693                         }
8694                         if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false)
8695                         {
8696                                 if (($port = substr($remaining, $port_start + 1)) === false)
8697                                 {
8698                                         $port = null;
8699                                 }
8700                                 $remaining = substr($remaining, 0, $port_start);
8701                         }
8702                         else
8703                         {
8704                                 $port = null;
8705                         }
8706
8707                         $return = $this->set_userinfo($iuserinfo) &&
8708                                           $this->set_host($remaining) &&
8709                                           $this->set_port($port);
8710
8711                         $cache[$authority] = array($this->iuserinfo,
8712                                                                            $this->ihost,
8713                                                                            $this->port,
8714                                                                            $return);
8715
8716                         return $return;
8717                 }
8718         }
8719
8720         /**
8721          * Set the iuserinfo.
8722          *
8723          * @param string $iuserinfo
8724          * @return bool
8725          */
8726         public function set_userinfo($iuserinfo)
8727         {
8728                 if ($iuserinfo === null)
8729                 {
8730                         $this->iuserinfo = null;
8731                 }
8732                 else
8733                 {
8734                         $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
8735                         $this->scheme_normalization();
8736                 }
8737
8738                 return true;
8739         }
8740
8741         /**
8742          * Set the ihost. Returns true on success, false on failure (if there are
8743          * any invalid characters).
8744          *
8745          * @param string $ihost
8746          * @return bool
8747          */
8748         public function set_host($ihost)
8749         {
8750                 if ($ihost === null)
8751                 {
8752                         $this->ihost = null;
8753                         return true;
8754                 }
8755                 elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']')
8756                 {
8757                         if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1)))
8758                         {
8759                                 $this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']';
8760                         }
8761                         else
8762                         {
8763                                 $this->ihost = null;
8764                                 return false;
8765                         }
8766                 }
8767                 else
8768                 {
8769                         $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
8770
8771                         // Lowercase, but ignore pct-encoded sections (as they should
8772                         // remain uppercase). This must be done after the previous step
8773                         // as that can add unescaped characters.
8774                         $position = 0;
8775                         $strlen = strlen($ihost);
8776                         while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen)
8777                         {
8778                                 if ($ihost[$position] === '%')
8779                                 {
8780                                         $position += 3;
8781                                 }
8782                                 else
8783                                 {
8784                                         $ihost[$position] = strtolower($ihost[$position]);
8785                                         $position++;
8786                                 }
8787                         }
8788
8789                         $this->ihost = $ihost;
8790                 }
8791
8792                 $this->scheme_normalization();
8793
8794                 return true;
8795         }
8796
8797         /**
8798          * Set the port. Returns true on success, false on failure (if there are
8799          * any invalid characters).
8800          *
8801          * @param string $port
8802          * @return bool
8803          */
8804         public function set_port($port)
8805         {
8806                 if ($port === null)
8807                 {
8808                         $this->port = null;
8809                         return true;
8810                 }
8811                 elseif (strspn($port, '0123456789') === strlen($port))
8812                 {
8813                         $this->port = (int) $port;
8814                         $this->scheme_normalization();
8815                         return true;
8816                 }
8817                 else
8818                 {
8819                         $this->port = null;
8820                         return false;
8821                 }
8822         }
8823
8824         /**
8825          * Set the ipath.
8826          *
8827          * @param string $ipath
8828          * @return bool
8829          */
8830         public function set_path($ipath)
8831         {
8832                 static $cache;
8833                 if (!$cache)
8834                 {
8835                         $cache = array();
8836                 }
8837
8838                 $ipath = (string) $ipath;
8839
8840                 if (isset($cache[$ipath]))
8841                 {
8842                         $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
8843                 }
8844                 else
8845                 {
8846                         $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
8847                         $removed = $this->remove_dot_segments($valid);
8848
8849                         $cache[$ipath] = array($valid, $removed);
8850                         $this->ipath =  ($this->scheme !== null) ? $removed : $valid;
8851                 }
8852
8853                 $this->scheme_normalization();
8854                 return true;
8855         }
8856
8857         /**
8858          * Set the iquery.
8859          *
8860          * @param string $iquery
8861          * @return bool
8862          */
8863         public function set_query($iquery)
8864         {
8865                 if ($iquery === null)
8866                 {
8867                         $this->iquery = null;
8868                 }
8869                 else
8870                 {
8871                         $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
8872                         $this->scheme_normalization();
8873                 }
8874                 return true;
8875         }
8876
8877         /**
8878          * Set the ifragment.
8879          *
8880          * @param string $ifragment
8881          * @return bool
8882          */
8883         public function set_fragment($ifragment)
8884         {
8885                 if ($ifragment === null)
8886                 {
8887                         $this->ifragment = null;
8888                 }
8889                 else
8890                 {
8891                         $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
8892                         $this->scheme_normalization();
8893                 }
8894                 return true;
8895         }
8896
8897         /**
8898          * Convert an IRI to a URI (or parts thereof)
8899          *
8900          * @return string
8901          */
8902         public function to_uri($string)
8903         {
8904                 static $non_ascii;
8905                 if (!$non_ascii)
8906                 {
8907                         $non_ascii = implode('', range("\x80", "\xFF"));
8908                 }
8909
8910                 $position = 0;
8911                 $strlen = strlen($string);
8912                 while (($position += strcspn($string, $non_ascii, $position)) < $strlen)
8913                 {
8914                         $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
8915                         $position += 3;
8916                         $strlen += 2;
8917                 }
8918
8919                 return $string;
8920         }
8921
8922         /**
8923          * Get the complete IRI
8924          *
8925          * @return string
8926          */
8927         public function get_iri()
8928         {
8929                 if (!$this->is_valid())
8930                 {
8931                         return false;
8932                 }
8933
8934                 $iri = '';
8935                 if ($this->scheme !== null)
8936                 {
8937                         $iri .= $this->scheme . ':';
8938                 }
8939                 if (($iauthority = $this->get_iauthority()) !== null)
8940                 {
8941                         $iri .= '//' . $iauthority;
8942                 }
8943                 if ($this->ipath !== '')
8944                 {
8945                         $iri .= $this->ipath;
8946                 }
8947                 elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '')
8948                 {
8949                         $iri .= $this->normalization[$this->scheme]['ipath'];
8950                 }
8951                 if ($this->iquery !== null)
8952                 {
8953                         $iri .= '?' . $this->iquery;
8954                 }
8955                 if ($this->ifragment !== null)
8956                 {
8957                         $iri .= '#' . $this->ifragment;
8958                 }
8959
8960                 return $iri;
8961         }
8962
8963         /**
8964          * Get the complete URI
8965          *
8966          * @return string
8967          */
8968         public function get_uri()
8969         {
8970                 return $this->to_uri($this->get_iri());
8971         }
8972
8973         /**
8974          * Get the complete iauthority
8975          *
8976          * @return string
8977          */
8978         protected function get_iauthority()
8979         {
8980                 if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null)
8981                 {
8982                         $iauthority = '';
8983                         if ($this->iuserinfo !== null)
8984                         {
8985                                 $iauthority .= $this->iuserinfo . '@';
8986                         }
8987                         if ($this->ihost !== null)
8988                         {
8989                                 $iauthority .= $this->ihost;
8990                         }
8991                         if ($this->port !== null)
8992                         {
8993                                 $iauthority .= ':' . $this->port;
8994                         }
8995                         return $iauthority;
8996                 }
8997                 else
8998                 {
8999                         return null;
9000                 }
9001         }
9002
9003         /**
9004          * Get the complete authority
9005          *
9006          * @return string
9007          */
9008         protected function get_authority()
9009         {
9010                 $iauthority = $this->get_iauthority();
9011                 if (is_string($iauthority))
9012                         return $this->to_uri($iauthority);
9013                 else
9014                         return $iauthority;
9015         }
9016 }
9017
9018 /**
9019  * Manages all item-related data
9020  *
9021  * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()}
9022  *
9023  * This class can be overloaded with {@see SimplePie::set_item_class()}
9024  *
9025  * @package SimplePie
9026  * @subpackage API
9027  */
9028 class SimplePie_Item
9029 {
9030         /**
9031          * Parent feed
9032          *
9033          * @access private
9034          * @var SimplePie
9035          */
9036         var $feed;
9037
9038         /**
9039          * Raw data
9040          *
9041          * @access private
9042          * @var array
9043          */
9044         var $data = array();
9045
9046         /**
9047          * Registry object
9048          *
9049          * @see set_registry
9050          * @var SimplePie_Registry
9051          */
9052         protected $registry;
9053
9054         /**
9055          * Create a new item object
9056          *
9057          * This is usually used by {@see SimplePie::get_items} and
9058          * {@see SimplePie::get_item}. Avoid creating this manually.
9059          *
9060          * @param SimplePie $feed Parent feed
9061          * @param array $data Raw data
9062          */
9063         public function __construct($feed, $data)
9064         {
9065                 $this->feed = $feed;
9066                 $this->data = $data;
9067         }
9068
9069         /**
9070          * Set the registry handler
9071          *
9072          * This is usually used by {@see SimplePie_Registry::create}
9073          *
9074          * @since 1.3
9075          * @param SimplePie_Registry $registry
9076          */
9077         public function set_registry(SimplePie_Registry $registry)
9078         {
9079                 $this->registry = $registry;
9080         }
9081
9082         /**
9083          * Get a string representation of the item
9084          *
9085          * @return string
9086          */
9087         public function __toString()
9088         {
9089                 return md5(serialize($this->data));
9090         }
9091
9092         /**
9093          * Remove items that link back to this before destroying this object
9094          */
9095         public function __destruct()
9096         {
9097                 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
9098                 {
9099                         unset($this->feed);
9100                 }
9101         }
9102
9103         /**
9104          * Get data for an item-level element
9105          *
9106          * This method allows you to get access to ANY element/attribute that is a
9107          * sub-element of the item/entry tag.
9108          *
9109          * See {@see SimplePie::get_feed_tags()} for a description of the return value
9110          *
9111          * @since 1.0
9112          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
9113          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
9114          * @param string $tag Tag name
9115          * @return array
9116          */
9117         public function get_item_tags($namespace, $tag)
9118         {
9119                 if (isset($this->data['child'][$namespace][$tag]))
9120                 {
9121                         return $this->data['child'][$namespace][$tag];
9122                 }
9123                 else
9124                 {
9125                         return null;
9126                 }
9127         }
9128
9129         /**
9130          * Get the base URL value from the parent feed
9131          *
9132          * Uses `<xml:base>`
9133          *
9134          * @param array $element
9135          * @return string
9136          */
9137         public function get_base($element = array())
9138         {
9139                 return $this->feed->get_base($element);
9140         }
9141
9142         /**
9143          * Sanitize feed data
9144          *
9145          * @access private
9146          * @see SimplePie::sanitize()
9147          * @param string $data Data to sanitize
9148          * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
9149          * @param string $base Base URL to resolve URLs against
9150          * @return string Sanitized data
9151          */
9152         public function sanitize($data, $type, $base = '')
9153         {
9154                 return $this->feed->sanitize($data, $type, $base);
9155         }
9156
9157         /**
9158          * Get the parent feed
9159          *
9160          * Note: this may not work as you think for multifeeds!
9161          *
9162          * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
9163          * @since 1.0
9164          * @return SimplePie
9165          */
9166         public function get_feed()
9167         {
9168                 return $this->feed;
9169         }
9170
9171         /**
9172          * Get the unique identifier for the item
9173          *
9174          * This is usually used when writing code to check for new items in a feed.
9175          *
9176          * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
9177          * for RDF. If none of these are supplied (or `$hash` is true), creates an
9178          * MD5 hash based on the permalink and title. If either of those are not
9179          * supplied, creates a hash based on the full feed data.
9180          *
9181          * @since Beta 2
9182          * @param boolean $hash Should we force using a hash instead of the supplied ID?
9183          * @return string
9184          */
9185         public function get_id($hash = false)
9186         {
9187                 if (!$hash)
9188                 {
9189                         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
9190                         {
9191                                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9192                         }
9193                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
9194                         {
9195                                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9196                         }
9197                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9198                         {
9199                                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9200                         }
9201                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
9202                         {
9203                                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9204                         }
9205                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
9206                         {
9207                                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9208                         }
9209                         elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about']))
9210                         {
9211                                 return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT);
9212                         }
9213                         elseif (($return = $this->get_permalink()) !== null)
9214                         {
9215                                 return $return;
9216                         }
9217                         elseif (($return = $this->get_title()) !== null)
9218                         {
9219                                 return $return;
9220                         }
9221                 }
9222                 if ($this->get_permalink() !== null || $this->get_title() !== null)
9223                 {
9224                         return md5($this->get_permalink() . $this->get_title());
9225                 }
9226                 else
9227                 {
9228                         return md5(serialize($this->data));
9229                 }
9230         }
9231
9232         /**
9233          * Get the title of the item
9234          *
9235          * Uses `<atom:title>`, `<title>` or `<dc:title>`
9236          *
9237          * @since Beta 2 (previously called `get_item_title` since 0.8)
9238          * @return string|null
9239          */
9240         public function get_title()
9241         {
9242                 if (!isset($this->data['title']))
9243                 {
9244                         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
9245                         {
9246                                 $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]));
9247                         }
9248                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
9249                         {
9250                                 $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]));
9251                         }
9252                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, '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_090, '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_RSS_20, 'title'))
9261                         {
9262                                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9263                         }
9264                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
9265                         {
9266                                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9267                         }
9268                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
9269                         {
9270                                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9271                         }
9272                         else
9273                         {
9274                                 $this->data['title'] = null;
9275                         }
9276                 }
9277                 return $this->data['title'];
9278         }
9279
9280         /**
9281          * Get the content for the item
9282          *
9283          * Prefers summaries over full content , but will return full content if a
9284          * summary does not exist.
9285          *
9286          * To prefer full content instead, use {@see get_content}
9287          *
9288          * Uses `<atom:summary>`, `<description>`, `<dc:description>` or
9289          * `<itunes:subtitle>`
9290          *
9291          * @since 0.8
9292          * @param boolean $description_only Should we avoid falling back to the content?
9293          * @return string|null
9294          */
9295         public function get_description($description_only = false)
9296         {
9297                 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary'))
9298                 {
9299                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9300                 }
9301                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary'))
9302                 {
9303                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9304                 }
9305                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
9306                 {
9307                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9308                 }
9309                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
9310                 {
9311                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9312                 }
9313                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
9314                 {
9315                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9316                 }
9317                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
9318                 {
9319                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9320                 }
9321                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
9322                 {
9323                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9324                 }
9325                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
9326                 {
9327                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9328                 }
9329                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
9330                 {
9331                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9332                 }
9333
9334                 elseif (!$description_only)
9335                 {
9336                         return $this->get_content(true);
9337                 }
9338                 else
9339                 {
9340                         return null;
9341                 }
9342         }
9343
9344         /**
9345          * Get the content for the item
9346          *
9347          * Prefers full content over summaries, but will return a summary if full
9348          * content does not exist.
9349          *
9350          * To prefer summaries instead, use {@see get_description}
9351          *
9352          * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
9353          *
9354          * @since 1.0
9355          * @param boolean $content_only Should we avoid falling back to the description?
9356          * @return string|null
9357          */
9358         public function get_content($content_only = false)
9359         {
9360                 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content'))
9361                 {
9362                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9363                 }
9364                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content'))
9365                 {
9366                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9367                 }
9368                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded'))
9369                 {
9370                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9371                 }
9372                 elseif (!$content_only)
9373                 {
9374                         return $this->get_description(true);
9375                 }
9376                 else
9377                 {
9378                         return null;
9379                 }
9380         }
9381
9382         /**
9383          * Get a category for the item
9384          *
9385          * @since Beta 3 (previously called `get_categories()` since Beta 2)
9386          * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
9387          * @return SimplePie_Category|null
9388          */
9389         public function get_category($key = 0)
9390         {
9391                 $categories = $this->get_categories();
9392                 if (isset($categories[$key]))
9393                 {
9394                         return $categories[$key];
9395                 }
9396                 else
9397                 {
9398                         return null;
9399                 }
9400         }
9401
9402         /**
9403          * Get all categories for the item
9404          *
9405          * Uses `<atom:category>`, `<category>` or `<dc:subject>`
9406          *
9407          * @since Beta 3
9408          * @return array|null List of {@see SimplePie_Category} objects
9409          */
9410         public function get_categories()
9411         {
9412                 $categories = array();
9413
9414                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
9415                 {
9416                         $term = null;
9417                         $scheme = null;
9418                         $label = null;
9419                         if (isset($category['attribs']['']['term']))
9420                         {
9421                                 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
9422                         }
9423                         if (isset($category['attribs']['']['scheme']))
9424                         {
9425                                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
9426                         }
9427                         if (isset($category['attribs']['']['label']))
9428                         {
9429                                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
9430                         }
9431                         $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
9432                 }
9433                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
9434                 {
9435                         // This is really the label, but keep this as the term also for BC.
9436                         // Label will also work on retrieving because that falls back to term.
9437                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9438                         if (isset($category['attribs']['']['domain']))
9439                         {
9440                                 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
9441                         }
9442                         else
9443                         {
9444                                 $scheme = null;
9445                         }
9446                         $categories[] = $this->registry->create('Category', array($term, $scheme, null));
9447                 }
9448                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
9449                 {
9450                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9451                 }
9452                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
9453                 {
9454                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9455                 }
9456
9457                 if (!empty($categories))
9458                 {
9459                         return array_unique($categories);
9460                 }
9461                 else
9462                 {
9463                         return null;
9464                 }
9465         }
9466
9467         /**
9468          * Get an author for the item
9469          *
9470          * @since Beta 2
9471          * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
9472          * @return SimplePie_Author|null
9473          */
9474         public function get_author($key = 0)
9475         {
9476                 $authors = $this->get_authors();
9477                 if (isset($authors[$key]))
9478                 {
9479                         return $authors[$key];
9480                 }
9481                 else
9482                 {
9483                         return null;
9484                 }
9485         }
9486
9487         /**
9488          * Get a contributor for the item
9489          *
9490          * @since 1.1
9491          * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
9492          * @return SimplePie_Author|null
9493          */
9494         public function get_contributor($key = 0)
9495         {
9496                 $contributors = $this->get_contributors();
9497                 if (isset($contributors[$key]))
9498                 {
9499                         return $contributors[$key];
9500                 }
9501                 else
9502                 {
9503                         return null;
9504                 }
9505         }
9506
9507         /**
9508          * Get all contributors for the item
9509          *
9510          * Uses `<atom:contributor>`
9511          *
9512          * @since 1.1
9513          * @return array|null List of {@see SimplePie_Author} objects
9514          */
9515         public function get_contributors()
9516         {
9517                 $contributors = array();
9518                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
9519                 {
9520                         $name = null;
9521                         $uri = null;
9522                         $email = null;
9523                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9524                         {
9525                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9526                         }
9527                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9528                         {
9529                                 $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]));
9530                         }
9531                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9532                         {
9533                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9534                         }
9535                         if ($name !== null || $email !== null || $uri !== null)
9536                         {
9537                                 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
9538                         }
9539                 }
9540                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
9541                 {
9542                         $name = null;
9543                         $url = null;
9544                         $email = null;
9545                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9546                         {
9547                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9548                         }
9549                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9550                         {
9551                                 $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]));
9552                         }
9553                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9554                         {
9555                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9556                         }
9557                         if ($name !== null || $email !== null || $url !== null)
9558                         {
9559                                 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
9560                         }
9561                 }
9562
9563                 if (!empty($contributors))
9564                 {
9565                         return array_unique($contributors);
9566                 }
9567                 else
9568                 {
9569                         return null;
9570                 }
9571         }
9572
9573         /**
9574          * Get all authors for the item
9575          *
9576          * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
9577          *
9578          * @since Beta 2
9579          * @return array|null List of {@see SimplePie_Author} objects
9580          */
9581         public function get_authors()
9582         {
9583                 $authors = array();
9584                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
9585                 {
9586                         $name = null;
9587                         $uri = null;
9588                         $email = null;
9589                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9590                         {
9591                                 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9592                         }
9593                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9594                         {
9595                                 $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]));
9596                         }
9597                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9598                         {
9599                                 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9600                         }
9601                         if ($name !== null || $email !== null || $uri !== null)
9602                         {
9603                                 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
9604                         }
9605                 }
9606                 if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
9607                 {
9608                         $name = null;
9609                         $url = null;
9610                         $email = null;
9611                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9612                         {
9613                                 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9614                         }
9615                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9616                         {
9617                                 $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]));
9618                         }
9619                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9620                         {
9621                                 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9622                         }
9623                         if ($name !== null || $email !== null || $url !== null)
9624                         {
9625                                 $authors[] = $this->registry->create('Author', array($name, $url, $email));
9626                         }
9627                 }
9628                 if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
9629                 {
9630                         $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)));
9631                 }
9632                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, '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_DC_10, 'creator') as $author)
9637                 {
9638                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9639                 }
9640                 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
9641                 {
9642                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9643                 }
9644
9645                 if (!empty($authors))
9646                 {
9647                         return array_unique($authors);
9648                 }
9649                 elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
9650                 {
9651                         return $authors;
9652                 }
9653                 elseif ($authors = $this->feed->get_authors())
9654                 {
9655                         return $authors;
9656                 }
9657                 else
9658                 {
9659                         return null;
9660                 }
9661         }
9662
9663         /**
9664          * Get the copyright info for the item
9665          *
9666          * Uses `<atom:rights>` or `<dc:rights>`
9667          *
9668          * @since 1.1
9669          * @return string
9670          */
9671         public function get_copyright()
9672         {
9673                 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
9674                 {
9675                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9676                 }
9677                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
9678                 {
9679                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9680                 }
9681                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
9682                 {
9683                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9684                 }
9685                 else
9686                 {
9687                         return null;
9688                 }
9689         }
9690
9691         /**
9692          * Get the posting date/time for the item
9693          *
9694          * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
9695          * `<atom:modified>`, `<pubDate>` or `<dc:date>`
9696          *
9697          * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9698          * {@see get_gmdate}
9699          *
9700          * @since Beta 2 (previously called `get_item_date` since 0.8)
9701          *
9702          * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9703          * @return int|string|null
9704          */
9705         public function get_date($date_format = 'j F Y, g:i a')
9706         {
9707                 if (!isset($this->data['date']))
9708                 {
9709                         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
9710                         {
9711                                 $this->data['date']['raw'] = $return[0]['data'];
9712                         }
9713                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9714                         {
9715                                 $this->data['date']['raw'] = $return[0]['data'];
9716                         }
9717                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
9718                         {
9719                                 $this->data['date']['raw'] = $return[0]['data'];
9720                         }
9721                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
9722                         {
9723                                 $this->data['date']['raw'] = $return[0]['data'];
9724                         }
9725                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
9726                         {
9727                                 $this->data['date']['raw'] = $return[0]['data'];
9728                         }
9729                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
9730                         {
9731                                 $this->data['date']['raw'] = $return[0]['data'];
9732                         }
9733                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
9734                         {
9735                                 $this->data['date']['raw'] = $return[0]['data'];
9736                         }
9737                         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
9738                         {
9739                                 $this->data['date']['raw'] = $return[0]['data'];
9740                         }
9741
9742                         if (!empty($this->data['date']['raw']))
9743                         {
9744                                 $parser = $this->registry->call('Parse_Date', 'get');
9745                                 $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
9746                         }
9747                         else
9748                         {
9749                                 $this->data['date'] = null;
9750                         }
9751                 }
9752                 if ($this->data['date'])
9753                 {
9754                         $date_format = (string) $date_format;
9755                         switch ($date_format)
9756                         {
9757                                 case '':
9758                                         return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
9759
9760                                 case 'U':
9761                                         return $this->data['date']['parsed'];
9762
9763                                 default:
9764                                         return date($date_format, $this->data['date']['parsed']);
9765                         }
9766                 }
9767                 else
9768                 {
9769                         return null;
9770                 }
9771         }
9772
9773         /**
9774          * Get the update date/time for the item
9775          *
9776          * Uses `<atom:updated>`
9777          *
9778          * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9779          * {@see get_gmdate}
9780          *
9781          * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9782          * @return int|string|null
9783          */
9784         public function get_updated_date($date_format = 'j F Y, g:i a')
9785         {
9786                 if (!isset($this->data['updated']))
9787                 {
9788                         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9789                         {
9790                                 $this->data['updated']['raw'] = $return[0]['data'];
9791                         }
9792
9793                         if (!empty($this->data['updated']['raw']))
9794                         {
9795                                 $parser = $this->registry->call('Parse_Date', 'get');
9796                                 $this->data['updated']['parsed'] = $parser->parse($this->data['date']['raw']);
9797                         }
9798                         else
9799                         {
9800                                 $this->data['updated'] = null;
9801                         }
9802                 }
9803                 if ($this->data['updated'])
9804                 {
9805                         $date_format = (string) $date_format;
9806                         switch ($date_format)
9807                         {
9808                                 case '':
9809                                         return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
9810
9811                                 case 'U':
9812                                         return $this->data['updated']['parsed'];
9813
9814                                 default:
9815                                         return date($date_format, $this->data['updated']['parsed']);
9816                         }
9817                 }
9818                 else
9819                 {
9820                         return null;
9821                 }
9822         }
9823
9824         /**
9825          * Get the localized posting date/time for the item
9826          *
9827          * Returns the date formatted in the localized language. To display in
9828          * languages other than the server's default, you need to change the locale
9829          * with {@link http://php.net/setlocale setlocale()}. The available
9830          * localizations depend on which ones are installed on your web server.
9831          *
9832          * @since 1.0
9833          *
9834          * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
9835          * @return int|string|null
9836          */
9837         public function get_local_date($date_format = '%c')
9838         {
9839                 if (!$date_format)
9840                 {
9841                         return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
9842                 }
9843                 elseif (($date = $this->get_date('U')) !== null && $date !== false)
9844                 {
9845                         return strftime($date_format, $date);
9846                 }
9847                 else
9848                 {
9849                         return null;
9850                 }
9851         }
9852
9853         /**
9854          * Get the posting date/time for the item (UTC time)
9855          *
9856          * @see get_date
9857          * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
9858          * @return int|string|null
9859          */
9860         public function get_gmdate($date_format = 'j F Y, g:i a')
9861         {
9862                 $date = $this->get_date('U');
9863                 if ($date === null)
9864                 {
9865                         return null;
9866                 }
9867
9868                 return gmdate($date_format, $date);
9869         }
9870
9871         /**
9872          * Get the update date/time for the item (UTC time)
9873          *
9874          * @see get_updated_date
9875          * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
9876          * @return int|string|null
9877          */
9878         public function get_updated_gmdate($date_format = 'j F Y, g:i a')
9879         {
9880                 $date = $this->get_updated_date('U');
9881                 if ($date === null)
9882                 {
9883                         return null;
9884                 }
9885
9886                 return gmdate($date_format, $date);
9887         }
9888
9889         /**
9890          * Get the permalink for the item
9891          *
9892          * Returns the first link available with a relationship of "alternate".
9893          * Identical to {@see get_link()} with key 0
9894          *
9895          * @see get_link
9896          * @since 0.8
9897          * @return string|null Permalink URL
9898          */
9899         public function get_permalink()
9900         {
9901                 $link = $this->get_link();
9902                 $enclosure = $this->get_enclosure(0);
9903                 if ($link !== null)
9904                 {
9905                         return $link;
9906                 }
9907                 elseif ($enclosure !== null)
9908                 {
9909                         return $enclosure->get_link();
9910                 }
9911                 else
9912                 {
9913                         return null;
9914                 }
9915         }
9916
9917         /**
9918          * Get a single link for the item
9919          *
9920          * @since Beta 3
9921          * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
9922          * @param string $rel The relationship of the link to return
9923          * @return string|null Link URL
9924          */
9925         public function get_link($key = 0, $rel = 'alternate')
9926         {
9927                 $links = $this->get_links($rel);
9928                 if ($links[$key] !== null)
9929                 {
9930                         return $links[$key];
9931                 }
9932                 else
9933                 {
9934                         return null;
9935                 }
9936         }
9937
9938         /**
9939          * Get all links for the item
9940          *
9941          * Uses `<atom:link>`, `<link>` or `<guid>`
9942          *
9943          * @since Beta 2
9944          * @param string $rel The relationship of links to return
9945          * @return array|null Links found for the item (strings)
9946          */
9947         public function get_links($rel = 'alternate')
9948         {
9949                 if (!isset($this->data['links']))
9950                 {
9951                         $this->data['links'] = array();
9952                         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
9953                         {
9954                                 if (isset($link['attribs']['']['href']))
9955                                 {
9956                                         $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
9957                                         $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
9958
9959                                 }
9960                         }
9961                         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
9962                         {
9963                                 if (isset($link['attribs']['']['href']))
9964                                 {
9965                                         $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
9966                                         $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
9967                                 }
9968                         }
9969                         if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, '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_090, '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, 'link'))
9978                         {
9979                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9980                         }
9981                         if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9982                         {
9983                                 if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
9984                                 {
9985                                         $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9986                                 }
9987                         }
9988
9989                         $keys = array_keys($this->data['links']);
9990                         foreach ($keys as $key)
9991                         {
9992                                 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
9993                                 {
9994                                         if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
9995                                         {
9996                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
9997                                                 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
9998                                         }
9999                                         else
10000                                         {
10001                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
10002                                         }
10003                                 }
10004                                 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
10005                                 {
10006                                         $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
10007                                 }
10008                                 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
10009                         }
10010                 }
10011                 if (isset($this->data['links'][$rel]))
10012                 {
10013                         return $this->data['links'][$rel];
10014                 }
10015                 else
10016                 {
10017                         return null;
10018                 }
10019         }
10020
10021         /**
10022          * Get an enclosure from the item
10023          *
10024          * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10025          *
10026          * @since Beta 2
10027          * @todo Add ability to prefer one type of content over another (in a media group).
10028          * @param int $key The enclosure that you want to return.  Remember that arrays begin with 0, not 1
10029          * @return SimplePie_Enclosure|null
10030          */
10031         public function get_enclosure($key = 0, $prefer = null)
10032         {
10033                 $enclosures = $this->get_enclosures();
10034                 if (isset($enclosures[$key]))
10035                 {
10036                         return $enclosures[$key];
10037                 }
10038                 else
10039                 {
10040                         return null;
10041                 }
10042         }
10043
10044         /**
10045          * Get all available enclosures (podcasts, etc.)
10046          *
10047          * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10048          *
10049          * At this point, we're pretty much assuming that all enclosures for an item
10050          * are the same content.  Anything else is too complicated to
10051          * properly support.
10052          *
10053          * @since Beta 2
10054          * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
10055          * @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).
10056          * @return array|null List of SimplePie_Enclosure items
10057          */
10058         public function get_enclosures()
10059         {
10060                 if (!isset($this->data['enclosures']))
10061                 {
10062                         $this->data['enclosures'] = array();
10063
10064                         // Elements
10065                         $captions_parent = null;
10066                         $categories_parent = null;
10067                         $copyrights_parent = null;
10068                         $credits_parent = null;
10069                         $description_parent = null;
10070                         $duration_parent = null;
10071                         $hashes_parent = null;
10072                         $keywords_parent = null;
10073                         $player_parent = null;
10074                         $ratings_parent = null;
10075                         $restrictions_parent = null;
10076                         $thumbnails_parent = null;
10077                         $title_parent = null;
10078
10079                         // Let's do the channel and item-level ones first, and just re-use them if we need to.
10080                         $parent = $this->get_feed();
10081
10082                         // CAPTIONS
10083                         if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10084                         {
10085                                 foreach ($captions as $caption)
10086                                 {
10087                                         $caption_type = null;
10088                                         $caption_lang = null;
10089                                         $caption_startTime = null;
10090                                         $caption_endTime = null;
10091                                         $caption_text = null;
10092                                         if (isset($caption['attribs']['']['type']))
10093                                         {
10094                                                 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10095                                         }
10096                                         if (isset($caption['attribs']['']['lang']))
10097                                         {
10098                                                 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10099                                         }
10100                                         if (isset($caption['attribs']['']['start']))
10101                                         {
10102                                                 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10103                                         }
10104                                         if (isset($caption['attribs']['']['end']))
10105                                         {
10106                                                 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10107                                         }
10108                                         if (isset($caption['data']))
10109                                         {
10110                                                 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10111                                         }
10112                                         $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10113                                 }
10114                         }
10115                         elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10116                         {
10117                                 foreach ($captions as $caption)
10118                                 {
10119                                         $caption_type = null;
10120                                         $caption_lang = null;
10121                                         $caption_startTime = null;
10122                                         $caption_endTime = null;
10123                                         $caption_text = null;
10124                                         if (isset($caption['attribs']['']['type']))
10125                                         {
10126                                                 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10127                                         }
10128                                         if (isset($caption['attribs']['']['lang']))
10129                                         {
10130                                                 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10131                                         }
10132                                         if (isset($caption['attribs']['']['start']))
10133                                         {
10134                                                 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10135                                         }
10136                                         if (isset($caption['attribs']['']['end']))
10137                                         {
10138                                                 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10139                                         }
10140                                         if (isset($caption['data']))
10141                                         {
10142                                                 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10143                                         }
10144                                         $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10145                                 }
10146                         }
10147                         if (is_array($captions_parent))
10148                         {
10149                                 $captions_parent = array_values(array_unique($captions_parent));
10150                         }
10151
10152                         // CATEGORIES
10153                         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10154                         {
10155                                 $term = null;
10156                                 $scheme = null;
10157                                 $label = null;
10158                                 if (isset($category['data']))
10159                                 {
10160                                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10161                                 }
10162                                 if (isset($category['attribs']['']['scheme']))
10163                                 {
10164                                         $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10165                                 }
10166                                 else
10167                                 {
10168                                         $scheme = 'http://search.yahoo.com/mrss/category_schema';
10169                                 }
10170                                 if (isset($category['attribs']['']['label']))
10171                                 {
10172                                         $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10173                                 }
10174                                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10175                         }
10176                         foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10177                         {
10178                                 $term = null;
10179                                 $scheme = null;
10180                                 $label = null;
10181                                 if (isset($category['data']))
10182                                 {
10183                                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10184                                 }
10185                                 if (isset($category['attribs']['']['scheme']))
10186                                 {
10187                                         $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10188                                 }
10189                                 else
10190                                 {
10191                                         $scheme = 'http://search.yahoo.com/mrss/category_schema';
10192                                 }
10193                                 if (isset($category['attribs']['']['label']))
10194                                 {
10195                                         $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10196                                 }
10197                                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10198                         }
10199                         foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
10200                         {
10201                                 $term = null;
10202                                 $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
10203                                 $label = null;
10204                                 if (isset($category['attribs']['']['text']))
10205                                 {
10206                                         $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10207                                 }
10208                                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10209
10210                                 if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
10211                                 {
10212                                         foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
10213                                         {
10214                                                 if (isset($subcategory['attribs']['']['text']))
10215                                                 {
10216                                                         $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10217                                                 }
10218                                                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10219                                         }
10220                                 }
10221                         }
10222                         if (is_array($categories_parent))
10223                         {
10224                                 $categories_parent = array_values(array_unique($categories_parent));
10225                         }
10226
10227                         // COPYRIGHT
10228                         if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10229                         {
10230                                 $copyright_url = null;
10231                                 $copyright_label = null;
10232                                 if (isset($copyright[0]['attribs']['']['url']))
10233                                 {
10234                                         $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10235                                 }
10236                                 if (isset($copyright[0]['data']))
10237                                 {
10238                                         $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10239                                 }
10240                                 $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10241                         }
10242                         elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10243                         {
10244                                 $copyright_url = null;
10245                                 $copyright_label = null;
10246                                 if (isset($copyright[0]['attribs']['']['url']))
10247                                 {
10248                                         $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10249                                 }
10250                                 if (isset($copyright[0]['data']))
10251                                 {
10252                                         $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10253                                 }
10254                                 $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10255                         }
10256
10257                         // CREDITS
10258                         if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10259                         {
10260                                 foreach ($credits as $credit)
10261                                 {
10262                                         $credit_role = null;
10263                                         $credit_scheme = null;
10264                                         $credit_name = null;
10265                                         if (isset($credit['attribs']['']['role']))
10266                                         {
10267                                                 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10268                                         }
10269                                         if (isset($credit['attribs']['']['scheme']))
10270                                         {
10271                                                 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10272                                         }
10273                                         else
10274                                         {
10275                                                 $credit_scheme = 'urn:ebu';
10276                                         }
10277                                         if (isset($credit['data']))
10278                                         {
10279                                                 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10280                                         }
10281                                         $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10282                                 }
10283                         }
10284                         elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10285                         {
10286                                 foreach ($credits as $credit)
10287                                 {
10288                                         $credit_role = null;
10289                                         $credit_scheme = null;
10290                                         $credit_name = null;
10291                                         if (isset($credit['attribs']['']['role']))
10292                                         {
10293                                                 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10294                                         }
10295                                         if (isset($credit['attribs']['']['scheme']))
10296                                         {
10297                                                 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10298                                         }
10299                                         else
10300                                         {
10301                                                 $credit_scheme = 'urn:ebu';
10302                                         }
10303                                         if (isset($credit['data']))
10304                                         {
10305                                                 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10306                                         }
10307                                         $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10308                                 }
10309                         }
10310                         if (is_array($credits_parent))
10311                         {
10312                                 $credits_parent = array_values(array_unique($credits_parent));
10313                         }
10314
10315                         // DESCRIPTION
10316                         if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10317                         {
10318                                 if (isset($description_parent[0]['data']))
10319                                 {
10320                                         $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10321                                 }
10322                         }
10323                         elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10324                         {
10325                                 if (isset($description_parent[0]['data']))
10326                                 {
10327                                         $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10328                                 }
10329                         }
10330
10331                         // DURATION
10332                         if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
10333                         {
10334                                 $seconds = null;
10335                                 $minutes = null;
10336                                 $hours = null;
10337                                 if (isset($duration_parent[0]['data']))
10338                                 {
10339                                         $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10340                                         if (sizeof($temp) > 0)
10341                                         {
10342                                                 $seconds = (int) array_pop($temp);
10343                                         }
10344                                         if (sizeof($temp) > 0)
10345                                         {
10346                                                 $minutes = (int) array_pop($temp);
10347                                                 $seconds += $minutes * 60;
10348                                         }
10349                                         if (sizeof($temp) > 0)
10350                                         {
10351                                                 $hours = (int) array_pop($temp);
10352                                                 $seconds += $hours * 3600;
10353                                         }
10354                                         unset($temp);
10355                                         $duration_parent = $seconds;
10356                                 }
10357                         }
10358
10359                         // HASHES
10360                         if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10361                         {
10362                                 foreach ($hashes_iterator as $hash)
10363                                 {
10364                                         $value = null;
10365                                         $algo = null;
10366                                         if (isset($hash['data']))
10367                                         {
10368                                                 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10369                                         }
10370                                         if (isset($hash['attribs']['']['algo']))
10371                                         {
10372                                                 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10373                                         }
10374                                         else
10375                                         {
10376                                                 $algo = 'md5';
10377                                         }
10378                                         $hashes_parent[] = $algo.':'.$value;
10379                                 }
10380                         }
10381                         elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10382                         {
10383                                 foreach ($hashes_iterator as $hash)
10384                                 {
10385                                         $value = null;
10386                                         $algo = null;
10387                                         if (isset($hash['data']))
10388                                         {
10389                                                 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10390                                         }
10391                                         if (isset($hash['attribs']['']['algo']))
10392                                         {
10393                                                 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10394                                         }
10395                                         else
10396                                         {
10397                                                 $algo = 'md5';
10398                                         }
10399                                         $hashes_parent[] = $algo.':'.$value;
10400                                 }
10401                         }
10402                         if (is_array($hashes_parent))
10403                         {
10404                                 $hashes_parent = array_values(array_unique($hashes_parent));
10405                         }
10406
10407                         // KEYWORDS
10408                         if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10409                         {
10410                                 if (isset($keywords[0]['data']))
10411                                 {
10412                                         $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10413                                         foreach ($temp as $word)
10414                                         {
10415                                                 $keywords_parent[] = trim($word);
10416                                         }
10417                                 }
10418                                 unset($temp);
10419                         }
10420                         elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10421                         {
10422                                 if (isset($keywords[0]['data']))
10423                                 {
10424                                         $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10425                                         foreach ($temp as $word)
10426                                         {
10427                                                 $keywords_parent[] = trim($word);
10428                                         }
10429                                 }
10430                                 unset($temp);
10431                         }
10432                         elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10433                         {
10434                                 if (isset($keywords[0]['data']))
10435                                 {
10436                                         $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10437                                         foreach ($temp as $word)
10438                                         {
10439                                                 $keywords_parent[] = trim($word);
10440                                         }
10441                                 }
10442                                 unset($temp);
10443                         }
10444                         elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10445                         {
10446                                 if (isset($keywords[0]['data']))
10447                                 {
10448                                         $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10449                                         foreach ($temp as $word)
10450                                         {
10451                                                 $keywords_parent[] = trim($word);
10452                                         }
10453                                 }
10454                                 unset($temp);
10455                         }
10456                         if (is_array($keywords_parent))
10457                         {
10458                                 $keywords_parent = array_values(array_unique($keywords_parent));
10459                         }
10460
10461                         // PLAYER
10462                         if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10463                         {
10464                                 if (isset($player_parent[0]['attribs']['']['url']))
10465                                 {
10466                                         $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10467                                 }
10468                         }
10469                         elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10470                         {
10471                                 if (isset($player_parent[0]['attribs']['']['url']))
10472                                 {
10473                                         $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10474                                 }
10475                         }
10476
10477                         // RATINGS
10478                         if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10479                         {
10480                                 foreach ($ratings as $rating)
10481                                 {
10482                                         $rating_scheme = null;
10483                                         $rating_value = null;
10484                                         if (isset($rating['attribs']['']['scheme']))
10485                                         {
10486                                                 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10487                                         }
10488                                         else
10489                                         {
10490                                                 $rating_scheme = 'urn:simple';
10491                                         }
10492                                         if (isset($rating['data']))
10493                                         {
10494                                                 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10495                                         }
10496                                         $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10497                                 }
10498                         }
10499                         elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10500                         {
10501                                 foreach ($ratings as $rating)
10502                                 {
10503                                         $rating_scheme = 'urn:itunes';
10504                                         $rating_value = null;
10505                                         if (isset($rating['data']))
10506                                         {
10507                                                 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10508                                         }
10509                                         $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10510                                 }
10511                         }
10512                         elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10513                         {
10514                                 foreach ($ratings as $rating)
10515                                 {
10516                                         $rating_scheme = null;
10517                                         $rating_value = null;
10518                                         if (isset($rating['attribs']['']['scheme']))
10519                                         {
10520                                                 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10521                                         }
10522                                         else
10523                                         {
10524                                                 $rating_scheme = 'urn:simple';
10525                                         }
10526                                         if (isset($rating['data']))
10527                                         {
10528                                                 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10529                                         }
10530                                         $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10531                                 }
10532                         }
10533                         elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10534                         {
10535                                 foreach ($ratings as $rating)
10536                                 {
10537                                         $rating_scheme = 'urn:itunes';
10538                                         $rating_value = null;
10539                                         if (isset($rating['data']))
10540                                         {
10541                                                 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10542                                         }
10543                                         $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10544                                 }
10545                         }
10546                         if (is_array($ratings_parent))
10547                         {
10548                                 $ratings_parent = array_values(array_unique($ratings_parent));
10549                         }
10550
10551                         // RESTRICTIONS
10552                         if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10553                         {
10554                                 foreach ($restrictions as $restriction)
10555                                 {
10556                                         $restriction_relationship = null;
10557                                         $restriction_type = null;
10558                                         $restriction_value = null;
10559                                         if (isset($restriction['attribs']['']['relationship']))
10560                                         {
10561                                                 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10562                                         }
10563                                         if (isset($restriction['attribs']['']['type']))
10564                                         {
10565                                                 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10566                                         }
10567                                         if (isset($restriction['data']))
10568                                         {
10569                                                 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10570                                         }
10571                                         $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10572                                 }
10573                         }
10574                         elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10575                         {
10576                                 foreach ($restrictions as $restriction)
10577                                 {
10578                                         $restriction_relationship = 'allow';
10579                                         $restriction_type = null;
10580                                         $restriction_value = 'itunes';
10581                                         if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10582                                         {
10583                                                 $restriction_relationship = 'deny';
10584                                         }
10585                                         $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10586                                 }
10587                         }
10588                         elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10589                         {
10590                                 foreach ($restrictions as $restriction)
10591                                 {
10592                                         $restriction_relationship = null;
10593                                         $restriction_type = null;
10594                                         $restriction_value = null;
10595                                         if (isset($restriction['attribs']['']['relationship']))
10596                                         {
10597                                                 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10598                                         }
10599                                         if (isset($restriction['attribs']['']['type']))
10600                                         {
10601                                                 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10602                                         }
10603                                         if (isset($restriction['data']))
10604                                         {
10605                                                 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10606                                         }
10607                                         $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10608                                 }
10609                         }
10610                         elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10611                         {
10612                                 foreach ($restrictions as $restriction)
10613                                 {
10614                                         $restriction_relationship = 'allow';
10615                                         $restriction_type = null;
10616                                         $restriction_value = 'itunes';
10617                                         if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10618                                         {
10619                                                 $restriction_relationship = 'deny';
10620                                         }
10621                                         $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10622                                 }
10623                         }
10624                         if (is_array($restrictions_parent))
10625                         {
10626                                 $restrictions_parent = array_values(array_unique($restrictions_parent));
10627                         }
10628                         else
10629                         {
10630                                 $restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default'));
10631                         }
10632
10633                         // THUMBNAILS
10634                         if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10635                         {
10636                                 foreach ($thumbnails as $thumbnail)
10637                                 {
10638                                         if (isset($thumbnail['attribs']['']['url']))
10639                                         {
10640                                                 $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10641                                         }
10642                                 }
10643                         }
10644                         elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10645                         {
10646                                 foreach ($thumbnails as $thumbnail)
10647                                 {
10648                                         if (isset($thumbnail['attribs']['']['url']))
10649                                         {
10650                                                 $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10651                                         }
10652                                 }
10653                         }
10654
10655                         // TITLES
10656                         if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10657                         {
10658                                 if (isset($title_parent[0]['data']))
10659                                 {
10660                                         $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10661                                 }
10662                         }
10663                         elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10664                         {
10665                                 if (isset($title_parent[0]['data']))
10666                                 {
10667                                         $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10668                                 }
10669                         }
10670
10671                         // Clear the memory
10672                         unset($parent);
10673
10674                         // Attributes
10675                         $bitrate = null;
10676                         $channels = null;
10677                         $duration = null;
10678                         $expression = null;
10679                         $framerate = null;
10680                         $height = null;
10681                         $javascript = null;
10682                         $lang = null;
10683                         $length = null;
10684                         $medium = null;
10685                         $samplingrate = null;
10686                         $type = null;
10687                         $url = null;
10688                         $width = null;
10689
10690                         // Elements
10691                         $captions = null;
10692                         $categories = null;
10693                         $copyrights = null;
10694                         $credits = null;
10695                         $description = null;
10696                         $hashes = null;
10697                         $keywords = null;
10698                         $player = null;
10699                         $ratings = null;
10700                         $restrictions = null;
10701                         $thumbnails = null;
10702                         $title = null;
10703
10704                         // If we have media:group tags, loop through them.
10705                         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
10706                         {
10707                                 if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
10708                                 {
10709                                         // If we have media:content tags, loop through them.
10710                                         foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
10711                                         {
10712                                                 if (isset($content['attribs']['']['url']))
10713                                                 {
10714                                                         // Attributes
10715                                                         $bitrate = null;
10716                                                         $channels = null;
10717                                                         $duration = null;
10718                                                         $expression = null;
10719                                                         $framerate = null;
10720                                                         $height = null;
10721                                                         $javascript = null;
10722                                                         $lang = null;
10723                                                         $length = null;
10724                                                         $medium = null;
10725                                                         $samplingrate = null;
10726                                                         $type = null;
10727                                                         $url = null;
10728                                                         $width = null;
10729
10730                                                         // Elements
10731                                                         $captions = null;
10732                                                         $categories = null;
10733                                                         $copyrights = null;
10734                                                         $credits = null;
10735                                                         $description = null;
10736                                                         $hashes = null;
10737                                                         $keywords = null;
10738                                                         $player = null;
10739                                                         $ratings = null;
10740                                                         $restrictions = null;
10741                                                         $thumbnails = null;
10742                                                         $title = null;
10743
10744                                                         // Start checking the attributes of media:content
10745                                                         if (isset($content['attribs']['']['bitrate']))
10746                                                         {
10747                                                                 $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10748                                                         }
10749                                                         if (isset($content['attribs']['']['channels']))
10750                                                         {
10751                                                                 $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
10752                                                         }
10753                                                         if (isset($content['attribs']['']['duration']))
10754                                                         {
10755                                                                 $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
10756                                                         }
10757                                                         else
10758                                                         {
10759                                                                 $duration = $duration_parent;
10760                                                         }
10761                                                         if (isset($content['attribs']['']['expression']))
10762                                                         {
10763                                                                 $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
10764                                                         }
10765                                                         if (isset($content['attribs']['']['framerate']))
10766                                                         {
10767                                                                 $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
10768                                                         }
10769                                                         if (isset($content['attribs']['']['height']))
10770                                                         {
10771                                                                 $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
10772                                                         }
10773                                                         if (isset($content['attribs']['']['lang']))
10774                                                         {
10775                                                                 $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10776                                                         }
10777                                                         if (isset($content['attribs']['']['fileSize']))
10778                                                         {
10779                                                                 $length = ceil($content['attribs']['']['fileSize']);
10780                                                         }
10781                                                         if (isset($content['attribs']['']['medium']))
10782                                                         {
10783                                                                 $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
10784                                                         }
10785                                                         if (isset($content['attribs']['']['samplingrate']))
10786                                                         {
10787                                                                 $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10788                                                         }
10789                                                         if (isset($content['attribs']['']['type']))
10790                                                         {
10791                                                                 $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10792                                                         }
10793                                                         if (isset($content['attribs']['']['width']))
10794                                                         {
10795                                                                 $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
10796                                                         }
10797                                                         $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10798
10799                                                         // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
10800
10801                                                         // CAPTIONS
10802                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
10803                                                         {
10804                                                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
10805                                                                 {
10806                                                                         $caption_type = null;
10807                                                                         $caption_lang = null;
10808                                                                         $caption_startTime = null;
10809                                                                         $caption_endTime = null;
10810                                                                         $caption_text = null;
10811                                                                         if (isset($caption['attribs']['']['type']))
10812                                                                         {
10813                                                                                 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10814                                                                         }
10815                                                                         if (isset($caption['attribs']['']['lang']))
10816                                                                         {
10817                                                                                 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10818                                                                         }
10819                                                                         if (isset($caption['attribs']['']['start']))
10820                                                                         {
10821                                                                                 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10822                                                                         }
10823                                                                         if (isset($caption['attribs']['']['end']))
10824                                                                         {
10825                                                                                 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10826                                                                         }
10827                                                                         if (isset($caption['data']))
10828                                                                         {
10829                                                                                 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10830                                                                         }
10831                                                                         $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10832                                                                 }
10833                                                                 if (is_array($captions))
10834                                                                 {
10835                                                                         $captions = array_values(array_unique($captions));
10836                                                                 }
10837                                                         }
10838                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
10839                                                         {
10840                                                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
10841                                                                 {
10842                                                                         $caption_type = null;
10843                                                                         $caption_lang = null;
10844                                                                         $caption_startTime = null;
10845                                                                         $caption_endTime = null;
10846                                                                         $caption_text = null;
10847                                                                         if (isset($caption['attribs']['']['type']))
10848                                                                         {
10849                                                                                 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10850                                                                         }
10851                                                                         if (isset($caption['attribs']['']['lang']))
10852                                                                         {
10853                                                                                 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10854                                                                         }
10855                                                                         if (isset($caption['attribs']['']['start']))
10856                                                                         {
10857                                                                                 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10858                                                                         }
10859                                                                         if (isset($caption['attribs']['']['end']))
10860                                                                         {
10861                                                                                 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10862                                                                         }
10863                                                                         if (isset($caption['data']))
10864                                                                         {
10865                                                                                 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10866                                                                         }
10867                                                                         $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10868                                                                 }
10869                                                                 if (is_array($captions))
10870                                                                 {
10871                                                                         $captions = array_values(array_unique($captions));
10872                                                                 }
10873                                                         }
10874                                                         else
10875                                                         {
10876                                                                 $captions = $captions_parent;
10877                                                         }
10878
10879                                                         // CATEGORIES
10880                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
10881                                                         {
10882                                                                 foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
10883                                                                 {
10884                                                                         $term = null;
10885                                                                         $scheme = null;
10886                                                                         $label = null;
10887                                                                         if (isset($category['data']))
10888                                                                         {
10889                                                                                 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10890                                                                         }
10891                                                                         if (isset($category['attribs']['']['scheme']))
10892                                                                         {
10893                                                                                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10894                                                                         }
10895                                                                         else
10896                                                                         {
10897                                                                                 $scheme = 'http://search.yahoo.com/mrss/category_schema';
10898                                                                         }
10899                                                                         if (isset($category['attribs']['']['label']))
10900                                                                         {
10901                                                                                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10902                                                                         }
10903                                                                         $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
10904                                                                 }
10905                                                         }
10906                                                         if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
10907                                                         {
10908                                                                 foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
10909                                                                 {
10910                                                                         $term = null;
10911                                                                         $scheme = null;
10912                                                                         $label = null;
10913                                                                         if (isset($category['data']))
10914                                                                         {
10915                                                                                 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10916                                                                         }
10917                                                                         if (isset($category['attribs']['']['scheme']))
10918                                                                         {
10919                                                                                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10920                                                                         }
10921                                                                         else
10922                                                                         {
10923                                                                                 $scheme = 'http://search.yahoo.com/mrss/category_schema';
10924                                                                         }
10925                                                                         if (isset($category['attribs']['']['label']))
10926                                                                         {
10927                                                                                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10928                                                                         }
10929                                                                         $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
10930                                                                 }
10931                                                         }
10932                                                         if (is_array($categories) && is_array($categories_parent))
10933                                                         {
10934                                                                 $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
10935                                                         }
10936                                                         elseif (is_array($categories))
10937                                                         {
10938                                                                 $categories = array_values(array_unique($categories));
10939                                                         }
10940                                                         elseif (is_array($categories_parent))
10941                                                         {
10942                                                                 $categories = array_values(array_unique($categories_parent));
10943                                                         }
10944
10945                                                         // COPYRIGHTS
10946                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
10947                                                         {
10948                                                                 $copyright_url = null;
10949                                                                 $copyright_label = null;
10950                                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
10951                                                                 {
10952                                                                         $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10953                                                                 }
10954                                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
10955                                                                 {
10956                                                                         $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10957                                                                 }
10958                                                                 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10959                                                         }
10960                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
10961                                                         {
10962                                                                 $copyright_url = null;
10963                                                                 $copyright_label = null;
10964                                                                 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
10965                                                                 {
10966                                                                         $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10967                                                                 }
10968                                                                 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
10969                                                                 {
10970                                                                         $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10971                                                                 }
10972                                                                 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10973                                                         }
10974                                                         else
10975                                                         {
10976                                                                 $copyrights = $copyrights_parent;
10977                                                         }
10978
10979                                                         // CREDITS
10980                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
10981                                                         {
10982                                                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
10983                                                                 {
10984                                                                         $credit_role = null;
10985                                                                         $credit_scheme = null;
10986                                                                         $credit_name = null;
10987                                                                         if (isset($credit['attribs']['']['role']))
10988                                                                         {
10989                                                                                 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10990                                                                         }
10991                                                                         if (isset($credit['attribs']['']['scheme']))
10992                                                                         {
10993                                                                                 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10994                                                                         }
10995                                                                         else
10996                                                                         {
10997                                                                                 $credit_scheme = 'urn:ebu';
10998                                                                         }
10999                                                                         if (isset($credit['data']))
11000                                                                         {
11001                                                                                 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11002                                                                         }
11003                                                                         $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11004                                                                 }
11005                                                                 if (is_array($credits))
11006                                                                 {
11007                                                                         $credits = array_values(array_unique($credits));
11008                                                                 }
11009                                                         }
11010                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11011                                                         {
11012                                                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11013                                                                 {
11014                                                                         $credit_role = null;
11015                                                                         $credit_scheme = null;
11016                                                                         $credit_name = null;
11017                                                                         if (isset($credit['attribs']['']['role']))
11018                                                                         {
11019                                                                                 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11020                                                                         }
11021                                                                         if (isset($credit['attribs']['']['scheme']))
11022                                                                         {
11023                                                                                 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11024                                                                         }
11025                                                                         else
11026                                                                         {
11027                                                                                 $credit_scheme = 'urn:ebu';
11028                                                                         }
11029                                                                         if (isset($credit['data']))
11030                                                                         {
11031                                                                                 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11032                                                                         }
11033                                                                         $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11034                                                                 }
11035                                                                 if (is_array($credits))
11036                                                                 {
11037                                                                         $credits = array_values(array_unique($credits));
11038                                                                 }
11039                                                         }
11040                                                         else
11041                                                         {
11042                                                                 $credits = $credits_parent;
11043                                                         }
11044
11045                                                         // DESCRIPTION
11046                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11047                                                         {
11048                                                                 $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11049                                                         }
11050                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11051                                                         {
11052                                                                 $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11053                                                         }
11054                                                         else
11055                                                         {
11056                                                                 $description = $description_parent;
11057                                                         }
11058
11059                                                         // HASHES
11060                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11061                                                         {
11062                                                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11063                                                                 {
11064                                                                         $value = null;
11065                                                                         $algo = null;
11066                                                                         if (isset($hash['data']))
11067                                                                         {
11068                                                                                 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11069                                                                         }
11070                                                                         if (isset($hash['attribs']['']['algo']))
11071                                                                         {
11072                                                                                 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11073                                                                         }
11074                                                                         else
11075                                                                         {
11076                                                                                 $algo = 'md5';
11077                                                                         }
11078                                                                         $hashes[] = $algo.':'.$value;
11079                                                                 }
11080                                                                 if (is_array($hashes))
11081                                                                 {
11082                                                                         $hashes = array_values(array_unique($hashes));
11083                                                                 }
11084                                                         }
11085                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11086                                                         {
11087                                                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11088                                                                 {
11089                                                                         $value = null;
11090                                                                         $algo = null;
11091                                                                         if (isset($hash['data']))
11092                                                                         {
11093                                                                                 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11094                                                                         }
11095                                                                         if (isset($hash['attribs']['']['algo']))
11096                                                                         {
11097                                                                                 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11098                                                                         }
11099                                                                         else
11100                                                                         {
11101                                                                                 $algo = 'md5';
11102                                                                         }
11103                                                                         $hashes[] = $algo.':'.$value;
11104                                                                 }
11105                                                                 if (is_array($hashes))
11106                                                                 {
11107                                                                         $hashes = array_values(array_unique($hashes));
11108                                                                 }
11109                                                         }
11110                                                         else
11111                                                         {
11112                                                                 $hashes = $hashes_parent;
11113                                                         }
11114
11115                                                         // KEYWORDS
11116                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11117                                                         {
11118                                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11119                                                                 {
11120                                                                         $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11121                                                                         foreach ($temp as $word)
11122                                                                         {
11123                                                                                 $keywords[] = trim($word);
11124                                                                         }
11125                                                                         unset($temp);
11126                                                                 }
11127                                                                 if (is_array($keywords))
11128                                                                 {
11129                                                                         $keywords = array_values(array_unique($keywords));
11130                                                                 }
11131                                                         }
11132                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11133                                                         {
11134                                                                 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11135                                                                 {
11136                                                                         $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11137                                                                         foreach ($temp as $word)
11138                                                                         {
11139                                                                                 $keywords[] = trim($word);
11140                                                                         }
11141                                                                         unset($temp);
11142                                                                 }
11143                                                                 if (is_array($keywords))
11144                                                                 {
11145                                                                         $keywords = array_values(array_unique($keywords));
11146                                                                 }
11147                                                         }
11148                                                         else
11149                                                         {
11150                                                                 $keywords = $keywords_parent;
11151                                                         }
11152
11153                                                         // PLAYER
11154                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11155                                                         {
11156                                                                 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11157                                                         }
11158                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11159                                                         {
11160                                                                 $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11161                                                         }
11162                                                         else
11163                                                         {
11164                                                                 $player = $player_parent;
11165                                                         }
11166
11167                                                         // RATINGS
11168                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11169                                                         {
11170                                                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11171                                                                 {
11172                                                                         $rating_scheme = null;
11173                                                                         $rating_value = null;
11174                                                                         if (isset($rating['attribs']['']['scheme']))
11175                                                                         {
11176                                                                                 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11177                                                                         }
11178                                                                         else
11179                                                                         {
11180                                                                                 $rating_scheme = 'urn:simple';
11181                                                                         }
11182                                                                         if (isset($rating['data']))
11183                                                                         {
11184                                                                                 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11185                                                                         }
11186                                                                         $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11187                                                                 }
11188                                                                 if (is_array($ratings))
11189                                                                 {
11190                                                                         $ratings = array_values(array_unique($ratings));
11191                                                                 }
11192                                                         }
11193                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11194                                                         {
11195                                                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11196                                                                 {
11197                                                                         $rating_scheme = null;
11198                                                                         $rating_value = null;
11199                                                                         if (isset($rating['attribs']['']['scheme']))
11200                                                                         {
11201                                                                                 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11202                                                                         }
11203                                                                         else
11204                                                                         {
11205                                                                                 $rating_scheme = 'urn:simple';
11206                                                                         }
11207                                                                         if (isset($rating['data']))
11208                                                                         {
11209                                                                                 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11210                                                                         }
11211                                                                         $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11212                                                                 }
11213                                                                 if (is_array($ratings))
11214                                                                 {
11215                                                                         $ratings = array_values(array_unique($ratings));
11216                                                                 }
11217                                                         }
11218                                                         else
11219                                                         {
11220                                                                 $ratings = $ratings_parent;
11221                                                         }
11222
11223                                                         // RESTRICTIONS
11224                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11225                                                         {
11226                                                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11227                                                                 {
11228                                                                         $restriction_relationship = null;
11229                                                                         $restriction_type = null;
11230                                                                         $restriction_value = null;
11231                                                                         if (isset($restriction['attribs']['']['relationship']))
11232                                                                         {
11233                                                                                 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11234                                                                         }
11235                                                                         if (isset($restriction['attribs']['']['type']))
11236                                                                         {
11237                                                                                 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11238                                                                         }
11239                                                                         if (isset($restriction['data']))
11240                                                                         {
11241                                                                                 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11242                                                                         }
11243                                                                         $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11244                                                                 }
11245                                                                 if (is_array($restrictions))
11246                                                                 {
11247                                                                         $restrictions = array_values(array_unique($restrictions));
11248                                                                 }
11249                                                         }
11250                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11251                                                         {
11252                                                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11253                                                                 {
11254                                                                         $restriction_relationship = null;
11255                                                                         $restriction_type = null;
11256                                                                         $restriction_value = null;
11257                                                                         if (isset($restriction['attribs']['']['relationship']))
11258                                                                         {
11259                                                                                 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11260                                                                         }
11261                                                                         if (isset($restriction['attribs']['']['type']))
11262                                                                         {
11263                                                                                 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11264                                                                         }
11265                                                                         if (isset($restriction['data']))
11266                                                                         {
11267                                                                                 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11268                                                                         }
11269                                                                         $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11270                                                                 }
11271                                                                 if (is_array($restrictions))
11272                                                                 {
11273                                                                         $restrictions = array_values(array_unique($restrictions));
11274                                                                 }
11275                                                         }
11276                                                         else
11277                                                         {
11278                                                                 $restrictions = $restrictions_parent;
11279                                                         }
11280
11281                                                         // THUMBNAILS
11282                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11283                                                         {
11284                                                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11285                                                                 {
11286                                                                         $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11287                                                                 }
11288                                                                 if (is_array($thumbnails))
11289                                                                 {
11290                                                                         $thumbnails = array_values(array_unique($thumbnails));
11291                                                                 }
11292                                                         }
11293                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11294                                                         {
11295                                                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11296                                                                 {
11297                                                                         $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11298                                                                 }
11299                                                                 if (is_array($thumbnails))
11300                                                                 {
11301                                                                         $thumbnails = array_values(array_unique($thumbnails));
11302                                                                 }
11303                                                         }
11304                                                         else
11305                                                         {
11306                                                                 $thumbnails = $thumbnails_parent;
11307                                                         }
11308
11309                                                         // TITLES
11310                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11311                                                         {
11312                                                                 $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11313                                                         }
11314                                                         elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11315                                                         {
11316                                                                 $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11317                                                         }
11318                                                         else
11319                                                         {
11320                                                                 $title = $title_parent;
11321                                                         }
11322
11323                                                         $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));
11324                                                 }
11325                                         }
11326                                 }
11327                         }
11328
11329                         // If we have standalone media:content tags, loop through them.
11330                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
11331                         {
11332                                 foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
11333                                 {
11334                                         if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11335                                         {
11336                                                 // Attributes
11337                                                 $bitrate = null;
11338                                                 $channels = null;
11339                                                 $duration = null;
11340                                                 $expression = null;
11341                                                 $framerate = null;
11342                                                 $height = null;
11343                                                 $javascript = null;
11344                                                 $lang = null;
11345                                                 $length = null;
11346                                                 $medium = null;
11347                                                 $samplingrate = null;
11348                                                 $type = null;
11349                                                 $url = null;
11350                                                 $width = null;
11351
11352                                                 // Elements
11353                                                 $captions = null;
11354                                                 $categories = null;
11355                                                 $copyrights = null;
11356                                                 $credits = null;
11357                                                 $description = null;
11358                                                 $hashes = null;
11359                                                 $keywords = null;
11360                                                 $player = null;
11361                                                 $ratings = null;
11362                                                 $restrictions = null;
11363                                                 $thumbnails = null;
11364                                                 $title = null;
11365
11366                                                 // Start checking the attributes of media:content
11367                                                 if (isset($content['attribs']['']['bitrate']))
11368                                                 {
11369                                                         $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11370                                                 }
11371                                                 if (isset($content['attribs']['']['channels']))
11372                                                 {
11373                                                         $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
11374                                                 }
11375                                                 if (isset($content['attribs']['']['duration']))
11376                                                 {
11377                                                         $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
11378                                                 }
11379                                                 else
11380                                                 {
11381                                                         $duration = $duration_parent;
11382                                                 }
11383                                                 if (isset($content['attribs']['']['expression']))
11384                                                 {
11385                                                         $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
11386                                                 }
11387                                                 if (isset($content['attribs']['']['framerate']))
11388                                                 {
11389                                                         $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
11390                                                 }
11391                                                 if (isset($content['attribs']['']['height']))
11392                                                 {
11393                                                         $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
11394                                                 }
11395                                                 if (isset($content['attribs']['']['lang']))
11396                                                 {
11397                                                         $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11398                                                 }
11399                                                 if (isset($content['attribs']['']['fileSize']))
11400                                                 {
11401                                                         $length = ceil($content['attribs']['']['fileSize']);
11402                                                 }
11403                                                 if (isset($content['attribs']['']['medium']))
11404                                                 {
11405                                                         $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
11406                                                 }
11407                                                 if (isset($content['attribs']['']['samplingrate']))
11408                                                 {
11409                                                         $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11410                                                 }
11411                                                 if (isset($content['attribs']['']['type']))
11412                                                 {
11413                                                         $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11414                                                 }
11415                                                 if (isset($content['attribs']['']['width']))
11416                                                 {
11417                                                         $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
11418                                                 }
11419                                                 if (isset($content['attribs']['']['url']))
11420                                                 {
11421                                                         $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11422                                                 }
11423                                                 // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
11424
11425                                                 // CAPTIONS
11426                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
11427                                                 {
11428                                                         foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
11429                                                         {
11430                                                                 $caption_type = null;
11431                                                                 $caption_lang = null;
11432                                                                 $caption_startTime = null;
11433                                                                 $caption_endTime = null;
11434                                                                 $caption_text = null;
11435                                                                 if (isset($caption['attribs']['']['type']))
11436                                                                 {
11437                                                                         $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11438                                                                 }
11439                                                                 if (isset($caption['attribs']['']['lang']))
11440                                                                 {
11441                                                                         $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11442                                                                 }
11443                                                                 if (isset($caption['attribs']['']['start']))
11444                                                                 {
11445                                                                         $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
11446                                                                 }
11447                                                                 if (isset($caption['attribs']['']['end']))
11448                                                                 {
11449                                                                         $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
11450                                                                 }
11451                                                                 if (isset($caption['data']))
11452                                                                 {
11453                                                                         $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11454                                                                 }
11455                                                                 $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
11456                                                         }
11457                                                         if (is_array($captions))
11458                                                         {
11459                                                                 $captions = array_values(array_unique($captions));
11460                                                         }
11461                                                 }
11462                                                 else
11463                                                 {
11464                                                         $captions = $captions_parent;
11465                                                 }
11466
11467                                                 // CATEGORIES
11468                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
11469                                                 {
11470                                                         foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
11471                                                         {
11472                                                                 $term = null;
11473                                                                 $scheme = null;
11474                                                                 $label = null;
11475                                                                 if (isset($category['data']))
11476                                                                 {
11477                                                                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11478                                                                 }
11479                                                                 if (isset($category['attribs']['']['scheme']))
11480                                                                 {
11481                                                                         $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11482                                                                 }
11483                                                                 else
11484                                                                 {
11485                                                                         $scheme = 'http://search.yahoo.com/mrss/category_schema';
11486                                                                 }
11487                                                                 if (isset($category['attribs']['']['label']))
11488                                                                 {
11489                                                                         $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
11490                                                                 }
11491                                                                 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
11492                                                         }
11493                                                 }
11494                                                 if (is_array($categories) && is_array($categories_parent))
11495                                                 {
11496                                                         $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
11497                                                 }
11498                                                 elseif (is_array($categories))
11499                                                 {
11500                                                         $categories = array_values(array_unique($categories));
11501                                                 }
11502                                                 elseif (is_array($categories_parent))
11503                                                 {
11504                                                         $categories = array_values(array_unique($categories_parent));
11505                                                 }
11506                                                 else
11507                                                 {
11508                                                         $categories = null;
11509                                                 }
11510
11511                                                 // COPYRIGHTS
11512                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
11513                                                 {
11514                                                         $copyright_url = null;
11515                                                         $copyright_label = null;
11516                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
11517                                                         {
11518                                                                 $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
11519                                                         }
11520                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
11521                                                         {
11522                                                                 $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11523                                                         }
11524                                                         $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
11525                                                 }
11526                                                 else
11527                                                 {
11528                                                         $copyrights = $copyrights_parent;
11529                                                 }
11530
11531                                                 // CREDITS
11532                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11533                                                 {
11534                                                         foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11535                                                         {
11536                                                                 $credit_role = null;
11537                                                                 $credit_scheme = null;
11538                                                                 $credit_name = null;
11539                                                                 if (isset($credit['attribs']['']['role']))
11540                                                                 {
11541                                                                         $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11542                                                                 }
11543                                                                 if (isset($credit['attribs']['']['scheme']))
11544                                                                 {
11545                                                                         $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11546                                                                 }
11547                                                                 else
11548                                                                 {
11549                                                                         $credit_scheme = 'urn:ebu';
11550                                                                 }
11551                                                                 if (isset($credit['data']))
11552                                                                 {
11553                                                                         $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11554                                                                 }
11555                                                                 $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11556                                                         }
11557                                                         if (is_array($credits))
11558                                                         {
11559                                                                 $credits = array_values(array_unique($credits));
11560                                                         }
11561                                                 }
11562                                                 else
11563                                                 {
11564                                                         $credits = $credits_parent;
11565                                                 }
11566
11567                                                 // DESCRIPTION
11568                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11569                                                 {
11570                                                         $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11571                                                 }
11572                                                 else
11573                                                 {
11574                                                         $description = $description_parent;
11575                                                 }
11576
11577                                                 // HASHES
11578                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11579                                                 {
11580                                                         foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11581                                                         {
11582                                                                 $value = null;
11583                                                                 $algo = null;
11584                                                                 if (isset($hash['data']))
11585                                                                 {
11586                                                                         $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11587                                                                 }
11588                                                                 if (isset($hash['attribs']['']['algo']))
11589                                                                 {
11590                                                                         $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11591                                                                 }
11592                                                                 else
11593                                                                 {
11594                                                                         $algo = 'md5';
11595                                                                 }
11596                                                                 $hashes[] = $algo.':'.$value;
11597                                                         }
11598                                                         if (is_array($hashes))
11599                                                         {
11600                                                                 $hashes = array_values(array_unique($hashes));
11601                                                         }
11602                                                 }
11603                                                 else
11604                                                 {
11605                                                         $hashes = $hashes_parent;
11606                                                 }
11607
11608                                                 // KEYWORDS
11609                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11610                                                 {
11611                                                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11612                                                         {
11613                                                                 $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11614                                                                 foreach ($temp as $word)
11615                                                                 {
11616                                                                         $keywords[] = trim($word);
11617                                                                 }
11618                                                                 unset($temp);
11619                                                         }
11620                                                         if (is_array($keywords))
11621                                                         {
11622                                                                 $keywords = array_values(array_unique($keywords));
11623                                                         }
11624                                                 }
11625                                                 else
11626                                                 {
11627                                                         $keywords = $keywords_parent;
11628                                                 }
11629
11630                                                 // PLAYER
11631                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11632                                                 {
11633                                                         $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11634                                                 }
11635                                                 else
11636                                                 {
11637                                                         $player = $player_parent;
11638                                                 }
11639
11640                                                 // RATINGS
11641                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11642                                                 {
11643                                                         foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11644                                                         {
11645                                                                 $rating_scheme = null;
11646                                                                 $rating_value = null;
11647                                                                 if (isset($rating['attribs']['']['scheme']))
11648                                                                 {
11649                                                                         $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11650                                                                 }
11651                                                                 else
11652                                                                 {
11653                                                                         $rating_scheme = 'urn:simple';
11654                                                                 }
11655                                                                 if (isset($rating['data']))
11656                                                                 {
11657                                                                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11658                                                                 }
11659                                                                 $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11660                                                         }
11661                                                         if (is_array($ratings))
11662                                                         {
11663                                                                 $ratings = array_values(array_unique($ratings));
11664                                                         }
11665                                                 }
11666                                                 else
11667                                                 {
11668                                                         $ratings = $ratings_parent;
11669                                                 }
11670
11671                                                 // RESTRICTIONS
11672                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11673                                                 {
11674                                                         foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11675                                                         {
11676                                                                 $restriction_relationship = null;
11677                                                                 $restriction_type = null;
11678                                                                 $restriction_value = null;
11679                                                                 if (isset($restriction['attribs']['']['relationship']))
11680                                                                 {
11681                                                                         $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11682                                                                 }
11683                                                                 if (isset($restriction['attribs']['']['type']))
11684                                                                 {
11685                                                                         $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11686                                                                 }
11687                                                                 if (isset($restriction['data']))
11688                                                                 {
11689                                                                         $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11690                                                                 }
11691                                                                 $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11692                                                         }
11693                                                         if (is_array($restrictions))
11694                                                         {
11695                                                                 $restrictions = array_values(array_unique($restrictions));
11696                                                         }
11697                                                 }
11698                                                 else
11699                                                 {
11700                                                         $restrictions = $restrictions_parent;
11701                                                 }
11702
11703                                                 // THUMBNAILS
11704                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11705                                                 {
11706                                                         foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11707                                                         {
11708                                                                 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11709                                                         }
11710                                                         if (is_array($thumbnails))
11711                                                         {
11712                                                                 $thumbnails = array_values(array_unique($thumbnails));
11713                                                         }
11714                                                 }
11715                                                 else
11716                                                 {
11717                                                         $thumbnails = $thumbnails_parent;
11718                                                 }
11719
11720                                                 // TITLES
11721                                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11722                                                 {
11723                                                         $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11724                                                 }
11725                                                 else
11726                                                 {
11727                                                         $title = $title_parent;
11728                                                 }
11729
11730                                                 $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));
11731                                         }
11732                                 }
11733                         }
11734
11735                         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
11736                         {
11737                                 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11738                                 {
11739                                         // Attributes
11740                                         $bitrate = null;
11741                                         $channels = null;
11742                                         $duration = null;
11743                                         $expression = null;
11744                                         $framerate = null;
11745                                         $height = null;
11746                                         $javascript = null;
11747                                         $lang = null;
11748                                         $length = null;
11749                                         $medium = null;
11750                                         $samplingrate = null;
11751                                         $type = null;
11752                                         $url = null;
11753                                         $width = null;
11754
11755                                         $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
11756                                         if (isset($link['attribs']['']['type']))
11757                                         {
11758                                                 $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11759                                         }
11760                                         if (isset($link['attribs']['']['length']))
11761                                         {
11762                                                 $length = ceil($link['attribs']['']['length']);
11763                                         }
11764
11765                                         // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11766                                         $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));
11767                                 }
11768                         }
11769
11770                         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
11771                         {
11772                                 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11773                                 {
11774                                         // Attributes
11775                                         $bitrate = null;
11776                                         $channels = null;
11777                                         $duration = null;
11778                                         $expression = null;
11779                                         $framerate = null;
11780                                         $height = null;
11781                                         $javascript = null;
11782                                         $lang = null;
11783                                         $length = null;
11784                                         $medium = null;
11785                                         $samplingrate = null;
11786                                         $type = null;
11787                                         $url = null;
11788                                         $width = null;
11789
11790                                         $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
11791                                         if (isset($link['attribs']['']['type']))
11792                                         {
11793                                                 $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11794                                         }
11795                                         if (isset($link['attribs']['']['length']))
11796                                         {
11797                                                 $length = ceil($link['attribs']['']['length']);
11798                                         }
11799
11800                                         // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11801                                         $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));
11802                                 }
11803                         }
11804
11805                         if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
11806                         {
11807                                 if (isset($enclosure[0]['attribs']['']['url']))
11808                                 {
11809                                         // Attributes
11810                                         $bitrate = null;
11811                                         $channels = null;
11812                                         $duration = null;
11813                                         $expression = null;
11814                                         $framerate = null;
11815                                         $height = null;
11816                                         $javascript = null;
11817                                         $lang = null;
11818                                         $length = null;
11819                                         $medium = null;
11820                                         $samplingrate = null;
11821                                         $type = null;
11822                                         $url = null;
11823                                         $width = null;
11824
11825                                         $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
11826                                         if (isset($enclosure[0]['attribs']['']['type']))
11827                                         {
11828                                                 $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11829                                         }
11830                                         if (isset($enclosure[0]['attribs']['']['length']))
11831                                         {
11832                                                 $length = ceil($enclosure[0]['attribs']['']['length']);
11833                                         }
11834
11835                                         // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11836                                         $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));
11837                                 }
11838                         }
11839
11840                         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))
11841                         {
11842                                 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11843                                 $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));
11844                         }
11845
11846                         $this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
11847                 }
11848                 if (!empty($this->data['enclosures']))
11849                 {
11850                         return $this->data['enclosures'];
11851                 }
11852                 else
11853                 {
11854                         return null;
11855                 }
11856         }
11857
11858         /**
11859          * Get the latitude coordinates for the item
11860          *
11861          * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
11862          *
11863          * Uses `<geo:lat>` or `<georss:point>`
11864          *
11865          * @since 1.0
11866          * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
11867          * @link http://www.georss.org/ GeoRSS
11868          * @return string|null
11869          */
11870         public function get_latitude()
11871         {
11872                 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
11873                 {
11874                         return (float) $return[0]['data'];
11875                 }
11876                 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))
11877                 {
11878                         return (float) $match[1];
11879                 }
11880                 else
11881                 {
11882                         return null;
11883                 }
11884         }
11885
11886         /**
11887          * Get the longitude coordinates for the item
11888          *
11889          * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
11890          *
11891          * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
11892          *
11893          * @since 1.0
11894          * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
11895          * @link http://www.georss.org/ GeoRSS
11896          * @return string|null
11897          */
11898         public function get_longitude()
11899         {
11900                 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
11901                 {
11902                         return (float) $return[0]['data'];
11903                 }
11904                 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
11905                 {
11906                         return (float) $return[0]['data'];
11907                 }
11908                 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))
11909                 {
11910                         return (float) $match[2];
11911                 }
11912                 else
11913                 {
11914                         return null;
11915                 }
11916         }
11917
11918         /**
11919          * Get the `<atom:source>` for the item
11920          *
11921          * @since 1.1
11922          * @return SimplePie_Source|null
11923          */
11924         public function get_source()
11925         {
11926                 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
11927                 {
11928                         return $this->registry->create('Source', array($this, $return[0]));
11929                 }
11930                 else
11931                 {
11932                         return null;
11933                 }
11934         }
11935 }
11936
11937 /**
11938  * Used for feed auto-discovery
11939  *
11940  *
11941  * This class can be overloaded with {@see SimplePie::set_locator_class()}
11942  *
11943  * @package SimplePie
11944  */
11945 class SimplePie_Locator
11946 {
11947         var $useragent;
11948         var $timeout;
11949         var $file;
11950         var $local = array();
11951         var $elsewhere = array();
11952         var $cached_entities = array();
11953         var $http_base;
11954         var $base;
11955         var $base_location = 0;
11956         var $checked_feeds = 0;
11957         var $max_checked_feeds = 10;
11958         protected $registry;
11959
11960         public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10)
11961         {
11962                 $this->file = $file;
11963                 $this->useragent = $useragent;
11964                 $this->timeout = $timeout;
11965                 $this->max_checked_feeds = $max_checked_feeds;
11966
11967                 if (class_exists('DOMDocument'))
11968                 {
11969                         $this->dom = new DOMDocument();
11970
11971                         set_error_handler(array('SimplePie_Misc', 'silence_errors'));
11972                         $this->dom->loadHTML($this->file->body);
11973                         restore_error_handler();
11974                 }
11975                 else
11976                 {
11977                         $this->dom = null;
11978                 }
11979         }
11980
11981         public function set_registry(SimplePie_Registry $registry)
11982         {
11983                 $this->registry = $registry;
11984         }
11985
11986         public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
11987         {
11988                 if ($this->is_feed($this->file))
11989                 {
11990                         return $this->file;
11991                 }
11992
11993                 if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
11994                 {
11995                         $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
11996                         if ($sniffer->get_type() !== 'text/html')
11997                         {
11998                                 return null;
11999                         }
12000                 }
12001
12002                 if ($type & ~SIMPLEPIE_LOCATOR_NONE)
12003                 {
12004                         $this->get_base();
12005                 }
12006
12007                 if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
12008                 {
12009                         return $working[0];
12010                 }
12011
12012                 if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
12013                 {
12014                         if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
12015                         {
12016                                 return $working;
12017                         }
12018
12019                         if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
12020                         {
12021                                 return $working;
12022                         }
12023
12024                         if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
12025                         {
12026                                 return $working;
12027                         }
12028
12029                         if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
12030                         {
12031                                 return $working;
12032                         }
12033                 }
12034                 return null;
12035         }
12036
12037         public function is_feed($file)
12038         {
12039                 if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
12040                 {
12041                         $sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
12042                         $sniffed = $sniffer->get_type();
12043                         if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml')))
12044                         {
12045                                 return true;
12046                         }
12047                         else
12048                         {
12049                                 return false;
12050                         }
12051                 }
12052                 elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
12053                 {
12054                         return true;
12055                 }
12056                 else
12057                 {
12058                         return false;
12059                 }
12060         }
12061
12062         public function get_base()
12063         {
12064                 if ($this->dom === null)
12065                 {
12066                         throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12067                 }
12068                 $this->http_base = $this->file->url;
12069                 $this->base = $this->http_base;
12070                 $elements = $this->dom->getElementsByTagName('base');
12071                 foreach ($elements as $element)
12072                 {
12073                         if ($element->hasAttribute('href'))
12074                         {
12075                                 $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
12076                                 if ($base === false)
12077                                 {
12078                                         continue;
12079                                 }
12080                                 $this->base = $base;
12081                                 $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
12082                                 break;
12083                         }
12084                 }
12085         }
12086
12087         public function autodiscovery()
12088         {
12089                 $done = array();
12090                 $feeds = array();
12091                 $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
12092                 $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
12093                 $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
12094
12095                 if (!empty($feeds))
12096                 {
12097                         return array_values($feeds);
12098                 }
12099                 else
12100                 {
12101                         return null;
12102                 }
12103         }
12104
12105         protected function search_elements_by_tag($name, &$done, $feeds)
12106         {
12107                 if ($this->dom === null)
12108                 {
12109                         throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12110                 }
12111
12112                 $links = $this->dom->getElementsByTagName($name);
12113                 foreach ($links as $link)
12114                 {
12115                         if ($this->checked_feeds === $this->max_checked_feeds)
12116                         {
12117                                 break;
12118                         }
12119                         if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
12120                         {
12121                                 $rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel')))));
12122                                 $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
12123
12124                                 if ($this->base_location < $line)
12125                                 {
12126                                         $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12127                                 }
12128                                 else
12129                                 {
12130                                         $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12131                                 }
12132                                 if ($href === false)
12133                                 {
12134                                         continue;
12135                                 }
12136
12137                                 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]))
12138                                 {
12139                                         $this->checked_feeds++;
12140                                         $headers = array(
12141                                                 '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',
12142                                         );
12143                                         $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent));
12144                                         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))
12145                                         {
12146                                                 $feeds[$href] = $feed;
12147                                         }
12148                                 }
12149                                 $done[] = $href;
12150                         }
12151                 }
12152
12153                 return $feeds;
12154         }
12155
12156         public function get_links()
12157         {
12158                 if ($this->dom === null)
12159                 {
12160                         throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12161                 }
12162
12163                 $links = $this->dom->getElementsByTagName('a');
12164                 foreach ($links as $link)
12165                 {
12166                         if ($link->hasAttribute('href'))
12167                         {
12168                                 $href = trim($link->getAttribute('href'));
12169                                 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
12170                                 if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme']))
12171                                 {
12172                                         if ($this->base_location < $link->getLineNo())
12173                                         {
12174                                                 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12175                                         }
12176                                         else
12177                                         {
12178                                                 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12179                                         }
12180                                         if ($href === false)
12181                                         {
12182                                                 continue;
12183                                         }
12184
12185                                         $current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
12186
12187                                         if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
12188                                         {
12189                                                 $this->local[] = $href;
12190                                         }
12191                                         else
12192                                         {
12193                                                 $this->elsewhere[] = $href;
12194                                         }
12195                                 }
12196                         }
12197                 }
12198                 $this->local = array_unique($this->local);
12199                 $this->elsewhere = array_unique($this->elsewhere);
12200                 if (!empty($this->local) || !empty($this->elsewhere))
12201                 {
12202                         return true;
12203                 }
12204                 return null;
12205         }
12206
12207         public function extension(&$array)
12208         {
12209                 foreach ($array as $key => $value)
12210                 {
12211                         if ($this->checked_feeds === $this->max_checked_feeds)
12212                         {
12213                                 break;
12214                         }
12215                         if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
12216                         {
12217                                 $this->checked_feeds++;
12218
12219                                 $headers = array(
12220                                         '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',
12221                                 );
12222                                 $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent));
12223                                 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))
12224                                 {
12225                                         return $feed;
12226                                 }
12227                                 else
12228                                 {
12229                                         unset($array[$key]);
12230                                 }
12231                         }
12232                 }
12233                 return null;
12234         }
12235
12236         public function body(&$array)
12237         {
12238                 foreach ($array as $key => $value)
12239                 {
12240                         if ($this->checked_feeds === $this->max_checked_feeds)
12241                         {
12242                                 break;
12243                         }
12244                         if (preg_match('/(rss|rdf|atom|xml)/i', $value))
12245                         {
12246                                 $this->checked_feeds++;
12247                                 $headers = array(
12248                                         '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',
12249                                 );
12250                                 $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent));
12251                                 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))
12252                                 {
12253                                         return $feed;
12254                                 }
12255                                 else
12256                                 {
12257                                         unset($array[$key]);
12258                                 }
12259                         }
12260                 }
12261                 return null;
12262         }
12263 }
12264
12265 /**
12266  * Miscellanous utilities
12267  *
12268  * @package SimplePie
12269  */
12270 class SimplePie_Misc
12271 {
12272         public static function time_hms($seconds)
12273         {
12274                 $time = '';
12275
12276                 $hours = floor($seconds / 3600);
12277                 $remainder = $seconds % 3600;
12278                 if ($hours > 0)
12279                 {
12280                         $time .= $hours.':';
12281                 }
12282
12283                 $minutes = floor($remainder / 60);
12284                 $seconds = $remainder % 60;
12285                 if ($minutes < 10 && $hours > 0)
12286                 {
12287                         $minutes = '0' . $minutes;
12288                 }
12289                 if ($seconds < 10)
12290                 {
12291                         $seconds = '0' . $seconds;
12292                 }
12293
12294                 $time .= $minutes.':';
12295                 $time .= $seconds;
12296
12297                 return $time;
12298         }
12299
12300         public static function absolutize_url($relative, $base)
12301         {
12302                 $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
12303                 if ($iri === false)
12304                 {
12305                         return false;
12306                 }
12307                 return $iri->get_uri();
12308         }
12309
12310         /**
12311          * Get a HTML/XML element from a HTML string
12312          *
12313          * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!)
12314          * @param string $realname Element name (including namespace prefix if applicable)
12315          * @param string $string HTML document
12316          * @return array
12317          */
12318         public static function get_element($realname, $string)
12319         {
12320                 $return = array();
12321                 $name = preg_quote($realname, '/');
12322                 if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
12323                 {
12324                         for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
12325                         {
12326                                 $return[$i]['tag'] = $realname;
12327                                 $return[$i]['full'] = $matches[$i][0][0];
12328                                 $return[$i]['offset'] = $matches[$i][0][1];
12329                                 if (strlen($matches[$i][3][0]) <= 2)
12330                                 {
12331                                         $return[$i]['self_closing'] = true;
12332                                 }
12333                                 else
12334                                 {
12335                                         $return[$i]['self_closing'] = false;
12336                                         $return[$i]['content'] = $matches[$i][4][0];
12337                                 }
12338                                 $return[$i]['attribs'] = array();
12339                                 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))
12340                                 {
12341                                         for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
12342                                         {
12343                                                 if (count($attribs[$j]) === 2)
12344                                                 {
12345                                                         $attribs[$j][2] = $attribs[$j][1];
12346                                                 }
12347                                                 $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8');
12348                                         }
12349                                 }
12350                         }
12351                 }
12352                 return $return;
12353         }
12354
12355         public static function element_implode($element)
12356         {
12357                 $full = "<$element[tag]";
12358                 foreach ($element['attribs'] as $key => $value)
12359                 {
12360                         $key = strtolower($key);
12361                         $full .= " $key=\"" . htmlspecialchars($value['data']) . '"';
12362                 }
12363                 if ($element['self_closing'])
12364                 {
12365                         $full .= ' />';
12366                 }
12367                 else
12368                 {
12369                         $full .= ">$element[content]</$element[tag]>";
12370                 }
12371                 return $full;
12372         }
12373
12374         public static function error($message, $level, $file, $line)
12375         {
12376                 if ((ini_get('error_reporting') & $level) > 0)
12377                 {
12378                         switch ($level)
12379                         {
12380                                 case E_USER_ERROR:
12381                                         $note = 'PHP Error';
12382                                         break;
12383                                 case E_USER_WARNING:
12384                                         $note = 'PHP Warning';
12385                                         break;
12386                                 case E_USER_NOTICE:
12387                                         $note = 'PHP Notice';
12388                                         break;
12389                                 default:
12390                                         $note = 'Unknown Error';
12391                                         break;
12392                         }
12393
12394                         $log_error = true;
12395                         if (!function_exists('error_log'))
12396                         {
12397                                 $log_error = false;
12398                         }
12399
12400                         $log_file = @ini_get('error_log');
12401                         if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file))
12402                         {
12403                                 $log_error = false;
12404                         }
12405
12406                         if ($log_error)
12407                         {
12408                                 @error_log("$note: $message in $file on line $line", 0);
12409                         }
12410                 }
12411
12412                 return $message;
12413         }
12414
12415         public static function fix_protocol($url, $http = 1)
12416         {
12417                 $url = SimplePie_Misc::normalize_url($url);
12418                 $parsed = SimplePie_Misc::parse_url($url);
12419                 if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
12420                 {
12421                         return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
12422                 }
12423
12424                 if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
12425                 {
12426                         return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
12427                 }
12428
12429                 if ($http === 2 && $parsed['scheme'] !== '')
12430                 {
12431                         return "feed:$url";
12432                 }
12433                 elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
12434                 {
12435                         return substr_replace($url, 'podcast', 0, 4);
12436                 }
12437                 elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
12438                 {
12439                         return substr_replace($url, 'itpc', 0, 4);
12440                 }
12441                 else
12442                 {
12443                         return $url;
12444                 }
12445         }
12446
12447         public static function parse_url($url)
12448         {
12449                 $iri = new SimplePie_IRI($url);
12450                 return array(
12451                         'scheme' => (string) $iri->scheme,
12452                         'authority' => (string) $iri->authority,
12453                         'path' => (string) $iri->path,
12454                         'query' => (string) $iri->query,
12455                         'fragment' => (string) $iri->fragment
12456                 );
12457         }
12458
12459         public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
12460         {
12461                 $iri = new SimplePie_IRI('');
12462                 $iri->scheme = $scheme;
12463                 $iri->authority = $authority;
12464                 $iri->path = $path;
12465                 $iri->query = $query;
12466                 $iri->fragment = $fragment;
12467                 return $iri->get_uri();
12468         }
12469
12470         public static function normalize_url($url)
12471         {
12472                 $iri = new SimplePie_IRI($url);
12473                 return $iri->get_uri();
12474         }
12475
12476         public static function percent_encoding_normalization($match)
12477         {
12478                 $integer = hexdec($match[1]);
12479                 if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
12480                 {
12481                         return chr($integer);
12482                 }
12483                 else
12484                 {
12485                         return strtoupper($match[0]);
12486                 }
12487         }
12488
12489         /**
12490          * Converts a Windows-1252 encoded string to a UTF-8 encoded string
12491          *
12492          * @static
12493          * @param string $string Windows-1252 encoded string
12494          * @return string UTF-8 encoded string
12495          */
12496         public static function windows_1252_to_utf8($string)
12497         {
12498                 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");
12499
12500                 return strtr($string, $convert_table);
12501         }
12502
12503         /**
12504          * Change a string from one encoding to another
12505          *
12506          * @param string $data Raw data in $input encoding
12507          * @param string $input Encoding of $data
12508          * @param string $output Encoding you want
12509          * @return string|boolean False if we can't convert it
12510          */
12511         public static function change_encoding($data, $input, $output)
12512         {
12513                 $input = SimplePie_Misc::encoding($input);
12514                 $output = SimplePie_Misc::encoding($output);
12515
12516                 // We fail to fail on non US-ASCII bytes
12517                 if ($input === 'US-ASCII')
12518                 {
12519                         static $non_ascii_octects = '';
12520                         if (!$non_ascii_octects)
12521                         {
12522                                 for ($i = 0x80; $i <= 0xFF; $i++)
12523                                 {
12524                                         $non_ascii_octects .= chr($i);
12525                                 }
12526                         }
12527                         $data = substr($data, 0, strcspn($data, $non_ascii_octects));
12528                 }
12529
12530                 // This is first, as behaviour of this is completely predictable
12531                 if ($input === 'windows-1252' && $output === 'UTF-8')
12532                 {
12533                         return SimplePie_Misc::windows_1252_to_utf8($data);
12534                 }
12535                 // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
12536                 elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output)))
12537                 {
12538                         return $return;
12539                 }
12540                 // This is last, as behaviour of this varies with OS userland and PHP version
12541                 elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output)))
12542                 {
12543                         return $return;
12544                 }
12545                 // If we can't do anything, just fail
12546                 else
12547                 {
12548                         return false;
12549                 }
12550         }
12551
12552         protected static function change_encoding_mbstring($data, $input, $output)
12553         {
12554                 if ($input === 'windows-949')
12555                 {
12556                         $input = 'EUC-KR';
12557                 }
12558                 if ($output === 'windows-949')
12559                 {
12560                         $output = 'EUC-KR';
12561                 }
12562                 if ($input === 'Windows-31J')
12563                 {
12564                         $input = 'SJIS';
12565                 }
12566                 if ($output === 'Windows-31J')
12567                 {
12568                         $output = 'SJIS';
12569                 }
12570
12571                 // Check that the encoding is supported
12572                 if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
12573                 {
12574                         return false;
12575                 }
12576                 if (!in_array($input, mb_list_encodings()))
12577                 {
12578                         return false;
12579                 }
12580
12581                 // Let's do some conversion
12582                 if ($return = @mb_convert_encoding($data, $output, $input))
12583                 {
12584                         return $return;
12585                 }
12586
12587                 return false;
12588         }
12589
12590         protected static function change_encoding_iconv($data, $input, $output)
12591         {
12592                 return @iconv($input, $output, $data);
12593         }
12594
12595         /**
12596          * Normalize an encoding name
12597          *
12598          * This is automatically generated by create.php
12599          *
12600          * To generate it, run `php create.php` on the command line, and copy the
12601          * output to replace this function.
12602          *
12603          * @param string $charset Character set to standardise
12604          * @return string Standardised name
12605          */
12606         public static function encoding($charset)
12607         {
12608                 // Normalization from UTS #22
12609                 switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
12610                 {
12611                         case 'adobestandardencoding':
12612                         case 'csadobestandardencoding':
12613                                 return 'Adobe-Standard-Encoding';
12614
12615                         case 'adobesymbolencoding':
12616                         case 'cshppsmath':
12617                                 return 'Adobe-Symbol-Encoding';
12618
12619                         case 'ami1251':
12620                         case 'amiga1251':
12621                                 return 'Amiga-1251';
12622
12623                         case 'ansix31101983':
12624                         case 'csat5001983':
12625                         case 'csiso99naplps':
12626                         case 'isoir99':
12627                         case 'naplps':
12628                                 return 'ANSI_X3.110-1983';
12629
12630                         case 'arabic7':
12631                         case 'asmo449':
12632                         case 'csiso89asmo449':
12633                         case 'iso9036':
12634                         case 'isoir89':
12635                                 return 'ASMO_449';
12636
12637                         case 'big5':
12638                         case 'csbig5':
12639                                 return 'Big5';
12640
12641                         case 'big5hkscs':
12642                                 return 'Big5-HKSCS';
12643
12644                         case 'bocu1':
12645                         case 'csbocu1':
12646                                 return 'BOCU-1';
12647
12648                         case 'brf':
12649                         case 'csbrf':
12650                                 return 'BRF';
12651
12652                         case 'bs4730':
12653                         case 'csiso4unitedkingdom':
12654                         case 'gb':
12655                         case 'iso646gb':
12656                         case 'isoir4':
12657                         case 'uk':
12658                                 return 'BS_4730';
12659
12660                         case 'bsviewdata':
12661                         case 'csiso47bsviewdata':
12662                         case 'isoir47':
12663                                 return 'BS_viewdata';
12664
12665                         case 'cesu8':
12666                         case 'cscesu8':
12667                                 return 'CESU-8';
12668
12669                         case 'ca':
12670                         case 'csa71':
12671                         case 'csaz243419851':
12672                         case 'csiso121canadian1':
12673                         case 'iso646ca':
12674                         case 'isoir121':
12675                                 return 'CSA_Z243.4-1985-1';
12676
12677                         case 'csa72':
12678                         case 'csaz243419852':
12679                         case 'csiso122canadian2':
12680                         case 'iso646ca2':
12681                         case 'isoir122':
12682                                 return 'CSA_Z243.4-1985-2';
12683
12684                         case 'csaz24341985gr':
12685                         case 'csiso123csaz24341985gr':
12686                         case 'isoir123':
12687                                 return 'CSA_Z243.4-1985-gr';
12688
12689                         case 'csiso139csn369103':
12690                         case 'csn369103':
12691                         case 'isoir139':
12692                                 return 'CSN_369103';
12693
12694                         case 'csdecmcs':
12695                         case 'dec':
12696                         case 'decmcs':
12697                                 return 'DEC-MCS';
12698
12699                         case 'csiso21german':
12700                         case 'de':
12701                         case 'din66003':
12702                         case 'iso646de':
12703                         case 'isoir21':
12704                                 return 'DIN_66003';
12705
12706                         case 'csdkus':
12707                         case 'dkus':
12708                                 return 'dk-us';
12709
12710                         case 'csiso646danish':
12711                         case 'dk':
12712                         case 'ds2089':
12713                         case 'iso646dk':
12714                                 return 'DS_2089';
12715
12716                         case 'csibmebcdicatde':
12717                         case 'ebcdicatde':
12718                                 return 'EBCDIC-AT-DE';
12719
12720                         case 'csebcdicatdea':
12721                         case 'ebcdicatdea':
12722                                 return 'EBCDIC-AT-DE-A';
12723
12724                         case 'csebcdiccafr':
12725                         case 'ebcdiccafr':
12726                                 return 'EBCDIC-CA-FR';
12727
12728                         case 'csebcdicdkno':
12729                         case 'ebcdicdkno':
12730                                 return 'EBCDIC-DK-NO';
12731
12732                         case 'csebcdicdknoa':
12733                         case 'ebcdicdknoa':
12734                                 return 'EBCDIC-DK-NO-A';
12735
12736                         case 'csebcdices':
12737                         case 'ebcdices':
12738                                 return 'EBCDIC-ES';
12739
12740                         case 'csebcdicesa':
12741                         case 'ebcdicesa':
12742                                 return 'EBCDIC-ES-A';
12743
12744                         case 'csebcdicess':
12745                         case 'ebcdicess':
12746                                 return 'EBCDIC-ES-S';
12747
12748                         case 'csebcdicfise':
12749                         case 'ebcdicfise':
12750                                 return 'EBCDIC-FI-SE';
12751
12752                         case 'csebcdicfisea':
12753                         case 'ebcdicfisea':
12754                                 return 'EBCDIC-FI-SE-A';
12755
12756                         case 'csebcdicfr':
12757                         case 'ebcdicfr':
12758                                 return 'EBCDIC-FR';
12759
12760                         case 'csebcdicit':
12761                         case 'ebcdicit':
12762                                 return 'EBCDIC-IT';
12763
12764                         case 'csebcdicpt':
12765                         case 'ebcdicpt':
12766                                 return 'EBCDIC-PT';
12767
12768                         case 'csebcdicuk':
12769                         case 'ebcdicuk':
12770                                 return 'EBCDIC-UK';
12771
12772                         case 'csebcdicus':
12773                         case 'ebcdicus':
12774                                 return 'EBCDIC-US';
12775
12776                         case 'csiso111ecmacyrillic':
12777                         case 'ecmacyrillic':
12778                         case 'isoir111':
12779                         case 'koi8e':
12780                                 return 'ECMA-cyrillic';
12781
12782                         case 'csiso17spanish':
12783                         case 'es':
12784                         case 'iso646es':
12785                         case 'isoir17':
12786                                 return 'ES';
12787
12788                         case 'csiso85spanish2':
12789                         case 'es2':
12790                         case 'iso646es2':
12791                         case 'isoir85':
12792                                 return 'ES2';
12793
12794                         case 'cseucpkdfmtjapanese':
12795                         case 'eucjp':
12796                         case 'extendedunixcodepackedformatforjapanese':
12797                                 return 'EUC-JP';
12798
12799                         case 'cseucfixwidjapanese':
12800                         case 'extendedunixcodefixedwidthforjapanese':
12801                                 return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
12802
12803                         case 'gb18030':
12804                                 return 'GB18030';
12805
12806                         case 'chinese':
12807                         case 'cp936':
12808                         case 'csgb2312':
12809                         case 'csiso58gb231280':
12810                         case 'gb2312':
12811                         case 'gb231280':
12812                         case 'gbk':
12813                         case 'isoir58':
12814                         case 'ms936':
12815                         case 'windows936':
12816                                 return 'GBK';
12817
12818                         case 'cn':
12819                         case 'csiso57gb1988':
12820                         case 'gb198880':
12821                         case 'iso646cn':
12822                         case 'isoir57':
12823                                 return 'GB_1988-80';
12824
12825                         case 'csiso153gost1976874':
12826                         case 'gost1976874':
12827                         case 'isoir153':
12828                         case 'stsev35888':
12829                                 return 'GOST_19768-74';
12830
12831                         case 'csiso150':
12832                         case 'csiso150greekccitt':
12833                         case 'greekccitt':
12834                         case 'isoir150':
12835                                 return 'greek-ccitt';
12836
12837                         case 'csiso88greek7':
12838                         case 'greek7':
12839                         case 'isoir88':
12840                                 return 'greek7';
12841
12842                         case 'csiso18greek7old':
12843                         case 'greek7old':
12844                         case 'isoir18':
12845                                 return 'greek7-old';
12846
12847                         case 'cshpdesktop':
12848                         case 'hpdesktop':
12849                                 return 'HP-DeskTop';
12850
12851                         case 'cshplegal':
12852                         case 'hplegal':
12853                                 return 'HP-Legal';
12854
12855                         case 'cshpmath8':
12856                         case 'hpmath8':
12857                                 return 'HP-Math8';
12858
12859                         case 'cshppifont':
12860                         case 'hppifont':
12861                                 return 'HP-Pi-font';
12862
12863                         case 'cshproman8':
12864                         case 'hproman8':
12865                         case 'r8':
12866                         case 'roman8':
12867                                 return 'hp-roman8';
12868
12869                         case 'hzgb2312':
12870                                 return 'HZ-GB-2312';
12871
12872                         case 'csibmsymbols':
12873                         case 'ibmsymbols':
12874                                 return 'IBM-Symbols';
12875
12876                         case 'csibmthai':
12877                         case 'ibmthai':
12878                                 return 'IBM-Thai';
12879
12880                         case 'cp37':
12881                         case 'csibm37':
12882                         case 'ebcdiccpca':
12883                         case 'ebcdiccpnl':
12884                         case 'ebcdiccpus':
12885                         case 'ebcdiccpwt':
12886                         case 'ibm37':
12887                                 return 'IBM037';
12888
12889                         case 'cp38':
12890                         case 'csibm38':
12891                         case 'ebcdicint':
12892                         case 'ibm38':
12893                                 return 'IBM038';
12894
12895                         case 'cp273':
12896                         case 'csibm273':
12897                         case 'ibm273':
12898                                 return 'IBM273';
12899
12900                         case 'cp274':
12901                         case 'csibm274':
12902                         case 'ebcdicbe':
12903                         case 'ibm274':
12904                                 return 'IBM274';
12905
12906                         case 'cp275':
12907                         case 'csibm275':
12908                         case 'ebcdicbr':
12909                         case 'ibm275':
12910                                 return 'IBM275';
12911
12912                         case 'csibm277':
12913                         case 'ebcdiccpdk':
12914                         case 'ebcdiccpno':
12915                         case 'ibm277':
12916                                 return 'IBM277';
12917
12918                         case 'cp278':
12919                         case 'csibm278':
12920                         case 'ebcdiccpfi':
12921                         case 'ebcdiccpse':
12922                         case 'ibm278':
12923                                 return 'IBM278';
12924
12925                         case 'cp280':
12926                         case 'csibm280':
12927                         case 'ebcdiccpit':
12928                         case 'ibm280':
12929                                 return 'IBM280';
12930
12931                         case 'cp281':
12932                         case 'csibm281':
12933                         case 'ebcdicjpe':
12934                         case 'ibm281':
12935                                 return 'IBM281';
12936
12937                         case 'cp284':
12938                         case 'csibm284':
12939                         case 'ebcdiccpes':
12940                         case 'ibm284':
12941                                 return 'IBM284';
12942
12943                         case 'cp285':
12944                         case 'csibm285':
12945                         case 'ebcdiccpgb':
12946                         case 'ibm285':
12947                                 return 'IBM285';
12948
12949                         case 'cp290':
12950                         case 'csibm290':
12951                         case 'ebcdicjpkana':
12952                         case 'ibm290':
12953                                 return 'IBM290';
12954
12955                         case 'cp297':
12956                         case 'csibm297':
12957                         case 'ebcdiccpfr':
12958                         case 'ibm297':
12959                                 return 'IBM297';
12960
12961                         case 'cp420':
12962                         case 'csibm420':
12963                         case 'ebcdiccpar1':
12964                         case 'ibm420':
12965                                 return 'IBM420';
12966
12967                         case 'cp423':
12968                         case 'csibm423':
12969                         case 'ebcdiccpgr':
12970                         case 'ibm423':
12971                                 return 'IBM423';
12972
12973                         case 'cp424':
12974                         case 'csibm424':
12975                         case 'ebcdiccphe':
12976                         case 'ibm424':
12977                                 return 'IBM424';
12978
12979                         case '437':
12980                         case 'cp437':
12981                         case 'cspc8codepage437':
12982                         case 'ibm437':
12983                                 return 'IBM437';
12984
12985                         case 'cp500':
12986                         case 'csibm500':
12987                         case 'ebcdiccpbe':
12988                         case 'ebcdiccpch':
12989                         case 'ibm500':
12990                                 return 'IBM500';
12991
12992                         case 'cp775':
12993                         case 'cspc775baltic':
12994                         case 'ibm775':
12995                                 return 'IBM775';
12996
12997                         case '850':
12998                         case 'cp850':
12999                         case 'cspc850multilingual':
13000                         case 'ibm850':
13001                                 return 'IBM850';
13002
13003                         case '851':
13004                         case 'cp851':
13005                         case 'csibm851':
13006                         case 'ibm851':
13007                                 return 'IBM851';
13008
13009                         case '852':
13010                         case 'cp852':
13011                         case 'cspcp852':
13012                         case 'ibm852':
13013                                 return 'IBM852';
13014
13015                         case '855':
13016                         case 'cp855':
13017                         case 'csibm855':
13018                         case 'ibm855':
13019                                 return 'IBM855';
13020
13021                         case '857':
13022                         case 'cp857':
13023                         case 'csibm857':
13024                         case 'ibm857':
13025                                 return 'IBM857';
13026
13027                         case 'ccsid858':
13028                         case 'cp858':
13029                         case 'ibm858':
13030                         case 'pcmultilingual850euro':
13031                                 return 'IBM00858';
13032
13033                         case '860':
13034                         case 'cp860':
13035                         case 'csibm860':
13036                         case 'ibm860':
13037                                 return 'IBM860';
13038
13039                         case '861':
13040                         case 'cp861':
13041                         case 'cpis':
13042                         case 'csibm861':
13043                         case 'ibm861':
13044                                 return 'IBM861';
13045
13046                         case '862':
13047                         case 'cp862':
13048                         case 'cspc862latinhebrew':
13049                         case 'ibm862':
13050                                 return 'IBM862';
13051
13052                         case '863':
13053                         case 'cp863':
13054                         case 'csibm863':
13055                         case 'ibm863':
13056                                 return 'IBM863';
13057
13058                         case 'cp864':
13059                         case 'csibm864':
13060                         case 'ibm864':
13061                                 return 'IBM864';
13062
13063                         case '865':
13064                         case 'cp865':
13065                         case 'csibm865':
13066                         case 'ibm865':
13067                                 return 'IBM865';
13068
13069                         case '866':
13070                         case 'cp866':
13071                         case 'csibm866':
13072                         case 'ibm866':
13073                                 return 'IBM866';
13074
13075                         case 'cp868':
13076                         case 'cpar':
13077                         case 'csibm868':
13078                         case 'ibm868':
13079                                 return 'IBM868';
13080
13081                         case '869':
13082                         case 'cp869':
13083                         case 'cpgr':
13084                         case 'csibm869':
13085                         case 'ibm869':
13086                                 return 'IBM869';
13087
13088                         case 'cp870':
13089                         case 'csibm870':
13090                         case 'ebcdiccproece':
13091                         case 'ebcdiccpyu':
13092                         case 'ibm870':
13093                                 return 'IBM870';
13094
13095                         case 'cp871':
13096                         case 'csibm871':
13097                         case 'ebcdiccpis':
13098                         case 'ibm871':
13099                                 return 'IBM871';
13100
13101                         case 'cp880':
13102                         case 'csibm880':
13103                         case 'ebcdiccyrillic':
13104                         case 'ibm880':
13105                                 return 'IBM880';
13106
13107                         case 'cp891':
13108                         case 'csibm891':
13109                         case 'ibm891':
13110                                 return 'IBM891';
13111
13112                         case 'cp903':
13113                         case 'csibm903':
13114                         case 'ibm903':
13115                                 return 'IBM903';
13116
13117                         case '904':
13118                         case 'cp904':
13119                         case 'csibbm904':
13120                         case 'ibm904':
13121                                 return 'IBM904';
13122
13123                         case 'cp905':
13124                         case 'csibm905':
13125                         case 'ebcdiccptr':
13126                         case 'ibm905':
13127                                 return 'IBM905';
13128
13129                         case 'cp918':
13130                         case 'csibm918':
13131                         case 'ebcdiccpar2':
13132                         case 'ibm918':
13133                                 return 'IBM918';
13134
13135                         case 'ccsid924':
13136                         case 'cp924':
13137                         case 'ebcdiclatin9euro':
13138                         case 'ibm924':
13139                                 return 'IBM00924';
13140
13141                         case 'cp1026':
13142                         case 'csibm1026':
13143                         case 'ibm1026':
13144                                 return 'IBM1026';
13145
13146                         case 'ibm1047':
13147                                 return 'IBM1047';
13148
13149                         case 'ccsid1140':
13150                         case 'cp1140':
13151                         case 'ebcdicus37euro':
13152                         case 'ibm1140':
13153                                 return 'IBM01140';
13154
13155                         case 'ccsid1141':
13156                         case 'cp1141':
13157                         case 'ebcdicde273euro':
13158                         case 'ibm1141':
13159                                 return 'IBM01141';
13160
13161                         case 'ccsid1142':
13162                         case 'cp1142':
13163                         case 'ebcdicdk277euro':
13164                         case 'ebcdicno277euro':
13165                         case 'ibm1142':
13166                                 return 'IBM01142';
13167
13168                         case 'ccsid1143':
13169                         case 'cp1143':
13170                         case 'ebcdicfi278euro':
13171                         case 'ebcdicse278euro':
13172                         case 'ibm1143':
13173                                 return 'IBM01143';
13174
13175                         case 'ccsid1144':
13176                         case 'cp1144':
13177                         case 'ebcdicit280euro':
13178                         case 'ibm1144':
13179                                 return 'IBM01144';
13180
13181                         case 'ccsid1145':
13182                         case 'cp1145':
13183                         case 'ebcdices284euro':
13184                         case 'ibm1145':
13185                                 return 'IBM01145';
13186
13187                         case 'ccsid1146':
13188                         case 'cp1146':
13189                         case 'ebcdicgb285euro':
13190                         case 'ibm1146':
13191                                 return 'IBM01146';
13192
13193                         case 'ccsid1147':
13194                         case 'cp1147':
13195                         case 'ebcdicfr297euro':
13196                         case 'ibm1147':
13197                                 return 'IBM01147';
13198
13199                         case 'ccsid1148':
13200                         case 'cp1148':
13201                         case 'ebcdicinternational500euro':
13202                         case 'ibm1148':
13203                                 return 'IBM01148';
13204
13205                         case 'ccsid1149':
13206                         case 'cp1149':
13207                         case 'ebcdicis871euro':
13208                         case 'ibm1149':
13209                                 return 'IBM01149';
13210
13211                         case 'csiso143iecp271':
13212                         case 'iecp271':
13213                         case 'isoir143':
13214                                 return 'IEC_P27-1';
13215
13216                         case 'csiso49inis':
13217                         case 'inis':
13218                         case 'isoir49':
13219                                 return 'INIS';
13220
13221                         case 'csiso50inis8':
13222                         case 'inis8':
13223                         case 'isoir50':
13224                                 return 'INIS-8';
13225
13226                         case 'csiso51iniscyrillic':
13227                         case 'iniscyrillic':
13228                         case 'isoir51':
13229                                 return 'INIS-cyrillic';
13230
13231                         case 'csinvariant':
13232                         case 'invariant':
13233                                 return 'INVARIANT';
13234
13235                         case 'iso2022cn':
13236                                 return 'ISO-2022-CN';
13237
13238                         case 'iso2022cnext':
13239                                 return 'ISO-2022-CN-EXT';
13240
13241                         case 'csiso2022jp':
13242                         case 'iso2022jp':
13243                                 return 'ISO-2022-JP';
13244
13245                         case 'csiso2022jp2':
13246                         case 'iso2022jp2':
13247                                 return 'ISO-2022-JP-2';
13248
13249                         case 'csiso2022kr':
13250                         case 'iso2022kr':
13251                                 return 'ISO-2022-KR';
13252
13253                         case 'cswindows30latin1':
13254                         case 'iso88591windows30latin1':
13255                                 return 'ISO-8859-1-Windows-3.0-Latin-1';
13256
13257                         case 'cswindows31latin1':
13258                         case 'iso88591windows31latin1':
13259                                 return 'ISO-8859-1-Windows-3.1-Latin-1';
13260
13261                         case 'csisolatin2':
13262                         case 'iso88592':
13263                         case 'iso885921987':
13264                         case 'isoir101':
13265                         case 'l2':
13266                         case 'latin2':
13267                                 return 'ISO-8859-2';
13268
13269                         case 'cswindows31latin2':
13270                         case 'iso88592windowslatin2':
13271                                 return 'ISO-8859-2-Windows-Latin-2';
13272
13273                         case 'csisolatin3':
13274                         case 'iso88593':
13275                         case 'iso885931988':
13276                         case 'isoir109':
13277                         case 'l3':
13278                         case 'latin3':
13279                                 return 'ISO-8859-3';
13280
13281                         case 'csisolatin4':
13282                         case 'iso88594':
13283                         case 'iso885941988':
13284                         case 'isoir110':
13285                         case 'l4':
13286                         case 'latin4':
13287                                 return 'ISO-8859-4';
13288
13289                         case 'csisolatincyrillic':
13290                         case 'cyrillic':
13291                         case 'iso88595':
13292                         case 'iso885951988':
13293                         case 'isoir144':
13294                                 return 'ISO-8859-5';
13295
13296                         case 'arabic':
13297                         case 'asmo708':
13298                         case 'csisolatinarabic':
13299                         case 'ecma114':
13300                         case 'iso88596':
13301                         case 'iso885961987':
13302                         case 'isoir127':
13303                                 return 'ISO-8859-6';
13304
13305                         case 'csiso88596e':
13306                         case 'iso88596e':
13307                                 return 'ISO-8859-6-E';
13308
13309                         case 'csiso88596i':
13310                         case 'iso88596i':
13311                                 return 'ISO-8859-6-I';
13312
13313                         case 'csisolatingreek':
13314                         case 'ecma118':
13315                         case 'elot928':
13316                         case 'greek':
13317                         case 'greek8':
13318                         case 'iso88597':
13319                         case 'iso885971987':
13320                         case 'isoir126':
13321                                 return 'ISO-8859-7';
13322
13323                         case 'csisolatinhebrew':
13324                         case 'hebrew':
13325                         case 'iso88598':
13326                         case 'iso885981988':
13327                         case 'isoir138':
13328                                 return 'ISO-8859-8';
13329
13330                         case 'csiso88598e':
13331                         case 'iso88598e':
13332                                 return 'ISO-8859-8-E';
13333
13334                         case 'csiso88598i':
13335                         case 'iso88598i':
13336                                 return 'ISO-8859-8-I';
13337
13338                         case 'cswindows31latin5':
13339                         case 'iso88599windowslatin5':
13340                                 return 'ISO-8859-9-Windows-Latin-5';
13341
13342                         case 'csisolatin6':
13343                         case 'iso885910':
13344                         case 'iso8859101992':
13345                         case 'isoir157':
13346                         case 'l6':
13347                         case 'latin6':
13348                                 return 'ISO-8859-10';
13349
13350                         case 'iso885913':
13351                                 return 'ISO-8859-13';
13352
13353                         case 'iso885914':
13354                         case 'iso8859141998':
13355                         case 'isoceltic':
13356                         case 'isoir199':
13357                         case 'l8':
13358                         case 'latin8':
13359                                 return 'ISO-8859-14';
13360
13361                         case 'iso885915':
13362                         case 'latin9':
13363                                 return 'ISO-8859-15';
13364
13365                         case 'iso885916':
13366                         case 'iso8859162001':
13367                         case 'isoir226':
13368                         case 'l10':
13369                         case 'latin10':
13370                                 return 'ISO-8859-16';
13371
13372                         case 'iso10646j1':
13373                                 return 'ISO-10646-J-1';
13374
13375                         case 'csunicode':
13376                         case 'iso10646ucs2':
13377                                 return 'ISO-10646-UCS-2';
13378
13379                         case 'csucs4':
13380                         case 'iso10646ucs4':
13381                                 return 'ISO-10646-UCS-4';
13382
13383                         case 'csunicodeascii':
13384                         case 'iso10646ucsbasic':
13385                                 return 'ISO-10646-UCS-Basic';
13386
13387                         case 'csunicodelatin1':
13388                         case 'iso10646':
13389                         case 'iso10646unicodelatin1':
13390                                 return 'ISO-10646-Unicode-Latin1';
13391
13392                         case 'csiso10646utf1':
13393                         case 'iso10646utf1':
13394                                 return 'ISO-10646-UTF-1';
13395
13396                         case 'csiso115481':
13397                         case 'iso115481':
13398                         case 'isotr115481':
13399                                 return 'ISO-11548-1';
13400
13401                         case 'csiso90':
13402                         case 'isoir90':
13403                                 return 'iso-ir-90';
13404
13405                         case 'csunicodeibm1261':
13406                         case 'isounicodeibm1261':
13407                                 return 'ISO-Unicode-IBM-1261';
13408
13409                         case 'csunicodeibm1264':
13410                         case 'isounicodeibm1264':
13411                                 return 'ISO-Unicode-IBM-1264';
13412
13413                         case 'csunicodeibm1265':
13414                         case 'isounicodeibm1265':
13415                                 return 'ISO-Unicode-IBM-1265';
13416
13417                         case 'csunicodeibm1268':
13418                         case 'isounicodeibm1268':
13419                                 return 'ISO-Unicode-IBM-1268';
13420
13421                         case 'csunicodeibm1276':
13422                         case 'isounicodeibm1276':
13423                                 return 'ISO-Unicode-IBM-1276';
13424
13425                         case 'csiso646basic1983':
13426                         case 'iso646basic1983':
13427                         case 'ref':
13428                                 return 'ISO_646.basic:1983';
13429
13430                         case 'csiso2intlrefversion':
13431                         case 'irv':
13432                         case 'iso646irv1983':
13433                         case 'isoir2':
13434                                 return 'ISO_646.irv:1983';
13435
13436                         case 'csiso2033':
13437                         case 'e13b':
13438                         case 'iso20331983':
13439                         case 'isoir98':
13440                                 return 'ISO_2033-1983';
13441
13442                         case 'csiso5427cyrillic':
13443                         case 'iso5427':
13444                         case 'isoir37':
13445                                 return 'ISO_5427';
13446
13447                         case 'iso5427cyrillic1981':
13448                         case 'iso54271981':
13449                         case 'isoir54':
13450                                 return 'ISO_5427:1981';
13451
13452                         case 'csiso5428greek':
13453                         case 'iso54281980':
13454                         case 'isoir55':
13455                                 return 'ISO_5428:1980';
13456
13457                         case 'csiso6937add':
13458                         case 'iso6937225':
13459                         case 'isoir152':
13460                                 return 'ISO_6937-2-25';
13461
13462                         case 'csisotextcomm':
13463                         case 'iso69372add':
13464                         case 'isoir142':
13465                                 return 'ISO_6937-2-add';
13466
13467                         case 'csiso8859supp':
13468                         case 'iso8859supp':
13469                         case 'isoir154':
13470                         case 'latin125':
13471                                 return 'ISO_8859-supp';
13472
13473                         case 'csiso10367box':
13474                         case 'iso10367box':
13475                         case 'isoir155':
13476                                 return 'ISO_10367-box';
13477
13478                         case 'csiso15italian':
13479                         case 'iso646it':
13480                         case 'isoir15':
13481                         case 'it':
13482                                 return 'IT';
13483
13484                         case 'csiso13jisc6220jp':
13485                         case 'isoir13':
13486                         case 'jisc62201969':
13487                         case 'jisc62201969jp':
13488                         case 'katakana':
13489                         case 'x2017':
13490                                 return 'JIS_C6220-1969-jp';
13491
13492                         case 'csiso14jisc6220ro':
13493                         case 'iso646jp':
13494                         case 'isoir14':
13495                         case 'jisc62201969ro':
13496                         case 'jp':
13497                                 return 'JIS_C6220-1969-ro';
13498
13499                         case 'csiso42jisc62261978':
13500                         case 'isoir42':
13501                         case 'jisc62261978':
13502                                 return 'JIS_C6226-1978';
13503
13504                         case 'csiso87jisx208':
13505                         case 'isoir87':
13506                         case 'jisc62261983':
13507                         case 'jisx2081983':
13508                         case 'x208':
13509                                 return 'JIS_C6226-1983';
13510
13511                         case 'csiso91jisc62291984a':
13512                         case 'isoir91':
13513                         case 'jisc62291984a':
13514                         case 'jpocra':
13515                                 return 'JIS_C6229-1984-a';
13516
13517                         case 'csiso92jisc62991984b':
13518                         case 'iso646jpocrb':
13519                         case 'isoir92':
13520                         case 'jisc62291984b':
13521                         case 'jpocrb':
13522                                 return 'JIS_C6229-1984-b';
13523
13524                         case 'csiso93jis62291984badd':
13525                         case 'isoir93':
13526                         case 'jisc62291984badd':
13527                         case 'jpocrbadd':
13528                                 return 'JIS_C6229-1984-b-add';
13529
13530                         case 'csiso94jis62291984hand':
13531                         case 'isoir94':
13532                         case 'jisc62291984hand':
13533                         case 'jpocrhand':
13534                                 return 'JIS_C6229-1984-hand';
13535
13536                         case 'csiso95jis62291984handadd':
13537                         case 'isoir95':
13538                         case 'jisc62291984handadd':
13539                         case 'jpocrhandadd':
13540                                 return 'JIS_C6229-1984-hand-add';
13541
13542                         case 'csiso96jisc62291984kana':
13543                         case 'isoir96':
13544                         case 'jisc62291984kana':
13545                                 return 'JIS_C6229-1984-kana';
13546
13547                         case 'csjisencoding':
13548                         case 'jisencoding':
13549                                 return 'JIS_Encoding';
13550
13551                         case 'cshalfwidthkatakana':
13552                         case 'jisx201':
13553                         case 'x201':
13554                                 return 'JIS_X0201';
13555
13556                         case 'csiso159jisx2121990':
13557                         case 'isoir159':
13558                         case 'jisx2121990':
13559                         case 'x212':
13560                                 return 'JIS_X0212-1990';
13561
13562                         case 'csiso141jusib1002':
13563                         case 'iso646yu':
13564                         case 'isoir141':
13565                         case 'js':
13566                         case 'jusib1002':
13567                         case 'yu':
13568                                 return 'JUS_I.B1.002';
13569
13570                         case 'csiso147macedonian':
13571                         case 'isoir147':
13572                         case 'jusib1003mac':
13573                         case 'macedonian':
13574                                 return 'JUS_I.B1.003-mac';
13575
13576                         case 'csiso146serbian':
13577                         case 'isoir146':
13578                         case 'jusib1003serb':
13579                         case 'serbian':
13580                                 return 'JUS_I.B1.003-serb';
13581
13582                         case 'koi7switched':
13583                                 return 'KOI7-switched';
13584
13585                         case 'cskoi8r':
13586                         case 'koi8r':
13587                                 return 'KOI8-R';
13588
13589                         case 'koi8u':
13590                                 return 'KOI8-U';
13591
13592                         case 'csksc5636':
13593                         case 'iso646kr':
13594                         case 'ksc5636':
13595                                 return 'KSC5636';
13596
13597                         case 'cskz1048':
13598                         case 'kz1048':
13599                         case 'rk1048':
13600                         case 'strk10482002':
13601                                 return 'KZ-1048';
13602
13603                         case 'csiso19latingreek':
13604                         case 'isoir19':
13605                         case 'latingreek':
13606                                 return 'latin-greek';
13607
13608                         case 'csiso27latingreek1':
13609                         case 'isoir27':
13610                         case 'latingreek1':
13611                                 return 'Latin-greek-1';
13612
13613                         case 'csiso158lap':
13614                         case 'isoir158':
13615                         case 'lap':
13616                         case 'latinlap':
13617                                 return 'latin-lap';
13618
13619                         case 'csmacintosh':
13620                         case 'mac':
13621                         case 'macintosh':
13622                                 return 'macintosh';
13623
13624                         case 'csmicrosoftpublishing':
13625                         case 'microsoftpublishing':
13626                                 return 'Microsoft-Publishing';
13627
13628                         case 'csmnem':
13629                         case 'mnem':
13630                                 return 'MNEM';
13631
13632                         case 'csmnemonic':
13633                         case 'mnemonic':
13634                                 return 'MNEMONIC';
13635
13636                         case 'csiso86hungarian':
13637                         case 'hu':
13638                         case 'iso646hu':
13639                         case 'isoir86':
13640                         case 'msz77953':
13641                                 return 'MSZ_7795.3';
13642
13643                         case 'csnatsdano':
13644                         case 'isoir91':
13645                         case 'natsdano':
13646                                 return 'NATS-DANO';
13647
13648                         case 'csnatsdanoadd':
13649                         case 'isoir92':
13650                         case 'natsdanoadd':
13651                                 return 'NATS-DANO-ADD';
13652
13653                         case 'csnatssefi':
13654                         case 'isoir81':
13655                         case 'natssefi':
13656                                 return 'NATS-SEFI';
13657
13658                         case 'csnatssefiadd':
13659                         case 'isoir82':
13660                         case 'natssefiadd':
13661                                 return 'NATS-SEFI-ADD';
13662
13663                         case 'csiso151cuba':
13664                         case 'cuba':
13665                         case 'iso646cu':
13666                         case 'isoir151':
13667                         case 'ncnc1081':
13668                                 return 'NC_NC00-10:81';
13669
13670                         case 'csiso69french':
13671                         case 'fr':
13672                         case 'iso646fr':
13673                         case 'isoir69':
13674                         case 'nfz62010':
13675                                 return 'NF_Z_62-010';
13676
13677                         case 'csiso25french':
13678                         case 'iso646fr1':
13679                         case 'isoir25':
13680                         case 'nfz620101973':
13681                                 return 'NF_Z_62-010_(1973)';
13682
13683                         case 'csiso60danishnorwegian':
13684                         case 'csiso60norwegian1':
13685                         case 'iso646no':
13686                         case 'isoir60':
13687                         case 'no':
13688                         case 'ns45511':
13689                                 return 'NS_4551-1';
13690
13691                         case 'csiso61norwegian2':
13692                         case 'iso646no2':
13693                         case 'isoir61':
13694                         case 'no2':
13695                         case 'ns45512':
13696                                 return 'NS_4551-2';
13697
13698                         case 'osdebcdicdf3irv':
13699                                 return 'OSD_EBCDIC_DF03_IRV';
13700
13701                         case 'osdebcdicdf41':
13702                                 return 'OSD_EBCDIC_DF04_1';
13703
13704                         case 'osdebcdicdf415':
13705                                 return 'OSD_EBCDIC_DF04_15';
13706
13707                         case 'cspc8danishnorwegian':
13708                         case 'pc8danishnorwegian':
13709                                 return 'PC8-Danish-Norwegian';
13710
13711                         case 'cspc8turkish':
13712                         case 'pc8turkish':
13713                                 return 'PC8-Turkish';
13714
13715                         case 'csiso16portuguese':
13716                         case 'iso646pt':
13717                         case 'isoir16':
13718                         case 'pt':
13719                                 return 'PT';
13720
13721                         case 'csiso84portuguese2':
13722                         case 'iso646pt2':
13723                         case 'isoir84':
13724                         case 'pt2':
13725                                 return 'PT2';
13726
13727                         case 'cp154':
13728                         case 'csptcp154':
13729                         case 'cyrillicasian':
13730                         case 'pt154':
13731                         case 'ptcp154':
13732                                 return 'PTCP154';
13733
13734                         case 'scsu':
13735                                 return 'SCSU';
13736
13737                         case 'csiso10swedish':
13738                         case 'fi':
13739                         case 'iso646fi':
13740                         case 'iso646se':
13741                         case 'isoir10':
13742                         case 'se':
13743                         case 'sen850200b':
13744                                 return 'SEN_850200_B';
13745
13746                         case 'csiso11swedishfornames':
13747                         case 'iso646se2':
13748                         case 'isoir11':
13749                         case 'se2':
13750                         case 'sen850200c':
13751                                 return 'SEN_850200_C';
13752
13753                         case 'csiso102t617bit':
13754                         case 'isoir102':
13755                         case 't617bit':
13756                                 return 'T.61-7bit';
13757
13758                         case 'csiso103t618bit':
13759                         case 'isoir103':
13760                         case 't61':
13761                         case 't618bit':
13762                                 return 'T.61-8bit';
13763
13764                         case 'csiso128t101g2':
13765                         case 'isoir128':
13766                         case 't101g2':
13767                                 return 'T.101-G2';
13768
13769                         case 'cstscii':
13770                         case 'tscii':
13771                                 return 'TSCII';
13772
13773                         case 'csunicode11':
13774                         case 'unicode11':
13775                                 return 'UNICODE-1-1';
13776
13777                         case 'csunicode11utf7':
13778                         case 'unicode11utf7':
13779                                 return 'UNICODE-1-1-UTF-7';
13780
13781                         case 'csunknown8bit':
13782                         case 'unknown8bit':
13783                                 return 'UNKNOWN-8BIT';
13784
13785                         case 'ansix341968':
13786                         case 'ansix341986':
13787                         case 'ascii':
13788                         case 'cp367':
13789                         case 'csascii':
13790                         case 'ibm367':
13791                         case 'iso646irv1991':
13792                         case 'iso646us':
13793                         case 'isoir6':
13794                         case 'us':
13795                         case 'usascii':
13796                                 return 'US-ASCII';
13797
13798                         case 'csusdk':
13799                         case 'usdk':
13800                                 return 'us-dk';
13801
13802                         case 'utf7':
13803                                 return 'UTF-7';
13804
13805                         case 'utf8':
13806                                 return 'UTF-8';
13807
13808                         case 'utf16':
13809                                 return 'UTF-16';
13810
13811                         case 'utf16be':
13812                                 return 'UTF-16BE';
13813
13814                         case 'utf16le':
13815                                 return 'UTF-16LE';
13816
13817                         case 'utf32':
13818                                 return 'UTF-32';
13819
13820                         case 'utf32be':
13821                                 return 'UTF-32BE';
13822
13823                         case 'utf32le':
13824                                 return 'UTF-32LE';
13825
13826                         case 'csventurainternational':
13827                         case 'venturainternational':
13828                                 return 'Ventura-International';
13829
13830                         case 'csventuramath':
13831                         case 'venturamath':
13832                                 return 'Ventura-Math';
13833
13834                         case 'csventuraus':
13835                         case 'venturaus':
13836                                 return 'Ventura-US';
13837
13838                         case 'csiso70videotexsupp1':
13839                         case 'isoir70':
13840                         case 'videotexsuppl':
13841                                 return 'videotex-suppl';
13842
13843                         case 'csviqr':
13844                         case 'viqr':
13845                                 return 'VIQR';
13846
13847                         case 'csviscii':
13848                         case 'viscii':
13849                                 return 'VISCII';
13850
13851                         case 'csshiftjis':
13852                         case 'cswindows31j':
13853                         case 'mskanji':
13854                         case 'shiftjis':
13855                         case 'windows31j':
13856                                 return 'Windows-31J';
13857
13858                         case 'iso885911':
13859                         case 'tis620':
13860                                 return 'windows-874';
13861
13862                         case 'cseuckr':
13863                         case 'csksc56011987':
13864                         case 'euckr':
13865                         case 'isoir149':
13866                         case 'korean':
13867                         case 'ksc5601':
13868                         case 'ksc56011987':
13869                         case 'ksc56011989':
13870                         case 'windows949':
13871                                 return 'windows-949';
13872
13873                         case 'windows1250':
13874                                 return 'windows-1250';
13875
13876                         case 'windows1251':
13877                                 return 'windows-1251';
13878
13879                         case 'cp819':
13880                         case 'csisolatin1':
13881                         case 'ibm819':
13882                         case 'iso88591':
13883                         case 'iso885911987':
13884                         case 'isoir100':
13885                         case 'l1':
13886                         case 'latin1':
13887                         case 'windows1252':
13888                                 return 'windows-1252';
13889
13890                         case 'windows1253':
13891                                 return 'windows-1253';
13892
13893                         case 'csisolatin5':
13894                         case 'iso88599':
13895                         case 'iso885991989':
13896                         case 'isoir148':
13897                         case 'l5':
13898                         case 'latin5':
13899                         case 'windows1254':
13900                                 return 'windows-1254';
13901
13902                         case 'windows1255':
13903                                 return 'windows-1255';
13904
13905                         case 'windows1256':
13906                                 return 'windows-1256';
13907
13908                         case 'windows1257':
13909                                 return 'windows-1257';
13910
13911                         case 'windows1258':
13912                                 return 'windows-1258';
13913
13914                         default:
13915                                 return $charset;
13916                 }
13917         }
13918
13919         public static function get_curl_version()
13920         {
13921                 if (is_array($curl = curl_version()))
13922                 {
13923                         $curl = $curl['version'];
13924                 }
13925                 elseif (substr($curl, 0, 5) === 'curl/')
13926                 {
13927                         $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
13928                 }
13929                 elseif (substr($curl, 0, 8) === 'libcurl/')
13930                 {
13931                         $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
13932                 }
13933                 else
13934                 {
13935                         $curl = 0;
13936                 }
13937                 return $curl;
13938         }
13939
13940         /**
13941          * Strip HTML comments
13942          *
13943          * @param string $data Data to strip comments from
13944          * @return string Comment stripped string
13945          */
13946         public static function strip_comments($data)
13947         {
13948                 $output = '';
13949                 while (($start = strpos($data, '<!--')) !== false)
13950                 {
13951                         $output .= substr($data, 0, $start);
13952                         if (($end = strpos($data, '-->', $start)) !== false)
13953                         {
13954                                 $data = substr_replace($data, '', 0, $end + 3);
13955                         }
13956                         else
13957                         {
13958                                 $data = '';
13959                         }
13960                 }
13961                 return $output . $data;
13962         }
13963
13964         public static function parse_date($dt)
13965         {
13966                 $parser = SimplePie_Parse_Date::get();
13967                 return $parser->parse($dt);
13968         }
13969
13970         /**
13971          * Decode HTML entities
13972          *
13973          * @deprecated Use DOMDocument instead
13974          * @param string $data Input data
13975          * @return string Output data
13976          */
13977         public static function entities_decode($data)
13978         {
13979                 $decoder = new SimplePie_Decode_HTML_Entities($data);
13980                 return $decoder->parse();
13981         }
13982
13983         /**
13984          * Remove RFC822 comments
13985          *
13986          * @param string $data Data to strip comments from
13987          * @return string Comment stripped string
13988          */
13989         public static function uncomment_rfc822($string)
13990         {
13991                 $string = (string) $string;
13992                 $position = 0;
13993                 $length = strlen($string);
13994                 $depth = 0;
13995
13996                 $output = '';
13997
13998                 while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
13999                 {
14000                         $output .= substr($string, $position, $pos - $position);
14001                         $position = $pos + 1;
14002                         if ($string[$pos - 1] !== '\\')
14003                         {
14004                                 $depth++;
14005                                 while ($depth && $position < $length)
14006                                 {
14007                                         $position += strcspn($string, '()', $position);
14008                                         if ($string[$position - 1] === '\\')
14009                                         {
14010                                                 $position++;
14011                                                 continue;
14012                                         }
14013                                         elseif (isset($string[$position]))
14014                                         {
14015                                                 switch ($string[$position])
14016                                                 {
14017                                                         case '(':
14018                                                                 $depth++;
14019                                                                 break;
14020
14021                                                         case ')':
14022                                                                 $depth--;
14023                                                                 break;
14024                                                 }
14025                                                 $position++;
14026                                         }
14027                                         else
14028                                         {
14029                                                 break;
14030                                         }
14031                                 }
14032                         }
14033                         else
14034                         {
14035                                 $output .= '(';
14036                         }
14037                 }
14038                 $output .= substr($string, $position);
14039
14040                 return $output;
14041         }
14042
14043         public static function parse_mime($mime)
14044         {
14045                 if (($pos = strpos($mime, ';')) === false)
14046                 {
14047                         return trim($mime);
14048                 }
14049                 else
14050                 {
14051                         return trim(substr($mime, 0, $pos));
14052                 }
14053         }
14054
14055         public static function atom_03_construct_type($attribs)
14056         {
14057                 if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
14058                 {
14059                         $mode = SIMPLEPIE_CONSTRUCT_BASE64;
14060                 }
14061                 else
14062                 {
14063                         $mode = SIMPLEPIE_CONSTRUCT_NONE;
14064                 }
14065                 if (isset($attribs['']['type']))
14066                 {
14067                         switch (strtolower(trim($attribs['']['type'])))
14068                         {
14069                                 case 'text':
14070                                 case 'text/plain':
14071                                         return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14072
14073                                 case 'html':
14074                                 case 'text/html':
14075                                         return SIMPLEPIE_CONSTRUCT_HTML | $mode;
14076
14077                                 case 'xhtml':
14078                                 case 'application/xhtml+xml':
14079                                         return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
14080
14081                                 default:
14082                                         return SIMPLEPIE_CONSTRUCT_NONE | $mode;
14083                         }
14084                 }
14085                 else
14086                 {
14087                         return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14088                 }
14089         }
14090
14091         public static function atom_10_construct_type($attribs)
14092         {
14093                 if (isset($attribs['']['type']))
14094                 {
14095                         switch (strtolower(trim($attribs['']['type'])))
14096                         {
14097                                 case 'text':
14098                                         return SIMPLEPIE_CONSTRUCT_TEXT;
14099
14100                                 case 'html':
14101                                         return SIMPLEPIE_CONSTRUCT_HTML;
14102
14103                                 case 'xhtml':
14104                                         return SIMPLEPIE_CONSTRUCT_XHTML;
14105
14106                                 default:
14107                                         return SIMPLEPIE_CONSTRUCT_NONE;
14108                         }
14109                 }
14110                 return SIMPLEPIE_CONSTRUCT_TEXT;
14111         }
14112
14113         public static function atom_10_content_construct_type($attribs)
14114         {
14115                 if (isset($attribs['']['type']))
14116                 {
14117                         $type = strtolower(trim($attribs['']['type']));
14118                         switch ($type)
14119                         {
14120                                 case 'text':
14121                                         return SIMPLEPIE_CONSTRUCT_TEXT;
14122
14123                                 case 'html':
14124                                         return SIMPLEPIE_CONSTRUCT_HTML;
14125
14126                                 case 'xhtml':
14127                                         return SIMPLEPIE_CONSTRUCT_XHTML;
14128                         }
14129                         if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
14130                         {
14131                                 return SIMPLEPIE_CONSTRUCT_NONE;
14132                         }
14133                         else
14134                         {
14135                                 return SIMPLEPIE_CONSTRUCT_BASE64;
14136                         }
14137                 }
14138                 else
14139                 {
14140                         return SIMPLEPIE_CONSTRUCT_TEXT;
14141                 }
14142         }
14143
14144         public static function is_isegment_nz_nc($string)
14145         {
14146                 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);
14147         }
14148
14149         public static function space_seperated_tokens($string)
14150         {
14151                 $space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
14152                 $string_length = strlen($string);
14153
14154                 $position = strspn($string, $space_characters);
14155                 $tokens = array();
14156
14157                 while ($position < $string_length)
14158                 {
14159                         $len = strcspn($string, $space_characters, $position);
14160                         $tokens[] = substr($string, $position, $len);
14161                         $position += $len;
14162                         $position += strspn($string, $space_characters, $position);
14163                 }
14164
14165                 return $tokens;
14166         }
14167
14168         /**
14169          * Converts a unicode codepoint to a UTF-8 character
14170          *
14171          * @static
14172          * @param int $codepoint Unicode codepoint
14173          * @return string UTF-8 character
14174          */
14175         public static function codepoint_to_utf8($codepoint)
14176         {
14177                 $codepoint = (int) $codepoint;
14178                 if ($codepoint < 0)
14179                 {
14180                         return false;
14181                 }
14182                 else if ($codepoint <= 0x7f)
14183                 {
14184                         return chr($codepoint);
14185                 }
14186                 else if ($codepoint <= 0x7ff)
14187                 {
14188                         return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
14189                 }
14190                 else if ($codepoint <= 0xffff)
14191                 {
14192                         return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14193                 }
14194                 else if ($codepoint <= 0x10ffff)
14195                 {
14196                         return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14197                 }
14198                 else
14199                 {
14200                         // U+FFFD REPLACEMENT CHARACTER
14201                         return "\xEF\xBF\xBD";
14202                 }
14203         }
14204
14205         /**
14206          * Similar to parse_str()
14207          *
14208          * Returns an associative array of name/value pairs, where the value is an
14209          * array of values that have used the same name
14210          *
14211          * @static
14212          * @param string $str The input string.
14213          * @return array
14214          */
14215         public static function parse_str($str)
14216         {
14217                 $return = array();
14218                 $str = explode('&', $str);
14219
14220                 foreach ($str as $section)
14221                 {
14222                         if (strpos($section, '=') !== false)
14223                         {
14224                                 list($name, $value) = explode('=', $section, 2);
14225                                 $return[urldecode($name)][] = urldecode($value);
14226                         }
14227                         else
14228                         {
14229                                 $return[urldecode($section)][] = null;
14230                         }
14231                 }
14232
14233                 return $return;
14234         }
14235
14236         /**
14237          * Detect XML encoding, as per XML 1.0 Appendix F.1
14238          *
14239          * @todo Add support for EBCDIC
14240          * @param string $data XML data
14241          * @param SimplePie_Registry $registry Class registry
14242          * @return array Possible encodings
14243          */
14244         public static function xml_encoding($data, $registry)
14245         {
14246                 // UTF-32 Big Endian BOM
14247                 if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
14248                 {
14249                         $encoding[] = 'UTF-32BE';
14250                 }
14251                 // UTF-32 Little Endian BOM
14252                 elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
14253                 {
14254                         $encoding[] = 'UTF-32LE';
14255                 }
14256                 // UTF-16 Big Endian BOM
14257                 elseif (substr($data, 0, 2) === "\xFE\xFF")
14258                 {
14259                         $encoding[] = 'UTF-16BE';
14260                 }
14261                 // UTF-16 Little Endian BOM
14262                 elseif (substr($data, 0, 2) === "\xFF\xFE")
14263                 {
14264                         $encoding[] = 'UTF-16LE';
14265                 }
14266                 // UTF-8 BOM
14267                 elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
14268                 {
14269                         $encoding[] = 'UTF-8';
14270                 }
14271                 // UTF-32 Big Endian Without BOM
14272                 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")
14273                 {
14274                         if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
14275                         {
14276                                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')));
14277                                 if ($parser->parse())
14278                                 {
14279                                         $encoding[] = $parser->encoding;
14280                                 }
14281                         }
14282                         $encoding[] = 'UTF-32BE';
14283                 }
14284                 // UTF-32 Little Endian Without BOM
14285                 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")
14286                 {
14287                         if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
14288                         {
14289                                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')));
14290                                 if ($parser->parse())
14291                                 {
14292                                         $encoding[] = $parser->encoding;
14293                                 }
14294                         }
14295                         $encoding[] = 'UTF-32LE';
14296                 }
14297                 // UTF-16 Big Endian Without BOM
14298                 elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
14299                 {
14300                         if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
14301                         {
14302                                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')));
14303                                 if ($parser->parse())
14304                                 {
14305                                         $encoding[] = $parser->encoding;
14306                                 }
14307                         }
14308                         $encoding[] = 'UTF-16BE';
14309                 }
14310                 // UTF-16 Little Endian Without BOM
14311                 elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
14312                 {
14313                         if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
14314                         {
14315                                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')));
14316                                 if ($parser->parse())
14317                                 {
14318                                         $encoding[] = $parser->encoding;
14319                                 }
14320                         }
14321                         $encoding[] = 'UTF-16LE';
14322                 }
14323                 // US-ASCII (or superset)
14324                 elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
14325                 {
14326                         if ($pos = strpos($data, "\x3F\x3E"))
14327                         {
14328                                 $parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
14329                                 if ($parser->parse())
14330                                 {
14331                                         $encoding[] = $parser->encoding;
14332                                 }
14333                         }
14334                         $encoding[] = 'UTF-8';
14335                 }
14336                 // Fallback to UTF-8
14337                 else
14338                 {
14339                         $encoding[] = 'UTF-8';
14340                 }
14341                 return $encoding;
14342         }
14343
14344         public static function output_javascript()
14345         {
14346                 if (function_exists('ob_gzhandler'))
14347                 {
14348                         ob_start('ob_gzhandler');
14349                 }
14350                 header('Content-type: text/javascript; charset: UTF-8');
14351                 header('Cache-Control: must-revalidate');
14352                 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
14353                 ?>
14354 function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
14355         if (placeholder != '') {
14356                 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>');
14357         }
14358         else {
14359                 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>');
14360         }
14361 }
14362
14363 function embed_flash(bgcolor, width, height, link, loop, type) {
14364         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>');
14365 }
14366
14367 function embed_flv(width, height, link, placeholder, loop, player) {
14368         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>');
14369 }
14370
14371 function embed_wmedia(width, height, link) {
14372         document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
14373 }
14374                 <?php
14375         }
14376
14377         /**
14378          * Get the SimplePie build timestamp
14379          *
14380          * Uses the git index if it exists, otherwise uses the modification time
14381          * of the newest file.
14382          */
14383         public static function get_build()
14384         {
14385                 $root = dirname(dirname(__FILE__));
14386                 if (file_exists($root . '/.git/index'))
14387                 {
14388                         return filemtime($root . '/.git/index');
14389                 }
14390                 elseif (file_exists($root . '/SimplePie'))
14391                 {
14392                         $time = 0;
14393                         foreach (glob($root . '/SimplePie/*.php') as $file)
14394                         {
14395                                 if (($mtime = filemtime($file)) > $time)
14396                                 {
14397                                         $time = $mtime;
14398                                 }
14399                         }
14400                         return $time;
14401                 }
14402                 elseif (file_exists(dirname(__FILE__) . '/Core.php'))
14403                 {
14404                         return filemtime(dirname(__FILE__) . '/Core.php');
14405                 }
14406                 else
14407                 {
14408                         return filemtime(__FILE__);
14409                 }
14410         }
14411
14412         /**
14413          * Format debugging information
14414          */
14415         public static function debug(&$sp)
14416         {
14417                 $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n";
14418                 $info .= 'PHP ' . PHP_VERSION . "\n";
14419                 if ($sp->error() !== null)
14420                 {
14421                         $info .= 'Error occurred: ' . $sp->error() . "\n";
14422                 }
14423                 else
14424                 {
14425                         $info .= "No error found.\n";
14426                 }
14427                 $info .= "Extensions:\n";
14428                 $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml');
14429                 foreach ($extensions as $ext)
14430                 {
14431                         if (extension_loaded($ext))
14432                         {
14433                                 $info .= "    $ext loaded\n";
14434                                 switch ($ext)
14435                                 {
14436                                         case 'pcre':
14437                                                 $info .= '      Version ' . PCRE_VERSION . "\n";
14438                                                 break;
14439                                         case 'curl':
14440                                                 $version = curl_version();
14441                                                 $info .= '      Version ' . $version['version'] . "\n";
14442                                                 break;
14443                                         case 'mbstring':
14444                                                 $info .= '      Overloading: ' . mb_get_info('func_overload') . "\n";
14445                                                 break;
14446                                         case 'iconv':
14447                                                 $info .= '      Version ' . ICONV_VERSION . "\n";
14448                                                 break;
14449                                         case 'xml':
14450                                                 $info .= '      Version ' . LIBXML_DOTTED_VERSION . "\n";
14451                                                 break;
14452                                 }
14453                         }
14454                         else
14455                         {
14456                                 $info .= "    $ext not loaded\n";
14457                         }
14458                 }
14459                 return $info;
14460         }
14461
14462         public static function silence_errors($num, $str)
14463         {
14464                 // No-op
14465         }
14466 }
14467
14468 /**
14469  * Class to validate and to work with IPv6 addresses.
14470  *
14471  * @package SimplePie
14472  * @subpackage HTTP
14473  * @copyright 2003-2005 The PHP Group
14474  * @license http://www.opensource.org/licenses/bsd-license.php
14475  * @link http://pear.php.net/package/Net_IPv6
14476  * @author Alexander Merz <alexander.merz@web.de>
14477  * @author elfrink at introweb dot nl
14478  * @author Josh Peck <jmp at joshpeck dot org>
14479  * @author Geoffrey Sneddon <geoffers@gmail.com>
14480  */
14481 class SimplePie_Net_IPv6
14482 {
14483         /**
14484          * Uncompresses an IPv6 address
14485          *
14486          * RFC 4291 allows you to compress concecutive zero pieces in an address to
14487          * '::'. This method expects a valid IPv6 address and expands the '::' to
14488          * the required number of zero pieces.
14489          *
14490          * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
14491          *           ::1         ->  0:0:0:0:0:0:0:1
14492          *
14493          * @author Alexander Merz <alexander.merz@web.de>
14494          * @author elfrink at introweb dot nl
14495          * @author Josh Peck <jmp at joshpeck dot org>
14496          * @copyright 2003-2005 The PHP Group
14497          * @license http://www.opensource.org/licenses/bsd-license.php
14498          * @param string $ip An IPv6 address
14499          * @return string The uncompressed IPv6 address
14500          */
14501         public static function uncompress($ip)
14502         {
14503                 $c1 = -1;
14504                 $c2 = -1;
14505                 if (substr_count($ip, '::') === 1)
14506                 {
14507                         list($ip1, $ip2) = explode('::', $ip);
14508                         if ($ip1 === '')
14509                         {
14510                                 $c1 = -1;
14511                         }
14512                         else
14513                         {
14514                                 $c1 = substr_count($ip1, ':');
14515                         }
14516                         if ($ip2 === '')
14517                         {
14518                                 $c2 = -1;
14519                         }
14520                         else
14521                         {
14522                                 $c2 = substr_count($ip2, ':');
14523                         }
14524                         if (strpos($ip2, '.') !== false)
14525                         {
14526                                 $c2++;
14527                         }
14528                         // ::
14529                         if ($c1 === -1 && $c2 === -1)
14530                         {
14531                                 $ip = '0:0:0:0:0:0:0:0';
14532                         }
14533                         // ::xxx
14534                         else if ($c1 === -1)
14535                         {
14536                                 $fill = str_repeat('0:', 7 - $c2);
14537                                 $ip = str_replace('::', $fill, $ip);
14538                         }
14539                         // xxx::
14540                         else if ($c2 === -1)
14541                         {
14542                                 $fill = str_repeat(':0', 7 - $c1);
14543                                 $ip = str_replace('::', $fill, $ip);
14544                         }
14545                         // xxx::xxx
14546                         else
14547                         {
14548                                 $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
14549                                 $ip = str_replace('::', $fill, $ip);
14550                         }
14551                 }
14552                 return $ip;
14553         }
14554
14555         /**
14556          * Compresses an IPv6 address
14557          *
14558          * RFC 4291 allows you to compress concecutive zero pieces in an address to
14559          * '::'. This method expects a valid IPv6 address and compresses consecutive
14560          * zero pieces to '::'.
14561          *
14562          * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
14563          *           0:0:0:0:0:0:0:1        ->  ::1
14564          *
14565          * @see uncompress()
14566          * @param string $ip An IPv6 address
14567          * @return string The compressed IPv6 address
14568          */
14569         public static function compress($ip)
14570         {
14571                 // Prepare the IP to be compressed
14572                 $ip = self::uncompress($ip);
14573                 $ip_parts = self::split_v6_v4($ip);
14574
14575                 // Replace all leading zeros
14576                 $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
14577
14578                 // Find bunches of zeros
14579                 if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE))
14580                 {
14581                         $max = 0;
14582                         $pos = null;
14583                         foreach ($matches[0] as $match)
14584                         {
14585                                 if (strlen($match[0]) > $max)
14586                                 {
14587                                         $max = strlen($match[0]);
14588                                         $pos = $match[1];
14589                                 }
14590                         }
14591
14592                         $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
14593                 }
14594
14595                 if ($ip_parts[1] !== '')
14596                 {
14597                         return implode(':', $ip_parts);
14598                 }
14599                 else
14600                 {
14601                         return $ip_parts[0];
14602                 }
14603         }
14604
14605         /**
14606          * Splits an IPv6 address into the IPv6 and IPv4 representation parts
14607          *
14608          * RFC 4291 allows you to represent the last two parts of an IPv6 address
14609          * using the standard IPv4 representation
14610          *
14611          * Example:  0:0:0:0:0:0:13.1.68.3
14612          *           0:0:0:0:0:FFFF:129.144.52.38
14613          *
14614          * @param string $ip An IPv6 address
14615          * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
14616          */
14617         private static function split_v6_v4($ip)
14618         {
14619                 if (strpos($ip, '.') !== false)
14620                 {
14621                         $pos = strrpos($ip, ':');
14622                         $ipv6_part = substr($ip, 0, $pos);
14623                         $ipv4_part = substr($ip, $pos + 1);
14624                         return array($ipv6_part, $ipv4_part);
14625                 }
14626                 else
14627                 {
14628                         return array($ip, '');
14629                 }
14630         }
14631
14632         /**
14633          * Checks an IPv6 address
14634          *
14635          * Checks if the given IP is a valid IPv6 address
14636          *
14637          * @param string $ip An IPv6 address
14638          * @return bool true if $ip is a valid IPv6 address
14639          */
14640         public static function check_ipv6($ip)
14641         {
14642                 $ip = self::uncompress($ip);
14643                 list($ipv6, $ipv4) = self::split_v6_v4($ip);
14644                 $ipv6 = explode(':', $ipv6);
14645                 $ipv4 = explode('.', $ipv4);
14646                 if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4)
14647                 {
14648                         foreach ($ipv6 as $ipv6_part)
14649                         {
14650                                 // The section can't be empty
14651                                 if ($ipv6_part === '')
14652                                         return false;
14653
14654                                 // Nor can it be over four characters
14655                                 if (strlen($ipv6_part) > 4)
14656                                         return false;
14657
14658                                 // Remove leading zeros (this is safe because of the above)
14659                                 $ipv6_part = ltrim($ipv6_part, '0');
14660                                 if ($ipv6_part === '')
14661                                         $ipv6_part = '0';
14662
14663                                 // Check the value is valid
14664                                 $value = hexdec($ipv6_part);
14665                                 if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF)
14666                                         return false;
14667                         }
14668                         if (count($ipv4) === 4)
14669                         {
14670                                 foreach ($ipv4 as $ipv4_part)
14671                                 {
14672                                         $value = (int) $ipv4_part;
14673                                         if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF)
14674                                                 return false;
14675                                 }
14676                         }
14677                         return true;
14678                 }
14679                 else
14680                 {
14681                         return false;
14682                 }
14683         }
14684
14685         /**
14686          * Checks if the given IP is a valid IPv6 address
14687          *
14688          * @codeCoverageIgnore
14689          * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead
14690          * @see check_ipv6
14691          * @param string $ip An IPv6 address
14692          * @return bool true if $ip is a valid IPv6 address
14693          */
14694         public static function checkIPv6($ip)
14695         {
14696                 return self::check_ipv6($ip);
14697         }
14698 }
14699
14700 /**
14701  * Date Parser
14702  *
14703  * @package SimplePie
14704  * @subpackage Parsing
14705  */
14706 class SimplePie_Parse_Date
14707 {
14708         /**
14709          * Input data
14710          *
14711          * @access protected
14712          * @var string
14713          */
14714         var $date;
14715
14716         /**
14717          * List of days, calendar day name => ordinal day number in the week
14718          *
14719          * @access protected
14720          * @var array
14721          */
14722         var $day = array(
14723                 // English
14724                 'mon' => 1,
14725                 'monday' => 1,
14726                 'tue' => 2,
14727                 'tuesday' => 2,
14728                 'wed' => 3,
14729                 'wednesday' => 3,
14730                 'thu' => 4,
14731                 'thursday' => 4,
14732                 'fri' => 5,
14733                 'friday' => 5,
14734                 'sat' => 6,
14735                 'saturday' => 6,
14736                 'sun' => 7,
14737                 'sunday' => 7,
14738                 // Dutch
14739                 'maandag' => 1,
14740                 'dinsdag' => 2,
14741                 'woensdag' => 3,
14742                 'donderdag' => 4,
14743                 'vrijdag' => 5,
14744                 'zaterdag' => 6,
14745                 'zondag' => 7,
14746                 // French
14747                 'lundi' => 1,
14748                 'mardi' => 2,
14749                 'mercredi' => 3,
14750                 'jeudi' => 4,
14751                 'vendredi' => 5,
14752                 'samedi' => 6,
14753                 'dimanche' => 7,
14754                 // German
14755                 'montag' => 1,
14756                 'dienstag' => 2,
14757                 'mittwoch' => 3,
14758                 'donnerstag' => 4,
14759                 'freitag' => 5,
14760                 'samstag' => 6,
14761                 'sonnabend' => 6,
14762                 'sonntag' => 7,
14763                 // Italian
14764                 'lunedì' => 1,
14765                 'martedì' => 2,
14766                 'mercoledì' => 3,
14767                 'giovedì' => 4,
14768                 'venerdì' => 5,
14769                 'sabato' => 6,
14770                 'domenica' => 7,
14771                 // Spanish
14772                 'lunes' => 1,
14773                 'martes' => 2,
14774                 'miércoles' => 3,
14775                 'jueves' => 4,
14776                 'viernes' => 5,
14777                 'sábado' => 6,
14778                 'domingo' => 7,
14779                 // Finnish
14780                 'maanantai' => 1,
14781                 'tiistai' => 2,
14782                 'keskiviikko' => 3,
14783                 'torstai' => 4,
14784                 'perjantai' => 5,
14785                 'lauantai' => 6,
14786                 'sunnuntai' => 7,
14787                 // Hungarian
14788                 'hétfő' => 1,
14789                 'kedd' => 2,
14790                 'szerda' => 3,
14791                 'csütörtok' => 4,
14792                 'péntek' => 5,
14793                 'szombat' => 6,
14794                 'vasárnap' => 7,
14795                 // Greek
14796                 'Δευ' => 1,
14797                 'Τρι' => 2,
14798                 'Τετ' => 3,
14799                 'Πεμ' => 4,
14800                 'Παρ' => 5,
14801                 'Σαβ' => 6,
14802                 'Κυρ' => 7,
14803         );
14804
14805         /**
14806          * List of months, calendar month name => calendar month number
14807          *
14808          * @access protected
14809          * @var array
14810          */
14811         var $month = array(
14812                 // English
14813                 'jan' => 1,
14814                 'january' => 1,
14815                 'feb' => 2,
14816                 'february' => 2,
14817                 'mar' => 3,
14818                 'march' => 3,
14819                 'apr' => 4,
14820                 'april' => 4,
14821                 'may' => 5,
14822                 // No long form of May
14823                 'jun' => 6,
14824                 'june' => 6,
14825                 'jul' => 7,
14826                 'july' => 7,
14827                 'aug' => 8,
14828                 'august' => 8,
14829                 'sep' => 9,
14830                 'september' => 8,
14831                 'oct' => 10,
14832                 'october' => 10,
14833                 'nov' => 11,
14834                 'november' => 11,
14835                 'dec' => 12,
14836                 'december' => 12,
14837                 // Dutch
14838                 'januari' => 1,
14839                 'februari' => 2,
14840                 'maart' => 3,
14841                 'april' => 4,
14842                 'mei' => 5,
14843                 'juni' => 6,
14844                 'juli' => 7,
14845                 'augustus' => 8,
14846                 'september' => 9,
14847                 'oktober' => 10,
14848                 'november' => 11,
14849                 'december' => 12,
14850                 // French
14851                 'janvier' => 1,
14852                 'février' => 2,
14853                 'mars' => 3,
14854                 'avril' => 4,
14855                 'mai' => 5,
14856                 'juin' => 6,
14857                 'juillet' => 7,
14858                 'août' => 8,
14859                 'septembre' => 9,
14860                 'octobre' => 10,
14861                 'novembre' => 11,
14862                 'décembre' => 12,
14863                 // German
14864                 'januar' => 1,
14865                 'februar' => 2,
14866                 'märz' => 3,
14867                 'april' => 4,
14868                 'mai' => 5,
14869                 'juni' => 6,
14870                 'juli' => 7,
14871                 'august' => 8,
14872                 'september' => 9,
14873                 'oktober' => 10,
14874                 'november' => 11,
14875                 'dezember' => 12,
14876                 // Italian
14877                 'gennaio' => 1,
14878                 'febbraio' => 2,
14879                 'marzo' => 3,
14880                 'aprile' => 4,
14881                 'maggio' => 5,
14882                 'giugno' => 6,
14883                 'luglio' => 7,
14884                 'agosto' => 8,
14885                 'settembre' => 9,
14886                 'ottobre' => 10,
14887                 'novembre' => 11,
14888                 'dicembre' => 12,
14889                 // Spanish
14890                 'enero' => 1,
14891                 'febrero' => 2,
14892                 'marzo' => 3,
14893                 'abril' => 4,
14894                 'mayo' => 5,
14895                 'junio' => 6,
14896                 'julio' => 7,
14897                 'agosto' => 8,
14898                 'septiembre' => 9,
14899                 'setiembre' => 9,
14900                 'octubre' => 10,
14901                 'noviembre' => 11,
14902                 'diciembre' => 12,
14903                 // Finnish
14904                 'tammikuu' => 1,
14905                 'helmikuu' => 2,
14906                 'maaliskuu' => 3,
14907                 'huhtikuu' => 4,
14908                 'toukokuu' => 5,
14909                 'kesäkuu' => 6,
14910                 'heinäkuu' => 7,
14911                 'elokuu' => 8,
14912                 'suuskuu' => 9,
14913                 'lokakuu' => 10,
14914                 'marras' => 11,
14915                 'joulukuu' => 12,
14916                 // Hungarian
14917                 'január' => 1,
14918                 'február' => 2,
14919                 'március' => 3,
14920                 'április' => 4,
14921                 'május' => 5,
14922                 'június' => 6,
14923                 'július' => 7,
14924                 'augusztus' => 8,
14925                 'szeptember' => 9,
14926                 'október' => 10,
14927                 'november' => 11,
14928                 'december' => 12,
14929                 // Greek
14930                 'Ιαν' => 1,
14931                 'Φεβ' => 2,
14932                 'Μάώ' => 3,
14933                 'Μαώ' => 3,
14934                 'Απρ' => 4,
14935                 'Μάι' => 5,
14936                 'Μαϊ' => 5,
14937                 'Μαι' => 5,
14938                 'Ιούν' => 6,
14939                 'Ιον' => 6,
14940                 'Ιούλ' => 7,
14941                 'Ιολ' => 7,
14942                 'Αύγ' => 8,
14943                 'Αυγ' => 8,
14944                 'Σεπ' => 9,
14945                 'Οκτ' => 10,
14946                 'Νοέ' => 11,
14947                 'Δεκ' => 12,
14948         );
14949
14950         /**
14951          * List of timezones, abbreviation => offset from UTC
14952          *
14953          * @access protected
14954          * @var array
14955          */
14956         var $timezone = array(
14957                 'ACDT' => 37800,
14958                 'ACIT' => 28800,
14959                 'ACST' => 34200,
14960                 'ACT' => -18000,
14961                 'ACWDT' => 35100,
14962                 'ACWST' => 31500,
14963                 'AEDT' => 39600,
14964                 'AEST' => 36000,
14965                 'AFT' => 16200,
14966                 'AKDT' => -28800,
14967                 'AKST' => -32400,
14968                 'AMDT' => 18000,
14969                 'AMT' => -14400,
14970                 'ANAST' => 46800,
14971                 'ANAT' => 43200,
14972                 'ART' => -10800,
14973                 'AZOST' => -3600,
14974                 'AZST' => 18000,
14975                 'AZT' => 14400,
14976                 'BIOT' => 21600,
14977                 'BIT' => -43200,
14978                 'BOT' => -14400,
14979                 'BRST' => -7200,
14980                 'BRT' => -10800,
14981                 'BST' => 3600,
14982                 'BTT' => 21600,
14983                 'CAST' => 18000,
14984                 'CAT' => 7200,
14985                 'CCT' => 23400,
14986                 'CDT' => -18000,
14987                 'CEDT' => 7200,
14988                 'CET' => 3600,
14989                 'CGST' => -7200,
14990                 'CGT' => -10800,
14991                 'CHADT' => 49500,
14992                 'CHAST' => 45900,
14993                 'CIST' => -28800,
14994                 'CKT' => -36000,
14995                 'CLDT' => -10800,
14996                 'CLST' => -14400,
14997                 'COT' => -18000,
14998                 'CST' => -21600,
14999                 'CVT' => -3600,
15000                 'CXT' => 25200,
15001                 'DAVT' => 25200,
15002                 'DTAT' => 36000,
15003                 'EADT' => -18000,
15004                 'EAST' => -21600,
15005                 'EAT' => 10800,
15006                 'ECT' => -18000,
15007                 'EDT' => -14400,
15008                 'EEST' => 10800,
15009                 'EET' => 7200,
15010                 'EGT' => -3600,
15011                 'EKST' => 21600,
15012                 'EST' => -18000,
15013                 'FJT' => 43200,
15014                 'FKDT' => -10800,
15015                 'FKST' => -14400,
15016                 'FNT' => -7200,
15017                 'GALT' => -21600,
15018                 'GEDT' => 14400,
15019                 'GEST' => 10800,
15020                 'GFT' => -10800,
15021                 'GILT' => 43200,
15022                 'GIT' => -32400,
15023                 'GST' => 14400,
15024                 'GST' => -7200,
15025                 'GYT' => -14400,
15026                 'HAA' => -10800,
15027                 'HAC' => -18000,
15028                 'HADT' => -32400,
15029                 'HAE' => -14400,
15030                 'HAP' => -25200,
15031                 'HAR' => -21600,
15032                 'HAST' => -36000,
15033                 'HAT' => -9000,
15034                 'HAY' => -28800,
15035                 'HKST' => 28800,
15036                 'HMT' => 18000,
15037                 'HNA' => -14400,
15038                 'HNC' => -21600,
15039                 'HNE' => -18000,
15040                 'HNP' => -28800,
15041                 'HNR' => -25200,
15042                 'HNT' => -12600,
15043                 'HNY' => -32400,
15044                 'IRDT' => 16200,
15045                 'IRKST' => 32400,
15046                 'IRKT' => 28800,
15047                 'IRST' => 12600,
15048                 'JFDT' => -10800,
15049                 'JFST' => -14400,
15050                 'JST' => 32400,
15051                 'KGST' => 21600,
15052                 'KGT' => 18000,
15053                 'KOST' => 39600,
15054                 'KOVST' => 28800,
15055                 'KOVT' => 25200,
15056                 'KRAST' => 28800,
15057                 'KRAT' => 25200,
15058                 'KST' => 32400,
15059                 'LHDT' => 39600,
15060                 'LHST' => 37800,
15061                 'LINT' => 50400,
15062                 'LKT' => 21600,
15063                 'MAGST' => 43200,
15064                 'MAGT' => 39600,
15065                 'MAWT' => 21600,
15066                 'MDT' => -21600,
15067                 'MESZ' => 7200,
15068                 'MEZ' => 3600,
15069                 'MHT' => 43200,
15070                 'MIT' => -34200,
15071                 'MNST' => 32400,
15072                 'MSDT' => 14400,
15073                 'MSST' => 10800,
15074                 'MST' => -25200,
15075                 'MUT' => 14400,
15076                 'MVT' => 18000,
15077                 'MYT' => 28800,
15078                 'NCT' => 39600,
15079                 'NDT' => -9000,
15080                 'NFT' => 41400,
15081                 'NMIT' => 36000,
15082                 'NOVST' => 25200,
15083                 'NOVT' => 21600,
15084                 'NPT' => 20700,
15085                 'NRT' => 43200,
15086                 'NST' => -12600,
15087                 'NUT' => -39600,
15088                 'NZDT' => 46800,
15089                 'NZST' => 43200,
15090                 'OMSST' => 25200,
15091                 'OMST' => 21600,
15092                 'PDT' => -25200,
15093                 'PET' => -18000,
15094                 'PETST' => 46800,
15095                 'PETT' => 43200,
15096                 'PGT' => 36000,
15097                 'PHOT' => 46800,
15098                 'PHT' => 28800,
15099                 'PKT' => 18000,
15100                 'PMDT' => -7200,
15101                 'PMST' => -10800,
15102                 'PONT' => 39600,
15103                 'PST' => -28800,
15104                 'PWT' => 32400,
15105                 'PYST' => -10800,
15106                 'PYT' => -14400,
15107                 'RET' => 14400,
15108                 'ROTT' => -10800,
15109                 'SAMST' => 18000,
15110                 'SAMT' => 14400,
15111                 'SAST' => 7200,
15112                 'SBT' => 39600,
15113                 'SCDT' => 46800,
15114                 'SCST' => 43200,
15115                 'SCT' => 14400,
15116                 'SEST' => 3600,
15117                 'SGT' => 28800,
15118                 'SIT' => 28800,
15119                 'SRT' => -10800,
15120                 'SST' => -39600,
15121                 'SYST' => 10800,
15122                 'SYT' => 7200,
15123                 'TFT' => 18000,
15124                 'THAT' => -36000,
15125                 'TJT' => 18000,
15126                 'TKT' => -36000,
15127                 'TMT' => 18000,
15128                 'TOT' => 46800,
15129                 'TPT' => 32400,
15130                 'TRUT' => 36000,
15131                 'TVT' => 43200,
15132                 'TWT' => 28800,
15133                 'UYST' => -7200,
15134                 'UYT' => -10800,
15135                 'UZT' => 18000,
15136                 'VET' => -14400,
15137                 'VLAST' => 39600,
15138                 'VLAT' => 36000,
15139                 'VOST' => 21600,
15140                 'VUT' => 39600,
15141                 'WAST' => 7200,
15142                 'WAT' => 3600,
15143                 'WDT' => 32400,
15144                 'WEST' => 3600,
15145                 'WFT' => 43200,
15146                 'WIB' => 25200,
15147                 'WIT' => 32400,
15148                 'WITA' => 28800,
15149                 'WKST' => 18000,
15150                 'WST' => 28800,
15151                 'YAKST' => 36000,
15152                 'YAKT' => 32400,
15153                 'YAPT' => 36000,
15154                 'YEKST' => 21600,
15155                 'YEKT' => 18000,
15156         );
15157
15158         /**
15159          * Cached PCRE for SimplePie_Parse_Date::$day
15160          *
15161          * @access protected
15162          * @var string
15163          */
15164         var $day_pcre;
15165
15166         /**
15167          * Cached PCRE for SimplePie_Parse_Date::$month
15168          *
15169          * @access protected
15170          * @var string
15171          */
15172         var $month_pcre;
15173
15174         /**
15175          * Array of user-added callback methods
15176          *
15177          * @access private
15178          * @var array
15179          */
15180         var $built_in = array();
15181
15182         /**
15183          * Array of user-added callback methods
15184          *
15185          * @access private
15186          * @var array
15187          */
15188         var $user = array();
15189
15190         /**
15191          * Create new SimplePie_Parse_Date object, and set self::day_pcre,
15192          * self::month_pcre, and self::built_in
15193          *
15194          * @access private
15195          */
15196         public function __construct()
15197         {
15198                 $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
15199                 $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
15200
15201                 static $cache;
15202                 if (!isset($cache[get_class($this)]))
15203                 {
15204                         $all_methods = get_class_methods($this);
15205
15206                         foreach ($all_methods as $method)
15207                         {
15208                                 if (strtolower(substr($method, 0, 5)) === 'date_')
15209                                 {
15210                                         $cache[get_class($this)][] = $method;
15211                                 }
15212                         }
15213                 }
15214
15215                 foreach ($cache[get_class($this)] as $method)
15216                 {
15217                         $this->built_in[] = $method;
15218                 }
15219         }
15220
15221         /**
15222          * Get the object
15223          *
15224          * @access public
15225          */
15226         public static function get()
15227         {
15228                 static $object;
15229                 if (!$object)
15230                 {
15231                         $object = new SimplePie_Parse_Date;
15232                 }
15233                 return $object;
15234         }
15235
15236         /**
15237          * Parse a date
15238          *
15239          * @final
15240          * @access public
15241          * @param string $date Date to parse
15242          * @return int Timestamp corresponding to date string, or false on failure
15243          */
15244         public function parse($date)
15245         {
15246                 foreach ($this->user as $method)
15247                 {
15248                         if (($returned = call_user_func($method, $date)) !== false)
15249                         {
15250                                 return $returned;
15251                         }
15252                 }
15253
15254                 foreach ($this->built_in as $method)
15255                 {
15256                         if (($returned = call_user_func(array($this, $method), $date)) !== false)
15257                         {
15258                                 return $returned;
15259                         }
15260                 }
15261
15262                 return false;
15263         }
15264
15265         /**
15266          * Add a callback method to parse a date
15267          *
15268          * @final
15269          * @access public
15270          * @param callback $callback
15271          */
15272         public function add_callback($callback)
15273         {
15274                 if (is_callable($callback))
15275                 {
15276                         $this->user[] = $callback;
15277                 }
15278                 else
15279                 {
15280                         trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
15281                 }
15282         }
15283
15284         /**
15285          * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
15286          * well as allowing any of upper or lower case "T", horizontal tabs, or
15287          * spaces to be used as the time seperator (including more than one))
15288          *
15289          * @access protected
15290          * @return int Timestamp
15291          */
15292         public function date_w3cdtf($date)
15293         {
15294                 static $pcre;
15295                 if (!$pcre)
15296                 {
15297                         $year = '([0-9]{4})';
15298                         $month = $day = $hour = $minute = $second = '([0-9]{2})';
15299                         $decimal = '([0-9]*)';
15300                         $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
15301                         $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
15302                 }
15303                 if (preg_match($pcre, $date, $match))
15304                 {
15305                         /*
15306                         Capturing subpatterns:
15307                         1: Year
15308                         2: Month
15309                         3: Day
15310                         4: Hour
15311                         5: Minute
15312                         6: Second
15313                         7: Decimal fraction of a second
15314                         8: Zulu
15315                         9: Timezone ±
15316                         10: Timezone hours
15317                         11: Timezone minutes
15318                         */
15319
15320                         // Fill in empty matches
15321                         for ($i = count($match); $i <= 3; $i++)
15322                         {
15323                                 $match[$i] = '1';
15324                         }
15325
15326                         for ($i = count($match); $i <= 7; $i++)
15327                         {
15328                                 $match[$i] = '0';
15329                         }
15330
15331                         // Numeric timezone
15332                         if (isset($match[9]) && $match[9] !== '')
15333                         {
15334                                 $timezone = $match[10] * 3600;
15335                                 $timezone += $match[11] * 60;
15336                                 if ($match[9] === '-')
15337                                 {
15338                                         $timezone = 0 - $timezone;
15339                                 }
15340                         }
15341                         else
15342                         {
15343                                 $timezone = 0;
15344                         }
15345
15346                         // Convert the number of seconds to an integer, taking decimals into account
15347                         $second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
15348
15349                         return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
15350                 }
15351                 else
15352                 {
15353                         return false;
15354                 }
15355         }
15356
15357         /**
15358          * Remove RFC822 comments
15359          *
15360          * @access protected
15361          * @param string $data Data to strip comments from
15362          * @return string Comment stripped string
15363          */
15364         public function remove_rfc2822_comments($string)
15365         {
15366                 $string = (string) $string;
15367                 $position = 0;
15368                 $length = strlen($string);
15369                 $depth = 0;
15370
15371                 $output = '';
15372
15373                 while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
15374                 {
15375                         $output .= substr($string, $position, $pos - $position);
15376                         $position = $pos + 1;
15377                         if ($string[$pos - 1] !== '\\')
15378                         {
15379                                 $depth++;
15380                                 while ($depth && $position < $length)
15381                                 {
15382                                         $position += strcspn($string, '()', $position);
15383                                         if ($string[$position - 1] === '\\')
15384                                         {
15385                                                 $position++;
15386                                                 continue;
15387                                         }
15388                                         elseif (isset($string[$position]))
15389                                         {
15390                                                 switch ($string[$position])
15391                                                 {
15392                                                         case '(':
15393                                                                 $depth++;
15394                                                                 break;
15395
15396                                                         case ')':
15397                                                                 $depth--;
15398                                                                 break;
15399                                                 }
15400                                                 $position++;
15401                                         }
15402                                         else
15403                                         {
15404                                                 break;
15405                                         }
15406                                 }
15407                         }
15408                         else
15409                         {
15410                                 $output .= '(';
15411                         }
15412                 }
15413                 $output .= substr($string, $position);
15414
15415                 return $output;
15416         }
15417
15418         /**
15419          * Parse RFC2822's date format
15420          *
15421          * @access protected
15422          * @return int Timestamp
15423          */
15424         public function date_rfc2822($date)
15425         {
15426                 static $pcre;
15427                 if (!$pcre)
15428                 {
15429                         $wsp = '[\x09\x20]';
15430                         $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
15431                         $optional_fws = $fws . '?';
15432                         $day_name = $this->day_pcre;
15433                         $month = $this->month_pcre;
15434                         $day = '([0-9]{1,2})';
15435                         $hour = $minute = $second = '([0-9]{2})';
15436                         $year = '([0-9]{2,4})';
15437                         $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
15438                         $character_zone = '([A-Z]{1,5})';
15439                         $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
15440                         $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';
15441                 }
15442                 if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
15443                 {
15444                         /*
15445                         Capturing subpatterns:
15446                         1: Day name
15447                         2: Day
15448                         3: Month
15449                         4: Year
15450                         5: Hour
15451                         6: Minute
15452                         7: Second
15453                         8: Timezone ±
15454                         9: Timezone hours
15455                         10: Timezone minutes
15456                         11: Alphabetic timezone
15457                         */
15458
15459                         // Find the month number
15460                         $month = $this->month[strtolower($match[3])];
15461
15462                         // Numeric timezone
15463                         if ($match[8] !== '')
15464                         {
15465                                 $timezone = $match[9] * 3600;
15466                                 $timezone += $match[10] * 60;
15467                                 if ($match[8] === '-')
15468                                 {
15469                                         $timezone = 0 - $timezone;
15470                                 }
15471                         }
15472                         // Character timezone
15473                         elseif (isset($this->timezone[strtoupper($match[11])]))
15474                         {
15475                                 $timezone = $this->timezone[strtoupper($match[11])];
15476                         }
15477                         // Assume everything else to be -0000
15478                         else
15479                         {
15480                                 $timezone = 0;
15481                         }
15482
15483                         // Deal with 2/3 digit years
15484                         if ($match[4] < 50)
15485                         {
15486                                 $match[4] += 2000;
15487                         }
15488                         elseif ($match[4] < 1000)
15489                         {
15490                                 $match[4] += 1900;
15491                         }
15492
15493                         // Second is optional, if it is empty set it to zero
15494                         if ($match[7] !== '')
15495                         {
15496                                 $second = $match[7];
15497                         }
15498                         else
15499                         {
15500                                 $second = 0;
15501                         }
15502
15503                         return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
15504                 }
15505                 else
15506                 {
15507                         return false;
15508                 }
15509         }
15510
15511         /**
15512          * Parse RFC850's date format
15513          *
15514          * @access protected
15515          * @return int Timestamp
15516          */
15517         public function date_rfc850($date)
15518         {
15519                 static $pcre;
15520                 if (!$pcre)
15521                 {
15522                         $space = '[\x09\x20]+';
15523                         $day_name = $this->day_pcre;
15524                         $month = $this->month_pcre;
15525                         $day = '([0-9]{1,2})';
15526                         $year = $hour = $minute = $second = '([0-9]{2})';
15527                         $zone = '([A-Z]{1,5})';
15528                         $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
15529                 }
15530                 if (preg_match($pcre, $date, $match))
15531                 {
15532                         /*
15533                         Capturing subpatterns:
15534                         1: Day name
15535                         2: Day
15536                         3: Month
15537                         4: Year
15538                         5: Hour
15539                         6: Minute
15540                         7: Second
15541                         8: Timezone
15542                         */
15543
15544                         // Month
15545                         $month = $this->month[strtolower($match[3])];
15546
15547                         // Character timezone
15548                         if (isset($this->timezone[strtoupper($match[8])]))
15549                         {
15550                                 $timezone = $this->timezone[strtoupper($match[8])];
15551                         }
15552                         // Assume everything else to be -0000
15553                         else
15554                         {
15555                                 $timezone = 0;
15556                         }
15557
15558                         // Deal with 2 digit year
15559                         if ($match[4] < 50)
15560                         {
15561                                 $match[4] += 2000;
15562                         }
15563                         else
15564                         {
15565                                 $match[4] += 1900;
15566                         }
15567
15568                         return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
15569                 }
15570                 else
15571                 {
15572                         return false;
15573                 }
15574         }
15575
15576         /**
15577          * Parse C99's asctime()'s date format
15578          *
15579          * @access protected
15580          * @return int Timestamp
15581          */
15582         public function date_asctime($date)
15583         {
15584                 static $pcre;
15585                 if (!$pcre)
15586                 {
15587                         $space = '[\x09\x20]+';
15588                         $wday_name = $this->day_pcre;
15589                         $mon_name = $this->month_pcre;
15590                         $day = '([0-9]{1,2})';
15591                         $hour = $sec = $min = '([0-9]{2})';
15592                         $year = '([0-9]{4})';
15593                         $terminator = '\x0A?\x00?';
15594                         $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
15595                 }
15596                 if (preg_match($pcre, $date, $match))
15597                 {
15598                         /*
15599                         Capturing subpatterns:
15600                         1: Day name
15601                         2: Month
15602                         3: Day
15603                         4: Hour
15604                         5: Minute
15605                         6: Second
15606                         7: Year
15607                         */
15608
15609                         $month = $this->month[strtolower($match[2])];
15610                         return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
15611                 }
15612                 else
15613                 {
15614                         return false;
15615                 }
15616         }
15617
15618         /**
15619          * Parse dates using strtotime()
15620          *
15621          * @access protected
15622          * @return int Timestamp
15623          */
15624         public function date_strtotime($date)
15625         {
15626                 $strtotime = strtotime($date);
15627                 if ($strtotime === -1 || $strtotime === false)
15628                 {
15629                         return false;
15630                 }
15631                 else
15632                 {
15633                         return $strtotime;
15634                 }
15635         }
15636 }
15637
15638 /**
15639  * Parses XML into something sane
15640  *
15641  *
15642  * This class can be overloaded with {@see SimplePie::set_parser_class()}
15643  *
15644  * @package SimplePie
15645  * @subpackage Parsing
15646  */
15647 class SimplePie_Parser
15648 {
15649         var $error_code;
15650         var $error_string;
15651         var $current_line;
15652         var $current_column;
15653         var $current_byte;
15654         var $separator = ' ';
15655         var $namespace = array('');
15656         var $element = array('');
15657         var $xml_base = array('');
15658         var $xml_base_explicit = array(false);
15659         var $xml_lang = array('');
15660         var $data = array();
15661         var $datas = array(array());
15662         var $current_xhtml_construct = -1;
15663         var $encoding;
15664         protected $registry;
15665
15666         public function set_registry(SimplePie_Registry $registry)
15667         {
15668                 $this->registry = $registry;
15669         }
15670
15671         public function parse(&$data, $encoding)
15672         {
15673                 // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
15674                 if (strtoupper($encoding) === 'US-ASCII')
15675                 {
15676                         $this->encoding = 'UTF-8';
15677                 }
15678                 else
15679                 {
15680                         $this->encoding = $encoding;
15681                 }
15682
15683                 // Strip BOM:
15684                 // UTF-32 Big Endian BOM
15685                 if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
15686                 {
15687                         $data = substr($data, 4);
15688                 }
15689                 // UTF-32 Little Endian BOM
15690                 elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
15691                 {
15692                         $data = substr($data, 4);
15693                 }
15694                 // UTF-16 Big Endian BOM
15695                 elseif (substr($data, 0, 2) === "\xFE\xFF")
15696                 {
15697                         $data = substr($data, 2);
15698                 }
15699                 // UTF-16 Little Endian BOM
15700                 elseif (substr($data, 0, 2) === "\xFF\xFE")
15701                 {
15702                         $data = substr($data, 2);
15703                 }
15704                 // UTF-8 BOM
15705                 elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
15706                 {
15707                         $data = substr($data, 3);
15708                 }
15709
15710                 if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
15711                 {
15712                         $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
15713                         if ($declaration->parse())
15714                         {
15715                                 $data = substr($data, $pos + 2);
15716                                 $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
15717                         }
15718                         else
15719                         {
15720                                 $this->error_string = 'SimplePie bug! Please report this!';
15721                                 return false;
15722                         }
15723                 }
15724
15725                 $return = true;
15726
15727                 static $xml_is_sane = null;
15728                 if ($xml_is_sane === null)
15729                 {
15730                         $parser_check = xml_parser_create();
15731                         xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
15732                         xml_parser_free($parser_check);
15733                         $xml_is_sane = isset($values[0]['value']);
15734                 }
15735
15736                 // Create the parser
15737                 if ($xml_is_sane)
15738                 {
15739                         $xml = xml_parser_create_ns($this->encoding, $this->separator);
15740                         xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
15741                         xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
15742                         xml_set_object($xml, $this);
15743                         xml_set_character_data_handler($xml, 'cdata');
15744                         xml_set_element_handler($xml, 'tag_open', 'tag_close');
15745
15746                         // Parse!
15747                         if (!xml_parse($xml, $data, true))
15748                         {
15749                                 $this->error_code = xml_get_error_code($xml);
15750                                 $this->error_string = xml_error_string($this->error_code);
15751                                 $return = false;
15752                         }
15753                         $this->current_line = xml_get_current_line_number($xml);
15754                         $this->current_column = xml_get_current_column_number($xml);
15755                         $this->current_byte = xml_get_current_byte_index($xml);
15756                         xml_parser_free($xml);
15757                         return $return;
15758                 }
15759                 else
15760                 {
15761                         libxml_clear_errors();
15762                         $xml = new XMLReader();
15763                         $xml->xml($data);
15764                         while (@$xml->read())
15765                         {
15766                                 switch ($xml->nodeType)
15767                                 {
15768
15769                                         case constant('XMLReader::END_ELEMENT'):
15770                                                 if ($xml->namespaceURI !== '')
15771                                                 {
15772                                                         $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
15773                                                 }
15774                                                 else
15775                                                 {
15776                                                         $tagName = $xml->localName;
15777                                                 }
15778                                                 $this->tag_close(null, $tagName);
15779                                                 break;
15780                                         case constant('XMLReader::ELEMENT'):
15781                                                 $empty = $xml->isEmptyElement;
15782                                                 if ($xml->namespaceURI !== '')
15783                                                 {
15784                                                         $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
15785                                                 }
15786                                                 else
15787                                                 {
15788                                                         $tagName = $xml->localName;
15789                                                 }
15790                                                 $attributes = array();
15791                                                 while ($xml->moveToNextAttribute())
15792                                                 {
15793                                                         if ($xml->namespaceURI !== '')
15794                                                         {
15795                                                                 $attrName = $xml->namespaceURI . $this->separator . $xml->localName;
15796                                                         }
15797                                                         else
15798                                                         {
15799                                                                 $attrName = $xml->localName;
15800                                                         }
15801                                                         $attributes[$attrName] = $xml->value;
15802                                                 }
15803                                                 $this->tag_open(null, $tagName, $attributes);
15804                                                 if ($empty)
15805                                                 {
15806                                                         $this->tag_close(null, $tagName);
15807                                                 }
15808                                                 break;
15809                                         case constant('XMLReader::TEXT'):
15810
15811                                         case constant('XMLReader::CDATA'):
15812                                                 $this->cdata(null, $xml->value);
15813                                                 break;
15814                                 }
15815                         }
15816                         if ($error = libxml_get_last_error())
15817                         {
15818                                 $this->error_code = $error->code;
15819                                 $this->error_string = $error->message;
15820                                 $this->current_line = $error->line;
15821                                 $this->current_column = $error->column;
15822                                 return false;
15823                         }
15824                         else
15825                         {
15826                                 return true;
15827                         }
15828                 }
15829         }
15830
15831         public function get_error_code()
15832         {
15833                 return $this->error_code;
15834         }
15835
15836         public function get_error_string()
15837         {
15838                 return $this->error_string;
15839         }
15840
15841         public function get_current_line()
15842         {
15843                 return $this->current_line;
15844         }
15845
15846         public function get_current_column()
15847         {
15848                 return $this->current_column;
15849         }
15850
15851         public function get_current_byte()
15852         {
15853                 return $this->current_byte;
15854         }
15855
15856         public function get_data()
15857         {
15858                 return $this->data;
15859         }
15860
15861         public function tag_open($parser, $tag, $attributes)
15862         {
15863                 list($this->namespace[], $this->element[]) = $this->split_ns($tag);
15864
15865                 $attribs = array();
15866                 foreach ($attributes as $name => $value)
15867                 {
15868                         list($attrib_namespace, $attribute) = $this->split_ns($name);
15869                         $attribs[$attrib_namespace][$attribute] = $value;
15870                 }
15871
15872                 if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
15873                 {
15874                         $base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)));
15875                         if ($base !== false)
15876                         {
15877                                 $this->xml_base[] = $base;
15878                                 $this->xml_base_explicit[] = true;
15879                         }
15880                 }
15881                 else
15882                 {
15883                         $this->xml_base[] = end($this->xml_base);
15884                         $this->xml_base_explicit[] = end($this->xml_base_explicit);
15885                 }
15886
15887                 if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
15888                 {
15889                         $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
15890                 }
15891                 else
15892                 {
15893                         $this->xml_lang[] = end($this->xml_lang);
15894                 }
15895
15896                 if ($this->current_xhtml_construct >= 0)
15897                 {
15898                         $this->current_xhtml_construct++;
15899                         if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
15900                         {
15901                                 $this->data['data'] .= '<' . end($this->element);
15902                                 if (isset($attribs['']))
15903                                 {
15904                                         foreach ($attribs[''] as $name => $value)
15905                                         {
15906                                                 $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
15907                                         }
15908                                 }
15909                                 $this->data['data'] .= '>';
15910                         }
15911                 }
15912                 else
15913                 {
15914                         $this->datas[] =& $this->data;
15915                         $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
15916                         $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));
15917                         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')
15918                         || (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')
15919                         || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title')))
15920                         || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title')))
15921                         || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title'))))
15922                         {
15923                                 $this->current_xhtml_construct = 0;
15924                         }
15925                 }
15926         }
15927
15928         public function cdata($parser, $cdata)
15929         {
15930                 if ($this->current_xhtml_construct >= 0)
15931                 {
15932                         $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
15933                 }
15934                 else
15935                 {
15936                         $this->data['data'] .= $cdata;
15937                 }
15938         }
15939
15940         public function tag_close($parser, $tag)
15941         {
15942                 if ($this->current_xhtml_construct >= 0)
15943                 {
15944                         $this->current_xhtml_construct--;
15945                         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')))
15946                         {
15947                                 $this->data['data'] .= '</' . end($this->element) . '>';
15948                         }
15949                 }
15950                 if ($this->current_xhtml_construct === -1)
15951                 {
15952                         $this->data =& $this->datas[count($this->datas) - 1];
15953                         array_pop($this->datas);
15954                 }
15955
15956                 array_pop($this->element);
15957                 array_pop($this->namespace);
15958                 array_pop($this->xml_base);
15959                 array_pop($this->xml_base_explicit);
15960                 array_pop($this->xml_lang);
15961         }
15962
15963         public function split_ns($string)
15964         {
15965                 static $cache = array();
15966                 if (!isset($cache[$string]))
15967                 {
15968                         if ($pos = strpos($string, $this->separator))
15969                         {
15970                                 static $separator_length;
15971                                 if (!$separator_length)
15972                                 {
15973                                         $separator_length = strlen($this->separator);
15974                                 }
15975                                 $namespace = substr($string, 0, $pos);
15976                                 $local_name = substr($string, $pos + $separator_length);
15977                                 if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
15978                                 {
15979                                         $namespace = SIMPLEPIE_NAMESPACE_ITUNES;
15980                                 }
15981
15982                                 // Normalize the Media RSS namespaces
15983                                 if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
15984                                         $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
15985                                         $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
15986                                         $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
15987                                         $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
15988                                 {
15989                                         $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
15990                                 }
15991                                 $cache[$string] = array($namespace, $local_name);
15992                         }
15993                         else
15994                         {
15995                                 $cache[$string] = array('', $string);
15996                         }
15997                 }
15998                 return $cache[$string];
15999         }
16000 }
16001
16002 /**
16003  * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
16004  *
16005  * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()}
16006  *
16007  * This class can be overloaded with {@see SimplePie::set_rating_class()}
16008  *
16009  * @package SimplePie
16010  * @subpackage API
16011  */
16012 class SimplePie_Rating
16013 {
16014         /**
16015          * Rating scheme
16016          *
16017          * @var string
16018          * @see get_scheme()
16019          */
16020         var $scheme;
16021
16022         /**
16023          * Rating value
16024          *
16025          * @var string
16026          * @see get_value()
16027          */
16028         var $value;
16029
16030         /**
16031          * Constructor, used to input the data
16032          *
16033          * For documentation on all the parameters, see the corresponding
16034          * properties and their accessors
16035          */
16036         public function __construct($scheme = null, $value = null)
16037         {
16038                 $this->scheme = $scheme;
16039                 $this->value = $value;
16040         }
16041
16042         /**
16043          * String-ified version
16044          *
16045          * @return string
16046          */
16047         public function __toString()
16048         {
16049                 // There is no $this->data here
16050                 return md5(serialize($this));
16051         }
16052
16053         /**
16054          * Get the organizational scheme for the rating
16055          *
16056          * @return string|null
16057          */
16058         public function get_scheme()
16059         {
16060                 if ($this->scheme !== null)
16061                 {
16062                         return $this->scheme;
16063                 }
16064                 else
16065                 {
16066                         return null;
16067                 }
16068         }
16069
16070         /**
16071          * Get the value of the rating
16072          *
16073          * @return string|null
16074          */
16075         public function get_value()
16076         {
16077                 if ($this->value !== null)
16078                 {
16079                         return $this->value;
16080                 }
16081                 else
16082                 {
16083                         return null;
16084                 }
16085         }
16086 }
16087
16088 /**
16089  * Handles creating objects and calling methods
16090  *
16091  * Access this via {@see SimplePie::get_registry()}
16092  *
16093  * @package SimplePie
16094  */
16095 class SimplePie_Registry
16096 {
16097         /**
16098          * Default class mapping
16099          *
16100          * Overriding classes *must* subclass these.
16101          *
16102          * @var array
16103          */
16104         protected $default = array(
16105                 'Cache' => 'SimplePie_Cache',
16106                 'Locator' => 'SimplePie_Locator',
16107                 'Parser' => 'SimplePie_Parser',
16108                 'File' => 'SimplePie_File',
16109                 'Sanitize' => 'SimplePie_Sanitize',
16110                 'Item' => 'SimplePie_Item',
16111                 'Author' => 'SimplePie_Author',
16112                 'Category' => 'SimplePie_Category',
16113                 'Enclosure' => 'SimplePie_Enclosure',
16114                 'Caption' => 'SimplePie_Caption',
16115                 'Copyright' => 'SimplePie_Copyright',
16116                 'Credit' => 'SimplePie_Credit',
16117                 'Rating' => 'SimplePie_Rating',
16118                 'Restriction' => 'SimplePie_Restriction',
16119                 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer',
16120                 'Source' => 'SimplePie_Source',
16121                 'Misc' => 'SimplePie_Misc',
16122                 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser',
16123                 'Parse_Date' => 'SimplePie_Parse_Date',
16124         );
16125
16126         /**
16127          * Class mapping
16128          *
16129          * @see register()
16130          * @var array
16131          */
16132         protected $classes = array();
16133
16134         /**
16135          * Legacy classes
16136          *
16137          * @see register()
16138          * @var array
16139          */
16140         protected $legacy = array();
16141
16142         /**
16143          * Constructor
16144          *
16145          * No-op
16146          */
16147         public function __construct() { }
16148
16149         /**
16150          * Register a class
16151          *
16152          * @param string $type See {@see $default} for names
16153          * @param string $class Class name, must subclass the corresponding default
16154          * @param bool $legacy Whether to enable legacy support for this class
16155          * @return bool Successfulness
16156          */
16157         public function register($type, $class, $legacy = false)
16158         {
16159                 if (!is_subclass_of($class, $this->default[$type]))
16160                 {
16161                         return false;
16162                 }
16163
16164                 $this->classes[$type] = $class;
16165
16166                 if ($legacy)
16167                 {
16168                         $this->legacy[] = $class;
16169                 }
16170
16171                 return true;
16172         }
16173
16174         /**
16175          * Get the class registered for a type
16176          *
16177          * Where possible, use {@see create()} or {@see call()} instead
16178          *
16179          * @param string $type
16180          * @return string|null
16181          */
16182         public function get_class($type)
16183         {
16184                 if (!empty($this->classes[$type]))
16185                 {
16186                         return $this->classes[$type];
16187                 }
16188                 if (!empty($this->default[$type]))
16189                 {
16190                         return $this->default[$type];
16191                 }
16192
16193                 return null;
16194         }
16195
16196         /**
16197          * Create a new instance of a given type
16198          *
16199          * @param string $type
16200          * @param array $parameters Parameters to pass to the constructor
16201          * @return object Instance of class
16202          */
16203         public function &create($type, $parameters = array())
16204         {
16205                 $class = $this->get_class($type);
16206
16207                 if (in_array($class, $this->legacy))
16208                 {
16209                         switch ($type)
16210                         {
16211                                 case 'locator':
16212                                         // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class
16213                                         // Specified: file, timeout, useragent, max_checked_feeds
16214                                         $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer'));
16215                                         array_splice($parameters, 3, 1, $replacement);
16216                                         break;
16217                         }
16218                 }
16219
16220                 if (!method_exists($class, '__construct'))
16221                 {
16222                         $instance = new $class;
16223                 }
16224                 else
16225                 {
16226                         $reflector = new ReflectionClass($class);
16227                         $instance = $reflector->newInstanceArgs($parameters);
16228                 }
16229
16230                 if (method_exists($instance, 'set_registry'))
16231                 {
16232                         $instance->set_registry($this);
16233                 }
16234                 return $instance;
16235         }
16236
16237         /**
16238          * Call a static method for a type
16239          *
16240          * @param string $type
16241          * @param string $method
16242          * @param array $parameters
16243          * @return mixed
16244          */
16245         public function &call($type, $method, $parameters = array())
16246         {
16247                 $class = $this->get_class($type);
16248
16249                 if (in_array($class, $this->legacy))
16250                 {
16251                         switch ($type)
16252                         {
16253                                 case 'Cache':
16254                                         // For backwards compatibility with old non-static
16255                                         // Cache::create() methods
16256                                         if ($method === 'get_handler')
16257                                         {
16258                                                 $result = @call_user_func_array(array($class, 'create'), $parameters);
16259                                                 return $result;
16260                                         }
16261                                         break;
16262                         }
16263                 }
16264
16265                 $result = call_user_func_array(array($class, $method), $parameters);
16266                 return $result;
16267         }
16268 }
16269
16270 /**
16271  * Handles `<media:restriction>` as defined in Media RSS
16272  *
16273  * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()}
16274  *
16275  * This class can be overloaded with {@see SimplePie::set_restriction_class()}
16276  *
16277  * @package SimplePie
16278  * @subpackage API
16279  */
16280 class SimplePie_Restriction
16281 {
16282         /**
16283          * Relationship ('allow'/'deny')
16284          *
16285          * @var string
16286          * @see get_relationship()
16287          */
16288         var $relationship;
16289
16290         /**
16291          * Type of restriction
16292          *
16293          * @var string
16294          * @see get_type()
16295          */
16296         var $type;
16297
16298         /**
16299          * Restricted values
16300          *
16301          * @var string
16302          * @see get_value()
16303          */
16304         var $value;
16305
16306         /**
16307          * Constructor, used to input the data
16308          *
16309          * For documentation on all the parameters, see the corresponding
16310          * properties and their accessors
16311          */
16312         public function __construct($relationship = null, $type = null, $value = null)
16313         {
16314                 $this->relationship = $relationship;
16315                 $this->type = $type;
16316                 $this->value = $value;
16317         }
16318
16319         /**
16320          * String-ified version
16321          *
16322          * @return string
16323          */
16324         public function __toString()
16325         {
16326                 // There is no $this->data here
16327                 return md5(serialize($this));
16328         }
16329
16330         /**
16331          * Get the relationship
16332          *
16333          * @return string|null Either 'allow' or 'deny'
16334          */
16335         public function get_relationship()
16336         {
16337                 if ($this->relationship !== null)
16338                 {
16339                         return $this->relationship;
16340                 }
16341                 else
16342                 {
16343                         return null;
16344                 }
16345         }
16346
16347         /**
16348          * Get the type
16349          *
16350          * @return string|null
16351          */
16352         public function get_type()
16353         {
16354                 if ($this->type !== null)
16355                 {
16356                         return $this->type;
16357                 }
16358                 else
16359                 {
16360                         return null;
16361                 }
16362         }
16363
16364         /**
16365          * Get the list of restricted things
16366          *
16367          * @return string|null
16368          */
16369         public function get_value()
16370         {
16371                 if ($this->value !== null)
16372                 {
16373                         return $this->value;
16374                 }
16375                 else
16376                 {
16377                         return null;
16378                 }
16379         }
16380 }
16381
16382 /**
16383  * Used for data cleanup and post-processing
16384  *
16385  *
16386  * This class can be overloaded with {@see SimplePie::set_sanitize_class()}
16387  *
16388  * @package SimplePie
16389  * @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
16390  */
16391 class SimplePie_Sanitize
16392 {
16393         // Private vars
16394         var $base;
16395
16396         // Options
16397         var $remove_div = true;
16398         var $image_handler = '';
16399         var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
16400         var $encode_instead_of_strip = false;
16401         var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
16402         var $strip_comments = false;
16403         var $output_encoding = 'UTF-8';
16404         var $enable_cache = true;
16405         var $cache_location = './cache';
16406         var $cache_name_function = 'md5';
16407         var $timeout = 10;
16408         var $useragent = '';
16409         var $force_fsockopen = false;
16410         var $replace_url_attributes = null;
16411
16412         public function __construct()
16413         {
16414                 // Set defaults
16415                 $this->set_url_replacements(null);
16416         }
16417
16418         public function remove_div($enable = true)
16419         {
16420                 $this->remove_div = (bool) $enable;
16421         }
16422
16423         public function set_image_handler($page = false)
16424         {
16425                 if ($page)
16426                 {
16427                         $this->image_handler = (string) $page;
16428                 }
16429                 else
16430                 {
16431                         $this->image_handler = false;
16432                 }
16433         }
16434
16435         public function set_registry(SimplePie_Registry $registry)
16436         {
16437                 $this->registry = $registry;
16438         }
16439
16440         public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
16441         {
16442                 if (isset($enable_cache))
16443                 {
16444                         $this->enable_cache = (bool) $enable_cache;
16445                 }
16446
16447                 if ($cache_location)
16448                 {
16449                         $this->cache_location = (string) $cache_location;
16450                 }
16451
16452                 if ($cache_name_function)
16453                 {
16454                         $this->cache_name_function = (string) $cache_name_function;
16455                 }
16456         }
16457
16458         public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
16459         {
16460                 if ($timeout)
16461                 {
16462                         $this->timeout = (string) $timeout;
16463                 }
16464
16465                 if ($useragent)
16466                 {
16467                         $this->useragent = (string) $useragent;
16468                 }
16469
16470                 if ($force_fsockopen)
16471                 {
16472                         $this->force_fsockopen = (string) $force_fsockopen;
16473                 }
16474         }
16475
16476         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'))
16477         {
16478                 if ($tags)
16479                 {
16480                         if (is_array($tags))
16481                         {
16482                                 $this->strip_htmltags = $tags;
16483                         }
16484                         else
16485                         {
16486                                 $this->strip_htmltags = explode(',', $tags);
16487                         }
16488                 }
16489                 else
16490                 {
16491                         $this->strip_htmltags = false;
16492                 }
16493         }
16494
16495         public function encode_instead_of_strip($encode = false)
16496         {
16497                 $this->encode_instead_of_strip = (bool) $encode;
16498         }
16499
16500         public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
16501         {
16502                 if ($attribs)
16503                 {
16504                         if (is_array($attribs))
16505                         {
16506                                 $this->strip_attributes = $attribs;
16507                         }
16508                         else
16509                         {
16510                                 $this->strip_attributes = explode(',', $attribs);
16511                         }
16512                 }
16513                 else
16514                 {
16515                         $this->strip_attributes = false;
16516                 }
16517         }
16518
16519         public function strip_comments($strip = false)
16520         {
16521                 $this->strip_comments = (bool) $strip;
16522         }
16523
16524         public function set_output_encoding($encoding = 'UTF-8')
16525         {
16526                 $this->output_encoding = (string) $encoding;
16527         }
16528
16529         /**
16530          * Set element/attribute key/value pairs of HTML attributes
16531          * containing URLs that need to be resolved relative to the feed
16532          *
16533          * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
16534          * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
16535          * |q|@cite
16536          *
16537          * @since 1.0
16538          * @param array|null $element_attribute Element/attribute key/value pairs, null for default
16539          */
16540         public function set_url_replacements($element_attribute = null)
16541         {
16542                 if ($element_attribute === null)
16543                 {
16544                         $element_attribute = array(
16545                                 'a' => 'href',
16546                                 'area' => 'href',
16547                                 'blockquote' => 'cite',
16548                                 'del' => 'cite',
16549                                 'form' => 'action',
16550                                 'img' => array(
16551                                         'longdesc',
16552                                         'src'
16553                                 ),
16554                                 'input' => 'src',
16555                                 'ins' => 'cite',
16556                                 'q' => 'cite'
16557                         );
16558                 }
16559                 $this->replace_url_attributes = (array) $element_attribute;
16560         }
16561
16562         public function sanitize($data, $type, $base = '')
16563         {
16564                 $data = trim($data);
16565                 if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
16566                 {
16567                         if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
16568                         {
16569                                 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))
16570                                 {
16571                                         $type |= SIMPLEPIE_CONSTRUCT_HTML;
16572                                 }
16573                                 else
16574                                 {
16575                                         $type |= SIMPLEPIE_CONSTRUCT_TEXT;
16576                                 }
16577                         }
16578
16579                         if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
16580                         {
16581                                 $data = base64_decode($data);
16582                         }
16583
16584                         if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
16585                         {
16586
16587                                 $document = new DOMDocument();
16588                                 $document->encoding = 'UTF-8';
16589                                 $data = $this->preprocess($data, $type);
16590
16591                                 set_error_handler(array('SimplePie_Misc', 'silence_errors'));
16592                                 $document->loadHTML($data);
16593                                 restore_error_handler();
16594
16595                                 // Strip comments
16596                                 if ($this->strip_comments)
16597                                 {
16598                                         $xpath = new DOMXPath($document);
16599                                         $comments = $xpath->query('//comment()');
16600
16601                                         foreach ($comments as $comment)
16602                                         {
16603                                                 $comment->parentNode->removeChild($comment);
16604                                         }
16605                                 }
16606
16607                                 // Strip out HTML tags and attributes that might cause various security problems.
16608                                 // Based on recommendations by Mark Pilgrim at:
16609                                 // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
16610                                 if ($this->strip_htmltags)
16611                                 {
16612                                         foreach ($this->strip_htmltags as $tag)
16613                                         {
16614                                                 $this->strip_tag($tag, $document, $type);
16615                                         }
16616                                 }
16617
16618                                 if ($this->strip_attributes)
16619                                 {
16620                                         foreach ($this->strip_attributes as $attrib)
16621                                         {
16622                                                 $this->strip_attr($attrib, $document);
16623                                         }
16624                                 }
16625
16626                                 // Replace relative URLs
16627                                 $this->base = $base;
16628                                 foreach ($this->replace_url_attributes as $element => $attributes)
16629                                 {
16630                                         $this->replace_urls($document, $element, $attributes);
16631                                 }
16632
16633                                 // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
16634                                 if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
16635                                 {
16636                                         $images = $document->getElementsByTagName('img');
16637                                         foreach ($images as $img)
16638                                         {
16639                                                 if ($img->hasAttribute('src'))
16640                                                 {
16641                                                         $image_url = call_user_func($this->cache_name_function, $img->getAttribute('src'));
16642                                                         $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi'));
16643
16644                                                         if ($cache->load())
16645                                                         {
16646                                                                 $img->setAttribute('src', $this->image_handler . $image_url);
16647                                                         }
16648                                                         else
16649                                                         {
16650                                                                 $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));
16651                                                                 $headers = $file->headers;
16652
16653                                                                 if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
16654                                                                 {
16655                                                                         if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
16656                                                                         {
16657                                                                                 $img->setAttribute('src', $this->image_handler . $image_url);
16658                                                                         }
16659                                                                         else
16660                                                                         {
16661                                                                                 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);
16662                                                                         }
16663                                                                 }
16664                                                         }
16665                                                 }
16666                                         }
16667                                 }
16668
16669                                 // Remove the DOCTYPE
16670                                 // Seems to cause segfaulting if we don't do this
16671                                 if ($document->firstChild instanceof DOMDocumentType)
16672                                 {
16673                                         $document->removeChild($document->firstChild);
16674                                 }
16675
16676                                 // Move everything from the body to the root
16677                                 $real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0);
16678                                 $document->replaceChild($real_body, $document->firstChild);
16679
16680                                 // Finally, convert to a HTML string
16681                                 $data = trim($document->saveHTML());
16682
16683                                 if ($this->remove_div)
16684                                 {
16685                                         $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
16686                                         $data = preg_replace('/<\/div>$/', '', $data);
16687                                 }
16688                                 else
16689                                 {
16690                                         $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
16691                                 }
16692                         }
16693
16694                         if ($type & SIMPLEPIE_CONSTRUCT_IRI)
16695                         {
16696                                 $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
16697                                 if ($absolute !== false)
16698                                 {
16699                                         $data = $absolute;
16700                                 }
16701                         }
16702
16703                         if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
16704                         {
16705                                 $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
16706                         }
16707
16708                         if ($this->output_encoding !== 'UTF-8')
16709                         {
16710                                 $data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding));
16711                         }
16712                 }
16713                 return $data;
16714         }
16715
16716         protected function preprocess($html, $type)
16717         {
16718                 $ret = '';
16719                 if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML)
16720                 {
16721                         // Atom XHTML constructs are wrapped with a div by default
16722                         // Note: No protection if $html contains a stray </div>!
16723                         $html = '<div>' . $html . '</div>';
16724                         $ret .= '<!DOCTYPE html>';
16725                         $content_type = 'text/html';
16726                 }
16727                 else
16728                 {
16729                         $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
16730                         $content_type = 'application/xhtml+xml';
16731                 }
16732
16733                 $ret .= '<html><head>';
16734                 $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
16735                 $ret .= '</head><body>' . $html . '</body></html>';
16736                 return $ret;
16737         }
16738
16739         public function replace_urls($document, $tag, $attributes)
16740         {
16741                 if (!is_array($attributes))
16742                 {
16743                         $attributes = array($attributes);
16744                 }
16745
16746                 if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
16747                 {
16748                         $elements = $document->getElementsByTagName($tag);
16749                         foreach ($elements as $element)
16750                         {
16751                                 foreach ($attributes as $attribute)
16752                                 {
16753                                         if ($element->hasAttribute($attribute))
16754                                         {
16755                                                 $value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base));
16756                                                 if ($value !== false)
16757                                                 {
16758                                                         $element->setAttribute($attribute, $value);
16759                                                 }
16760                                         }
16761                                 }
16762                         }
16763                 }
16764         }
16765
16766         public function do_strip_htmltags($match)
16767         {
16768                 if ($this->encode_instead_of_strip)
16769                 {
16770                         if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
16771                         {
16772                                 $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
16773                                 $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
16774                                 return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
16775                         }
16776                         else
16777                         {
16778                                 return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
16779                         }
16780                 }
16781                 elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
16782                 {
16783                         return $match[4];
16784                 }
16785                 else
16786                 {
16787                         return '';
16788                 }
16789         }
16790
16791         protected function strip_tag($tag, $document, $type)
16792         {
16793                 $xpath = new DOMXPath($document);
16794                 $elements = $xpath->query('body//' . $tag);
16795                 if ($this->encode_instead_of_strip)
16796                 {
16797                         foreach ($elements as $element)
16798                         {
16799                                 $fragment = $document->createDocumentFragment();
16800
16801                                 // For elements which aren't script or style, include the tag itself
16802                                 if (!in_array($tag, array('script', 'style')))
16803                                 {
16804                                         $text = '<' . $tag;
16805                                         if ($element->hasAttributes())
16806                                         {
16807                                                 $attrs = array();
16808                                                 foreach ($element->attributes as $name => $attr)
16809                                                 {
16810                                                         $value = $attr->value;
16811
16812                                                         // In XHTML, empty values should never exist, so we repeat the value
16813                                                         if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML))
16814                                                         {
16815                                                                 $value = $name;
16816                                                         }
16817                                                         // For HTML, empty is fine
16818                                                         elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML))
16819                                                         {
16820                                                                 $attrs[] = $name;
16821                                                                 continue;
16822                                                         }
16823
16824                                                         // Standard attribute text
16825                                                         $attrs[] = $name . '="' . $attr->value . '"';
16826                                                 }
16827                                                 $text .= ' ' . implode(' ', $attrs);
16828                                         }
16829                                         $text .= '>';
16830                                         $fragment->appendChild(new DOMText($text));
16831                                 }
16832
16833                                 $number = $element->childNodes->length;
16834                                 for ($i = $number; $i > 0; $i--)
16835                                 {
16836                                         $child = $element->childNodes->item(0);
16837                                         $fragment->appendChild($child);
16838                                 }
16839
16840                                 if (!in_array($tag, array('script', 'style')))
16841                                 {
16842                                         $fragment->appendChild(new DOMText('</' . $tag . '>'));
16843                                 }
16844
16845                                 $element->parentNode->replaceChild($fragment, $element);
16846                         }
16847
16848                         return;
16849                 }
16850                 elseif (in_array($tag, array('script', 'style')))
16851                 {
16852                         foreach ($elements as $element)
16853                         {
16854                                 $element->parentNode->removeChild($element);
16855                         }
16856
16857                         return;
16858                 }
16859                 else
16860                 {
16861                         foreach ($elements as $element)
16862                         {
16863                                 $fragment = $document->createDocumentFragment();
16864                                 $number = $element->childNodes->length;
16865                                 for ($i = $number; $i > 0; $i--)
16866                                 {
16867                                         $child = $element->childNodes->item(0);
16868                                         $fragment->appendChild($child);
16869                                 }
16870
16871                                 $element->parentNode->replaceChild($fragment, $element);
16872                         }
16873                 }
16874         }
16875
16876         protected function strip_attr($attrib, $document)
16877         {
16878                 $xpath = new DOMXPath($document);
16879                 $elements = $xpath->query('//*[@' . $attrib . ']');
16880
16881                 foreach ($elements as $element)
16882                 {
16883                         $element->removeAttribute($attrib);
16884                 }
16885         }
16886 }
16887
16888 /**
16889  * Handles `<atom:source>`
16890  *
16891  * Used by {@see SimplePie_Item::get_source()}
16892  *
16893  * This class can be overloaded with {@see SimplePie::set_source_class()}
16894  *
16895  * @package SimplePie
16896  * @subpackage API
16897  */
16898 class SimplePie_Source
16899 {
16900         var $item;
16901         var $data = array();
16902         protected $registry;
16903
16904         public function __construct($item, $data)
16905         {
16906                 $this->item = $item;
16907                 $this->data = $data;
16908         }
16909
16910         public function set_registry(SimplePie_Registry $registry)
16911         {
16912                 $this->registry = $registry;
16913         }
16914
16915         public function __toString()
16916         {
16917                 return md5(serialize($this->data));
16918         }
16919
16920         public function get_source_tags($namespace, $tag)
16921         {
16922                 if (isset($this->data['child'][$namespace][$tag]))
16923                 {
16924                         return $this->data['child'][$namespace][$tag];
16925                 }
16926                 else
16927                 {
16928                         return null;
16929                 }
16930         }
16931
16932         public function get_base($element = array())
16933         {
16934                 return $this->item->get_base($element);
16935         }
16936
16937         public function sanitize($data, $type, $base = '')
16938         {
16939                 return $this->item->sanitize($data, $type, $base);
16940         }
16941
16942         public function get_item()
16943         {
16944                 return $this->item;
16945         }
16946
16947         public function get_title()
16948         {
16949                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
16950                 {
16951                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
16952                 }
16953                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
16954                 {
16955                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
16956                 }
16957                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, '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_090, '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_RSS_20, 'title'))
16966                 {
16967                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16968                 }
16969                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
16970                 {
16971                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
16972                 }
16973                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
16974                 {
16975                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
16976                 }
16977                 else
16978                 {
16979                         return null;
16980                 }
16981         }
16982
16983         public function get_category($key = 0)
16984         {
16985                 $categories = $this->get_categories();
16986                 if (isset($categories[$key]))
16987                 {
16988                         return $categories[$key];
16989                 }
16990                 else
16991                 {
16992                         return null;
16993                 }
16994         }
16995
16996         public function get_categories()
16997         {
16998                 $categories = array();
16999
17000                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
17001                 {
17002                         $term = null;
17003                         $scheme = null;
17004                         $label = null;
17005                         if (isset($category['attribs']['']['term']))
17006                         {
17007                                 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
17008                         }
17009                         if (isset($category['attribs']['']['scheme']))
17010                         {
17011                                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
17012                         }
17013                         if (isset($category['attribs']['']['label']))
17014                         {
17015                                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
17016                         }
17017                         $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
17018                 }
17019                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
17020                 {
17021                         // This is really the label, but keep this as the term also for BC.
17022                         // Label will also work on retrieving because that falls back to term.
17023                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17024                         if (isset($category['attribs']['']['domain']))
17025                         {
17026                                 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
17027                         }
17028                         else
17029                         {
17030                                 $scheme = null;
17031                         }
17032                         $categories[] = $this->registry->create('Category', array($term, $scheme, null));
17033                 }
17034                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
17035                 {
17036                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17037                 }
17038                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
17039                 {
17040                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17041                 }
17042
17043                 if (!empty($categories))
17044                 {
17045                         return array_unique($categories);
17046                 }
17047                 else
17048                 {
17049                         return null;
17050                 }
17051         }
17052
17053         public function get_author($key = 0)
17054         {
17055                 $authors = $this->get_authors();
17056                 if (isset($authors[$key]))
17057                 {
17058                         return $authors[$key];
17059                 }
17060                 else
17061                 {
17062                         return null;
17063                 }
17064         }
17065
17066         public function get_authors()
17067         {
17068                 $authors = array();
17069                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
17070                 {
17071                         $name = null;
17072                         $uri = null;
17073                         $email = null;
17074                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17075                         {
17076                                 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17077                         }
17078                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17079                         {
17080                                 $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]));
17081                         }
17082                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17083                         {
17084                                 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17085                         }
17086                         if ($name !== null || $email !== null || $uri !== null)
17087                         {
17088                                 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
17089                         }
17090                 }
17091                 if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
17092                 {
17093                         $name = null;
17094                         $url = null;
17095                         $email = null;
17096                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17097                         {
17098                                 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17099                         }
17100                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17101                         {
17102                                 $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]));
17103                         }
17104                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17105                         {
17106                                 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17107                         }
17108                         if ($name !== null || $email !== null || $url !== null)
17109                         {
17110                                 $authors[] = $this->registry->create('Author', array($name, $url, $email));
17111                         }
17112                 }
17113                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, '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_DC_10, 'creator') as $author)
17118                 {
17119                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17120                 }
17121                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
17122                 {
17123                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17124                 }
17125
17126                 if (!empty($authors))
17127                 {
17128                         return array_unique($authors);
17129                 }
17130                 else
17131                 {
17132                         return null;
17133                 }
17134         }
17135
17136         public function get_contributor($key = 0)
17137         {
17138                 $contributors = $this->get_contributors();
17139                 if (isset($contributors[$key]))
17140                 {
17141                         return $contributors[$key];
17142                 }
17143                 else
17144                 {
17145                         return null;
17146                 }
17147         }
17148
17149         public function get_contributors()
17150         {
17151                 $contributors = array();
17152                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
17153                 {
17154                         $name = null;
17155                         $uri = null;
17156                         $email = null;
17157                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17158                         {
17159                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17160                         }
17161                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17162                         {
17163                                 $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]));
17164                         }
17165                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17166                         {
17167                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17168                         }
17169                         if ($name !== null || $email !== null || $uri !== null)
17170                         {
17171                                 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
17172                         }
17173                 }
17174                 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
17175                 {
17176                         $name = null;
17177                         $url = null;
17178                         $email = null;
17179                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17180                         {
17181                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17182                         }
17183                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17184                         {
17185                                 $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]));
17186                         }
17187                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17188                         {
17189                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17190                         }
17191                         if ($name !== null || $email !== null || $url !== null)
17192                         {
17193                                 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
17194                         }
17195                 }
17196
17197                 if (!empty($contributors))
17198                 {
17199                         return array_unique($contributors);
17200                 }
17201                 else
17202                 {
17203                         return null;
17204                 }
17205         }
17206
17207         public function get_link($key = 0, $rel = 'alternate')
17208         {
17209                 $links = $this->get_links($rel);
17210                 if (isset($links[$key]))
17211                 {
17212                         return $links[$key];
17213                 }
17214                 else
17215                 {
17216                         return null;
17217                 }
17218         }
17219
17220         /**
17221          * Added for parity between the parent-level and the item/entry-level.
17222          */
17223         public function get_permalink()
17224         {
17225                 return $this->get_link(0);
17226         }
17227
17228         public function get_links($rel = 'alternate')
17229         {
17230                 if (!isset($this->data['links']))
17231                 {
17232                         $this->data['links'] = array();
17233                         if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
17234                         {
17235                                 foreach ($links as $link)
17236                                 {
17237                                         if (isset($link['attribs']['']['href']))
17238                                         {
17239                                                 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17240                                                 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17241                                         }
17242                                 }
17243                         }
17244                         if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
17245                         {
17246                                 foreach ($links as $link)
17247                                 {
17248                                         if (isset($link['attribs']['']['href']))
17249                                         {
17250                                                 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17251                                                 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17252
17253                                         }
17254                                 }
17255                         }
17256                         if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, '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_090, 'link'))
17261                         {
17262                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17263                         }
17264                         if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
17265                         {
17266                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17267                         }
17268
17269                         $keys = array_keys($this->data['links']);
17270                         foreach ($keys as $key)
17271                         {
17272                                 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
17273                                 {
17274                                         if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
17275                                         {
17276                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
17277                                                 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
17278                                         }
17279                                         else
17280                                         {
17281                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
17282                                         }
17283                                 }
17284                                 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
17285                                 {
17286                                         $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
17287                                 }
17288                                 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
17289                         }
17290                 }
17291
17292                 if (isset($this->data['links'][$rel]))
17293                 {
17294                         return $this->data['links'][$rel];
17295                 }
17296                 else
17297                 {
17298                         return null;
17299                 }
17300         }
17301
17302         public function get_description()
17303         {
17304                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
17305                 {
17306                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17307                 }
17308                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
17309                 {
17310                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17311                 }
17312                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, '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_090, '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_RSS_20, 'description'))
17321                 {
17322                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17323                 }
17324                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
17325                 {
17326                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17327                 }
17328                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
17329                 {
17330                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17331                 }
17332                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
17333                 {
17334                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17335                 }
17336                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
17337                 {
17338                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17339                 }
17340                 else
17341                 {
17342                         return null;
17343                 }
17344         }
17345
17346         public function get_copyright()
17347         {
17348                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
17349                 {
17350                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17351                 }
17352                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
17353                 {
17354                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17355                 }
17356                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
17357                 {
17358                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17359                 }
17360                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
17361                 {
17362                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17363                 }
17364                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
17365                 {
17366                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17367                 }
17368                 else
17369                 {
17370                         return null;
17371                 }
17372         }
17373
17374         public function get_language()
17375         {
17376                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
17377                 {
17378                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17379                 }
17380                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
17381                 {
17382                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17383                 }
17384                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
17385                 {
17386                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17387                 }
17388                 elseif (isset($this->data['xml_lang']))
17389                 {
17390                         return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
17391                 }
17392                 else
17393                 {
17394                         return null;
17395                 }
17396         }
17397
17398         public function get_latitude()
17399         {
17400                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
17401                 {
17402                         return (float) $return[0]['data'];
17403                 }
17404                 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))
17405                 {
17406                         return (float) $match[1];
17407                 }
17408                 else
17409                 {
17410                         return null;
17411                 }
17412         }
17413
17414         public function get_longitude()
17415         {
17416                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
17417                 {
17418                         return (float) $return[0]['data'];
17419                 }
17420                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
17421                 {
17422                         return (float) $return[0]['data'];
17423                 }
17424                 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))
17425                 {
17426                         return (float) $match[2];
17427                 }
17428                 else
17429                 {
17430                         return null;
17431                 }
17432         }
17433
17434         public function get_image_url()
17435         {
17436                 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
17437                 {
17438                         return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
17439                 }
17440                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
17441                 {
17442                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
17443                 }
17444                 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
17445                 {
17446                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
17447                 }
17448                 else
17449                 {
17450                         return null;
17451                 }
17452         }
17453 }
17454
17455 /**
17456  * Parses the XML Declaration
17457  *
17458  * @package SimplePie
17459  * @subpackage Parsing
17460  */
17461 class SimplePie_XML_Declaration_Parser
17462 {
17463         /**
17464          * XML Version
17465          *
17466          * @access public
17467          * @var string
17468          */
17469         var $version = '1.0';
17470
17471         /**
17472          * Encoding
17473          *
17474          * @access public
17475          * @var string
17476          */
17477         var $encoding = 'UTF-8';
17478
17479         /**
17480          * Standalone
17481          *
17482          * @access public
17483          * @var bool
17484          */
17485         var $standalone = false;
17486
17487         /**
17488          * Current state of the state machine
17489          *
17490          * @access private
17491          * @var string
17492          */
17493         var $state = 'before_version_name';
17494
17495         /**
17496          * Input data
17497          *
17498          * @access private
17499          * @var string
17500          */
17501         var $data = '';
17502
17503         /**
17504          * Input data length (to avoid calling strlen() everytime this is needed)
17505          *
17506          * @access private
17507          * @var int
17508          */
17509         var $data_length = 0;
17510
17511         /**
17512          * Current position of the pointer
17513          *
17514          * @var int
17515          * @access private
17516          */
17517         var $position = 0;
17518
17519         /**
17520          * Create an instance of the class with the input data
17521          *
17522          * @access public
17523          * @param string $data Input data
17524          */
17525         public function __construct($data)
17526         {
17527                 $this->data = $data;
17528                 $this->data_length = strlen($this->data);
17529         }
17530
17531         /**
17532          * Parse the input data
17533          *
17534          * @access public
17535          * @return bool true on success, false on failure
17536          */
17537         public function parse()
17538         {
17539                 while ($this->state && $this->state !== 'emit' && $this->has_data())
17540                 {
17541                         $state = $this->state;
17542                         $this->$state();
17543                 }
17544                 $this->data = '';
17545                 if ($this->state === 'emit')
17546                 {
17547                         return true;
17548                 }
17549                 else
17550                 {
17551                         $this->version = '';
17552                         $this->encoding = '';
17553                         $this->standalone = '';
17554                         return false;
17555                 }
17556         }
17557
17558         /**
17559          * Check whether there is data beyond the pointer
17560          *
17561          * @access private
17562          * @return bool true if there is further data, false if not
17563          */
17564         public function has_data()
17565         {
17566                 return (bool) ($this->position < $this->data_length);
17567         }
17568
17569         /**
17570          * Advance past any whitespace
17571          *
17572          * @return int Number of whitespace characters passed
17573          */
17574         public function skip_whitespace()
17575         {
17576                 $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
17577                 $this->position += $whitespace;
17578                 return $whitespace;
17579         }
17580
17581         /**
17582          * Read value
17583          */
17584         public function get_value()
17585         {
17586                 $quote = substr($this->data, $this->position, 1);
17587                 if ($quote === '"' || $quote === "'")
17588                 {
17589                         $this->position++;
17590                         $len = strcspn($this->data, $quote, $this->position);
17591                         if ($this->has_data())
17592                         {
17593                                 $value = substr($this->data, $this->position, $len);
17594                                 $this->position += $len + 1;
17595                                 return $value;
17596                         }
17597                 }
17598                 return false;
17599         }
17600
17601         public function before_version_name()
17602         {
17603                 if ($this->skip_whitespace())
17604                 {
17605                         $this->state = 'version_name';
17606                 }
17607                 else
17608                 {
17609                         $this->state = false;
17610                 }
17611         }
17612
17613         public function version_name()
17614         {
17615                 if (substr($this->data, $this->position, 7) === 'version')
17616                 {
17617                         $this->position += 7;
17618                         $this->skip_whitespace();
17619                         $this->state = 'version_equals';
17620                 }
17621                 else
17622                 {
17623                         $this->state = false;
17624                 }
17625         }
17626
17627         public function version_equals()
17628         {
17629                 if (substr($this->data, $this->position, 1) === '=')
17630                 {
17631                         $this->position++;
17632                         $this->skip_whitespace();
17633                         $this->state = 'version_value';
17634                 }
17635                 else
17636                 {
17637                         $this->state = false;
17638                 }
17639         }
17640
17641         public function version_value()
17642         {
17643                 if ($this->version = $this->get_value())
17644                 {
17645                         $this->skip_whitespace();
17646                         if ($this->has_data())
17647                         {
17648                                 $this->state = 'encoding_name';
17649                         }
17650                         else
17651                         {
17652                                 $this->state = 'emit';
17653                         }
17654                 }
17655                 else
17656                 {
17657                         $this->state = false;
17658                 }
17659         }
17660
17661         public function encoding_name()
17662         {
17663                 if (substr($this->data, $this->position, 8) === 'encoding')
17664                 {
17665                         $this->position += 8;
17666                         $this->skip_whitespace();
17667                         $this->state = 'encoding_equals';
17668                 }
17669                 else
17670                 {
17671                         $this->state = 'standalone_name';
17672                 }
17673         }
17674
17675         public function encoding_equals()
17676         {
17677                 if (substr($this->data, $this->position, 1) === '=')
17678                 {
17679                         $this->position++;
17680                         $this->skip_whitespace();
17681                         $this->state = 'encoding_value';
17682                 }
17683                 else
17684                 {
17685                         $this->state = false;
17686                 }
17687         }
17688
17689         public function encoding_value()
17690         {
17691                 if ($this->encoding = $this->get_value())
17692                 {
17693                         $this->skip_whitespace();
17694                         if ($this->has_data())
17695                         {
17696                                 $this->state = 'standalone_name';
17697                         }
17698                         else
17699                         {
17700                                 $this->state = 'emit';
17701                         }
17702                 }
17703                 else
17704                 {
17705                         $this->state = false;
17706                 }
17707         }
17708
17709         public function standalone_name()
17710         {
17711                 if (substr($this->data, $this->position, 10) === 'standalone')
17712                 {
17713                         $this->position += 10;
17714                         $this->skip_whitespace();
17715                         $this->state = 'standalone_equals';
17716                 }
17717                 else
17718                 {
17719                         $this->state = false;
17720                 }
17721         }
17722
17723         public function standalone_equals()
17724         {
17725                 if (substr($this->data, $this->position, 1) === '=')
17726                 {
17727                         $this->position++;
17728                         $this->skip_whitespace();
17729                         $this->state = 'standalone_value';
17730                 }
17731                 else
17732                 {
17733                         $this->state = false;
17734                 }
17735         }
17736
17737         public function standalone_value()
17738         {
17739                 if ($standalone = $this->get_value())
17740                 {
17741                         switch ($standalone)
17742                         {
17743                                 case 'yes':
17744                                         $this->standalone = true;
17745                                         break;
17746
17747                                 case 'no':
17748                                         $this->standalone = false;
17749                                         break;
17750
17751                                 default:
17752                                         $this->state = false;
17753                                         return;
17754                         }
17755
17756                         $this->skip_whitespace();
17757                         if ($this->has_data())
17758                         {
17759                                 $this->state = false;
17760                         }
17761                         else
17762                         {
17763                                 $this->state = 'emit';
17764                         }
17765                 }
17766                 else
17767                 {
17768                         $this->state = false;
17769                 }
17770         }
17771 }
17772