Project

General

Profile

Download (472 KB) Statistics
| Branch: | Tag: | Revision:
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-2016, 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
 * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
40
 * @author Ryan Parman
41
 * @author Geoffrey Sneddon
42
 * @author Ryan McCue
43
 * @link http://simplepie.org/ SimplePie
44
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
45
 */
46

    
47
/**
48
 * SimplePie Name
49
 */
50
define('SIMPLEPIE_NAME', 'SimplePie');
51

    
52
/**
53
 * SimplePie Version
54
 */
55
define('SIMPLEPIE_VERSION', '1.4.3');
56

    
57
/**
58
 * SimplePie Build
59
 * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc)
60
 */
61
define('SIMPLEPIE_BUILD', '20161209182444');
62

    
63
/**
64
 * SimplePie Website URL
65
 */
66
define('SIMPLEPIE_URL', 'http://simplepie.org');
67

    
68
/**
69
 * SimplePie Useragent
70
 * @see SimplePie::set_useragent()
71
 */
72
define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
73

    
74
/**
75
 * SimplePie Linkback
76
 */
77
define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>');
78

    
79
/**
80
 * No Autodiscovery
81
 * @see SimplePie::set_autodiscovery_level()
82
 */
83
define('SIMPLEPIE_LOCATOR_NONE', 0);
84

    
85
/**
86
 * Feed Link Element Autodiscovery
87
 * @see SimplePie::set_autodiscovery_level()
88
 */
89
define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
90

    
91
/**
92
 * Local Feed Extension Autodiscovery
93
 * @see SimplePie::set_autodiscovery_level()
94
 */
95
define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
96

    
97
/**
98
 * Local Feed Body Autodiscovery
99
 * @see SimplePie::set_autodiscovery_level()
100
 */
101
define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
102

    
103
/**
104
 * Remote Feed Extension Autodiscovery
105
 * @see SimplePie::set_autodiscovery_level()
106
 */
107
define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
108

    
109
/**
110
 * Remote Feed Body Autodiscovery
111
 * @see SimplePie::set_autodiscovery_level()
112
 */
113
define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
114

    
115
/**
116
 * All Feed Autodiscovery
117
 * @see SimplePie::set_autodiscovery_level()
118
 */
119
define('SIMPLEPIE_LOCATOR_ALL', 31);
120

    
121
/**
122
 * No known feed type
123
 */
124
define('SIMPLEPIE_TYPE_NONE', 0);
125

    
126
/**
127
 * RSS 0.90
128
 */
129
define('SIMPLEPIE_TYPE_RSS_090', 1);
130

    
131
/**
132
 * RSS 0.91 (Netscape)
133
 */
134
define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
135

    
136
/**
137
 * RSS 0.91 (Userland)
138
 */
139
define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
140

    
141
/**
142
 * RSS 0.91 (both Netscape and Userland)
143
 */
144
define('SIMPLEPIE_TYPE_RSS_091', 6);
145

    
146
/**
147
 * RSS 0.92
148
 */
149
define('SIMPLEPIE_TYPE_RSS_092', 8);
150

    
151
/**
152
 * RSS 0.93
153
 */
154
define('SIMPLEPIE_TYPE_RSS_093', 16);
155

    
156
/**
157
 * RSS 0.94
158
 */
159
define('SIMPLEPIE_TYPE_RSS_094', 32);
160

    
161
/**
162
 * RSS 1.0
163
 */
164
define('SIMPLEPIE_TYPE_RSS_10', 64);
165

    
166
/**
167
 * RSS 2.0
168
 */
169
define('SIMPLEPIE_TYPE_RSS_20', 128);
170

    
171
/**
172
 * RDF-based RSS
173
 */
174
define('SIMPLEPIE_TYPE_RSS_RDF', 65);
175

    
176
/**
177
 * Non-RDF-based RSS (truly intended as syndication format)
178
 */
179
define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
180

    
181
/**
182
 * All RSS
183
 */
184
define('SIMPLEPIE_TYPE_RSS_ALL', 255);
185

    
186
/**
187
 * Atom 0.3
188
 */
189
define('SIMPLEPIE_TYPE_ATOM_03', 256);
190

    
191
/**
192
 * Atom 1.0
193
 */
194
define('SIMPLEPIE_TYPE_ATOM_10', 512);
195

    
196
/**
197
 * All Atom
198
 */
199
define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
200

    
201
/**
202
 * All feed types
203
 */
204
define('SIMPLEPIE_TYPE_ALL', 1023);
205

    
206
/**
207
 * No construct
208
 */
209
define('SIMPLEPIE_CONSTRUCT_NONE', 0);
210

    
211
/**
212
 * Text construct
213
 */
214
define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
215

    
216
/**
217
 * HTML construct
218
 */
219
define('SIMPLEPIE_CONSTRUCT_HTML', 2);
220

    
221
/**
222
 * XHTML construct
223
 */
224
define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
225

    
226
/**
227
 * base64-encoded construct
228
 */
229
define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
230

    
231
/**
232
 * IRI construct
233
 */
234
define('SIMPLEPIE_CONSTRUCT_IRI', 16);
235

    
236
/**
237
 * A construct that might be HTML
238
 */
239
define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
240

    
241
/**
242
 * All constructs
243
 */
244
define('SIMPLEPIE_CONSTRUCT_ALL', 63);
245

    
246
/**
247
 * Don't change case
248
 */
249
define('SIMPLEPIE_SAME_CASE', 1);
250

    
251
/**
252
 * Change to lowercase
253
 */
254
define('SIMPLEPIE_LOWERCASE', 2);
255

    
256
/**
257
 * Change to uppercase
258
 */
259
define('SIMPLEPIE_UPPERCASE', 4);
260

    
261
/**
262
 * PCRE for HTML attributes
263
 */
264
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]*');
265

    
266
/**
267
 * PCRE for XML attributes
268
 */
269
define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
270

    
271
/**
272
 * XML Namespace
273
 */
274
define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
275

    
276
/**
277
 * Atom 1.0 Namespace
278
 */
279
define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
280

    
281
/**
282
 * Atom 0.3 Namespace
283
 */
284
define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
285

    
286
/**
287
 * RDF Namespace
288
 */
289
define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
290

    
291
/**
292
 * RSS 0.90 Namespace
293
 */
294
define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
295

    
296
/**
297
 * RSS 1.0 Namespace
298
 */
299
define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
300

    
301
/**
302
 * RSS 1.0 Content Module Namespace
303
 */
304
define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/');
305

    
306
/**
307
 * RSS 2.0 Namespace
308
 * (Stupid, I know, but I'm certain it will confuse people less with support.)
309
 */
310
define('SIMPLEPIE_NAMESPACE_RSS_20', '');
311

    
312
/**
313
 * DC 1.0 Namespace
314
 */
315
define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
316

    
317
/**
318
 * DC 1.1 Namespace
319
 */
320
define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
321

    
322
/**
323
 * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
324
 */
325
define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
326

    
327
/**
328
 * GeoRSS Namespace
329
 */
330
define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
331

    
332
/**
333
 * Media RSS Namespace
334
 */
335
define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/');
336

    
337
/**
338
 * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec.
339
 */
340
define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss');
341

    
342
/**
343
 * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5.
344
 */
345
define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss');
346

    
347
/**
348
 * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace.
349
 */
350
define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/');
351

    
352
/**
353
 * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace.
354
 */
355
define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss');
356

    
357
/**
358
 * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL.
359
 */
360
define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/');
361

    
362
/**
363
 * iTunes RSS Namespace
364
 */
365
define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
366

    
367
/**
368
 * XHTML Namespace
369
 */
370
define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
371

    
372
/**
373
 * IANA Link Relations Registry
374
 */
375
define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
376

    
377
/**
378
 * No file source
379
 */
380
define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
381

    
382
/**
383
 * Remote file source
384
 */
385
define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
386

    
387
/**
388
 * Local file source
389
 */
390
define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
391

    
392
/**
393
 * fsockopen() file source
394
 */
395
define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
396

    
397
/**
398
 * cURL file source
399
 */
400
define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
401

    
402
/**
403
 * file_get_contents() file source
404
 */
405
define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
406

    
407
/**
408
 * SimplePie
409
 *
410
 * @package SimplePie
411
 * @subpackage API
412
 */
413
class SimplePie
414
{
415
	/**
416
	 * @var array Raw data
417
	 * @access private
418
	 */
419
	public $data = array();
420

    
421
	/**
422
	 * @var mixed Error string
423
	 * @access private
424
	 */
425
	public $error;
426

    
427
	/**
428
	 * @var object Instance of SimplePie_Sanitize (or other class)
429
	 * @see SimplePie::set_sanitize_class()
430
	 * @access private
431
	 */
432
	public $sanitize;
433

    
434
	/**
435
	 * @var string SimplePie Useragent
436
	 * @see SimplePie::set_useragent()
437
	 * @access private
438
	 */
439
	public $useragent = SIMPLEPIE_USERAGENT;
440

    
441
	/**
442
	 * @var string Feed URL
443
	 * @see SimplePie::set_feed_url()
444
	 * @access private
445
	 */
446
	public $feed_url;
447

    
448
	/**
449
	 * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently
450
	 * @see SimplePie::subscribe_url()
451
	 * @access private
452
	 */
453
	public $permanent_url = null;
454

    
455
	/**
456
	 * @var object Instance of SimplePie_File to use as a feed
457
	 * @see SimplePie::set_file()
458
	 * @access private
459
	 */
460
	public $file;
461

    
462
	/**
463
	 * @var string Raw feed data
464
	 * @see SimplePie::set_raw_data()
465
	 * @access private
466
	 */
467
	public $raw_data;
468

    
469
	/**
470
	 * @var int Timeout for fetching remote files
471
	 * @see SimplePie::set_timeout()
472
	 * @access private
473
	 */
474
	public $timeout = 10;
475

    
476
	/**
477
	 * @var array Custom curl options
478
	 * @see SimplePie::set_curl_options()
479
	 * @access private
480
	 */
481
	public $curl_options = array();
482

    
483
	/**
484
	 * @var bool Forces fsockopen() to be used for remote files instead
485
	 * of cURL, even if a new enough version is installed
486
	 * @see SimplePie::force_fsockopen()
487
	 * @access private
488
	 */
489
	public $force_fsockopen = false;
490

    
491
	/**
492
	 * @var bool Force the given data/URL to be treated as a feed no matter what
493
	 * it appears like
494
	 * @see SimplePie::force_feed()
495
	 * @access private
496
	 */
497
	public $force_feed = false;
498

    
499
	/**
500
	 * @var bool Enable/Disable Caching
501
	 * @see SimplePie::enable_cache()
502
	 * @access private
503
	 */
504
	public $cache = true;
505

    
506
	/**
507
	 * @var bool Force SimplePie to fallback to expired cache, if enabled,
508
	 * when feed is unavailable.
509
	 * @see SimplePie::force_cache_fallback()
510
	 * @access private
511
	 */
512
	public $force_cache_fallback = false;
513

    
514
	/**
515
	 * @var int Cache duration (in seconds)
516
	 * @see SimplePie::set_cache_duration()
517
	 * @access private
518
	 */
519
	public $cache_duration = 3600;
520

    
521
	/**
522
	 * @var int Auto-discovery cache duration (in seconds)
523
	 * @see SimplePie::set_autodiscovery_cache_duration()
524
	 * @access private
525
	 */
526
	public $autodiscovery_cache_duration = 604800; // 7 Days.
527

    
528
	/**
529
	 * @var string Cache location (relative to executing script)
530
	 * @see SimplePie::set_cache_location()
531
	 * @access private
532
	 */
533
	public $cache_location = './cache';
534

    
535
	/**
536
	 * @var string Function that creates the cache filename
537
	 * @see SimplePie::set_cache_name_function()
538
	 * @access private
539
	 */
540
	public $cache_name_function = 'md5';
541

    
542
	/**
543
	 * @var bool Reorder feed by date descending
544
	 * @see SimplePie::enable_order_by_date()
545
	 * @access private
546
	 */
547
	public $order_by_date = true;
548

    
549
	/**
550
	 * @var mixed Force input encoding to be set to the follow value
551
	 * (false, or anything type-cast to false, disables this feature)
552
	 * @see SimplePie::set_input_encoding()
553
	 * @access private
554
	 */
555
	public $input_encoding = false;
556

    
557
	/**
558
	 * @var int Feed Autodiscovery Level
559
	 * @see SimplePie::set_autodiscovery_level()
560
	 * @access private
561
	 */
562
	public $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
563

    
564
	/**
565
	 * Class registry object
566
	 *
567
	 * @var SimplePie_Registry
568
	 */
569
	public $registry;
570

    
571
	/**
572
	 * @var int Maximum number of feeds to check with autodiscovery
573
	 * @see SimplePie::set_max_checked_feeds()
574
	 * @access private
575
	 */
576
	public $max_checked_feeds = 10;
577

    
578
	/**
579
	 * @var array All the feeds found during the autodiscovery process
580
	 * @see SimplePie::get_all_discovered_feeds()
581
	 * @access private
582
	 */
583
	public $all_discovered_feeds = array();
584

    
585
	/**
586
	 * @var string Web-accessible path to the handler_image.php file.
587
	 * @see SimplePie::set_image_handler()
588
	 * @access private
589
	 */
590
	public $image_handler = '';
591

    
592
	/**
593
	 * @var array Stores the URLs when multiple feeds are being initialized.
594
	 * @see SimplePie::set_feed_url()
595
	 * @access private
596
	 */
597
	public $multifeed_url = array();
598

    
599
	/**
600
	 * @var array Stores SimplePie objects when multiple feeds initialized.
601
	 * @access private
602
	 */
603
	public $multifeed_objects = array();
604

    
605
	/**
606
	 * @var array Stores the get_object_vars() array for use with multifeeds.
607
	 * @see SimplePie::set_feed_url()
608
	 * @access private
609
	 */
610
	public $config_settings = null;
611

    
612
	/**
613
	 * @var integer Stores the number of items to return per-feed with multifeeds.
614
	 * @see SimplePie::set_item_limit()
615
	 * @access private
616
	 */
617
	public $item_limit = 0;
618

    
619
	/**
620
	 * @var bool Stores if last-modified and/or etag headers were sent with the
621
	 * request when checking a feed.
622
	 */
623
	public $check_modified = false;
624

    
625
	/**
626
	 * @var array Stores the default attributes to be stripped by strip_attributes().
627
	 * @see SimplePie::strip_attributes()
628
	 * @access private
629
	 */
630
	public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
631

    
632
	/**
633
	 * @var array Stores the default attributes to add to different tags by add_attributes().
634
	 * @see SimplePie::add_attributes()
635
	 * @access private
636
	 */
637
	public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none'));
638

    
639
	/**
640
	 * @var array Stores the default tags to be stripped by strip_htmltags().
641
	 * @see SimplePie::strip_htmltags()
642
	 * @access private
643
	 */
644
	public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
645

    
646
	/**
647
	 * The SimplePie class contains feed level data and options
648
	 *
649
	 * To use SimplePie, create the SimplePie object with no parameters. You can
650
	 * then set configuration options using the provided methods. After setting
651
	 * them, you must initialise the feed using $feed->init(). At that point the
652
	 * object's methods and properties will be available to you.
653
	 *
654
	 * Previously, it was possible to pass in the feed URL along with cache
655
	 * options directly into the constructor. This has been removed as of 1.3 as
656
	 * it caused a lot of confusion.
657
	 *
658
	 * @since 1.0 Preview Release
659
	 */
660
	public function __construct()
661
	{
662
		if (version_compare(PHP_VERSION, '5.3', '<'))
663
		{
664
			trigger_error('Please upgrade to PHP 5.3 or newer.');
665
			die();
666
		}
667

    
668
		// Other objects, instances created here so we can set options on them
669
		$this->sanitize = new SimplePie_Sanitize();
670
		$this->registry = new SimplePie_Registry();
671

    
672
		if (func_num_args() > 0)
673
		{
674
			$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
675
			trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', $level);
676

    
677
			$args = func_get_args();
678
			switch (count($args)) {
679
				case 3:
680
					$this->set_cache_duration($args[2]);
681
				case 2:
682
					$this->set_cache_location($args[1]);
683
				case 1:
684
					$this->set_feed_url($args[0]);
685
					$this->init();
686
			}
687
		}
688
	}
689

    
690
	/**
691
	 * Used for converting object to a string
692
	 */
693
	public function __toString()
694
	{
695
		return md5(serialize($this->data));
696
	}
697

    
698
	/**
699
	 * Remove items that link back to this before destroying this object
700
	 */
701
	public function __destruct()
702
	{
703
		if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
704
		{
705
			if (!empty($this->data['items']))
706
			{
707
				foreach ($this->data['items'] as $item)
708
				{
709
					$item->__destruct();
710
				}
711
				unset($item, $this->data['items']);
712
			}
713
			if (!empty($this->data['ordered_items']))
714
			{
715
				foreach ($this->data['ordered_items'] as $item)
716
				{
717
					$item->__destruct();
718
				}
719
				unset($item, $this->data['ordered_items']);
720
			}
721
		}
722
	}
723

    
724
	/**
725
	 * Force the given data/URL to be treated as a feed
726
	 *
727
	 * This tells SimplePie to ignore the content-type provided by the server.
728
	 * Be careful when using this option, as it will also disable autodiscovery.
729
	 *
730
	 * @since 1.1
731
	 * @param bool $enable Force the given data/URL to be treated as a feed
732
	 */
733
	public function force_feed($enable = false)
734
	{
735
		$this->force_feed = (bool) $enable;
736
	}
737

    
738
	/**
739
	 * Set the URL of the feed you want to parse
740
	 *
741
	 * This allows you to enter the URL of the feed you want to parse, or the
742
	 * website you want to try to use auto-discovery on. This takes priority
743
	 * over any set raw data.
744
	 *
745
	 * You can set multiple feeds to mash together by passing an array instead
746
	 * of a string for the $url. Remember that with each additional feed comes
747
	 * additional processing and resources.
748
	 *
749
	 * @since 1.0 Preview Release
750
	 * @see set_raw_data()
751
	 * @param string|array $url This is the URL (or array of URLs) that you want to parse.
752
	 */
753
	public function set_feed_url($url)
754
	{
755
		$this->multifeed_url = array();
756
		if (is_array($url))
757
		{
758
			foreach ($url as $value)
759
			{
760
				$this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1));
761
			}
762
		}
763
		else
764
		{
765
			$this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1));
766
			$this->permanent_url = $this->feed_url;
767
		}
768
	}
769

    
770
	/**
771
	 * Set an instance of {@see SimplePie_File} to use as a feed
772
	 *
773
	 * @param SimplePie_File &$file
774
	 * @return bool True on success, false on failure
775
	 */
776
	public function set_file(&$file)
777
	{
778
		if ($file instanceof SimplePie_File)
779
		{
780
			$this->feed_url = $file->url;
781
			$this->permanent_url = $this->feed_url;
782
			$this->file =& $file;
783
			return true;
784
		}
785
		return false;
786
	}
787

    
788
	/**
789
	 * Set the raw XML data to parse
790
	 *
791
	 * Allows you to use a string of RSS/Atom data instead of a remote feed.
792
	 *
793
	 * If you have a feed available as a string in PHP, you can tell SimplePie
794
	 * to parse that data string instead of a remote feed. Any set feed URL
795
	 * takes precedence.
796
	 *
797
	 * @since 1.0 Beta 3
798
	 * @param string $data RSS or Atom data as a string.
799
	 * @see set_feed_url()
800
	 */
801
	public function set_raw_data($data)
802
	{
803
		$this->raw_data = $data;
804
	}
805

    
806
	/**
807
	 * Set the the default timeout for fetching remote feeds
808
	 *
809
	 * This allows you to change the maximum time the feed's server to respond
810
	 * and send the feed back.
811
	 *
812
	 * @since 1.0 Beta 3
813
	 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
814
	 */
815
	public function set_timeout($timeout = 10)
816
	{
817
		$this->timeout = (int) $timeout;
818
	}
819

    
820
	/**
821
	 * Set custom curl options
822
	 *
823
	 * This allows you to change default curl options
824
	 *
825
	 * @since 1.0 Beta 3
826
	 * @param array $curl_options Curl options to add to default settings
827
	 */
828
	public function set_curl_options(array $curl_options = array())
829
	{
830
		$this->curl_options = $curl_options;
831
	}
832

    
833
	/**
834
	 * Force SimplePie to use fsockopen() instead of cURL
835
	 *
836
	 * @since 1.0 Beta 3
837
	 * @param bool $enable Force fsockopen() to be used
838
	 */
839
	public function force_fsockopen($enable = false)
840
	{
841
		$this->force_fsockopen = (bool) $enable;
842
	}
843

    
844
	/**
845
	 * Enable/disable caching in SimplePie.
846
	 *
847
	 * This option allows you to disable caching all-together in SimplePie.
848
	 * However, disabling the cache can lead to longer load times.
849
	 *
850
	 * @since 1.0 Preview Release
851
	 * @param bool $enable Enable caching
852
	 */
853
	public function enable_cache($enable = true)
854
	{
855
		$this->cache = (bool) $enable;
856
	}
857

    
858
	/**
859
	 * SimplePie to continue to fall back to expired cache, if enabled, when
860
	 * feed is unavailable.
861
	 *
862
	 * This tells SimplePie to ignore any file errors and fall back to cache
863
	 * instead. This only works if caching is enabled and cached content
864
	 * still exists.
865

    
866
	 * @param bool $enable Force use of cache on fail.
867
	 */
868
	public function force_cache_fallback($enable = false)
869
	{
870
		$this->force_cache_fallback= (bool) $enable;
871
	}
872

    
873
	/**
874
	 * Set the length of time (in seconds) that the contents of a feed will be
875
	 * cached
876
	 *
877
	 * @param int $seconds The feed content cache duration
878
	 */
879
	public function set_cache_duration($seconds = 3600)
880
	{
881
		$this->cache_duration = (int) $seconds;
882
	}
883

    
884
	/**
885
	 * Set the length of time (in seconds) that the autodiscovered feed URL will
886
	 * be cached
887
	 *
888
	 * @param int $seconds The autodiscovered feed URL cache duration.
889
	 */
890
	public function set_autodiscovery_cache_duration($seconds = 604800)
891
	{
892
		$this->autodiscovery_cache_duration = (int) $seconds;
893
	}
894

    
895
	/**
896
	 * Set the file system location where the cached files should be stored
897
	 *
898
	 * @param string $location The file system location.
899
	 */
900
	public function set_cache_location($location = './cache')
901
	{
902
		$this->cache_location = (string) $location;
903
	}
904

    
905
	/**
906
	 * Set whether feed items should be sorted into reverse chronological order
907
	 *
908
	 * @param bool $enable Sort as reverse chronological order.
909
	 */
910
	public function enable_order_by_date($enable = true)
911
	{
912
		$this->order_by_date = (bool) $enable;
913
	}
914

    
915
	/**
916
	 * Set the character encoding used to parse the feed
917
	 *
918
	 * This overrides the encoding reported by the feed, however it will fall
919
	 * back to the normal encoding detection if the override fails
920
	 *
921
	 * @param string $encoding Character encoding
922
	 */
923
	public function set_input_encoding($encoding = false)
924
	{
925
		if ($encoding)
926
		{
927
			$this->input_encoding = (string) $encoding;
928
		}
929
		else
930
		{
931
			$this->input_encoding = false;
932
		}
933
	}
934

    
935
	/**
936
	 * Set how much feed autodiscovery to do
937
	 *
938
	 * @see SIMPLEPIE_LOCATOR_NONE
939
	 * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
940
	 * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
941
	 * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
942
	 * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
943
	 * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
944
	 * @see SIMPLEPIE_LOCATOR_ALL
945
	 * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator)
946
	 */
947
	public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
948
	{
949
		$this->autodiscovery = (int) $level;
950
	}
951

    
952
	/**
953
	 * Get the class registry
954
	 *
955
	 * Use this to override SimplePie's default classes
956
	 * @see SimplePie_Registry
957
	 * @return SimplePie_Registry
958
	 */
959
	public function &get_registry()
960
	{
961
		return $this->registry;
962
	}
963

    
964
	/**#@+
965
	 * Useful when you are overloading or extending SimplePie's default classes.
966
	 *
967
	 * @deprecated Use {@see get_registry()} instead
968
	 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
969
	 * @param string $class Name of custom class
970
	 * @return boolean True on success, false otherwise
971
	 */
972
	/**
973
	 * Set which class SimplePie uses for caching
974
	 */
975
	public function set_cache_class($class = 'SimplePie_Cache')
976
	{
977
		return $this->registry->register('Cache', $class, true);
978
	}
979

    
980
	/**
981
	 * Set which class SimplePie uses for auto-discovery
982
	 */
983
	public function set_locator_class($class = 'SimplePie_Locator')
984
	{
985
		return $this->registry->register('Locator', $class, true);
986
	}
987

    
988
	/**
989
	 * Set which class SimplePie uses for XML parsing
990
	 */
991
	public function set_parser_class($class = 'SimplePie_Parser')
992
	{
993
		return $this->registry->register('Parser', $class, true);
994
	}
995

    
996
	/**
997
	 * Set which class SimplePie uses for remote file fetching
998
	 */
999
	public function set_file_class($class = 'SimplePie_File')
1000
	{
1001
		return $this->registry->register('File', $class, true);
1002
	}
1003

    
1004
	/**
1005
	 * Set which class SimplePie uses for data sanitization
1006
	 */
1007
	public function set_sanitize_class($class = 'SimplePie_Sanitize')
1008
	{
1009
		return $this->registry->register('Sanitize', $class, true);
1010
	}
1011

    
1012
	/**
1013
	 * Set which class SimplePie uses for handling feed items
1014
	 */
1015
	public function set_item_class($class = 'SimplePie_Item')
1016
	{
1017
		return $this->registry->register('Item', $class, true);
1018
	}
1019

    
1020
	/**
1021
	 * Set which class SimplePie uses for handling author data
1022
	 */
1023
	public function set_author_class($class = 'SimplePie_Author')
1024
	{
1025
		return $this->registry->register('Author', $class, true);
1026
	}
1027

    
1028
	/**
1029
	 * Set which class SimplePie uses for handling category data
1030
	 */
1031
	public function set_category_class($class = 'SimplePie_Category')
1032
	{
1033
		return $this->registry->register('Category', $class, true);
1034
	}
1035

    
1036
	/**
1037
	 * Set which class SimplePie uses for feed enclosures
1038
	 */
1039
	public function set_enclosure_class($class = 'SimplePie_Enclosure')
1040
	{
1041
		return $this->registry->register('Enclosure', $class, true);
1042
	}
1043

    
1044
	/**
1045
	 * Set which class SimplePie uses for `<media:text>` captions
1046
	 */
1047
	public function set_caption_class($class = 'SimplePie_Caption')
1048
	{
1049
		return $this->registry->register('Caption', $class, true);
1050
	}
1051

    
1052
	/**
1053
	 * Set which class SimplePie uses for `<media:copyright>`
1054
	 */
1055
	public function set_copyright_class($class = 'SimplePie_Copyright')
1056
	{
1057
		return $this->registry->register('Copyright', $class, true);
1058
	}
1059

    
1060
	/**
1061
	 * Set which class SimplePie uses for `<media:credit>`
1062
	 */
1063
	public function set_credit_class($class = 'SimplePie_Credit')
1064
	{
1065
		return $this->registry->register('Credit', $class, true);
1066
	}
1067

    
1068
	/**
1069
	 * Set which class SimplePie uses for `<media:rating>`
1070
	 */
1071
	public function set_rating_class($class = 'SimplePie_Rating')
1072
	{
1073
		return $this->registry->register('Rating', $class, true);
1074
	}
1075

    
1076
	/**
1077
	 * Set which class SimplePie uses for `<media:restriction>`
1078
	 */
1079
	public function set_restriction_class($class = 'SimplePie_Restriction')
1080
	{
1081
		return $this->registry->register('Restriction', $class, true);
1082
	}
1083

    
1084
	/**
1085
	 * Set which class SimplePie uses for content-type sniffing
1086
	 */
1087
	public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
1088
	{
1089
		return $this->registry->register('Content_Type_Sniffer', $class, true);
1090
	}
1091

    
1092
	/**
1093
	 * Set which class SimplePie uses item sources
1094
	 */
1095
	public function set_source_class($class = 'SimplePie_Source')
1096
	{
1097
		return $this->registry->register('Source', $class, true);
1098
	}
1099
	/**#@-*/
1100

    
1101
	/**
1102
	 * Set the user agent string
1103
	 *
1104
	 * @param string $ua New user agent string.
1105
	 */
1106
	public function set_useragent($ua = SIMPLEPIE_USERAGENT)
1107
	{
1108
		$this->useragent = (string) $ua;
1109
	}
1110

    
1111
	/**
1112
	 * Set callback function to create cache filename with
1113
	 *
1114
	 * @param mixed $function Callback function
1115
	 */
1116
	public function set_cache_name_function($function = 'md5')
1117
	{
1118
		if (is_callable($function))
1119
		{
1120
			$this->cache_name_function = $function;
1121
		}
1122
	}
1123

    
1124
	/**
1125
	 * Set options to make SP as fast as possible
1126
	 *
1127
	 * Forgoes a substantial amount of data sanitization in favor of speed. This
1128
	 * turns SimplePie into a dumb parser of feeds.
1129
	 *
1130
	 * @param bool $set Whether to set them or not
1131
	 */
1132
	public function set_stupidly_fast($set = false)
1133
	{
1134
		if ($set)
1135
		{
1136
			$this->enable_order_by_date(false);
1137
			$this->remove_div(false);
1138
			$this->strip_comments(false);
1139
			$this->strip_htmltags(false);
1140
			$this->strip_attributes(false);
1141
			$this->add_attributes(false);
1142
			$this->set_image_handler(false);
1143
		}
1144
	}
1145

    
1146
	/**
1147
	 * Set maximum number of feeds to check with autodiscovery
1148
	 *
1149
	 * @param int $max Maximum number of feeds to check
1150
	 */
1151
	public function set_max_checked_feeds($max = 10)
1152
	{
1153
		$this->max_checked_feeds = (int) $max;
1154
	}
1155

    
1156
	public function remove_div($enable = true)
1157
	{
1158
		$this->sanitize->remove_div($enable);
1159
	}
1160

    
1161
	public function strip_htmltags($tags = '', $encode = null)
1162
	{
1163
		if ($tags === '')
1164
		{
1165
			$tags = $this->strip_htmltags;
1166
		}
1167
		$this->sanitize->strip_htmltags($tags);
1168
		if ($encode !== null)
1169
		{
1170
			$this->sanitize->encode_instead_of_strip($tags);
1171
		}
1172
	}
1173

    
1174
	public function encode_instead_of_strip($enable = true)
1175
	{
1176
		$this->sanitize->encode_instead_of_strip($enable);
1177
	}
1178

    
1179
	public function strip_attributes($attribs = '')
1180
	{
1181
		if ($attribs === '')
1182
		{
1183
			$attribs = $this->strip_attributes;
1184
		}
1185
		$this->sanitize->strip_attributes($attribs);
1186
	}
1187

    
1188
	public function add_attributes($attribs = '')
1189
	{
1190
		if ($attribs === '')
1191
		{
1192
			$attribs = $this->add_attributes;
1193
		}
1194
		$this->sanitize->add_attributes($attribs);
1195
	}
1196

    
1197
	/**
1198
	 * Set the output encoding
1199
	 *
1200
	 * Allows you to override SimplePie's output to match that of your webpage.
1201
	 * This is useful for times when your webpages are not being served as
1202
	 * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and
1203
	 * is similar to {@see set_input_encoding()}.
1204
	 *
1205
	 * It should be noted, however, that not all character encodings can support
1206
	 * all characters. If your page is being served as ISO-8859-1 and you try
1207
	 * to display a Japanese feed, you'll likely see garbled characters.
1208
	 * Because of this, it is highly recommended to ensure that your webpages
1209
	 * are served as UTF-8.
1210
	 *
1211
	 * The number of supported character encodings depends on whether your web
1212
	 * host supports {@link http://php.net/mbstring mbstring},
1213
	 * {@link http://php.net/iconv iconv}, or both. See
1214
	 * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for
1215
	 * more information.
1216
	 *
1217
	 * @param string $encoding
1218
	 */
1219
	public function set_output_encoding($encoding = 'UTF-8')
1220
	{
1221
		$this->sanitize->set_output_encoding($encoding);
1222
	}
1223

    
1224
	public function strip_comments($strip = false)
1225
	{
1226
		$this->sanitize->strip_comments($strip);
1227
	}
1228

    
1229
	/**
1230
	 * Set element/attribute key/value pairs of HTML attributes
1231
	 * containing URLs that need to be resolved relative to the feed
1232
	 *
1233
	 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
1234
	 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
1235
	 * |q|@cite
1236
	 *
1237
	 * @since 1.0
1238
	 * @param array|null $element_attribute Element/attribute key/value pairs, null for default
1239
	 */
1240
	public function set_url_replacements($element_attribute = null)
1241
	{
1242
		$this->sanitize->set_url_replacements($element_attribute);
1243
	}
1244

    
1245
	/**
1246
	 * Set the handler to enable the display of cached images.
1247
	 *
1248
	 * @param str $page Web-accessible path to the handler_image.php file.
1249
	 * @param str $qs The query string that the value should be passed to.
1250
	 */
1251
	public function set_image_handler($page = false, $qs = 'i')
1252
	{
1253
		if ($page !== false)
1254
		{
1255
			$this->sanitize->set_image_handler($page . '?' . $qs . '=');
1256
		}
1257
		else
1258
		{
1259
			$this->image_handler = '';
1260
		}
1261
	}
1262

    
1263
	/**
1264
	 * Set the limit for items returned per-feed with multifeeds
1265
	 *
1266
	 * @param integer $limit The maximum number of items to return.
1267
	 */
1268
	public function set_item_limit($limit = 0)
1269
	{
1270
		$this->item_limit = (int) $limit;
1271
	}
1272

    
1273
	/**
1274
	 * Enable throwing exceptions
1275
	 *
1276
	 * @param boolean $enable Should we throw exceptions, or use the old-style error property?
1277
	 */
1278
	public function enable_exceptions($enable = true)
1279
	{
1280
		$this->enable_exceptions = $enable;
1281
	}
1282

    
1283
	/**
1284
	 * Initialize the feed object
1285
	 *
1286
	 * This is what makes everything happen. Period. This is where all of the
1287
	 * configuration options get processed, feeds are fetched, cached, and
1288
	 * parsed, and all of that other good stuff.
1289
	 *
1290
	 * @return boolean True if successful, false otherwise
1291
	 */
1292
	public function init()
1293
	{
1294
		// Check absolute bare minimum requirements.
1295
		if (!extension_loaded('xml') || !extension_loaded('pcre'))
1296
		{
1297
			$this->error = 'XML or PCRE extensions not loaded!';
1298
			return false;
1299
		}
1300
		// 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.
1301
		elseif (!extension_loaded('xmlreader'))
1302
		{
1303
			static $xml_is_sane = null;
1304
			if ($xml_is_sane === null)
1305
			{
1306
				$parser_check = xml_parser_create();
1307
				xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1308
				xml_parser_free($parser_check);
1309
				$xml_is_sane = isset($values[0]['value']);
1310
			}
1311
			if (!$xml_is_sane)
1312
			{
1313
				return false;
1314
			}
1315
		}
1316

    
1317
		if (method_exists($this->sanitize, 'set_registry'))
1318
		{
1319
			$this->sanitize->set_registry($this->registry);
1320
		}
1321

    
1322
		// Pass whatever was set with config options over to the sanitizer.
1323
		// Pass the classes in for legacy support; new classes should use the registry instead
1324
		$this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache'));
1325
		$this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options);
1326

    
1327
		if (!empty($this->multifeed_url))
1328
		{
1329
			$i = 0;
1330
			$success = 0;
1331
			$this->multifeed_objects = array();
1332
			$this->error = array();
1333
			foreach ($this->multifeed_url as $url)
1334
			{
1335
				$this->multifeed_objects[$i] = clone $this;
1336
				$this->multifeed_objects[$i]->set_feed_url($url);
1337
				$single_success = $this->multifeed_objects[$i]->init();
1338
				$success |= $single_success;
1339
				if (!$single_success)
1340
				{
1341
					$this->error[$i] = $this->multifeed_objects[$i]->error();
1342
				}
1343
				$i++;
1344
			}
1345
			return (bool) $success;
1346
		}
1347
		elseif ($this->feed_url === null && $this->raw_data === null)
1348
		{
1349
			return false;
1350
		}
1351

    
1352
		$this->error = null;
1353
		$this->data = array();
1354
		$this->check_modified = false;
1355
		$this->multifeed_objects = array();
1356
		$cache = false;
1357

    
1358
		if ($this->feed_url !== null)
1359
		{
1360
			$parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url));
1361

    
1362
			// Decide whether to enable caching
1363
			if ($this->cache && $parsed_feed_url['scheme'] !== '')
1364
			{
1365
				$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'));
1366
			}
1367

    
1368
			// Fetch the data via SimplePie_File into $this->raw_data
1369
			if (($fetched = $this->fetch_data($cache)) === true)
1370
			{
1371
				return true;
1372
			}
1373
			elseif ($fetched === false) {
1374
				return false;
1375
			}
1376

    
1377
			list($headers, $sniffed) = $fetched;
1378
		}
1379
		
1380
		// Empty response check
1381
		if(empty($this->raw_data)){
1382
			$this->error = "A feed could not be found at `$this->feed_url`. Empty body.";
1383
			$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1384
			return false;
1385
		}
1386

    
1387
		// Set up array of possible encodings
1388
		$encodings = array();
1389

    
1390
		// First check to see if input has been overridden.
1391
		if ($this->input_encoding !== false)
1392
		{
1393
			$encodings[] = strtoupper($this->input_encoding);
1394
		}
1395

    
1396
		$application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
1397
		$text_types = array('text/xml', 'text/xml-external-parsed-entity');
1398

    
1399
		// RFC 3023 (only applies to sniffed content)
1400
		if (isset($sniffed))
1401
		{
1402
			if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
1403
			{
1404
				if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1405
				{
1406
					$encodings[] = strtoupper($charset[1]);
1407
				}
1408
				$encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1409
				$encodings[] = 'UTF-8';
1410
			}
1411
			elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
1412
			{
1413
				if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1414
				{
1415
					$encodings[] = strtoupper($charset[1]);
1416
				}
1417
				$encodings[] = 'US-ASCII';
1418
			}
1419
			// Text MIME-type default
1420
			elseif (substr($sniffed, 0, 5) === 'text/')
1421
			{
1422
				$encodings[] = 'UTF-8';
1423
			}
1424
		}
1425

    
1426
		// Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
1427
		$encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1428
		$encodings[] = 'UTF-8';
1429
		$encodings[] = 'ISO-8859-1';
1430

    
1431
		// There's no point in trying an encoding twice
1432
		$encodings = array_unique($encodings);
1433

    
1434
		// Loop through each possible encoding, till we return something, or run out of possibilities
1435
		foreach ($encodings as $encoding)
1436
		{
1437
			// Change the encoding to UTF-8 (as we always use UTF-8 internally)
1438
			if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8')))
1439
			{
1440
				// Create new parser
1441
				$parser = $this->registry->create('Parser');
1442

    
1443
				// If it's parsed fine
1444
				if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url))
1445
				{
1446
					$this->data = $parser->get_data();
1447
					if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE))
1448
					{
1449
						$this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed.";
1450
						$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1451
						return false;
1452
					}
1453

    
1454
					if (isset($headers))
1455
					{
1456
						$this->data['headers'] = $headers;
1457
					}
1458
					$this->data['build'] = SIMPLEPIE_BUILD;
1459

    
1460
					// Cache the file if caching is enabled
1461
					if ($cache && !$cache->save($this))
1462
					{
1463
						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);
1464
					}
1465
					return true;
1466
				}
1467
			}
1468
		}
1469

    
1470
		if (isset($parser))
1471
		{
1472
			// We have an error, just set SimplePie_Misc::error to it and quit
1473
			$this->error = $this->feed_url;
1474
			$this->error .= sprintf(' is invalid XML, 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());
1475
		}
1476
		else
1477
		{
1478
			$this->error = 'The data could not be converted to UTF-8.';
1479
			if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) {
1480
				$this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.';
1481
			} else {
1482
				$missingExtensions = array();
1483
				if (!extension_loaded('iconv')) {
1484
					$missingExtensions[] = 'iconv';
1485
				}
1486
				if (!extension_loaded('mbstring')) {
1487
					$missingExtensions[] = 'mbstring';
1488
				}
1489
				if (!class_exists('\UConverter')) {
1490
					$missingExtensions[] = 'intl (PHP 5.5+)';
1491
				}
1492
				$this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.';
1493
			}
1494
		}
1495

    
1496
		$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1497

    
1498
		return false;
1499
	}
1500

    
1501
	/**
1502
	 * Fetch the data via SimplePie_File
1503
	 *
1504
	 * If the data is already cached, attempt to fetch it from there instead
1505
	 * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache
1506
	 * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
1507
	 */
1508
	protected function fetch_data(&$cache)
1509
	{
1510
		// If it's enabled, use the cache
1511
		if ($cache)
1512
		{
1513
			// Load the Cache
1514
			$this->data = $cache->load();
1515
			if (!empty($this->data))
1516
			{
1517
				// If the cache is for an outdated build of SimplePie
1518
				if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
1519
				{
1520
					$cache->unlink();
1521
					$this->data = array();
1522
				}
1523
				// If we've hit a collision just rerun it with caching disabled
1524
				elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
1525
				{
1526
					$cache = false;
1527
					$this->data = array();
1528
				}
1529
				// If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
1530
				elseif (isset($this->data['feed_url']))
1531
				{
1532
					// If the autodiscovery cache is still valid use it.
1533
					if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
1534
					{
1535
						// Do not need to do feed autodiscovery yet.
1536
						if ($this->data['feed_url'] !== $this->data['url'])
1537
						{
1538
							$this->set_feed_url($this->data['feed_url']);
1539
							return $this->init();
1540
						}
1541

    
1542
						$cache->unlink();
1543
						$this->data = array();
1544
					}
1545
				}
1546
				// Check if the cache has been updated
1547
				elseif ($cache->mtime() + $this->cache_duration < time())
1548
				{
1549
					// Want to know if we tried to send last-modified and/or etag headers
1550
					// when requesting this file. (Note that it's up to the file to
1551
					// support this, but we don't always send the headers either.)
1552
					$this->check_modified = true;
1553
					if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
1554
					{
1555
						$headers = array(
1556
							'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',
1557
						);
1558
						if (isset($this->data['headers']['last-modified']))
1559
						{
1560
							$headers['if-modified-since'] = $this->data['headers']['last-modified'];
1561
						}
1562
						if (isset($this->data['headers']['etag']))
1563
						{
1564
							$headers['if-none-match'] = $this->data['headers']['etag'];
1565
						}
1566

    
1567
						$file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
1568

    
1569
						if ($file->success)
1570
						{
1571
							if ($file->status_code === 304)
1572
							{
1573
								// Set raw_data to false here too, to signify that the cache
1574
								// is still valid.
1575
								$this->raw_data = false;
1576
								$cache->touch();
1577
								return true;
1578
							}
1579
						}
1580
						else
1581
						{
1582
							$this->check_modified = false;
1583
							if($this->force_cache_fallback)
1584
							{
1585
								$cache->touch();
1586
								return true;
1587
							}
1588

    
1589
							unset($file);
1590
						}
1591
					}
1592
				}
1593
				// If the cache is still valid, just return true
1594
				else
1595
				{
1596
					$this->raw_data = false;
1597
					return true;
1598
				}
1599
			}
1600
			// If the cache is empty, delete it
1601
			else
1602
			{
1603
				$cache->unlink();
1604
				$this->data = array();
1605
			}
1606
		}
1607
		// 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.
1608
		if (!isset($file))
1609
		{
1610
			if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url)
1611
			{
1612
				$file =& $this->file;
1613
			}
1614
			else
1615
			{
1616
				$headers = array(
1617
					'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',
1618
				);
1619
				$file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
1620
			}
1621
		}
1622
		// If the file connection has an error, set SimplePie::error to that and quit
1623
		if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
1624
		{
1625
			$this->error = $file->error;
1626
			return !empty($this->data);
1627
		}
1628

    
1629
		if (!$this->force_feed)
1630
		{
1631
			// Check if the supplied URL is a feed, if it isn't, look for it.
1632
			$locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds));
1633

    
1634
			if (!$locate->is_feed($file))
1635
			{
1636
				$copyStatusCode = $file->status_code;
1637
				$copyContentType = $file->headers['content-type'];
1638
				try
1639
				{
1640
					$microformats = false;
1641
					if (function_exists('Mf2\parse')) {
1642
						// Check for both h-feed and h-entry, as both a feed with no entries
1643
						// and a list of entries without an h-feed wrapper are both valid.
1644
						$position = 0;
1645
						while ($position = strpos($file->body, 'h-feed', $position))
1646
						{
1647
							$start = $position < 200 ? 0 : $position - 200;
1648
							$check = substr($file->body, $start, 400);
1649
							if ($microformats = preg_match('/class="[^"]*h-feed/', $check))
1650
							{
1651
								break;
1652
							}
1653
							$position += 7;
1654
						}
1655
						$position = 0;
1656
						while ($position = strpos($file->body, 'h-entry', $position))
1657
						{
1658
							$start = $position < 200 ? 0 : $position - 200;
1659
							$check = substr($file->body, $start, 400);
1660
							if ($microformats = preg_match('/class="[^"]*h-entry/', $check))
1661
							{
1662
								break;
1663
							}
1664
							$position += 7;
1665
						}
1666
					}
1667
					// Now also do feed discovery, but if an h-entry was found don't
1668
					// overwrite the current value of file.
1669
					$discovered = $locate->find($this->autodiscovery,
1670
					                            $this->all_discovered_feeds);
1671
					if ($microformats)
1672
					{
1673
						if ($hub = $locate->get_rel_link('hub'))
1674
						{
1675
							$self = $locate->get_rel_link('self');
1676
							$this->store_links($file, $hub, $self);
1677
						}
1678
						// Push the current file onto all_discovered feeds so the user can
1679
						// be shown this as one of the options.
1680
						if (isset($this->all_discovered_feeds)) {
1681
							$this->all_discovered_feeds[] = $file;
1682
						}
1683
					}
1684
					else
1685
					{
1686
						if ($discovered)
1687
						{
1688
							$file = $discovered;
1689
						}
1690
						else
1691
						{
1692
							// We need to unset this so that if SimplePie::set_file() has
1693
							// been called that object is untouched
1694
							unset($file);
1695
							$this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`";
1696
							$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1697
							return false;
1698
						}
1699
					}
1700
				}
1701
				catch (SimplePie_Exception $e)
1702
				{
1703
					// We need to unset this so that if SimplePie::set_file() has been called that object is untouched
1704
					unset($file);
1705
					// This is usually because DOMDocument doesn't exist
1706
					$this->error = $e->getMessage();
1707
					$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()));
1708
					return false;
1709
				}
1710
				if ($cache)
1711
				{
1712
					$this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
1713
					if (!$cache->save($this))
1714
					{
1715
						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);
1716
					}
1717
					$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
1718
				}
1719
				$this->feed_url = $file->url;
1720
			}
1721
			$locate = null;
1722
		}
1723

    
1724
		$this->raw_data = $file->body;
1725
		$this->permanent_url = $file->permanent_url;
1726
		$headers = $file->headers;
1727
		$sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
1728
		$sniffed = $sniffer->get_type();
1729

    
1730
		return array($headers, $sniffed);
1731
	}
1732

    
1733
	/**
1734
	 * Get the error message for the occured error
1735
	 *
1736
	 * @return string|array Error message, or array of messages for multifeeds
1737
	 */
1738
	public function error()
1739
	{
1740
		return $this->error;
1741
	}
1742

    
1743
	/**
1744
	 * Get the raw XML
1745
	 *
1746
	 * This is the same as the old `$feed->enable_xml_dump(true)`, but returns
1747
	 * the data instead of printing it.
1748
	 *
1749
	 * @return string|boolean Raw XML data, false if the cache is used
1750
	 */
1751
	public function get_raw_data()
1752
	{
1753
		return $this->raw_data;
1754
	}
1755

    
1756
	/**
1757
	 * Get the character encoding used for output
1758
	 *
1759
	 * @since Preview Release
1760
	 * @return string
1761
	 */
1762
	public function get_encoding()
1763
	{
1764
		return $this->sanitize->output_encoding;
1765
	}
1766

    
1767
	/**
1768
	 * Send the content-type header with correct encoding
1769
	 *
1770
	 * This method ensures that the SimplePie-enabled page is being served with
1771
	 * the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
1772
	 * and character encoding HTTP headers (character encoding determined by the
1773
	 * {@see set_output_encoding} config option).
1774
	 *
1775
	 * This won't work properly if any content or whitespace has already been
1776
	 * sent to the browser, because it relies on PHP's
1777
	 * {@link http://php.net/header header()} function, and these are the
1778
	 * circumstances under which the function works.
1779
	 *
1780
	 * Because it's setting these settings for the entire page (as is the nature
1781
	 * of HTTP headers), this should only be used once per page (again, at the
1782
	 * top).
1783
	 *
1784
	 * @param string $mime MIME type to serve the page as
1785
	 */
1786
	public function handle_content_type($mime = 'text/html')
1787
	{
1788
		if (!headers_sent())
1789
		{
1790
			$header = "Content-type: $mime;";
1791
			if ($this->get_encoding())
1792
			{
1793
				$header .= ' charset=' . $this->get_encoding();
1794
			}
1795
			else
1796
			{
1797
				$header .= ' charset=UTF-8';
1798
			}
1799
			header($header);
1800
		}
1801
	}
1802

    
1803
	/**
1804
	 * Get the type of the feed
1805
	 *
1806
	 * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against
1807
	 * using {@link http://php.net/language.operators.bitwise bitwise operators}
1808
	 *
1809
	 * @since 0.8 (usage changed to using constants in 1.0)
1810
	 * @see SIMPLEPIE_TYPE_NONE Unknown.
1811
	 * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90.
1812
	 * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
1813
	 * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
1814
	 * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91.
1815
	 * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92.
1816
	 * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93.
1817
	 * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94.
1818
	 * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0.
1819
	 * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x.
1820
	 * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS.
1821
	 * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
1822
	 * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS.
1823
	 * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3.
1824
	 * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0.
1825
	 * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom.
1826
	 * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type.
1827
	 * @return int SIMPLEPIE_TYPE_* constant
1828
	 */
1829
	public function get_type()
1830
	{
1831
		if (!isset($this->data['type']))
1832
		{
1833
			$this->data['type'] = SIMPLEPIE_TYPE_ALL;
1834
			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
1835
			{
1836
				$this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
1837
			}
1838
			elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
1839
			{
1840
				$this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
1841
			}
1842
			elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
1843
			{
1844
				if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
1845
				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
1846
				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
1847
				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
1848
				{
1849
					$this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
1850
				}
1851
				if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
1852
				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
1853
				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
1854
				|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
1855
				{
1856
					$this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
1857
				}
1858
			}
1859
			elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
1860
			{
1861
				$this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
1862
				if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1863
				{
1864
					switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1865
					{
1866
						case '0.91':
1867
							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
1868
							if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1869
							{
1870
								switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1871
								{
1872
									case '0':
1873
										$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
1874
										break;
1875

    
1876
									case '24':
1877
										$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
1878
										break;
1879
								}
1880
							}
1881
							break;
1882

    
1883
						case '0.92':
1884
							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
1885
							break;
1886

    
1887
						case '0.93':
1888
							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
1889
							break;
1890

    
1891
						case '0.94':
1892
							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
1893
							break;
1894

    
1895
						case '2.0':
1896
							$this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
1897
							break;
1898
					}
1899
				}
1900
			}
1901
			else
1902
			{
1903
				$this->data['type'] = SIMPLEPIE_TYPE_NONE;
1904
			}
1905
		}
1906
		return $this->data['type'];
1907
	}
1908

    
1909
	/**
1910
	 * Get the URL for the feed
1911
	 * 
1912
	 * When the 'permanent' mode is enabled, returns the original feed URL,
1913
	 * except in the case of an `HTTP 301 Moved Permanently` status response,
1914
	 * in which case the location of the first redirection is returned.
1915
	 *
1916
	 * When the 'permanent' mode is disabled (default),
1917
	 * may or may not be different from the URL passed to {@see set_feed_url()},
1918
	 * depending on whether auto-discovery was used.
1919
	 *
1920
	 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
1921
	 * @todo Support <itunes:new-feed-url>
1922
	 * @todo Also, |atom:link|@rel=self
1923
	 * @param bool $permanent Permanent mode to return only the original URL or the first redirection
1924
	 * iff it is a 301 redirection
1925
	 * @return string|null
1926
	 */
1927
	public function subscribe_url($permanent = false)
1928
	{
1929
		if ($permanent)
1930
		{
1931
			if ($this->permanent_url !== null)
1932
			{
1933
				// sanitize encodes ampersands which are required when used in a url.
1934
				return str_replace('&amp;', '&',
1935
				                   $this->sanitize($this->permanent_url,
1936
				                                   SIMPLEPIE_CONSTRUCT_IRI));
1937
			}
1938
		}
1939
		else
1940
		{
1941
			if ($this->feed_url !== null)
1942
			{
1943
				return str_replace('&amp;', '&',
1944
				                   $this->sanitize($this->feed_url,
1945
				                                   SIMPLEPIE_CONSTRUCT_IRI));
1946
			}
1947
		}
1948
		return null;
1949
	}
1950

    
1951
	/**
1952
	 * Get data for an feed-level element
1953
	 *
1954
	 * This method allows you to get access to ANY element/attribute that is a
1955
	 * sub-element of the opening feed tag.
1956
	 *
1957
	 * The return value is an indexed array of elements matching the given
1958
	 * namespace and tag name. Each element has `attribs`, `data` and `child`
1959
	 * subkeys. For `attribs` and `child`, these contain namespace subkeys.
1960
	 * `attribs` then has one level of associative name => value data (where
1961
	 * `value` is a string) after the namespace. `child` has tag-indexed keys
1962
	 * after the namespace, each member of which is an indexed array matching
1963
	 * this same format.
1964
	 *
1965
	 * For example:
1966
	 * <pre>
1967
	 * // This is probably a bad example because we already support
1968
	 * // <media:content> natively, but it shows you how to parse through
1969
	 * // the nodes.
1970
	 * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group');
1971
	 * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'];
1972
	 * $file = $content[0]['attribs']['']['url'];
1973
	 * echo $file;
1974
	 * </pre>
1975
	 *
1976
	 * @since 1.0
1977
	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1978
	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1979
	 * @param string $tag Tag name
1980
	 * @return array
1981
	 */
1982
	public function get_feed_tags($namespace, $tag)
1983
	{
1984
		$type = $this->get_type();
1985
		if ($type & SIMPLEPIE_TYPE_ATOM_10)
1986
		{
1987
			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
1988
			{
1989
				return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
1990
			}
1991
		}
1992
		if ($type & SIMPLEPIE_TYPE_ATOM_03)
1993
		{
1994
			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
1995
			{
1996
				return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
1997
			}
1998
		}
1999
		if ($type & SIMPLEPIE_TYPE_RSS_RDF)
2000
		{
2001
			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
2002
			{
2003
				return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
2004
			}
2005
		}
2006
		if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
2007
		{
2008
			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
2009
			{
2010
				return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
2011
			}
2012
		}
2013
		return null;
2014
	}
2015

    
2016
	/**
2017
	 * Get data for an channel-level element
2018
	 *
2019
	 * This method allows you to get access to ANY element/attribute in the
2020
	 * channel/header section of the feed.
2021
	 *
2022
	 * See {@see SimplePie::get_feed_tags()} for a description of the return value
2023
	 *
2024
	 * @since 1.0
2025
	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
2026
	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
2027
	 * @param string $tag Tag name
2028
	 * @return array
2029
	 */
2030
	public function get_channel_tags($namespace, $tag)
2031
	{
2032
		$type = $this->get_type();
2033
		if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
2034
		{
2035
			if ($return = $this->get_feed_tags($namespace, $tag))
2036
			{
2037
				return $return;
2038
			}
2039
		}
2040
		if ($type & SIMPLEPIE_TYPE_RSS_10)
2041
		{
2042
			if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
2043
			{
2044
				if (isset($channel[0]['child'][$namespace][$tag]))
2045
				{
2046
					return $channel[0]['child'][$namespace][$tag];
2047
				}
2048
			}
2049
		}
2050
		if ($type & SIMPLEPIE_TYPE_RSS_090)
2051
		{
2052
			if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
2053
			{
2054
				if (isset($channel[0]['child'][$namespace][$tag]))
2055
				{
2056
					return $channel[0]['child'][$namespace][$tag];
2057
				}
2058
			}
2059
		}
2060
		if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
2061
		{
2062
			if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
2063
			{
2064
				if (isset($channel[0]['child'][$namespace][$tag]))
2065
				{
2066
					return $channel[0]['child'][$namespace][$tag];
2067
				}
2068
			}
2069
		}
2070
		return null;
2071
	}
2072

    
2073
	/**
2074
	 * Get data for an channel-level element
2075
	 *
2076
	 * This method allows you to get access to ANY element/attribute in the
2077
	 * image/logo section of the feed.
2078
	 *
2079
	 * See {@see SimplePie::get_feed_tags()} for a description of the return value
2080
	 *
2081
	 * @since 1.0
2082
	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
2083
	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
2084
	 * @param string $tag Tag name
2085
	 * @return array
2086
	 */
2087
	public function get_image_tags($namespace, $tag)
2088
	{
2089
		$type = $this->get_type();
2090
		if ($type & SIMPLEPIE_TYPE_RSS_10)
2091
		{
2092
			if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
2093
			{
2094
				if (isset($image[0]['child'][$namespace][$tag]))
2095
				{
2096
					return $image[0]['child'][$namespace][$tag];
2097
				}
2098
			}
2099
		}
2100
		if ($type & SIMPLEPIE_TYPE_RSS_090)
2101
		{
2102
			if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
2103
			{
2104
				if (isset($image[0]['child'][$namespace][$tag]))
2105
				{
2106
					return $image[0]['child'][$namespace][$tag];
2107
				}
2108
			}
2109
		}
2110
		if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
2111
		{
2112
			if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
2113
			{
2114
				if (isset($image[0]['child'][$namespace][$tag]))
2115
				{
2116
					return $image[0]['child'][$namespace][$tag];
2117
				}
2118
			}
2119
		}
2120
		return null;
2121
	}
2122

    
2123
	/**
2124
	 * Get the base URL value from the feed
2125
	 *
2126
	 * Uses `<xml:base>` if available, otherwise uses the first link in the
2127
	 * feed, or failing that, the URL of the feed itself.
2128
	 *
2129
	 * @see get_link
2130
	 * @see subscribe_url
2131
	 *
2132
	 * @param array $element
2133
	 * @return string
2134
	 */
2135
	public function get_base($element = array())
2136
	{
2137
		if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
2138
		{
2139
			return $element['xml_base'];
2140
		}
2141
		elseif ($this->get_link() !== null)
2142
		{
2143
			return $this->get_link();
2144
		}
2145
		else
2146
		{
2147
			return $this->subscribe_url();
2148
		}
2149
	}
2150

    
2151
	/**
2152
	 * Sanitize feed data
2153
	 *
2154
	 * @access private
2155
	 * @see SimplePie_Sanitize::sanitize()
2156
	 * @param string $data Data to sanitize
2157
	 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
2158
	 * @param string $base Base URL to resolve URLs against
2159
	 * @return string Sanitized data
2160
	 */
2161
	public function sanitize($data, $type, $base = '')
2162
	{
2163
		try
2164
		{
2165
			return $this->sanitize->sanitize($data, $type, $base);
2166
		}
2167
		catch (SimplePie_Exception $e)
2168
		{
2169
			if (!$this->enable_exceptions)
2170
			{
2171
				$this->error = $e->getMessage();
2172
				$this->registry->call('Misc', 'error', array($this->error, E_USER_WARNING, $e->getFile(), $e->getLine()));
2173
				return '';
2174
			}
2175

    
2176
			throw $e;
2177
		}
2178
	}
2179

    
2180
	/**
2181
	 * Get the title of the feed
2182
	 *
2183
	 * Uses `<atom:title>`, `<title>` or `<dc:title>`
2184
	 *
2185
	 * @since 1.0 (previously called `get_feed_title` since 0.8)
2186
	 * @return string|null
2187
	 */
2188
	public function get_title()
2189
	{
2190
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
2191
		{
2192
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2193
		}
2194
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
2195
		{
2196
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2197
		}
2198
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2199
		{
2200
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2201
		}
2202
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2203
		{
2204
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2205
		}
2206
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2207
		{
2208
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2209
		}
2210
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2211
		{
2212
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2213
		}
2214
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2215
		{
2216
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2217
		}
2218
		else
2219
		{
2220
			return null;
2221
		}
2222
	}
2223

    
2224
	/**
2225
	 * Get a category for the feed
2226
	 *
2227
	 * @since Unknown
2228
	 * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1
2229
	 * @return SimplePie_Category|null
2230
	 */
2231
	public function get_category($key = 0)
2232
	{
2233
		$categories = $this->get_categories();
2234
		if (isset($categories[$key]))
2235
		{
2236
			return $categories[$key];
2237
		}
2238
		else
2239
		{
2240
			return null;
2241
		}
2242
	}
2243

    
2244
	/**
2245
	 * Get all categories for the feed
2246
	 *
2247
	 * Uses `<atom:category>`, `<category>` or `<dc:subject>`
2248
	 *
2249
	 * @since Unknown
2250
	 * @return array|null List of {@see SimplePie_Category} objects
2251
	 */
2252
	public function get_categories()
2253
	{
2254
		$categories = array();
2255

    
2256
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
2257
		{
2258
			$term = null;
2259
			$scheme = null;
2260
			$label = null;
2261
			if (isset($category['attribs']['']['term']))
2262
			{
2263
				$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
2264
			}
2265
			if (isset($category['attribs']['']['scheme']))
2266
			{
2267
				$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2268
			}
2269
			if (isset($category['attribs']['']['label']))
2270
			{
2271
				$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2272
			}
2273
			$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
2274
		}
2275
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
2276
		{
2277
			// This is really the label, but keep this as the term also for BC.
2278
			// Label will also work on retrieving because that falls back to term.
2279
			$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2280
			if (isset($category['attribs']['']['domain']))
2281
			{
2282
				$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
2283
			}
2284
			else
2285
			{
2286
				$scheme = null;
2287
			}
2288
			$categories[] = $this->registry->create('Category', array($term, $scheme, null));
2289
		}
2290
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
2291
		{
2292
			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2293
		}
2294
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
2295
		{
2296
			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2297
		}
2298

    
2299
		if (!empty($categories))
2300
		{
2301
			return array_unique($categories);
2302
		}
2303
		else
2304
		{
2305
			return null;
2306
		}
2307
	}
2308

    
2309
	/**
2310
	 * Get an author for the feed
2311
	 *
2312
	 * @since 1.1
2313
	 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1
2314
	 * @return SimplePie_Author|null
2315
	 */
2316
	public function get_author($key = 0)
2317
	{
2318
		$authors = $this->get_authors();
2319
		if (isset($authors[$key]))
2320
		{
2321
			return $authors[$key];
2322
		}
2323
		else
2324
		{
2325
			return null;
2326
		}
2327
	}
2328

    
2329
	/**
2330
	 * Get all authors for the feed
2331
	 *
2332
	 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
2333
	 *
2334
	 * @since 1.1
2335
	 * @return array|null List of {@see SimplePie_Author} objects
2336
	 */
2337
	public function get_authors()
2338
	{
2339
		$authors = array();
2340
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
2341
		{
2342
			$name = null;
2343
			$uri = null;
2344
			$email = null;
2345
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2346
			{
2347
				$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2348
			}
2349
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2350
			{
2351
				$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]));
2352
			}
2353
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2354
			{
2355
				$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2356
			}
2357
			if ($name !== null || $email !== null || $uri !== null)
2358
			{
2359
				$authors[] = $this->registry->create('Author', array($name, $uri, $email));
2360
			}
2361
		}
2362
		if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
2363
		{
2364
			$name = null;
2365
			$url = null;
2366
			$email = null;
2367
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2368
			{
2369
				$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2370
			}
2371
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2372
			{
2373
				$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]));
2374
			}
2375
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2376
			{
2377
				$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2378
			}
2379
			if ($name !== null || $email !== null || $url !== null)
2380
			{
2381
				$authors[] = $this->registry->create('Author', array($name, $url, $email));
2382
			}
2383
		}
2384
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
2385
		{
2386
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2387
		}
2388
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
2389
		{
2390
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2391
		}
2392
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
2393
		{
2394
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2395
		}
2396

    
2397
		if (!empty($authors))
2398
		{
2399
			return array_unique($authors);
2400
		}
2401
		else
2402
		{
2403
			return null;
2404
		}
2405
	}
2406

    
2407
	/**
2408
	 * Get a contributor for the feed
2409
	 *
2410
	 * @since 1.1
2411
	 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1
2412
	 * @return SimplePie_Author|null
2413
	 */
2414
	public function get_contributor($key = 0)
2415
	{
2416
		$contributors = $this->get_contributors();
2417
		if (isset($contributors[$key]))
2418
		{
2419
			return $contributors[$key];
2420
		}
2421
		else
2422
		{
2423
			return null;
2424
		}
2425
	}
2426

    
2427
	/**
2428
	 * Get all contributors for the feed
2429
	 *
2430
	 * Uses `<atom:contributor>`
2431
	 *
2432
	 * @since 1.1
2433
	 * @return array|null List of {@see SimplePie_Author} objects
2434
	 */
2435
	public function get_contributors()
2436
	{
2437
		$contributors = array();
2438
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
2439
		{
2440
			$name = null;
2441
			$uri = null;
2442
			$email = null;
2443
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2444
			{
2445
				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2446
			}
2447
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2448
			{
2449
				$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]));
2450
			}
2451
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2452
			{
2453
				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2454
			}
2455
			if ($name !== null || $email !== null || $uri !== null)
2456
			{
2457
				$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
2458
			}
2459
		}
2460
		foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
2461
		{
2462
			$name = null;
2463
			$url = null;
2464
			$email = null;
2465
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2466
			{
2467
				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2468
			}
2469
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2470
			{
2471
				$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]));
2472
			}
2473
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2474
			{
2475
				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2476
			}
2477
			if ($name !== null || $email !== null || $url !== null)
2478
			{
2479
				$contributors[] = $this->registry->create('Author', array($name, $url, $email));
2480
			}
2481
		}
2482

    
2483
		if (!empty($contributors))
2484
		{
2485
			return array_unique($contributors);
2486
		}
2487
		else
2488
		{
2489
			return null;
2490
		}
2491
	}
2492

    
2493
	/**
2494
	 * Get a single link for the feed
2495
	 *
2496
	 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2497
	 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1
2498
	 * @param string $rel The relationship of the link to return
2499
	 * @return string|null Link URL
2500
	 */
2501
	public function get_link($key = 0, $rel = 'alternate')
2502
	{
2503
		$links = $this->get_links($rel);
2504
		if (isset($links[$key]))
2505
		{
2506
			return $links[$key];
2507
		}
2508
		else
2509
		{
2510
			return null;
2511
		}
2512
	}
2513

    
2514
	/**
2515
	 * Get the permalink for the item
2516
	 *
2517
	 * Returns the first link available with a relationship of "alternate".
2518
	 * Identical to {@see get_link()} with key 0
2519
	 *
2520
	 * @see get_link
2521
	 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2522
	 * @internal Added for parity between the parent-level and the item/entry-level.
2523
	 * @return string|null Link URL
2524
	 */
2525
	public function get_permalink()
2526
	{
2527
		return $this->get_link(0);
2528
	}
2529

    
2530
	/**
2531
	 * Get all links for the feed
2532
	 *
2533
	 * Uses `<atom:link>` or `<link>`
2534
	 *
2535
	 * @since Beta 2
2536
	 * @param string $rel The relationship of links to return
2537
	 * @return array|null Links found for the feed (strings)
2538
	 */
2539
	public function get_links($rel = 'alternate')
2540
	{
2541
		if (!isset($this->data['links']))
2542
		{
2543
			$this->data['links'] = array();
2544
			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
2545
			{
2546
				foreach ($links as $link)
2547
				{
2548
					if (isset($link['attribs']['']['href']))
2549
					{
2550
						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2551
						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2552
					}
2553
				}
2554
			}
2555
			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
2556
			{
2557
				foreach ($links as $link)
2558
				{
2559
					if (isset($link['attribs']['']['href']))
2560
					{
2561
						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2562
						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2563

    
2564
					}
2565
				}
2566
			}
2567
			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2568
			{
2569
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2570
			}
2571
			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2572
			{
2573
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2574
			}
2575
			if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2576
			{
2577
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2578
			}
2579

    
2580
			$keys = array_keys($this->data['links']);
2581
			foreach ($keys as $key)
2582
			{
2583
				if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
2584
				{
2585
					if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
2586
					{
2587
						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
2588
						$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
2589
					}
2590
					else
2591
					{
2592
						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
2593
					}
2594
				}
2595
				elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
2596
				{
2597
					$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
2598
				}
2599
				$this->data['links'][$key] = array_unique($this->data['links'][$key]);
2600
			}
2601
		}
2602

    
2603
		if (isset($this->data['links'][$rel]))
2604
		{
2605
			return $this->data['links'][$rel];
2606
		}
2607
		else if (isset($this->data['headers']['link']) &&
2608
		         preg_match('/<([^>]+)>; rel='.preg_quote($rel).'/',
2609
		                    $this->data['headers']['link'], $match))
2610
		{
2611
			return array($match[1]);
2612
		}
2613
		else
2614
		{
2615
			return null;
2616
		}
2617
	}
2618

    
2619
	public function get_all_discovered_feeds()
2620
	{
2621
		return $this->all_discovered_feeds;
2622
	}
2623

    
2624
	/**
2625
	 * Get the content for the item
2626
	 *
2627
	 * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`,
2628
	 * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>`
2629
	 *
2630
	 * @since 1.0 (previously called `get_feed_description()` since 0.8)
2631
	 * @return string|null
2632
	 */
2633
	public function get_description()
2634
	{
2635
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
2636
		{
2637
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2638
		}
2639
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
2640
		{
2641
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2642
		}
2643
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
2644
		{
2645
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2646
		}
2647
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
2648
		{
2649
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2650
		}
2651
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
2652
		{
2653
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2654
		}
2655
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
2656
		{
2657
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2658
		}
2659
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
2660
		{
2661
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2662
		}
2663
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
2664
		{
2665
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2666
		}
2667
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
2668
		{
2669
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2670
		}
2671
		else
2672
		{
2673
			return null;
2674
		}
2675
	}
2676

    
2677
	/**
2678
	 * Get the copyright info for the feed
2679
	 *
2680
	 * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>`
2681
	 *
2682
	 * @since 1.0 (previously called `get_feed_copyright()` since 0.8)
2683
	 * @return string|null
2684
	 */
2685
	public function get_copyright()
2686
	{
2687
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
2688
		{
2689
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2690
		}
2691
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
2692
		{
2693
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2694
		}
2695
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
2696
		{
2697
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2698
		}
2699
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
2700
		{
2701
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2702
		}
2703
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
2704
		{
2705
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2706
		}
2707
		else
2708
		{
2709
			return null;
2710
		}
2711
	}
2712

    
2713
	/**
2714
	 * Get the language for the feed
2715
	 *
2716
	 * Uses `<language>`, `<dc:language>`, or @xml_lang
2717
	 *
2718
	 * @since 1.0 (previously called `get_feed_language()` since 0.8)
2719
	 * @return string|null
2720
	 */
2721
	public function get_language()
2722
	{
2723
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
2724
		{
2725
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2726
		}
2727
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
2728
		{
2729
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2730
		}
2731
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
2732
		{
2733
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2734
		}
2735
		elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
2736
		{
2737
			return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2738
		}
2739
		elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
2740
		{
2741
			return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2742
		}
2743
		elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
2744
		{
2745
			return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2746
		}
2747
		elseif (isset($this->data['headers']['content-language']))
2748
		{
2749
			return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
2750
		}
2751
		else
2752
		{
2753
			return null;
2754
		}
2755
	}
2756

    
2757
	/**
2758
	 * Get the latitude coordinates for the item
2759
	 *
2760
	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2761
	 *
2762
	 * Uses `<geo:lat>` or `<georss:point>`
2763
	 *
2764
	 * @since 1.0
2765
	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2766
	 * @link http://www.georss.org/ GeoRSS
2767
	 * @return string|null
2768
	 */
2769
	public function get_latitude()
2770
	{
2771

    
2772
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2773
		{
2774
			return (float) $return[0]['data'];
2775
		}
2776
		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))
2777
		{
2778
			return (float) $match[1];
2779
		}
2780
		else
2781
		{
2782
			return null;
2783
		}
2784
	}
2785

    
2786
	/**
2787
	 * Get the longitude coordinates for the feed
2788
	 *
2789
	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2790
	 *
2791
	 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
2792
	 *
2793
	 * @since 1.0
2794
	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2795
	 * @link http://www.georss.org/ GeoRSS
2796
	 * @return string|null
2797
	 */
2798
	public function get_longitude()
2799
	{
2800
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2801
		{
2802
			return (float) $return[0]['data'];
2803
		}
2804
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2805
		{
2806
			return (float) $return[0]['data'];
2807
		}
2808
		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))
2809
		{
2810
			return (float) $match[2];
2811
		}
2812
		else
2813
		{
2814
			return null;
2815
		}
2816
	}
2817

    
2818
	/**
2819
	 * Get the feed logo's title
2820
	 *
2821
	 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title.
2822
	 *
2823
	 * Uses `<image><title>` or `<image><dc:title>`
2824
	 *
2825
	 * @return string|null
2826
	 */
2827
	public function get_image_title()
2828
	{
2829
		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2830
		{
2831
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2832
		}
2833
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2834
		{
2835
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2836
		}
2837
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2838
		{
2839
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2840
		}
2841
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2842
		{
2843
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2844
		}
2845
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2846
		{
2847
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2848
		}
2849
		else
2850
		{
2851
			return null;
2852
		}
2853
	}
2854

    
2855
	/**
2856
	 * Get the feed logo's URL
2857
	 *
2858
	 * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to
2859
	 * have a "feed logo" URL. This points directly to the image itself.
2860
	 *
2861
	 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2862
	 * `<image><title>` or `<image><dc:title>`
2863
	 *
2864
	 * @return string|null
2865
	 */
2866
	public function get_image_url()
2867
	{
2868
		if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
2869
		{
2870
			return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
2871
		}
2872
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
2873
		{
2874
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2875
		}
2876
		elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
2877
		{
2878
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2879
		}
2880
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
2881
		{
2882
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2883
		}
2884
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
2885
		{
2886
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2887
		}
2888
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2889
		{
2890
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2891
		}
2892
		else
2893
		{
2894
			return null;
2895
		}
2896
	}
2897

    
2898
	/**
2899
	 * Get the feed logo's link
2900
	 *
2901
	 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This
2902
	 * points to a human-readable page that the image should link to.
2903
	 *
2904
	 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2905
	 * `<image><title>` or `<image><dc:title>`
2906
	 *
2907
	 * @return string|null
2908
	 */
2909
	public function get_image_link()
2910
	{
2911
		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2912
		{
2913
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2914
		}
2915
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2916
		{
2917
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2918
		}
2919
		elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2920
		{
2921
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2922
		}
2923
		else
2924
		{
2925
			return null;
2926
		}
2927
	}
2928

    
2929
	/**
2930
	 * Get the feed logo's link
2931
	 *
2932
	 * RSS 2.0 feeds are allowed to have a "feed logo" width.
2933
	 *
2934
	 * Uses `<image><width>` or defaults to 88.0 if no width is specified and
2935
	 * the feed is an RSS 2.0 feed.
2936
	 *
2937
	 * @return int|float|null
2938
	 */
2939
	public function get_image_width()
2940
	{
2941
		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
2942
		{
2943
			return round($return[0]['data']);
2944
		}
2945
		elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2946
		{
2947
			return 88.0;
2948
		}
2949
		else
2950
		{
2951
			return null;
2952
		}
2953
	}
2954

    
2955
	/**
2956
	 * Get the feed logo's height
2957
	 *
2958
	 * RSS 2.0 feeds are allowed to have a "feed logo" height.
2959
	 *
2960
	 * Uses `<image><height>` or defaults to 31.0 if no height is specified and
2961
	 * the feed is an RSS 2.0 feed.
2962
	 *
2963
	 * @return int|float|null
2964
	 */
2965
	public function get_image_height()
2966
	{
2967
		if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
2968
		{
2969
			return round($return[0]['data']);
2970
		}
2971
		elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2972
		{
2973
			return 31.0;
2974
		}
2975
		else
2976
		{
2977
			return null;
2978
		}
2979
	}
2980

    
2981
	/**
2982
	 * Get the number of items in the feed
2983
	 *
2984
	 * This is well-suited for {@link http://php.net/for for()} loops with
2985
	 * {@see get_item()}
2986
	 *
2987
	 * @param int $max Maximum value to return. 0 for no limit
2988
	 * @return int Number of items in the feed
2989
	 */
2990
	public function get_item_quantity($max = 0)
2991
	{
2992
		$max = (int) $max;
2993
		$qty = count($this->get_items());
2994
		if ($max === 0)
2995
		{
2996
			return $qty;
2997
		}
2998
		else
2999
		{
3000
			return ($qty > $max) ? $max : $qty;
3001
		}
3002
	}
3003

    
3004
	/**
3005
	 * Get a single item from the feed
3006
	 *
3007
	 * This is better suited for {@link http://php.net/for for()} loops, whereas
3008
	 * {@see get_items()} is better suited for
3009
	 * {@link http://php.net/foreach foreach()} loops.
3010
	 *
3011
	 * @see get_item_quantity()
3012
	 * @since Beta 2
3013
	 * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1
3014
	 * @return SimplePie_Item|null
3015
	 */
3016
	public function get_item($key = 0)
3017
	{
3018
		$items = $this->get_items();
3019
		if (isset($items[$key]))
3020
		{
3021
			return $items[$key];
3022
		}
3023
		else
3024
		{
3025
			return null;
3026
		}
3027
	}
3028

    
3029
	/**
3030
	 * Get all items from the feed
3031
	 *
3032
	 * This is better suited for {@link http://php.net/for for()} loops, whereas
3033
	 * {@see get_items()} is better suited for
3034
	 * {@link http://php.net/foreach foreach()} loops.
3035
	 *
3036
	 * @see get_item_quantity
3037
	 * @since Beta 2
3038
	 * @param int $start Index to start at
3039
	 * @param int $end Number of items to return. 0 for all items after `$start`
3040
	 * @return SimplePie_Item[]|null List of {@see SimplePie_Item} objects
3041
	 */
3042
	public function get_items($start = 0, $end = 0)
3043
	{
3044
		if (!isset($this->data['items']))
3045
		{
3046
			if (!empty($this->multifeed_objects))
3047
			{
3048
				$this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
3049
				if (empty($this->data['items']))
3050
				{
3051
					return array();
3052
				}
3053
				return $this->data['items'];
3054
			}
3055
			$this->data['items'] = array();
3056
			if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry'))
3057
			{
3058
				$keys = array_keys($items);
3059
				foreach ($keys as $key)
3060
				{
3061
					$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
3062
				}
3063
			}
3064
			if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry'))
3065
			{
3066
				$keys = array_keys($items);
3067
				foreach ($keys as $key)
3068
				{
3069
					$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
3070
				}
3071
			}
3072
			if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item'))
3073
			{
3074
				$keys = array_keys($items);
3075
				foreach ($keys as $key)
3076
				{
3077
					$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
3078
				}
3079
			}
3080
			if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item'))
3081
			{
3082
				$keys = array_keys($items);
3083
				foreach ($keys as $key)
3084
				{
3085
					$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
3086
				}
3087
			}
3088
			if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item'))
3089
			{
3090
				$keys = array_keys($items);
3091
				foreach ($keys as $key)
3092
				{
3093
					$this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
3094
				}
3095
			}
3096
		}
3097

    
3098
		if (empty($this->data['items']))
3099
		{
3100
			return array();
3101
		}
3102

    
3103
		if ($this->order_by_date)
3104
		{
3105
			if (!isset($this->data['ordered_items']))
3106
			{
3107
				$this->data['ordered_items'] = $this->data['items'];
3108
				usort($this->data['ordered_items'], array(get_class($this), 'sort_items'));
3109
		 	}
3110
			$items = $this->data['ordered_items'];
3111
		}
3112
		else
3113
		{
3114
			$items = $this->data['items'];
3115
		}
3116
		// Slice the data as desired
3117
		if ($end === 0)
3118
		{
3119
			return array_slice($items, $start);
3120
		}
3121
		else
3122
		{
3123
			return array_slice($items, $start, $end);
3124
		}
3125
	}
3126

    
3127
	/**
3128
	 * Set the favicon handler
3129
	 *
3130
	 * @deprecated Use your own favicon handling instead
3131
	 */
3132
	public function set_favicon_handler($page = false, $qs = 'i')
3133
	{
3134
		$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
3135
		trigger_error('Favicon handling has been removed, please use your own handling', $level);
3136
		return false;
3137
	}
3138

    
3139
	/**
3140
	 * Get the favicon for the current feed
3141
	 *
3142
	 * @deprecated Use your own favicon handling instead
3143
	 */
3144
	public function get_favicon()
3145
	{
3146
		$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
3147
		trigger_error('Favicon handling has been removed, please use your own handling', $level);
3148

    
3149
		if (($url = $this->get_link()) !== null)
3150
		{
3151
			return 'http://g.etfv.co/' . urlencode($url);
3152
		}
3153

    
3154
		return false;
3155
	}
3156

    
3157
	/**
3158
	 * Magic method handler
3159
	 *
3160
	 * @param string $method Method name
3161
	 * @param array $args Arguments to the method
3162
	 * @return mixed
3163
	 */
3164
	public function __call($method, $args)
3165
	{
3166
		if (strpos($method, 'subscribe_') === 0)
3167
		{
3168
			$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
3169
			trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level);
3170
			return '';
3171
		}
3172
		if ($method === 'enable_xml_dump')
3173
		{
3174
			$level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
3175
			trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level);
3176
			return false;
3177
		}
3178

    
3179
		$class = get_class($this);
3180
		$trace = debug_backtrace();
3181
		$file = $trace[0]['file'];
3182
		$line = $trace[0]['line'];
3183
		trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
3184
	}
3185

    
3186
	/**
3187
	 * Sorting callback for items
3188
	 *
3189
	 * @access private
3190
	 * @param SimplePie $a
3191
	 * @param SimplePie $b
3192
	 * @return boolean
3193
	 */
3194
	public static function sort_items($a, $b)
3195
	{
3196
		$a_date = $a->get_date('U');
3197
		$b_date = $b->get_date('U');
3198
		if ($a_date && $b_date) {
3199
			return $a_date > $b_date ? -1 : 1;
3200
		}
3201
		// Sort items without dates to the top.
3202
		if ($a_date) {
3203
			return 1;
3204
		}
3205
		if ($b_date) {
3206
			return -1;
3207
		}
3208
		return 0;
3209
	}
3210

    
3211
	/**
3212
	 * Merge items from several feeds into one
3213
	 *
3214
	 * If you're merging multiple feeds together, they need to all have dates
3215
	 * for the items or else SimplePie will refuse to sort them.
3216
	 *
3217
	 * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings
3218
	 * @param array $urls List of SimplePie feed objects to merge
3219
	 * @param int $start Starting item
3220
	 * @param int $end Number of items to return
3221
	 * @param int $limit Maximum number of items per feed
3222
	 * @return array
3223
	 */
3224
	public static function merge_items($urls, $start = 0, $end = 0, $limit = 0)
3225
	{
3226
		if (is_array($urls) && sizeof($urls) > 0)
3227
		{
3228
			$items = array();
3229
			foreach ($urls as $arg)
3230
			{
3231
				if ($arg instanceof SimplePie)
3232
				{
3233
					$items = array_merge($items, $arg->get_items(0, $limit));
3234
				}
3235
				else
3236
				{
3237
					trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
3238
				}
3239
			}
3240

    
3241
			usort($items, array(get_class($urls[0]), 'sort_items'));
3242

    
3243
			if ($end === 0)
3244
			{
3245
				return array_slice($items, $start);
3246
			}
3247
			else
3248
			{
3249
				return array_slice($items, $start, $end);
3250
			}
3251
		}
3252
		else
3253
		{
3254
			trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
3255
			return array();
3256
		}
3257
	}
3258

    
3259
	/**
3260
	 * Store PubSubHubbub links as headers
3261
	 *
3262
	 * There is no way to find PuSH links in the body of a microformats feed,
3263
	 * so they are added to the headers when found, to be used later by get_links.
3264
	 * @param SimplePie_File $file
3265
	 * @param string $hub
3266
	 * @param string $self
3267
	 */
3268
	private function store_links(&$file, $hub, $self) {
3269
		if (isset($file->headers['link']['hub']) ||
3270
			  (isset($file->headers['link']) &&
3271
			   preg_match('/rel=hub/', $file->headers['link'])))
3272
		{
3273
			return;
3274
		}
3275

    
3276
		if ($hub)
3277
		{
3278
			if (isset($file->headers['link']))
3279
			{
3280
				if ($file->headers['link'] !== '')
3281
				{
3282
					$file->headers['link'] = ', ';
3283
				}
3284
			}
3285
			else
3286
			{
3287
				$file->headers['link'] = '';
3288
			}
3289
			$file->headers['link'] .= '<'.$hub.'>; rel=hub';
3290
			if ($self)
3291
			{
3292
				$file->headers['link'] .= ', <'.$self.'>; rel=self';
3293
			}
3294
		}
3295
	}
3296
}
3297

    
3298
/**
3299
 * Manages all author-related data
3300
 *
3301
 * Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()}
3302
 *
3303
 * This class can be overloaded with {@see SimplePie::set_author_class()}
3304
 *
3305
 * @package SimplePie
3306
 * @subpackage API
3307
 */
3308
class SimplePie_Author
3309
{
3310
	/**
3311
	 * Author's name
3312
	 *
3313
	 * @var string
3314
	 * @see get_name()
3315
	 */
3316
	var $name;
3317

    
3318
	/**
3319
	 * Author's link
3320
	 *
3321
	 * @var string
3322
	 * @see get_link()
3323
	 */
3324
	var $link;
3325

    
3326
	/**
3327
	 * Author's email address
3328
	 *
3329
	 * @var string
3330
	 * @see get_email()
3331
	 */
3332
	var $email;
3333

    
3334
	/**
3335
	 * Constructor, used to input the data
3336
	 *
3337
	 * @param string $name
3338
	 * @param string $link
3339
	 * @param string $email
3340
	 */
3341
	public function __construct($name = null, $link = null, $email = null)
3342
	{
3343
		$this->name = $name;
3344
		$this->link = $link;
3345
		$this->email = $email;
3346
	}
3347

    
3348
	/**
3349
	 * String-ified version
3350
	 *
3351
	 * @return string
3352
	 */
3353
	public function __toString()
3354
	{
3355
		// There is no $this->data here
3356
		return md5(serialize($this));
3357
	}
3358

    
3359
	/**
3360
	 * Author's name
3361
	 *
3362
	 * @return string|null
3363
	 */
3364
	public function get_name()
3365
	{
3366
		if ($this->name !== null)
3367
		{
3368
			return $this->name;
3369
		}
3370
		else
3371
		{
3372
			return null;
3373
		}
3374
	}
3375

    
3376
	/**
3377
	 * Author's link
3378
	 *
3379
	 * @return string|null
3380
	 */
3381
	public function get_link()
3382
	{
3383
		if ($this->link !== null)
3384
		{
3385
			return $this->link;
3386
		}
3387
		else
3388
		{
3389
			return null;
3390
		}
3391
	}
3392

    
3393
	/**
3394
	 * Author's email address
3395
	 *
3396
	 * @return string|null
3397
	 */
3398
	public function get_email()
3399
	{
3400
		if ($this->email !== null)
3401
		{
3402
			return $this->email;
3403
		}
3404
		else
3405
		{
3406
			return null;
3407
		}
3408
	}
3409
}
3410

    
3411
/**
3412
 * Used to create cache objects
3413
 *
3414
 * This class can be overloaded with {@see SimplePie::set_cache_class()},
3415
 * although the preferred way is to create your own handler
3416
 * via {@see register()}
3417
 *
3418
 * @package SimplePie
3419
 * @subpackage Caching
3420
 */
3421
class SimplePie_Cache
3422
{
3423
	/**
3424
	 * Cache handler classes
3425
	 *
3426
	 * These receive 3 parameters to their constructor, as documented in
3427
	 * {@see register()}
3428
	 * @var array
3429
	 */
3430
	protected static $handlers = array(
3431
		'mysql'     => 'SimplePie_Cache_MySQL',
3432
		'memcache'  => 'SimplePie_Cache_Memcache',
3433
		'memcached' => 'SimplePie_Cache_Memcached',
3434
		'redis'     => 'SimplePie_Cache_Redis'
3435
	);
3436

    
3437
	/**
3438
	 * Don't call the constructor. Please.
3439
	 */
3440
	private function __construct() { }
3441

    
3442
	/**
3443
	 * Create a new SimplePie_Cache object
3444
	 *
3445
	 * @param string $location URL location (scheme is used to determine handler)
3446
	 * @param string $filename Unique identifier for cache object
3447
	 * @param string $extension 'spi' or 'spc'
3448
	 * @return SimplePie_Cache_Base Type of object depends on scheme of `$location`
3449
	 */
3450
	public static function get_handler($location, $filename, $extension)
3451
	{
3452
		$type = explode(':', $location, 2);
3453
		$type = $type[0];
3454
		if (!empty(self::$handlers[$type]))
3455
		{
3456
			$class = self::$handlers[$type];
3457
			return new $class($location, $filename, $extension);
3458
		}
3459

    
3460
		return new SimplePie_Cache_File($location, $filename, $extension);
3461
	}
3462

    
3463
	/**
3464
	 * Create a new SimplePie_Cache object
3465
	 *
3466
	 * @deprecated Use {@see get_handler} instead
3467
	 */
3468
	public function create($location, $filename, $extension)
3469
	{
3470
		trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED);
3471
		return self::get_handler($location, $filename, $extension);
3472
	}
3473

    
3474
	/**
3475
	 * Register a handler
3476
	 *
3477
	 * @param string $type DSN type to register for
3478
	 * @param string $class Name of handler class. Must implement SimplePie_Cache_Base
3479
	 */
3480
	public static function register($type, $class)
3481
	{
3482
		self::$handlers[$type] = $class;
3483
	}
3484

    
3485
	/**
3486
	 * Parse a URL into an array
3487
	 *
3488
	 * @param string $url
3489
	 * @return array
3490
	 */
3491
	public static function parse_URL($url)
3492
	{
3493
		$params = parse_url($url);
3494
		$params['extras'] = array();
3495
		if (isset($params['query']))
3496
		{
3497
			parse_str($params['query'], $params['extras']);
3498
		}
3499
		return $params;
3500
	}
3501
}
3502

    
3503
/**
3504
 * Base for cache objects
3505
 *
3506
 * Classes to be used with {@see SimplePie_Cache::register()} are expected
3507
 * to implement this interface.
3508
 *
3509
 * @package SimplePie
3510
 * @subpackage Caching
3511
 */
3512
interface SimplePie_Cache_Base
3513
{
3514
	/**
3515
	 * Feed cache type
3516
	 *
3517
	 * @var string
3518
	 */
3519
	const TYPE_FEED = 'spc';
3520

    
3521
	/**
3522
	 * Image cache type
3523
	 *
3524
	 * @var string
3525
	 */
3526
	const TYPE_IMAGE = 'spi';
3527

    
3528
	/**
3529
	 * Create a new cache object
3530
	 *
3531
	 * @param string $location Location string (from SimplePie::$cache_location)
3532
	 * @param string $name Unique ID for the cache
3533
	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3534
	 */
3535
	public function __construct($location, $name, $type);
3536

    
3537
	/**
3538
	 * Save data to the cache
3539
	 *
3540
	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3541
	 * @return bool Successfulness
3542
	 */
3543
	public function save($data);
3544

    
3545
	/**
3546
	 * Retrieve the data saved to the cache
3547
	 *
3548
	 * @return array Data for SimplePie::$data
3549
	 */
3550
	public function load();
3551

    
3552
	/**
3553
	 * Retrieve the last modified time for the cache
3554
	 *
3555
	 * @return int Timestamp
3556
	 */
3557
	public function mtime();
3558

    
3559
	/**
3560
	 * Set the last modified time to the current time
3561
	 *
3562
	 * @return bool Success status
3563
	 */
3564
	public function touch();
3565

    
3566
	/**
3567
	 * Remove the cache
3568
	 *
3569
	 * @return bool Success status
3570
	 */
3571
	public function unlink();
3572
}
3573

    
3574
/**
3575
 * Base class for database-based caches
3576
 *
3577
 * @package SimplePie
3578
 * @subpackage Caching
3579
 */
3580
abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base
3581
{
3582
	/**
3583
	 * Helper for database conversion
3584
	 *
3585
	 * Converts a given {@see SimplePie} object into data to be stored
3586
	 *
3587
	 * @param SimplePie $data
3588
	 * @return array First item is the serialized data for storage, second item is the unique ID for this item
3589
	 */
3590
	protected static function prepare_simplepie_object_for_cache($data)
3591
	{
3592
		$items = $data->get_items();
3593
		$items_by_id = array();
3594

    
3595
		if (!empty($items))
3596
		{
3597
			foreach ($items as $item)
3598
			{
3599
				$items_by_id[$item->get_id()] = $item;
3600
			}
3601

    
3602
			if (count($items_by_id) !== count($items))
3603
			{
3604
				$items_by_id = array();
3605
				foreach ($items as $item)
3606
				{
3607
					$items_by_id[$item->get_id(true)] = $item;
3608
				}
3609
			}
3610

    
3611
			if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
3612
			{
3613
				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
3614
			}
3615
			elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
3616
			{
3617
				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
3618
			}
3619
			elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
3620
			{
3621
				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
3622
			}
3623
			elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
3624
			{
3625
				$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
3626
			}
3627
			else
3628
			{
3629
				$channel = null;
3630
			}
3631

    
3632
			if ($channel !== null)
3633
			{
3634
				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
3635
				{
3636
					unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
3637
				}
3638
				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
3639
				{
3640
					unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
3641
				}
3642
				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
3643
				{
3644
					unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
3645
				}
3646
				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
3647
				{
3648
					unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
3649
				}
3650
				if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
3651
				{
3652
					unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
3653
				}
3654
			}
3655
			if (isset($data->data['items']))
3656
			{
3657
				unset($data->data['items']);
3658
			}
3659
			if (isset($data->data['ordered_items']))
3660
			{
3661
				unset($data->data['ordered_items']);
3662
			}
3663
		}
3664
		return array(serialize($data->data), $items_by_id);
3665
	}
3666
}
3667

    
3668
/**
3669
 * Caches data to the filesystem
3670
 *
3671
 * @package SimplePie
3672
 * @subpackage Caching
3673
 */
3674
class SimplePie_Cache_File implements SimplePie_Cache_Base
3675
{
3676
	/**
3677
	 * Location string
3678
	 *
3679
	 * @see SimplePie::$cache_location
3680
	 * @var string
3681
	 */
3682
	protected $location;
3683

    
3684
	/**
3685
	 * Filename
3686
	 *
3687
	 * @var string
3688
	 */
3689
	protected $filename;
3690

    
3691
	/**
3692
	 * File extension
3693
	 *
3694
	 * @var string
3695
	 */
3696
	protected $extension;
3697

    
3698
	/**
3699
	 * File path
3700
	 *
3701
	 * @var string
3702
	 */
3703
	protected $name;
3704

    
3705
	/**
3706
	 * Create a new cache object
3707
	 *
3708
	 * @param string $location Location string (from SimplePie::$cache_location)
3709
	 * @param string $name Unique ID for the cache
3710
	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3711
	 */
3712
	public function __construct($location, $name, $type)
3713
	{
3714
		$this->location = $location;
3715
		$this->filename = $name;
3716
		$this->extension = $type;
3717
		$this->name = "$this->location/$this->filename.$this->extension";
3718
	}
3719

    
3720
	/**
3721
	 * Save data to the cache
3722
	 *
3723
	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3724
	 * @return bool Successfulness
3725
	 */
3726
	public function save($data)
3727
	{
3728
		if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
3729
		{
3730
			if ($data instanceof SimplePie)
3731
			{
3732
				$data = $data->data;
3733
			}
3734

    
3735
			$data = serialize($data);
3736
			return (bool) file_put_contents($this->name, $data);
3737
		}
3738
		return false;
3739
	}
3740

    
3741
	/**
3742
	 * Retrieve the data saved to the cache
3743
	 *
3744
	 * @return array Data for SimplePie::$data
3745
	 */
3746
	public function load()
3747
	{
3748
		if (file_exists($this->name) && is_readable($this->name))
3749
		{
3750
			return unserialize(file_get_contents($this->name));
3751
		}
3752
		return false;
3753
	}
3754

    
3755
	/**
3756
	 * Retrieve the last modified time for the cache
3757
	 *
3758
	 * @return int Timestamp
3759
	 */
3760
	public function mtime()
3761
	{
3762
		return @filemtime($this->name);
3763
	}
3764

    
3765
	/**
3766
	 * Set the last modified time to the current time
3767
	 *
3768
	 * @return bool Success status
3769
	 */
3770
	public function touch()
3771
	{
3772
		return @touch($this->name);
3773
	}
3774

    
3775
	/**
3776
	 * Remove the cache
3777
	 *
3778
	 * @return bool Success status
3779
	 */
3780
	public function unlink()
3781
	{
3782
		if (file_exists($this->name))
3783
		{
3784
			return unlink($this->name);
3785
		}
3786
		return false;
3787
	}
3788
}
3789

    
3790
/**
3791
 * Caches data to memcache
3792
 *
3793
 * Registered for URLs with the "memcache" protocol
3794
 *
3795
 * For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will
3796
 * connect to memcache on `localhost` on port 11211. All tables will be
3797
 * prefixed with `sp_` and data will expire after 3600 seconds
3798
 *
3799
 * @package SimplePie
3800
 * @subpackage Caching
3801
 * @uses Memcache
3802
 */
3803
class SimplePie_Cache_Memcache implements SimplePie_Cache_Base
3804
{
3805
	/**
3806
	 * Memcache instance
3807
	 *
3808
	 * @var Memcache
3809
	 */
3810
	protected $cache;
3811

    
3812
	/**
3813
	 * Options
3814
	 *
3815
	 * @var array
3816
	 */
3817
	protected $options;
3818

    
3819
	/**
3820
	 * Cache name
3821
	 *
3822
	 * @var string
3823
	 */
3824
	protected $name;
3825

    
3826
	/**
3827
	 * Create a new cache object
3828
	 *
3829
	 * @param string $location Location string (from SimplePie::$cache_location)
3830
	 * @param string $name Unique ID for the cache
3831
	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3832
	 */
3833
	public function __construct($location, $name, $type)
3834
	{
3835
		$this->options = array(
3836
			'host' => '127.0.0.1',
3837
			'port' => 11211,
3838
			'extras' => array(
3839
				'timeout' => 3600, // one hour
3840
				'prefix' => 'simplepie_',
3841
			),
3842
		);
3843
		$this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
3844

    
3845
		$this->name = $this->options['extras']['prefix'] . md5("$name:$type");
3846

    
3847
		$this->cache = new Memcache();
3848
		$this->cache->addServer($this->options['host'], (int) $this->options['port']);
3849
	}
3850

    
3851
	/**
3852
	 * Save data to the cache
3853
	 *
3854
	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3855
	 * @return bool Successfulness
3856
	 */
3857
	public function save($data)
3858
	{
3859
		if ($data instanceof SimplePie)
3860
		{
3861
			$data = $data->data;
3862
		}
3863
		return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
3864
	}
3865

    
3866
	/**
3867
	 * Retrieve the data saved to the cache
3868
	 *
3869
	 * @return array Data for SimplePie::$data
3870
	 */
3871
	public function load()
3872
	{
3873
		$data = $this->cache->get($this->name);
3874

    
3875
		if ($data !== false)
3876
		{
3877
			return unserialize($data);
3878
		}
3879
		return false;
3880
	}
3881

    
3882
	/**
3883
	 * Retrieve the last modified time for the cache
3884
	 *
3885
	 * @return int Timestamp
3886
	 */
3887
	public function mtime()
3888
	{
3889
		$data = $this->cache->get($this->name);
3890

    
3891
		if ($data !== false)
3892
		{
3893
			// essentially ignore the mtime because Memcache expires on its own
3894
			return time();
3895
		}
3896

    
3897
		return false;
3898
	}
3899

    
3900
	/**
3901
	 * Set the last modified time to the current time
3902
	 *
3903
	 * @return bool Success status
3904
	 */
3905
	public function touch()
3906
	{
3907
		$data = $this->cache->get($this->name);
3908

    
3909
		if ($data !== false)
3910
		{
3911
			return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
3912
		}
3913

    
3914
		return false;
3915
	}
3916

    
3917
	/**
3918
	 * Remove the cache
3919
	 *
3920
	 * @return bool Success status
3921
	 */
3922
	public function unlink()
3923
	{
3924
		return $this->cache->delete($this->name, 0);
3925
	}
3926
}
3927

    
3928
/**
3929
 * Caches data to memcached
3930
 *
3931
 * Registered for URLs with the "memcached" protocol
3932
 *
3933
 * For example, `memcached://localhost:11211/?timeout=3600&prefix=sp_` will
3934
 * connect to memcached on `localhost` on port 11211. All tables will be
3935
 * prefixed with `sp_` and data will expire after 3600 seconds
3936
 *
3937
 * @package    SimplePie
3938
 * @subpackage Caching
3939
 * @author     Paul L. McNeely
3940
 * @uses       Memcached
3941
 */
3942
class SimplePie_Cache_Memcached implements SimplePie_Cache_Base
3943
{
3944
    /**
3945
     * Memcached instance
3946
     * @var Memcached
3947
     */
3948
    protected $cache;
3949

    
3950
    /**
3951
     * Options
3952
     * @var array
3953
     */
3954
    protected $options;
3955

    
3956
    /**
3957
     * Cache name
3958
     * @var string
3959
     */
3960
    protected $name;
3961

    
3962
    /**
3963
     * Create a new cache object
3964
     * @param string $location Location string (from SimplePie::$cache_location)
3965
     * @param string $name     Unique ID for the cache
3966
     * @param string $type     Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3967
     */
3968
    public function __construct($location, $name, $type) {
3969
        $this->options = array(
3970
            'host'   => '127.0.0.1',
3971
            'port'   => 11211,
3972
            'extras' => array(
3973
                'timeout' => 3600, // one hour
3974
                'prefix'  => 'simplepie_',
3975
            ),
3976
        );
3977
        $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
3978

    
3979
        $this->name = $this->options['extras']['prefix'] . md5("$name:$type");
3980

    
3981
        $this->cache = new Memcached();
3982
        $this->cache->addServer($this->options['host'], (int)$this->options['port']);
3983
    }
3984

    
3985
    /**
3986
     * Save data to the cache
3987
     * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3988
     * @return bool Successfulness
3989
     */
3990
    public function save($data) {
3991
        if ($data instanceof SimplePie) {
3992
            $data = $data->data;
3993
        }
3994

    
3995
        return $this->setData(serialize($data));
3996
    }
3997

    
3998
    /**
3999
     * Retrieve the data saved to the cache
4000
     * @return array Data for SimplePie::$data
4001
     */
4002
    public function load() {
4003
        $data = $this->cache->get($this->name);
4004

    
4005
        if ($data !== false) {
4006
            return unserialize($data);
4007
        }
4008
        return false;
4009
    }
4010

    
4011
    /**
4012
     * Retrieve the last modified time for the cache
4013
     * @return int Timestamp
4014
     */
4015
    public function mtime() {
4016
        $data = $this->cache->get($this->name . '_mtime');
4017
        return (int) $data;
4018
    }
4019

    
4020
    /**
4021
     * Set the last modified time to the current time
4022
     * @return bool Success status
4023
     */
4024
    public function touch() {
4025
        $data = $this->cache->get($this->name);
4026
        return $this->setData($data);
4027
    }
4028

    
4029
    /**
4030
     * Remove the cache
4031
     * @return bool Success status
4032
     */
4033
    public function unlink() {
4034
        return $this->cache->delete($this->name, 0);
4035
    }
4036

    
4037
    /**
4038
     * Set the last modified time and data to Memcached
4039
     * @return bool Success status
4040
     */
4041
    private function setData($data) {
4042

    
4043
        if ($data !== false) {
4044
            $this->cache->set($this->name . '_mtime', time(), (int)$this->options['extras']['timeout']);
4045
            return $this->cache->set($this->name, $data, (int)$this->options['extras']['timeout']);
4046
        }
4047

    
4048
        return false;
4049
    }
4050
}
4051

    
4052
/**
4053
 * Caches data to a MySQL database
4054
 *
4055
 * Registered for URLs with the "mysql" protocol
4056
 *
4057
 * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
4058
 * connect to the `mydb` database on `localhost` on port 3306, with the user
4059
 * `root` and the password `password`. All tables will be prefixed with `sp_`
4060
 *
4061
 * @package SimplePie
4062
 * @subpackage Caching
4063
 */
4064
class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
4065
{
4066
	/**
4067
	 * PDO instance
4068
	 *
4069
	 * @var PDO
4070
	 */
4071
	protected $mysql;
4072

    
4073
	/**
4074
	 * Options
4075
	 *
4076
	 * @var array
4077
	 */
4078
	protected $options;
4079

    
4080
	/**
4081
	 * Cache ID
4082
	 *
4083
	 * @var string
4084
	 */
4085
	protected $id;
4086

    
4087
	/**
4088
	 * Create a new cache object
4089
	 *
4090
	 * @param string $location Location string (from SimplePie::$cache_location)
4091
	 * @param string $name Unique ID for the cache
4092
	 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
4093
	 */
4094
	public function __construct($location, $name, $type)
4095
	{
4096
		$this->options = array(
4097
			'user' => null,
4098
			'pass' => null,
4099
			'host' => '127.0.0.1',
4100
			'port' => '3306',
4101
			'path' => '',
4102
			'extras' => array(
4103
				'prefix' => '',
4104
				'cache_purge_time' => 2592000
4105
			),
4106
		);
4107
		
4108
		$this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
4109

    
4110
		// Path is prefixed with a "/"
4111
		$this->options['dbname'] = substr($this->options['path'], 1);
4112

    
4113
		try
4114
		{
4115
			$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'));
4116
		}
4117
		catch (PDOException $e)
4118
		{
4119
			$this->mysql = null;
4120
			return;
4121
		}
4122

    
4123
		$this->id = $name . $type;
4124

    
4125
		if (!$query = $this->mysql->query('SHOW TABLES'))
4126
		{
4127
			$this->mysql = null;
4128
			return;
4129
		}
4130

    
4131
		$db = array();
4132
		while ($row = $query->fetchColumn())
4133
		{
4134
			$db[] = $row;
4135
		}
4136

    
4137
		if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
4138
		{
4139
			$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)))');
4140
			if ($query === false)
4141
			{
4142
				trigger_error("Can't create " . $this->options['extras']['prefix'] . "cache_data table, check permissions", E_USER_WARNING);
4143
				$this->mysql = null;
4144
				return;
4145
			}
4146
		}
4147

    
4148
		if (!in_array($this->options['extras']['prefix'] . 'items', $db))
4149
		{
4150
			$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` MEDIUMBLOB NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
4151
			if ($query === false)
4152
			{
4153
				trigger_error("Can't create " . $this->options['extras']['prefix'] . "items table, check permissions", E_USER_WARNING);
4154
				$this->mysql = null;
4155
				return;
4156
			}
4157
		}
4158
	}
4159

    
4160
	/**
4161
	 * Save data to the cache
4162
	 *
4163
	 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
4164
	 * @return bool Successfulness
4165
	 */
4166
	public function save($data)
4167
	{
4168
		if ($this->mysql === null)
4169
		{
4170
			return false;
4171
		}
4172

    
4173
		$query = $this->mysql->prepare('DELETE i, cd FROM `' . $this->options['extras']['prefix'] . 'cache_data` cd, ' .
4174
			'`' . $this->options['extras']['prefix'] . 'items` i ' .
4175
			'WHERE cd.id = i.feed_id ' .
4176
			'AND cd.mtime < (unix_timestamp() - :purge_time)');
4177
		$query->bindValue(':purge_time', $this->options['extras']['cache_purge_time']);
4178

    
4179
		if (!$query->execute())
4180
		{
4181
			return false;
4182
		}
4183

    
4184
		if ($data instanceof SimplePie)
4185
		{
4186
			$data = clone $data;
4187

    
4188
			$prepared = self::prepare_simplepie_object_for_cache($data);
4189

    
4190
			$query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
4191
			$query->bindValue(':feed', $this->id);
4192
			if ($query->execute())
4193
			{
4194
				if ($query->fetchColumn() > 0)
4195
				{
4196
					$items = count($prepared[1]);
4197
					if ($items)
4198
					{
4199
						$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
4200
						$query = $this->mysql->prepare($sql);
4201
						$query->bindValue(':items', $items);
4202
					}
4203
					else
4204
					{
4205
						$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
4206
						$query = $this->mysql->prepare($sql);
4207
					}
4208

    
4209
					$query->bindValue(':data', $prepared[0]);
4210
					$query->bindValue(':time', time());
4211
					$query->bindValue(':feed', $this->id);
4212
					if (!$query->execute())
4213
					{
4214
						return false;
4215
					}
4216
				}
4217
				else
4218
				{
4219
					$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
4220
					$query->bindValue(':feed', $this->id);
4221
					$query->bindValue(':count', count($prepared[1]));
4222
					$query->bindValue(':data', $prepared[0]);
4223
					$query->bindValue(':time', time());
4224
					if (!$query->execute())
4225
					{
4226
						return false;
4227
					}
4228
				}
4229

    
4230
				$ids = array_keys($prepared[1]);
4231
				if (!empty($ids))
4232
				{
4233
					foreach ($ids as $id)
4234
					{
4235
						$database_ids[] = $this->mysql->quote($id);
4236
					}
4237

    
4238
					$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
4239
					$query->bindValue(':feed', $this->id);
4240

    
4241
					if ($query->execute())
4242
					{
4243
						$existing_ids = array();
4244
						while ($row = $query->fetchColumn())
4245
						{
4246
							$existing_ids[] = $row;
4247
						}
4248

    
4249
						$new_ids = array_diff($ids, $existing_ids);
4250

    
4251
						foreach ($new_ids as $new_id)
4252
						{
4253
							if (!($date = $prepared[1][$new_id]->get_date('U')))
4254
							{
4255
								$date = time();
4256
							}
4257

    
4258
							$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
4259
							$query->bindValue(':feed', $this->id);
4260
							$query->bindValue(':id', $new_id);
4261
							$query->bindValue(':data', serialize($prepared[1][$new_id]->data));
4262
							$query->bindValue(':date', $date);
4263
							if (!$query->execute())
4264
							{
4265
								return false;
4266
							}
4267
						}
4268
						return true;
4269
					}
4270
				}
4271
				else
4272
				{
4273
					return true;
4274
				}
4275
			}
4276
		}
4277
		else
4278
		{
4279
			$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
4280
			$query->bindValue(':feed', $this->id);
4281
			if ($query->execute())
4282
			{
4283
				if ($query->rowCount() > 0)
4284
				{
4285
					$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
4286
					$query->bindValue(':data', serialize($data));
4287
					$query->bindValue(':time', time());
4288
					$query->bindValue(':feed', $this->id);
4289
					if ($this->execute())
4290
					{
4291
						return true;
4292
					}
4293
				}
4294
				else
4295
				{
4296
					$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
4297
					$query->bindValue(':id', $this->id);
4298
					$query->bindValue(':data', serialize($data));
4299
					$query->bindValue(':time', time());
4300
					if ($query->execute())
4301
					{
4302
						return true;
4303
					}
4304
				}
4305
			}
4306
		}
4307
		return false;
4308
	}
4309

    
4310
	/**
4311
	 * Retrieve the data saved to the cache
4312
	 *
4313
	 * @return array Data for SimplePie::$data
4314
	 */
4315
	public function load()
4316
	{
4317
		if ($this->mysql === null)
4318
		{
4319
			return false;
4320
		}
4321

    
4322
		$query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
4323
		$query->bindValue(':id', $this->id);
4324
		if ($query->execute() && ($row = $query->fetch()))
4325
		{
4326
			$data = unserialize($row[1]);
4327

    
4328
			if (isset($this->options['items'][0]))
4329
			{
4330
				$items = (int) $this->options['items'][0];
4331
			}
4332
			else
4333
			{
4334
				$items = (int) $row[0];
4335
			}
4336

    
4337
			if ($items !== 0)
4338
			{
4339
				if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
4340
				{
4341
					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
4342
				}
4343
				elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
4344
				{
4345
					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
4346
				}
4347
				elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
4348
				{
4349
					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
4350
				}
4351
				elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
4352
				{
4353
					$feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
4354
				}
4355
				else
4356
				{
4357
					$feed = null;
4358
				}
4359

    
4360
				if ($feed !== null)
4361
				{
4362
					$sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
4363
					if ($items > 0)
4364
					{
4365
						$sql .= ' LIMIT ' . $items;
4366
					}
4367

    
4368
					$query = $this->mysql->prepare($sql);
4369
					$query->bindValue(':feed', $this->id);
4370
					if ($query->execute())
4371
					{
4372
						while ($row = $query->fetchColumn())
4373
						{
4374
							$feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
4375
						}
4376
					}
4377
					else
4378
					{
4379
						return false;
4380
					}
4381
				}
4382
			}
4383
			return $data;
4384
		}
4385
		return false;
4386
	}
4387

    
4388
	/**
4389
	 * Retrieve the last modified time for the cache
4390
	 *
4391
	 * @return int Timestamp
4392
	 */
4393
	public function mtime()
4394
	{
4395
		if ($this->mysql === null)
4396
		{
4397
			return false;
4398
		}
4399

    
4400
		$query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
4401
		$query->bindValue(':id', $this->id);
4402
		if ($query->execute() && ($time = $query->fetchColumn()))
4403
		{
4404
			return $time;
4405
		}
4406
		else
4407
		{
4408
			return false;
4409
		}
4410
	}
4411

    
4412
	/**
4413
	 * Set the last modified time to the current time
4414
	 *
4415
	 * @return bool Success status
4416
	 */
4417
	public function touch()
4418
	{
4419
		if ($this->mysql === null)
4420
		{
4421
			return false;
4422
		}
4423

    
4424
		$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
4425
		$query->bindValue(':time', time());
4426
		$query->bindValue(':id', $this->id);
4427
		if ($query->execute() && $query->rowCount() > 0)
4428
		{
4429
			return true;
4430
		}
4431
		else
4432
		{
4433
			return false;
4434
		}
4435
	}
4436

    
4437
	/**
4438
	 * Remove the cache
4439
	 *
4440
	 * @return bool Success status
4441
	 */
4442
	public function unlink()
4443
	{
4444
		if ($this->mysql === null)
4445
		{
4446
			return false;
4447
		}
4448

    
4449
		$query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
4450
		$query->bindValue(':id', $this->id);
4451
		$query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
4452
		$query2->bindValue(':id', $this->id);
4453
		if ($query->execute() && $query2->execute())
4454
		{
4455
			return true;
4456
		}
4457
		else
4458
		{
4459
			return false;
4460
		}
4461
	}
4462
}
4463

    
4464
/**
4465
 * Caches data to redis
4466
 *
4467
 * Registered for URLs with the "redis" protocol
4468
 *
4469
 * For example, `redis://localhost:6379/?timeout=3600&prefix=sp_&dbIndex=0` will
4470
 * connect to redis on `localhost` on port 6379. All tables will be
4471
 * prefixed with `simple_primary-` and data will expire after 3600 seconds
4472
 *
4473
 * @package SimplePie
4474
 * @subpackage Caching
4475
 * @uses Redis
4476
 */
4477
class SimplePie_Cache_Redis implements SimplePie_Cache_Base {
4478
    /**
4479
     * Redis instance
4480
     *
4481
     * @var \Redis
4482
     */
4483
    protected $cache;
4484

    
4485
    /**
4486
     * Options
4487
     *
4488
     * @var array
4489
     */
4490
    protected $options;
4491

    
4492
    /**
4493
     * Cache name
4494
     *
4495
     * @var string
4496
     */
4497
    protected $name;
4498

    
4499
    /**
4500
     * Cache Data
4501
     *
4502
     * @var type
4503
     */
4504
    protected $data;
4505

    
4506
    /**
4507
     * Create a new cache object
4508
     *
4509
     * @param string $location Location string (from SimplePie::$cache_location)
4510
     * @param string $name Unique ID for the cache
4511
     * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
4512
     */
4513
    public function __construct($location, $name, $options = null) {
4514
        //$this->cache = \flow\simple\cache\Redis::getRedisClientInstance();
4515
        $parsed = SimplePie_Cache::parse_URL($location);
4516
        $redis = new Redis();
4517
        $redis->connect($parsed['host'], $parsed['port']);
4518
        $this->cache = $redis;
4519

    
4520
        if (!is_null($options) && is_array($options)) {
4521
            $this->options = $options;
4522
        } else {
4523
            $this->options = array (
4524
                'prefix' => 'rss:simple_primary:',
4525
                'expire' => 0,
4526
            );
4527
        }
4528

    
4529
        $this->name = $this->options['prefix'] . $name;
4530
    }
4531

    
4532
    /**
4533
     * @param \Redis $cache
4534
     */
4535
    public function setRedisClient(\Redis $cache) {
4536
        $this->cache = $cache;
4537
    }
4538

    
4539
    /**
4540
     * Save data to the cache
4541
     *
4542
     * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
4543
     * @return bool Successfulness
4544
     */
4545
    public function save($data) {
4546
        if ($data instanceof SimplePie) {
4547
            $data = $data->data;
4548
        }
4549
        $response = $this->cache->set($this->name, serialize($data));
4550
        if ($this->options['expire']) {
4551
            $this->cache->expire($this->name, $this->options['expire']);
4552
        }
4553

    
4554
        return $response;
4555
    }
4556

    
4557
    /**
4558
     * Retrieve the data saved to the cache
4559
     *
4560
     * @return array Data for SimplePie::$data
4561
     */
4562
    public function load() {
4563
        $data = $this->cache->get($this->name);
4564

    
4565
        if ($data !== false) {
4566
            return unserialize($data);
4567
        }
4568
        return false;
4569
    }
4570

    
4571
    /**
4572
     * Retrieve the last modified time for the cache
4573
     *
4574
     * @return int Timestamp
4575
     */
4576
    public function mtime() {
4577

    
4578
        $data = $this->cache->get($this->name);
4579

    
4580
        if ($data !== false) {
4581
            return time();
4582
        }
4583

    
4584
        return false;
4585
    }
4586

    
4587
    /**
4588
     * Set the last modified time to the current time
4589
     *
4590
     * @return bool Success status
4591
     */
4592
    public function touch() {
4593

    
4594
        $data = $this->cache->get($this->name);
4595

    
4596
        if ($data !== false) {
4597
            $return = $this->cache->set($this->name, $data);
4598
            if ($this->options['expire']) {
4599
                return $this->cache->expire($this->name, $this->ttl);
4600
            }
4601
            return $return;
4602
        }
4603

    
4604
        return false;
4605
    }
4606

    
4607
    /**
4608
     * Remove the cache
4609
     *
4610
     * @return bool Success status
4611
     */
4612
    public function unlink() {
4613
        return $this->cache->set($this->name, null);
4614
    }
4615

    
4616
}
4617

    
4618
/**
4619
 * Handles `<media:text>` captions as defined in Media RSS.
4620
 *
4621
 * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()}
4622
 *
4623
 * This class can be overloaded with {@see SimplePie::set_caption_class()}
4624
 *
4625
 * @package SimplePie
4626
 * @subpackage API
4627
 */
4628
class SimplePie_Caption
4629
{
4630
	/**
4631
	 * Content type
4632
	 *
4633
	 * @var string
4634
	 * @see get_type()
4635
	 */
4636
	var $type;
4637

    
4638
	/**
4639
	 * Language
4640
	 *
4641
	 * @var string
4642
	 * @see get_language()
4643
	 */
4644
	var $lang;
4645

    
4646
	/**
4647
	 * Start time
4648
	 *
4649
	 * @var string
4650
	 * @see get_starttime()
4651
	 */
4652
	var $startTime;
4653

    
4654
	/**
4655
	 * End time
4656
	 *
4657
	 * @var string
4658
	 * @see get_endtime()
4659
	 */
4660
	var $endTime;
4661

    
4662
	/**
4663
	 * Caption text
4664
	 *
4665
	 * @var string
4666
	 * @see get_text()
4667
	 */
4668
	var $text;
4669

    
4670
	/**
4671
	 * Constructor, used to input the data
4672
	 *
4673
	 * For documentation on all the parameters, see the corresponding
4674
	 * properties and their accessors
4675
	 */
4676
	public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
4677
	{
4678
		$this->type = $type;
4679
		$this->lang = $lang;
4680
		$this->startTime = $startTime;
4681
		$this->endTime = $endTime;
4682
		$this->text = $text;
4683
	}
4684

    
4685
	/**
4686
	 * String-ified version
4687
	 *
4688
	 * @return string
4689
	 */
4690
	public function __toString()
4691
	{
4692
		// There is no $this->data here
4693
		return md5(serialize($this));
4694
	}
4695

    
4696
	/**
4697
	 * Get the end time
4698
	 *
4699
	 * @return string|null Time in the format 'hh:mm:ss.SSS'
4700
	 */
4701
	public function get_endtime()
4702
	{
4703
		if ($this->endTime !== null)
4704
		{
4705
			return $this->endTime;
4706
		}
4707
		else
4708
		{
4709
			return null;
4710
		}
4711
	}
4712

    
4713
	/**
4714
	 * Get the language
4715
	 *
4716
	 * @link http://tools.ietf.org/html/rfc3066
4717
	 * @return string|null Language code as per RFC 3066
4718
	 */
4719
	public function get_language()
4720
	{
4721
		if ($this->lang !== null)
4722
		{
4723
			return $this->lang;
4724
		}
4725
		else
4726
		{
4727
			return null;
4728
		}
4729
	}
4730

    
4731
	/**
4732
	 * Get the start time
4733
	 *
4734
	 * @return string|null Time in the format 'hh:mm:ss.SSS'
4735
	 */
4736
	public function get_starttime()
4737
	{
4738
		if ($this->startTime !== null)
4739
		{
4740
			return $this->startTime;
4741
		}
4742
		else
4743
		{
4744
			return null;
4745
		}
4746
	}
4747

    
4748
	/**
4749
	 * Get the text of the caption
4750
	 *
4751
	 * @return string|null
4752
	 */
4753
	public function get_text()
4754
	{
4755
		if ($this->text !== null)
4756
		{
4757
			return $this->text;
4758
		}
4759
		else
4760
		{
4761
			return null;
4762
		}
4763
	}
4764

    
4765
	/**
4766
	 * Get the content type (not MIME type)
4767
	 *
4768
	 * @return string|null Either 'text' or 'html'
4769
	 */
4770
	public function get_type()
4771
	{
4772
		if ($this->type !== null)
4773
		{
4774
			return $this->type;
4775
		}
4776
		else
4777
		{
4778
			return null;
4779
		}
4780
	}
4781
}
4782

    
4783
/**
4784
 * Manages all category-related data
4785
 *
4786
 * Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()}
4787
 *
4788
 * This class can be overloaded with {@see SimplePie::set_category_class()}
4789
 *
4790
 * @package SimplePie
4791
 * @subpackage API
4792
 */
4793
class SimplePie_Category
4794
{
4795
	/**
4796
	 * Category identifier
4797
	 *
4798
	 * @var string
4799
	 * @see get_term
4800
	 */
4801
	var $term;
4802

    
4803
	/**
4804
	 * Categorization scheme identifier
4805
	 *
4806
	 * @var string
4807
	 * @see get_scheme()
4808
	 */
4809
	var $scheme;
4810

    
4811
	/**
4812
	 * Human readable label
4813
	 *
4814
	 * @var string
4815
	 * @see get_label()
4816
	 */
4817
	var $label;
4818

    
4819
	/**
4820
	 * Constructor, used to input the data
4821
	 *
4822
	 * @param string $term
4823
	 * @param string $scheme
4824
	 * @param string $label
4825
	 */
4826
	public function __construct($term = null, $scheme = null, $label = null)
4827
	{
4828
		$this->term = $term;
4829
		$this->scheme = $scheme;
4830
		$this->label = $label;
4831
	}
4832

    
4833
	/**
4834
	 * String-ified version
4835
	 *
4836
	 * @return string
4837
	 */
4838
	public function __toString()
4839
	{
4840
		// There is no $this->data here
4841
		return md5(serialize($this));
4842
	}
4843

    
4844
	/**
4845
	 * Get the category identifier
4846
	 *
4847
	 * @return string|null
4848
	 */
4849
	public function get_term()
4850
	{
4851
		if ($this->term !== null)
4852
		{
4853
			return $this->term;
4854
		}
4855
		else
4856
		{
4857
			return null;
4858
		}
4859
	}
4860

    
4861
	/**
4862
	 * Get the categorization scheme identifier
4863
	 *
4864
	 * @return string|null
4865
	 */
4866
	public function get_scheme()
4867
	{
4868
		if ($this->scheme !== null)
4869
		{
4870
			return $this->scheme;
4871
		}
4872
		else
4873
		{
4874
			return null;
4875
		}
4876
	}
4877

    
4878
	/**
4879
	 * Get the human readable label
4880
	 *
4881
	 * @return string|null
4882
	 */
4883
	public function get_label()
4884
	{
4885
		if ($this->label !== null)
4886
		{
4887
			return $this->label;
4888
		}
4889
		else
4890
		{
4891
			return $this->get_term();
4892
		}
4893
	}
4894
}
4895

    
4896
/**
4897
 * Content-type sniffing
4898
 *
4899
 * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06
4900
 *
4901
 * This is used since we can't always trust Content-Type headers, and is based
4902
 * upon the HTML5 parsing rules.
4903
 *
4904
 *
4905
 * This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()}
4906
 *
4907
 * @package SimplePie
4908
 * @subpackage HTTP
4909
 */
4910
class SimplePie_Content_Type_Sniffer
4911
{
4912
	/**
4913
	 * File object
4914
	 *
4915
	 * @var SimplePie_File
4916
	 */
4917
	var $file;
4918

    
4919
	/**
4920
	 * Create an instance of the class with the input file
4921
	 *
4922
	 * @param SimplePie_Content_Type_Sniffer $file Input file
4923
	 */
4924
	public function __construct($file)
4925
	{
4926
		$this->file = $file;
4927
	}
4928

    
4929
	/**
4930
	 * Get the Content-Type of the specified file
4931
	 *
4932
	 * @return string Actual Content-Type
4933
	 */
4934
	public function get_type()
4935
	{
4936
		if (isset($this->file->headers['content-type']))
4937
		{
4938
			if (!isset($this->file->headers['content-encoding'])
4939
				&& ($this->file->headers['content-type'] === 'text/plain'
4940
					|| $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
4941
					|| $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
4942
					|| $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
4943
			{
4944
				return $this->text_or_binary();
4945
			}
4946

    
4947
			if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
4948
			{
4949
				$official = substr($this->file->headers['content-type'], 0, $pos);
4950
			}
4951
			else
4952
			{
4953
				$official = $this->file->headers['content-type'];
4954
			}
4955
			$official = trim(strtolower($official));
4956

    
4957
			if ($official === 'unknown/unknown'
4958
				|| $official === 'application/unknown')
4959
			{
4960
				return $this->unknown();
4961
			}
4962
			elseif (substr($official, -4) === '+xml'
4963
				|| $official === 'text/xml'
4964
				|| $official === 'application/xml')
4965
			{
4966
				return $official;
4967
			}
4968
			elseif (substr($official, 0, 6) === 'image/')
4969
			{
4970
				if ($return = $this->image())
4971
				{
4972
					return $return;
4973
				}
4974
				else
4975
				{
4976
					return $official;
4977
				}
4978
			}
4979
			elseif ($official === 'text/html')
4980
			{
4981
				return $this->feed_or_html();
4982
			}
4983
			else
4984
			{
4985
				return $official;
4986
			}
4987
		}
4988
		else
4989
		{
4990
			return $this->unknown();
4991
		}
4992
	}
4993

    
4994
	/**
4995
	 * Sniff text or binary
4996
	 *
4997
	 * @return string Actual Content-Type
4998
	 */
4999
	public function text_or_binary()
5000
	{
5001
		if (substr($this->file->body, 0, 2) === "\xFE\xFF"
5002
			|| substr($this->file->body, 0, 2) === "\xFF\xFE"
5003
			|| substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
5004
			|| substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
5005
		{
5006
			return 'text/plain';
5007
		}
5008
		elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
5009
		{
5010
			return 'application/octect-stream';
5011
		}
5012
		else
5013
		{
5014
			return 'text/plain';
5015
		}
5016
	}
5017

    
5018
	/**
5019
	 * Sniff unknown
5020
	 *
5021
	 * @return string Actual Content-Type
5022
	 */
5023
	public function unknown()
5024
	{
5025
		$ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
5026
		if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
5027
			|| strtolower(substr($this->file->body, $ws, 5)) === '<html'
5028
			|| strtolower(substr($this->file->body, $ws, 7)) === '<script')
5029
		{
5030
			return 'text/html';
5031
		}
5032
		elseif (substr($this->file->body, 0, 5) === '%PDF-')
5033
		{
5034
			return 'application/pdf';
5035
		}
5036
		elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
5037
		{
5038
			return 'application/postscript';
5039
		}
5040
		elseif (substr($this->file->body, 0, 6) === 'GIF87a'
5041
			|| substr($this->file->body, 0, 6) === 'GIF89a')
5042
		{
5043
			return 'image/gif';
5044
		}
5045
		elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
5046
		{
5047
			return 'image/png';
5048
		}
5049
		elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
5050
		{
5051
			return 'image/jpeg';
5052
		}
5053
		elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
5054
		{
5055
			return 'image/bmp';
5056
		}
5057
		elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
5058
		{
5059
			return 'image/vnd.microsoft.icon';
5060
		}
5061
		else
5062
		{
5063
			return $this->text_or_binary();
5064
		}
5065
	}
5066

    
5067
	/**
5068
	 * Sniff images
5069
	 *
5070
	 * @return string Actual Content-Type
5071
	 */
5072
	public function image()
5073
	{
5074
		if (substr($this->file->body, 0, 6) === 'GIF87a'
5075
			|| substr($this->file->body, 0, 6) === 'GIF89a')
5076
		{
5077
			return 'image/gif';
5078
		}
5079
		elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
5080
		{
5081
			return 'image/png';
5082
		}
5083
		elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
5084
		{
5085
			return 'image/jpeg';
5086
		}
5087
		elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
5088
		{
5089
			return 'image/bmp';
5090
		}
5091
		elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
5092
		{
5093
			return 'image/vnd.microsoft.icon';
5094
		}
5095
		else
5096
		{
5097
			return false;
5098
		}
5099
	}
5100

    
5101
	/**
5102
	 * Sniff HTML
5103
	 *
5104
	 * @return string Actual Content-Type
5105
	 */
5106
	public function feed_or_html()
5107
	{
5108
		$len = strlen($this->file->body);
5109
		$pos = strspn($this->file->body, "\x09\x0A\x0D\x20\xEF\xBB\xBF");
5110

    
5111
		while ($pos < $len)
5112
		{
5113
			switch ($this->file->body[$pos])
5114
			{
5115
				case "\x09":
5116
				case "\x0A":
5117
				case "\x0D":
5118
				case "\x20":
5119
					$pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
5120
					continue 2;
5121

    
5122
				case '<':
5123
					$pos++;
5124
					break;
5125

    
5126
				default:
5127
					return 'text/html';
5128
			}
5129

    
5130
			if (substr($this->file->body, $pos, 3) === '!--')
5131
			{
5132
				$pos += 3;
5133
				if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
5134
				{
5135
					$pos += 3;
5136
				}
5137
				else
5138
				{
5139
					return 'text/html';
5140
				}
5141
			}
5142
			elseif (substr($this->file->body, $pos, 1) === '!')
5143
			{
5144
				if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
5145
				{
5146
					$pos++;
5147
				}
5148
				else
5149
				{
5150
					return 'text/html';
5151
				}
5152
			}
5153
			elseif (substr($this->file->body, $pos, 1) === '?')
5154
			{
5155
				if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
5156
				{
5157
					$pos += 2;
5158
				}
5159
				else
5160
				{
5161
					return 'text/html';
5162
				}
5163
			}
5164
			elseif (substr($this->file->body, $pos, 3) === 'rss'
5165
				|| substr($this->file->body, $pos, 7) === 'rdf:RDF')
5166
			{
5167
				return 'application/rss+xml';
5168
			}
5169
			elseif (substr($this->file->body, $pos, 4) === 'feed')
5170
			{
5171
				return 'application/atom+xml';
5172
			}
5173
			else
5174
			{
5175
				return 'text/html';
5176
			}
5177
		}
5178

    
5179
		return 'text/html';
5180
	}
5181
}
5182

    
5183
/**
5184
 * Manages `<media:copyright>` copyright tags as defined in Media RSS
5185
 *
5186
 * Used by {@see SimplePie_Enclosure::get_copyright()}
5187
 *
5188
 * This class can be overloaded with {@see SimplePie::set_copyright_class()}
5189
 *
5190
 * @package SimplePie
5191
 * @subpackage API
5192
 */
5193
class SimplePie_Copyright
5194
{
5195
	/**
5196
	 * Copyright URL
5197
	 *
5198
	 * @var string
5199
	 * @see get_url()
5200
	 */
5201
	var $url;
5202

    
5203
	/**
5204
	 * Attribution
5205
	 *
5206
	 * @var string
5207
	 * @see get_attribution()
5208
	 */
5209
	var $label;
5210

    
5211
	/**
5212
	 * Constructor, used to input the data
5213
	 *
5214
	 * For documentation on all the parameters, see the corresponding
5215
	 * properties and their accessors
5216
	 */
5217
	public function __construct($url = null, $label = null)
5218
	{
5219
		$this->url = $url;
5220
		$this->label = $label;
5221
	}
5222

    
5223
	/**
5224
	 * String-ified version
5225
	 *
5226
	 * @return string
5227
	 */
5228
	public function __toString()
5229
	{
5230
		// There is no $this->data here
5231
		return md5(serialize($this));
5232
	}
5233

    
5234
	/**
5235
	 * Get the copyright URL
5236
	 *
5237
	 * @return string|null URL to copyright information
5238
	 */
5239
	public function get_url()
5240
	{
5241
		if ($this->url !== null)
5242
		{
5243
			return $this->url;
5244
		}
5245
		else
5246
		{
5247
			return null;
5248
		}
5249
	}
5250

    
5251
	/**
5252
	 * Get the attribution text
5253
	 *
5254
	 * @return string|null
5255
	 */
5256
	public function get_attribution()
5257
	{
5258
		if ($this->label !== null)
5259
		{
5260
			return $this->label;
5261
		}
5262
		else
5263
		{
5264
			return null;
5265
		}
5266
	}
5267
}
5268

    
5269
/**
5270
 * SimplePie class.
5271
 *
5272
 * Class for backward compatibility.
5273
 *
5274
 * @deprecated Use {@see SimplePie} directly
5275
 * @package SimplePie
5276
 * @subpackage API
5277
 */
5278
class SimplePie_Core extends SimplePie
5279
{
5280

    
5281
}
5282

    
5283
/**
5284
 * Handles `<media:credit>` as defined in Media RSS
5285
 *
5286
 * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()}
5287
 *
5288
 * This class can be overloaded with {@see SimplePie::set_credit_class()}
5289
 *
5290
 * @package SimplePie
5291
 * @subpackage API
5292
 */
5293
class SimplePie_Credit
5294
{
5295
	/**
5296
	 * Credited role
5297
	 *
5298
	 * @var string
5299
	 * @see get_role()
5300
	 */
5301
	var $role;
5302

    
5303
	/**
5304
	 * Organizational scheme
5305
	 *
5306
	 * @var string
5307
	 * @see get_scheme()
5308
	 */
5309
	var $scheme;
5310

    
5311
	/**
5312
	 * Credited name
5313
	 *
5314
	 * @var string
5315
	 * @see get_name()
5316
	 */
5317
	var $name;
5318

    
5319
	/**
5320
	 * Constructor, used to input the data
5321
	 *
5322
	 * For documentation on all the parameters, see the corresponding
5323
	 * properties and their accessors
5324
	 */
5325
	public function __construct($role = null, $scheme = null, $name = null)
5326
	{
5327
		$this->role = $role;
5328
		$this->scheme = $scheme;
5329
		$this->name = $name;
5330
	}
5331

    
5332
	/**
5333
	 * String-ified version
5334
	 *
5335
	 * @return string
5336
	 */
5337
	public function __toString()
5338
	{
5339
		// There is no $this->data here
5340
		return md5(serialize($this));
5341
	}
5342

    
5343
	/**
5344
	 * Get the role of the person receiving credit
5345
	 *
5346
	 * @return string|null
5347
	 */
5348
	public function get_role()
5349
	{
5350
		if ($this->role !== null)
5351
		{
5352
			return $this->role;
5353
		}
5354
		else
5355
		{
5356
			return null;
5357
		}
5358
	}
5359

    
5360
	/**
5361
	 * Get the organizational scheme
5362
	 *
5363
	 * @return string|null
5364
	 */
5365
	public function get_scheme()
5366
	{
5367
		if ($this->scheme !== null)
5368
		{
5369
			return $this->scheme;
5370
		}
5371
		else
5372
		{
5373
			return null;
5374
		}
5375
	}
5376

    
5377
	/**
5378
	 * Get the credited person/entity's name
5379
	 *
5380
	 * @return string|null
5381
	 */
5382
	public function get_name()
5383
	{
5384
		if ($this->name !== null)
5385
		{
5386
			return $this->name;
5387
		}
5388
		else
5389
		{
5390
			return null;
5391
		}
5392
	}
5393
}
5394

    
5395
/**
5396
 * Decode HTML Entities
5397
 *
5398
 * This implements HTML5 as of revision 967 (2007-06-28)
5399
 *
5400
 * @deprecated Use DOMDocument instead!
5401
 * @package SimplePie
5402
 */
5403
class SimplePie_Decode_HTML_Entities
5404
{
5405
	/**
5406
	 * Data to be parsed
5407
	 *
5408
	 * @access private
5409
	 * @var string
5410
	 */
5411
	var $data = '';
5412

    
5413
	/**
5414
	 * Currently consumed bytes
5415
	 *
5416
	 * @access private
5417
	 * @var string
5418
	 */
5419
	var $consumed = '';
5420

    
5421
	/**
5422
	 * Position of the current byte being parsed
5423
	 *
5424
	 * @access private
5425
	 * @var int
5426
	 */
5427
	var $position = 0;
5428

    
5429
	/**
5430
	 * Create an instance of the class with the input data
5431
	 *
5432
	 * @access public
5433
	 * @param string $data Input data
5434
	 */
5435
	public function __construct($data)
5436
	{
5437
		$this->data = $data;
5438
	}
5439

    
5440
	/**
5441
	 * Parse the input data
5442
	 *
5443
	 * @access public
5444
	 * @return string Output data
5445
	 */
5446
	public function parse()
5447
	{
5448
		while (($this->position = strpos($this->data, '&', $this->position)) !== false)
5449
		{
5450
			$this->consume();
5451
			$this->entity();
5452
			$this->consumed = '';
5453
		}
5454
		return $this->data;
5455
	}
5456

    
5457
	/**
5458
	 * Consume the next byte
5459
	 *
5460
	 * @access private
5461
	 * @return mixed The next byte, or false, if there is no more data
5462
	 */
5463
	public function consume()
5464
	{
5465
		if (isset($this->data[$this->position]))
5466
		{
5467
			$this->consumed .= $this->data[$this->position];
5468
			return $this->data[$this->position++];
5469
		}
5470
		else
5471
		{
5472
			return false;
5473
		}
5474
	}
5475

    
5476
	/**
5477
	 * Consume a range of characters
5478
	 *
5479
	 * @access private
5480
	 * @param string $chars Characters to consume
5481
	 * @return mixed A series of characters that match the range, or false
5482
	 */
5483
	public function consume_range($chars)
5484
	{
5485
		if ($len = strspn($this->data, $chars, $this->position))
5486
		{
5487
			$data = substr($this->data, $this->position, $len);
5488
			$this->consumed .= $data;
5489
			$this->position += $len;
5490
			return $data;
5491
		}
5492
		else
5493
		{
5494
			return false;
5495
		}
5496
	}
5497

    
5498
	/**
5499
	 * Unconsume one byte
5500
	 *
5501
	 * @access private
5502
	 */
5503
	public function unconsume()
5504
	{
5505
		$this->consumed = substr($this->consumed, 0, -1);
5506
		$this->position--;
5507
	}
5508

    
5509
	/**
5510
	 * Decode an entity
5511
	 *
5512
	 * @access private
5513
	 */
5514
	public function entity()
5515
	{
5516
		switch ($this->consume())
5517
		{
5518
			case "\x09":
5519
			case "\x0A":
5520
			case "\x0B":
5521
			case "\x0C":
5522
			case "\x20":
5523
			case "\x3C":
5524
			case "\x26":
5525
			case false:
5526
				break;
5527

    
5528
			case "\x23":
5529
				switch ($this->consume())
5530
				{
5531
					case "\x78":
5532
					case "\x58":
5533
						$range = '0123456789ABCDEFabcdef';
5534
						$hex = true;
5535
						break;
5536

    
5537
					default:
5538
						$range = '0123456789';
5539
						$hex = false;
5540
						$this->unconsume();
5541
						break;
5542
				}
5543

    
5544
				if ($codepoint = $this->consume_range($range))
5545
				{
5546
					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");
5547

    
5548
					if ($hex)
5549
					{
5550
						$codepoint = hexdec($codepoint);
5551
					}
5552
					else
5553
					{
5554
						$codepoint = intval($codepoint);
5555
					}
5556

    
5557
					if (isset($windows_1252_specials[$codepoint]))
5558
					{
5559
						$replacement = $windows_1252_specials[$codepoint];
5560
					}
5561
					else
5562
					{
5563
						$replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
5564
					}
5565

    
5566
					if (!in_array($this->consume(), array(';', false), true))
5567
					{
5568
						$this->unconsume();
5569
					}
5570

    
5571
					$consumed_length = strlen($this->consumed);
5572
					$this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
5573
					$this->position += strlen($replacement) - $consumed_length;
5574
				}
5575
				break;
5576

    
5577
			default:
5578
				static $entities = array(
5579
					'Aacute' => "\xC3\x81",
5580
					'aacute' => "\xC3\xA1",
5581
					'Aacute;' => "\xC3\x81",
5582
					'aacute;' => "\xC3\xA1",
5583
					'Acirc' => "\xC3\x82",
5584
					'acirc' => "\xC3\xA2",
5585
					'Acirc;' => "\xC3\x82",
5586
					'acirc;' => "\xC3\xA2",
5587
					'acute' => "\xC2\xB4",
5588
					'acute;' => "\xC2\xB4",
5589
					'AElig' => "\xC3\x86",
5590
					'aelig' => "\xC3\xA6",
5591
					'AElig;' => "\xC3\x86",
5592
					'aelig;' => "\xC3\xA6",
5593
					'Agrave' => "\xC3\x80",
5594
					'agrave' => "\xC3\xA0",
5595
					'Agrave;' => "\xC3\x80",
5596
					'agrave;' => "\xC3\xA0",
5597
					'alefsym;' => "\xE2\x84\xB5",
5598
					'Alpha;' => "\xCE\x91",
5599
					'alpha;' => "\xCE\xB1",
5600
					'AMP' => "\x26",
5601
					'amp' => "\x26",
5602
					'AMP;' => "\x26",
5603
					'amp;' => "\x26",
5604
					'and;' => "\xE2\x88\xA7",
5605
					'ang;' => "\xE2\x88\xA0",
5606
					'apos;' => "\x27",
5607
					'Aring' => "\xC3\x85",
5608
					'aring' => "\xC3\xA5",
5609
					'Aring;' => "\xC3\x85",
5610
					'aring;' => "\xC3\xA5",
5611
					'asymp;' => "\xE2\x89\x88",
5612
					'Atilde' => "\xC3\x83",
5613
					'atilde' => "\xC3\xA3",
5614
					'Atilde;' => "\xC3\x83",
5615
					'atilde;' => "\xC3\xA3",
5616
					'Auml' => "\xC3\x84",
5617
					'auml' => "\xC3\xA4",
5618
					'Auml;' => "\xC3\x84",
5619
					'auml;' => "\xC3\xA4",
5620
					'bdquo;' => "\xE2\x80\x9E",
5621
					'Beta;' => "\xCE\x92",
5622
					'beta;' => "\xCE\xB2",
5623
					'brvbar' => "\xC2\xA6",
5624
					'brvbar;' => "\xC2\xA6",
5625
					'bull;' => "\xE2\x80\xA2",
5626
					'cap;' => "\xE2\x88\xA9",
5627
					'Ccedil' => "\xC3\x87",
5628
					'ccedil' => "\xC3\xA7",
5629
					'Ccedil;' => "\xC3\x87",
5630
					'ccedil;' => "\xC3\xA7",
5631
					'cedil' => "\xC2\xB8",
5632
					'cedil;' => "\xC2\xB8",
5633
					'cent' => "\xC2\xA2",
5634
					'cent;' => "\xC2\xA2",
5635
					'Chi;' => "\xCE\xA7",
5636
					'chi;' => "\xCF\x87",
5637
					'circ;' => "\xCB\x86",
5638
					'clubs;' => "\xE2\x99\xA3",
5639
					'cong;' => "\xE2\x89\x85",
5640
					'COPY' => "\xC2\xA9",
5641
					'copy' => "\xC2\xA9",
5642
					'COPY;' => "\xC2\xA9",
5643
					'copy;' => "\xC2\xA9",
5644
					'crarr;' => "\xE2\x86\xB5",
5645
					'cup;' => "\xE2\x88\xAA",
5646
					'curren' => "\xC2\xA4",
5647
					'curren;' => "\xC2\xA4",
5648
					'Dagger;' => "\xE2\x80\xA1",
5649
					'dagger;' => "\xE2\x80\xA0",
5650
					'dArr;' => "\xE2\x87\x93",
5651
					'darr;' => "\xE2\x86\x93",
5652
					'deg' => "\xC2\xB0",
5653
					'deg;' => "\xC2\xB0",
5654
					'Delta;' => "\xCE\x94",
5655
					'delta;' => "\xCE\xB4",
5656
					'diams;' => "\xE2\x99\xA6",
5657
					'divide' => "\xC3\xB7",
5658
					'divide;' => "\xC3\xB7",
5659
					'Eacute' => "\xC3\x89",
5660
					'eacute' => "\xC3\xA9",
5661
					'Eacute;' => "\xC3\x89",
5662
					'eacute;' => "\xC3\xA9",
5663
					'Ecirc' => "\xC3\x8A",
5664
					'ecirc' => "\xC3\xAA",
5665
					'Ecirc;' => "\xC3\x8A",
5666
					'ecirc;' => "\xC3\xAA",
5667
					'Egrave' => "\xC3\x88",
5668
					'egrave' => "\xC3\xA8",
5669
					'Egrave;' => "\xC3\x88",
5670
					'egrave;' => "\xC3\xA8",
5671
					'empty;' => "\xE2\x88\x85",
5672
					'emsp;' => "\xE2\x80\x83",
5673
					'ensp;' => "\xE2\x80\x82",
5674
					'Epsilon;' => "\xCE\x95",
5675
					'epsilon;' => "\xCE\xB5",
5676
					'equiv;' => "\xE2\x89\xA1",
5677
					'Eta;' => "\xCE\x97",
5678
					'eta;' => "\xCE\xB7",
5679
					'ETH' => "\xC3\x90",
5680
					'eth' => "\xC3\xB0",
5681
					'ETH;' => "\xC3\x90",
5682
					'eth;' => "\xC3\xB0",
5683
					'Euml' => "\xC3\x8B",
5684
					'euml' => "\xC3\xAB",
5685
					'Euml;' => "\xC3\x8B",
5686
					'euml;' => "\xC3\xAB",
5687
					'euro;' => "\xE2\x82\xAC",
5688
					'exist;' => "\xE2\x88\x83",
5689
					'fnof;' => "\xC6\x92",
5690
					'forall;' => "\xE2\x88\x80",
5691
					'frac12' => "\xC2\xBD",
5692
					'frac12;' => "\xC2\xBD",
5693
					'frac14' => "\xC2\xBC",
5694
					'frac14;' => "\xC2\xBC",
5695
					'frac34' => "\xC2\xBE",
5696
					'frac34;' => "\xC2\xBE",
5697
					'frasl;' => "\xE2\x81\x84",
5698
					'Gamma;' => "\xCE\x93",
5699
					'gamma;' => "\xCE\xB3",
5700
					'ge;' => "\xE2\x89\xA5",
5701
					'GT' => "\x3E",
5702
					'gt' => "\x3E",
5703
					'GT;' => "\x3E",
5704
					'gt;' => "\x3E",
5705
					'hArr;' => "\xE2\x87\x94",
5706
					'harr;' => "\xE2\x86\x94",
5707
					'hearts;' => "\xE2\x99\xA5",
5708
					'hellip;' => "\xE2\x80\xA6",
5709
					'Iacute' => "\xC3\x8D",
5710
					'iacute' => "\xC3\xAD",
5711
					'Iacute;' => "\xC3\x8D",
5712
					'iacute;' => "\xC3\xAD",
5713
					'Icirc' => "\xC3\x8E",
5714
					'icirc' => "\xC3\xAE",
5715
					'Icirc;' => "\xC3\x8E",
5716
					'icirc;' => "\xC3\xAE",
5717
					'iexcl' => "\xC2\xA1",
5718
					'iexcl;' => "\xC2\xA1",
5719
					'Igrave' => "\xC3\x8C",
5720
					'igrave' => "\xC3\xAC",
5721
					'Igrave;' => "\xC3\x8C",
5722
					'igrave;' => "\xC3\xAC",
5723
					'image;' => "\xE2\x84\x91",
5724
					'infin;' => "\xE2\x88\x9E",
5725
					'int;' => "\xE2\x88\xAB",
5726
					'Iota;' => "\xCE\x99",
5727
					'iota;' => "\xCE\xB9",
5728
					'iquest' => "\xC2\xBF",
5729
					'iquest;' => "\xC2\xBF",
5730
					'isin;' => "\xE2\x88\x88",
5731
					'Iuml' => "\xC3\x8F",
5732
					'iuml' => "\xC3\xAF",
5733
					'Iuml;' => "\xC3\x8F",
5734
					'iuml;' => "\xC3\xAF",
5735
					'Kappa;' => "\xCE\x9A",
5736
					'kappa;' => "\xCE\xBA",
5737
					'Lambda;' => "\xCE\x9B",
5738
					'lambda;' => "\xCE\xBB",
5739
					'lang;' => "\xE3\x80\x88",
5740
					'laquo' => "\xC2\xAB",
5741
					'laquo;' => "\xC2\xAB",
5742
					'lArr;' => "\xE2\x87\x90",
5743
					'larr;' => "\xE2\x86\x90",
5744
					'lceil;' => "\xE2\x8C\x88",
5745
					'ldquo;' => "\xE2\x80\x9C",
5746
					'le;' => "\xE2\x89\xA4",
5747
					'lfloor;' => "\xE2\x8C\x8A",
5748
					'lowast;' => "\xE2\x88\x97",
5749
					'loz;' => "\xE2\x97\x8A",
5750
					'lrm;' => "\xE2\x80\x8E",
5751
					'lsaquo;' => "\xE2\x80\xB9",
5752
					'lsquo;' => "\xE2\x80\x98",
5753
					'LT' => "\x3C",
5754
					'lt' => "\x3C",
5755
					'LT;' => "\x3C",
5756
					'lt;' => "\x3C",
5757
					'macr' => "\xC2\xAF",
5758
					'macr;' => "\xC2\xAF",
5759
					'mdash;' => "\xE2\x80\x94",
5760
					'micro' => "\xC2\xB5",
5761
					'micro;' => "\xC2\xB5",
5762
					'middot' => "\xC2\xB7",
5763
					'middot;' => "\xC2\xB7",
5764
					'minus;' => "\xE2\x88\x92",
5765
					'Mu;' => "\xCE\x9C",
5766
					'mu;' => "\xCE\xBC",
5767
					'nabla;' => "\xE2\x88\x87",
5768
					'nbsp' => "\xC2\xA0",
5769
					'nbsp;' => "\xC2\xA0",
5770
					'ndash;' => "\xE2\x80\x93",
5771
					'ne;' => "\xE2\x89\xA0",
5772
					'ni;' => "\xE2\x88\x8B",
5773
					'not' => "\xC2\xAC",
5774
					'not;' => "\xC2\xAC",
5775
					'notin;' => "\xE2\x88\x89",
5776
					'nsub;' => "\xE2\x8A\x84",
5777
					'Ntilde' => "\xC3\x91",
5778
					'ntilde' => "\xC3\xB1",
5779
					'Ntilde;' => "\xC3\x91",
5780
					'ntilde;' => "\xC3\xB1",
5781
					'Nu;' => "\xCE\x9D",
5782
					'nu;' => "\xCE\xBD",
5783
					'Oacute' => "\xC3\x93",
5784
					'oacute' => "\xC3\xB3",
5785
					'Oacute;' => "\xC3\x93",
5786
					'oacute;' => "\xC3\xB3",
5787
					'Ocirc' => "\xC3\x94",
5788
					'ocirc' => "\xC3\xB4",
5789
					'Ocirc;' => "\xC3\x94",
5790
					'ocirc;' => "\xC3\xB4",
5791
					'OElig;' => "\xC5\x92",
5792
					'oelig;' => "\xC5\x93",
5793
					'Ograve' => "\xC3\x92",
5794
					'ograve' => "\xC3\xB2",
5795
					'Ograve;' => "\xC3\x92",
5796
					'ograve;' => "\xC3\xB2",
5797
					'oline;' => "\xE2\x80\xBE",
5798
					'Omega;' => "\xCE\xA9",
5799
					'omega;' => "\xCF\x89",
5800
					'Omicron;' => "\xCE\x9F",
5801
					'omicron;' => "\xCE\xBF",
5802
					'oplus;' => "\xE2\x8A\x95",
5803
					'or;' => "\xE2\x88\xA8",
5804
					'ordf' => "\xC2\xAA",
5805
					'ordf;' => "\xC2\xAA",
5806
					'ordm' => "\xC2\xBA",
5807
					'ordm;' => "\xC2\xBA",
5808
					'Oslash' => "\xC3\x98",
5809
					'oslash' => "\xC3\xB8",
5810
					'Oslash;' => "\xC3\x98",
5811
					'oslash;' => "\xC3\xB8",
5812
					'Otilde' => "\xC3\x95",
5813
					'otilde' => "\xC3\xB5",
5814
					'Otilde;' => "\xC3\x95",
5815
					'otilde;' => "\xC3\xB5",
5816
					'otimes;' => "\xE2\x8A\x97",
5817
					'Ouml' => "\xC3\x96",
5818
					'ouml' => "\xC3\xB6",
5819
					'Ouml;' => "\xC3\x96",
5820
					'ouml;' => "\xC3\xB6",
5821
					'para' => "\xC2\xB6",
5822
					'para;' => "\xC2\xB6",
5823
					'part;' => "\xE2\x88\x82",
5824
					'permil;' => "\xE2\x80\xB0",
5825
					'perp;' => "\xE2\x8A\xA5",
5826
					'Phi;' => "\xCE\xA6",
5827
					'phi;' => "\xCF\x86",
5828
					'Pi;' => "\xCE\xA0",
5829
					'pi;' => "\xCF\x80",
5830
					'piv;' => "\xCF\x96",
5831
					'plusmn' => "\xC2\xB1",
5832
					'plusmn;' => "\xC2\xB1",
5833
					'pound' => "\xC2\xA3",
5834
					'pound;' => "\xC2\xA3",
5835
					'Prime;' => "\xE2\x80\xB3",
5836
					'prime;' => "\xE2\x80\xB2",
5837
					'prod;' => "\xE2\x88\x8F",
5838
					'prop;' => "\xE2\x88\x9D",
5839
					'Psi;' => "\xCE\xA8",
5840
					'psi;' => "\xCF\x88",
5841
					'QUOT' => "\x22",
5842
					'quot' => "\x22",
5843
					'QUOT;' => "\x22",
5844
					'quot;' => "\x22",
5845
					'radic;' => "\xE2\x88\x9A",
5846
					'rang;' => "\xE3\x80\x89",
5847
					'raquo' => "\xC2\xBB",
5848
					'raquo;' => "\xC2\xBB",
5849
					'rArr;' => "\xE2\x87\x92",
5850
					'rarr;' => "\xE2\x86\x92",
5851
					'rceil;' => "\xE2\x8C\x89",
5852
					'rdquo;' => "\xE2\x80\x9D",
5853
					'real;' => "\xE2\x84\x9C",
5854
					'REG' => "\xC2\xAE",
5855
					'reg' => "\xC2\xAE",
5856
					'REG;' => "\xC2\xAE",
5857
					'reg;' => "\xC2\xAE",
5858
					'rfloor;' => "\xE2\x8C\x8B",
5859
					'Rho;' => "\xCE\xA1",
5860
					'rho;' => "\xCF\x81",
5861
					'rlm;' => "\xE2\x80\x8F",
5862
					'rsaquo;' => "\xE2\x80\xBA",
5863
					'rsquo;' => "\xE2\x80\x99",
5864
					'sbquo;' => "\xE2\x80\x9A",
5865
					'Scaron;' => "\xC5\xA0",
5866
					'scaron;' => "\xC5\xA1",
5867
					'sdot;' => "\xE2\x8B\x85",
5868
					'sect' => "\xC2\xA7",
5869
					'sect;' => "\xC2\xA7",
5870
					'shy' => "\xC2\xAD",
5871
					'shy;' => "\xC2\xAD",
5872
					'Sigma;' => "\xCE\xA3",
5873
					'sigma;' => "\xCF\x83",
5874
					'sigmaf;' => "\xCF\x82",
5875
					'sim;' => "\xE2\x88\xBC",
5876
					'spades;' => "\xE2\x99\xA0",
5877
					'sub;' => "\xE2\x8A\x82",
5878
					'sube;' => "\xE2\x8A\x86",
5879
					'sum;' => "\xE2\x88\x91",
5880
					'sup;' => "\xE2\x8A\x83",
5881
					'sup1' => "\xC2\xB9",
5882
					'sup1;' => "\xC2\xB9",
5883
					'sup2' => "\xC2\xB2",
5884
					'sup2;' => "\xC2\xB2",
5885
					'sup3' => "\xC2\xB3",
5886
					'sup3;' => "\xC2\xB3",
5887
					'supe;' => "\xE2\x8A\x87",
5888
					'szlig' => "\xC3\x9F",
5889
					'szlig;' => "\xC3\x9F",
5890
					'Tau;' => "\xCE\xA4",
5891
					'tau;' => "\xCF\x84",
5892
					'there4;' => "\xE2\x88\xB4",
5893
					'Theta;' => "\xCE\x98",
5894
					'theta;' => "\xCE\xB8",
5895
					'thetasym;' => "\xCF\x91",
5896
					'thinsp;' => "\xE2\x80\x89",
5897
					'THORN' => "\xC3\x9E",
5898
					'thorn' => "\xC3\xBE",
5899
					'THORN;' => "\xC3\x9E",
5900
					'thorn;' => "\xC3\xBE",
5901
					'tilde;' => "\xCB\x9C",
5902
					'times' => "\xC3\x97",
5903
					'times;' => "\xC3\x97",
5904
					'TRADE;' => "\xE2\x84\xA2",
5905
					'trade;' => "\xE2\x84\xA2",
5906
					'Uacute' => "\xC3\x9A",
5907
					'uacute' => "\xC3\xBA",
5908
					'Uacute;' => "\xC3\x9A",
5909
					'uacute;' => "\xC3\xBA",
5910
					'uArr;' => "\xE2\x87\x91",
5911
					'uarr;' => "\xE2\x86\x91",
5912
					'Ucirc' => "\xC3\x9B",
5913
					'ucirc' => "\xC3\xBB",
5914
					'Ucirc;' => "\xC3\x9B",
5915
					'ucirc;' => "\xC3\xBB",
5916
					'Ugrave' => "\xC3\x99",
5917
					'ugrave' => "\xC3\xB9",
5918
					'Ugrave;' => "\xC3\x99",
5919
					'ugrave;' => "\xC3\xB9",
5920
					'uml' => "\xC2\xA8",
5921
					'uml;' => "\xC2\xA8",
5922
					'upsih;' => "\xCF\x92",
5923
					'Upsilon;' => "\xCE\xA5",
5924
					'upsilon;' => "\xCF\x85",
5925
					'Uuml' => "\xC3\x9C",
5926
					'uuml' => "\xC3\xBC",
5927
					'Uuml;' => "\xC3\x9C",
5928
					'uuml;' => "\xC3\xBC",
5929
					'weierp;' => "\xE2\x84\x98",
5930
					'Xi;' => "\xCE\x9E",
5931
					'xi;' => "\xCE\xBE",
5932
					'Yacute' => "\xC3\x9D",
5933
					'yacute' => "\xC3\xBD",
5934
					'Yacute;' => "\xC3\x9D",
5935
					'yacute;' => "\xC3\xBD",
5936
					'yen' => "\xC2\xA5",
5937
					'yen;' => "\xC2\xA5",
5938
					'yuml' => "\xC3\xBF",
5939
					'Yuml;' => "\xC5\xB8",
5940
					'yuml;' => "\xC3\xBF",
5941
					'Zeta;' => "\xCE\x96",
5942
					'zeta;' => "\xCE\xB6",
5943
					'zwj;' => "\xE2\x80\x8D",
5944
					'zwnj;' => "\xE2\x80\x8C"
5945
				);
5946

    
5947
				for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
5948
				{
5949
					$consumed = substr($this->consumed, 1);
5950
					if (isset($entities[$consumed]))
5951
					{
5952
						$match = $consumed;
5953
					}
5954
				}
5955

    
5956
				if ($match !== null)
5957
				{
5958
 					$this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
5959
					$this->position += strlen($entities[$match]) - strlen($consumed) - 1;
5960
				}
5961
				break;
5962
		}
5963
	}
5964
}
5965

    
5966
/**
5967
 * Handles everything related to enclosures (including Media RSS and iTunes RSS)
5968
 *
5969
 * Used by {@see SimplePie_Item::get_enclosure()} and {@see SimplePie_Item::get_enclosures()}
5970
 *
5971
 * This class can be overloaded with {@see SimplePie::set_enclosure_class()}
5972
 *
5973
 * @package SimplePie
5974
 * @subpackage API
5975
 */
5976
class SimplePie_Enclosure
5977
{
5978
	/**
5979
	 * @var string
5980
	 * @see get_bitrate()
5981
	 */
5982
	var $bitrate;
5983

    
5984
	/**
5985
	 * @var array
5986
	 * @see get_captions()
5987
	 */
5988
	var $captions;
5989

    
5990
	/**
5991
	 * @var array
5992
	 * @see get_categories()
5993
	 */
5994
	var $categories;
5995

    
5996
	/**
5997
	 * @var int
5998
	 * @see get_channels()
5999
	 */
6000
	var $channels;
6001

    
6002
	/**
6003
	 * @var SimplePie_Copyright
6004
	 * @see get_copyright()
6005
	 */
6006
	var $copyright;
6007

    
6008
	/**
6009
	 * @var array
6010
	 * @see get_credits()
6011
	 */
6012
	var $credits;
6013

    
6014
	/**
6015
	 * @var string
6016
	 * @see get_description()
6017
	 */
6018
	var $description;
6019

    
6020
	/**
6021
	 * @var int
6022
	 * @see get_duration()
6023
	 */
6024
	var $duration;
6025

    
6026
	/**
6027
	 * @var string
6028
	 * @see get_expression()
6029
	 */
6030
	var $expression;
6031

    
6032
	/**
6033
	 * @var string
6034
	 * @see get_framerate()
6035
	 */
6036
	var $framerate;
6037

    
6038
	/**
6039
	 * @var string
6040
	 * @see get_handler()
6041
	 */
6042
	var $handler;
6043

    
6044
	/**
6045
	 * @var array
6046
	 * @see get_hashes()
6047
	 */
6048
	var $hashes;
6049

    
6050
	/**
6051
	 * @var string
6052
	 * @see get_height()
6053
	 */
6054
	var $height;
6055

    
6056
	/**
6057
	 * @deprecated
6058
	 * @var null
6059
	 */
6060
	var $javascript;
6061

    
6062
	/**
6063
	 * @var array
6064
	 * @see get_keywords()
6065
	 */
6066
	var $keywords;
6067

    
6068
	/**
6069
	 * @var string
6070
	 * @see get_language()
6071
	 */
6072
	var $lang;
6073

    
6074
	/**
6075
	 * @var string
6076
	 * @see get_length()
6077
	 */
6078
	var $length;
6079

    
6080
	/**
6081
	 * @var string
6082
	 * @see get_link()
6083
	 */
6084
	var $link;
6085

    
6086
	/**
6087
	 * @var string
6088
	 * @see get_medium()
6089
	 */
6090
	var $medium;
6091

    
6092
	/**
6093
	 * @var string
6094
	 * @see get_player()
6095
	 */
6096
	var $player;
6097

    
6098
	/**
6099
	 * @var array
6100
	 * @see get_ratings()
6101
	 */
6102
	var $ratings;
6103

    
6104
	/**
6105
	 * @var array
6106
	 * @see get_restrictions()
6107
	 */
6108
	var $restrictions;
6109

    
6110
	/**
6111
	 * @var string
6112
	 * @see get_sampling_rate()
6113
	 */
6114
	var $samplingrate;
6115

    
6116
	/**
6117
	 * @var array
6118
	 * @see get_thumbnails()
6119
	 */
6120
	var $thumbnails;
6121

    
6122
	/**
6123
	 * @var string
6124
	 * @see get_title()
6125
	 */
6126
	var $title;
6127

    
6128
	/**
6129
	 * @var string
6130
	 * @see get_type()
6131
	 */
6132
	var $type;
6133

    
6134
	/**
6135
	 * @var string
6136
	 * @see get_width()
6137
	 */
6138
	var $width;
6139

    
6140
	/**
6141
	 * Constructor, used to input the data
6142
	 *
6143
	 * For documentation on all the parameters, see the corresponding
6144
	 * properties and their accessors
6145
	 *
6146
	 * @uses idna_convert If available, this will convert an IDN
6147
	 */
6148
	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)
6149
	{
6150
		$this->bitrate = $bitrate;
6151
		$this->captions = $captions;
6152
		$this->categories = $categories;
6153
		$this->channels = $channels;
6154
		$this->copyright = $copyright;
6155
		$this->credits = $credits;
6156
		$this->description = $description;
6157
		$this->duration = $duration;
6158
		$this->expression = $expression;
6159
		$this->framerate = $framerate;
6160
		$this->hashes = $hashes;
6161
		$this->height = $height;
6162
		$this->keywords = $keywords;
6163
		$this->lang = $lang;
6164
		$this->length = $length;
6165
		$this->link = $link;
6166
		$this->medium = $medium;
6167
		$this->player = $player;
6168
		$this->ratings = $ratings;
6169
		$this->restrictions = $restrictions;
6170
		$this->samplingrate = $samplingrate;
6171
		$this->thumbnails = $thumbnails;
6172
		$this->title = $title;
6173
		$this->type = $type;
6174
		$this->width = $width;
6175

    
6176
		if (class_exists('idna_convert'))
6177
		{
6178
			$idn = new idna_convert();
6179
			$parsed = SimplePie_Misc::parse_url($link);
6180
			$this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
6181
		}
6182
		$this->handler = $this->get_handler(); // Needs to load last
6183
	}
6184

    
6185
	/**
6186
	 * String-ified version
6187
	 *
6188
	 * @return string
6189
	 */
6190
	public function __toString()
6191
	{
6192
		// There is no $this->data here
6193
		return md5(serialize($this));
6194
	}
6195

    
6196
	/**
6197
	 * Get the bitrate
6198
	 *
6199
	 * @return string|null
6200
	 */
6201
	public function get_bitrate()
6202
	{
6203
		if ($this->bitrate !== null)
6204
		{
6205
			return $this->bitrate;
6206
		}
6207
		else
6208
		{
6209
			return null;
6210
		}
6211
	}
6212

    
6213
	/**
6214
	 * Get a single caption
6215
	 *
6216
	 * @param int $key
6217
	 * @return SimplePie_Caption|null
6218
	 */
6219
	public function get_caption($key = 0)
6220
	{
6221
		$captions = $this->get_captions();
6222
		if (isset($captions[$key]))
6223
		{
6224
			return $captions[$key];
6225
		}
6226
		else
6227
		{
6228
			return null;
6229
		}
6230
	}
6231

    
6232
	/**
6233
	 * Get all captions
6234
	 *
6235
	 * @return array|null Array of {@see SimplePie_Caption} objects
6236
	 */
6237
	public function get_captions()
6238
	{
6239
		if ($this->captions !== null)
6240
		{
6241
			return $this->captions;
6242
		}
6243
		else
6244
		{
6245
			return null;
6246
		}
6247
	}
6248

    
6249
	/**
6250
	 * Get a single category
6251
	 *
6252
	 * @param int $key
6253
	 * @return SimplePie_Category|null
6254
	 */
6255
	public function get_category($key = 0)
6256
	{
6257
		$categories = $this->get_categories();
6258
		if (isset($categories[$key]))
6259
		{
6260
			return $categories[$key];
6261
		}
6262
		else
6263
		{
6264
			return null;
6265
		}
6266
	}
6267

    
6268
	/**
6269
	 * Get all categories
6270
	 *
6271
	 * @return array|null Array of {@see SimplePie_Category} objects
6272
	 */
6273
	public function get_categories()
6274
	{
6275
		if ($this->categories !== null)
6276
		{
6277
			return $this->categories;
6278
		}
6279
		else
6280
		{
6281
			return null;
6282
		}
6283
	}
6284

    
6285
	/**
6286
	 * Get the number of audio channels
6287
	 *
6288
	 * @return int|null
6289
	 */
6290
	public function get_channels()
6291
	{
6292
		if ($this->channels !== null)
6293
		{
6294
			return $this->channels;
6295
		}
6296
		else
6297
		{
6298
			return null;
6299
		}
6300
	}
6301

    
6302
	/**
6303
	 * Get the copyright information
6304
	 *
6305
	 * @return SimplePie_Copyright|null
6306
	 */
6307
	public function get_copyright()
6308
	{
6309
		if ($this->copyright !== null)
6310
		{
6311
			return $this->copyright;
6312
		}
6313
		else
6314
		{
6315
			return null;
6316
		}
6317
	}
6318

    
6319
	/**
6320
	 * Get a single credit
6321
	 *
6322
	 * @param int $key
6323
	 * @return SimplePie_Credit|null
6324
	 */
6325
	public function get_credit($key = 0)
6326
	{
6327
		$credits = $this->get_credits();
6328
		if (isset($credits[$key]))
6329
		{
6330
			return $credits[$key];
6331
		}
6332
		else
6333
		{
6334
			return null;
6335
		}
6336
	}
6337

    
6338
	/**
6339
	 * Get all credits
6340
	 *
6341
	 * @return array|null Array of {@see SimplePie_Credit} objects
6342
	 */
6343
	public function get_credits()
6344
	{
6345
		if ($this->credits !== null)
6346
		{
6347
			return $this->credits;
6348
		}
6349
		else
6350
		{
6351
			return null;
6352
		}
6353
	}
6354

    
6355
	/**
6356
	 * Get the description of the enclosure
6357
	 *
6358
	 * @return string|null
6359
	 */
6360
	public function get_description()
6361
	{
6362
		if ($this->description !== null)
6363
		{
6364
			return $this->description;
6365
		}
6366
		else
6367
		{
6368
			return null;
6369
		}
6370
	}
6371

    
6372
	/**
6373
	 * Get the duration of the enclosure
6374
	 *
6375
	 * @param bool $convert Convert seconds into hh:mm:ss
6376
	 * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found)
6377
	 */
6378
	public function get_duration($convert = false)
6379
	{
6380
		if ($this->duration !== null)
6381
		{
6382
			if ($convert)
6383
			{
6384
				$time = SimplePie_Misc::time_hms($this->duration);
6385
				return $time;
6386
			}
6387
			else
6388
			{
6389
				return $this->duration;
6390
			}
6391
		}
6392
		else
6393
		{
6394
			return null;
6395
		}
6396
	}
6397

    
6398
	/**
6399
	 * Get the expression
6400
	 *
6401
	 * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full'
6402
	 */
6403
	public function get_expression()
6404
	{
6405
		if ($this->expression !== null)
6406
		{
6407
			return $this->expression;
6408
		}
6409
		else
6410
		{
6411
			return 'full';
6412
		}
6413
	}
6414

    
6415
	/**
6416
	 * Get the file extension
6417
	 *
6418
	 * @return string|null
6419
	 */
6420
	public function get_extension()
6421
	{
6422
		if ($this->link !== null)
6423
		{
6424
			$url = SimplePie_Misc::parse_url($this->link);
6425
			if ($url['path'] !== '')
6426
			{
6427
				return pathinfo($url['path'], PATHINFO_EXTENSION);
6428
			}
6429
		}
6430
		return null;
6431
	}
6432

    
6433
	/**
6434
	 * Get the framerate (in frames-per-second)
6435
	 *
6436
	 * @return string|null
6437
	 */
6438
	public function get_framerate()
6439
	{
6440
		if ($this->framerate !== null)
6441
		{
6442
			return $this->framerate;
6443
		}
6444
		else
6445
		{
6446
			return null;
6447
		}
6448
	}
6449

    
6450
	/**
6451
	 * Get the preferred handler
6452
	 *
6453
	 * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3'
6454
	 */
6455
	public function get_handler()
6456
	{
6457
		return $this->get_real_type(true);
6458
	}
6459

    
6460
	/**
6461
	 * Get a single hash
6462
	 *
6463
	 * @link http://www.rssboard.org/media-rss#media-hash
6464
	 * @param int $key
6465
	 * @return string|null Hash as per `media:hash`, prefixed with "$algo:"
6466
	 */
6467
	public function get_hash($key = 0)
6468
	{
6469
		$hashes = $this->get_hashes();
6470
		if (isset($hashes[$key]))
6471
		{
6472
			return $hashes[$key];
6473
		}
6474
		else
6475
		{
6476
			return null;
6477
		}
6478
	}
6479

    
6480
	/**
6481
	 * Get all credits
6482
	 *
6483
	 * @return array|null Array of strings, see {@see get_hash()}
6484
	 */
6485
	public function get_hashes()
6486
	{
6487
		if ($this->hashes !== null)
6488
		{
6489
			return $this->hashes;
6490
		}
6491
		else
6492
		{
6493
			return null;
6494
		}
6495
	}
6496

    
6497
	/**
6498
	 * Get the height
6499
	 *
6500
	 * @return string|null
6501
	 */
6502
	public function get_height()
6503
	{
6504
		if ($this->height !== null)
6505
		{
6506
			return $this->height;
6507
		}
6508
		else
6509
		{
6510
			return null;
6511
		}
6512
	}
6513

    
6514
	/**
6515
	 * Get the language
6516
	 *
6517
	 * @link http://tools.ietf.org/html/rfc3066
6518
	 * @return string|null Language code as per RFC 3066
6519
	 */
6520
	public function get_language()
6521
	{
6522
		if ($this->lang !== null)
6523
		{
6524
			return $this->lang;
6525
		}
6526
		else
6527
		{
6528
			return null;
6529
		}
6530
	}
6531

    
6532
	/**
6533
	 * Get a single keyword
6534
	 *
6535
	 * @param int $key
6536
	 * @return string|null
6537
	 */
6538
	public function get_keyword($key = 0)
6539
	{
6540
		$keywords = $this->get_keywords();
6541
		if (isset($keywords[$key]))
6542
		{
6543
			return $keywords[$key];
6544
		}
6545
		else
6546
		{
6547
			return null;
6548
		}
6549
	}
6550

    
6551
	/**
6552
	 * Get all keywords
6553
	 *
6554
	 * @return array|null Array of strings
6555
	 */
6556
	public function get_keywords()
6557
	{
6558
		if ($this->keywords !== null)
6559
		{
6560
			return $this->keywords;
6561
		}
6562
		else
6563
		{
6564
			return null;
6565
		}
6566
	}
6567

    
6568
	/**
6569
	 * Get length
6570
	 *
6571
	 * @return float Length in bytes
6572
	 */
6573
	public function get_length()
6574
	{
6575
		if ($this->length !== null)
6576
		{
6577
			return $this->length;
6578
		}
6579
		else
6580
		{
6581
			return null;
6582
		}
6583
	}
6584

    
6585
	/**
6586
	 * Get the URL
6587
	 *
6588
	 * @return string|null
6589
	 */
6590
	public function get_link()
6591
	{
6592
		if ($this->link !== null)
6593
		{
6594
			return urldecode($this->link);
6595
		}
6596
		else
6597
		{
6598
			return null;
6599
		}
6600
	}
6601

    
6602
	/**
6603
	 * Get the medium
6604
	 *
6605
	 * @link http://www.rssboard.org/media-rss#media-content
6606
	 * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable'
6607
	 */
6608
	public function get_medium()
6609
	{
6610
		if ($this->medium !== null)
6611
		{
6612
			return $this->medium;
6613
		}
6614
		else
6615
		{
6616
			return null;
6617
		}
6618
	}
6619

    
6620
	/**
6621
	 * Get the player URL
6622
	 *
6623
	 * Typically the same as {@see get_permalink()}
6624
	 * @return string|null Player URL
6625
	 */
6626
	public function get_player()
6627
	{
6628
		if ($this->player !== null)
6629
		{
6630
			return $this->player;
6631
		}
6632
		else
6633
		{
6634
			return null;
6635
		}
6636
	}
6637

    
6638
	/**
6639
	 * Get a single rating
6640
	 *
6641
	 * @param int $key
6642
	 * @return SimplePie_Rating|null
6643
	 */
6644
	public function get_rating($key = 0)
6645
	{
6646
		$ratings = $this->get_ratings();
6647
		if (isset($ratings[$key]))
6648
		{
6649
			return $ratings[$key];
6650
		}
6651
		else
6652
		{
6653
			return null;
6654
		}
6655
	}
6656

    
6657
	/**
6658
	 * Get all ratings
6659
	 *
6660
	 * @return array|null Array of {@see SimplePie_Rating} objects
6661
	 */
6662
	public function get_ratings()
6663
	{
6664
		if ($this->ratings !== null)
6665
		{
6666
			return $this->ratings;
6667
		}
6668
		else
6669
		{
6670
			return null;
6671
		}
6672
	}
6673

    
6674
	/**
6675
	 * Get a single restriction
6676
	 *
6677
	 * @param int $key
6678
	 * @return SimplePie_Restriction|null
6679
	 */
6680
	public function get_restriction($key = 0)
6681
	{
6682
		$restrictions = $this->get_restrictions();
6683
		if (isset($restrictions[$key]))
6684
		{
6685
			return $restrictions[$key];
6686
		}
6687
		else
6688
		{
6689
			return null;
6690
		}
6691
	}
6692

    
6693
	/**
6694
	 * Get all restrictions
6695
	 *
6696
	 * @return array|null Array of {@see SimplePie_Restriction} objects
6697
	 */
6698
	public function get_restrictions()
6699
	{
6700
		if ($this->restrictions !== null)
6701
		{
6702
			return $this->restrictions;
6703
		}
6704
		else
6705
		{
6706
			return null;
6707
		}
6708
	}
6709

    
6710
	/**
6711
	 * Get the sampling rate (in kHz)
6712
	 *
6713
	 * @return string|null
6714
	 */
6715
	public function get_sampling_rate()
6716
	{
6717
		if ($this->samplingrate !== null)
6718
		{
6719
			return $this->samplingrate;
6720
		}
6721
		else
6722
		{
6723
			return null;
6724
		}
6725
	}
6726

    
6727
	/**
6728
	 * Get the file size (in MiB)
6729
	 *
6730
	 * @return float|null File size in mebibytes (1048 bytes)
6731
	 */
6732
	public function get_size()
6733
	{
6734
		$length = $this->get_length();
6735
		if ($length !== null)
6736
		{
6737
			return round($length/1048576, 2);
6738
		}
6739
		else
6740
		{
6741
			return null;
6742
		}
6743
	}
6744

    
6745
	/**
6746
	 * Get a single thumbnail
6747
	 *
6748
	 * @param int $key
6749
	 * @return string|null Thumbnail URL
6750
	 */
6751
	public function get_thumbnail($key = 0)
6752
	{
6753
		$thumbnails = $this->get_thumbnails();
6754
		if (isset($thumbnails[$key]))
6755
		{
6756
			return $thumbnails[$key];
6757
		}
6758
		else
6759
		{
6760
			return null;
6761
		}
6762
	}
6763

    
6764
	/**
6765
	 * Get all thumbnails
6766
	 *
6767
	 * @return array|null Array of thumbnail URLs
6768
	 */
6769
	public function get_thumbnails()
6770
	{
6771
		if ($this->thumbnails !== null)
6772
		{
6773
			return $this->thumbnails;
6774
		}
6775
		else
6776
		{
6777
			return null;
6778
		}
6779
	}
6780

    
6781
	/**
6782
	 * Get the title
6783
	 *
6784
	 * @return string|null
6785
	 */
6786
	public function get_title()
6787
	{
6788
		if ($this->title !== null)
6789
		{
6790
			return $this->title;
6791
		}
6792
		else
6793
		{
6794
			return null;
6795
		}
6796
	}
6797

    
6798
	/**
6799
	 * Get mimetype of the enclosure
6800
	 *
6801
	 * @see get_real_type()
6802
	 * @return string|null MIME type
6803
	 */
6804
	public function get_type()
6805
	{
6806
		if ($this->type !== null)
6807
		{
6808
			return $this->type;
6809
		}
6810
		else
6811
		{
6812
			return null;
6813
		}
6814
	}
6815

    
6816
	/**
6817
	 * Get the width
6818
	 *
6819
	 * @return string|null
6820
	 */
6821
	public function get_width()
6822
	{
6823
		if ($this->width !== null)
6824
		{
6825
			return $this->width;
6826
		}
6827
		else
6828
		{
6829
			return null;
6830
		}
6831
	}
6832

    
6833
	/**
6834
	 * Embed the enclosure using `<embed>`
6835
	 *
6836
	 * @deprecated Use the second parameter to {@see embed} instead
6837
	 *
6838
	 * @param array|string $options See first paramter to {@see embed}
6839
	 * @return string HTML string to output
6840
	 */
6841
	public function native_embed($options='')
6842
	{
6843
		return $this->embed($options, true);
6844
	}
6845

    
6846
	/**
6847
	 * Embed the enclosure using Javascript
6848
	 *
6849
	 * `$options` is an array or comma-separated key:value string, with the
6850
	 * following properties:
6851
	 *
6852
	 * - `alt` (string): Alternate content for when an end-user does not have
6853
	 *    the appropriate handler installed or when a file type is
6854
	 *    unsupported. Can be any text or HTML. Defaults to blank.
6855
	 * - `altclass` (string): If a file type is unsupported, the end-user will
6856
	 *    see the alt text (above) linked directly to the content. That link
6857
	 *    will have this value as its class name. Defaults to blank.
6858
	 * - `audio` (string): This is an image that should be used as a
6859
	 *    placeholder for audio files before they're loaded (QuickTime-only).
6860
	 *    Can be any relative or absolute URL. Defaults to blank.
6861
	 * - `bgcolor` (string): The background color for the media, if not
6862
	 *    already transparent. Defaults to `#ffffff`.
6863
	 * - `height` (integer): The height of the embedded media. Accepts any
6864
	 *    numeric pixel value (such as `360`) or `auto`. Defaults to `auto`,
6865
	 *    and it is recommended that you use this default.
6866
	 * - `loop` (boolean): Do you want the media to loop when it's done?
6867
	 *    Defaults to `false`.
6868
	 * - `mediaplayer` (string): The location of the included
6869
	 *    `mediaplayer.swf` file. This allows for the playback of Flash Video
6870
	 *    (`.flv`) files, and is the default handler for non-Odeo MP3's.
6871
	 *    Defaults to blank.
6872
	 * - `video` (string): This is an image that should be used as a
6873
	 *    placeholder for video files before they're loaded (QuickTime-only).
6874
	 *    Can be any relative or absolute URL. Defaults to blank.
6875
	 * - `width` (integer): The width of the embedded media. Accepts any
6876
	 *    numeric pixel value (such as `480`) or `auto`. Defaults to `auto`,
6877
	 *    and it is recommended that you use this default.
6878
	 * - `widescreen` (boolean): Is the enclosure widescreen or standard?
6879
	 *    This applies only to video enclosures, and will automatically resize
6880
	 *    the content appropriately.  Defaults to `false`, implying 4:3 mode.
6881
	 *
6882
	 * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto`
6883
	 * will default to 480x360 video resolution.  Widescreen (16:9) mode with
6884
	 * `width` and `height` set to `auto` will default to 480x270 video resolution.
6885
	 *
6886
	 * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
6887
	 * @param array|string $options Comma-separated key:value list, or array
6888
	 * @param bool $native Use `<embed>`
6889
	 * @return string HTML string to output
6890
	 */
6891
	public function embed($options = '', $native = false)
6892
	{
6893
		// Set up defaults
6894
		$audio = '';
6895
		$video = '';
6896
		$alt = '';
6897
		$altclass = '';
6898
		$loop = 'false';
6899
		$width = 'auto';
6900
		$height = 'auto';
6901
		$bgcolor = '#ffffff';
6902
		$mediaplayer = '';
6903
		$widescreen = false;
6904
		$handler = $this->get_handler();
6905
		$type = $this->get_real_type();
6906

    
6907
		// Process options and reassign values as necessary
6908
		if (is_array($options))
6909
		{
6910
			extract($options);
6911
		}
6912
		else
6913
		{
6914
			$options = explode(',', $options);
6915
			foreach($options as $option)
6916
			{
6917
				$opt = explode(':', $option, 2);
6918
				if (isset($opt[0], $opt[1]))
6919
				{
6920
					$opt[0] = trim($opt[0]);
6921
					$opt[1] = trim($opt[1]);
6922
					switch ($opt[0])
6923
					{
6924
						case 'audio':
6925
							$audio = $opt[1];
6926
							break;
6927

    
6928
						case 'video':
6929
							$video = $opt[1];
6930
							break;
6931

    
6932
						case 'alt':
6933
							$alt = $opt[1];
6934
							break;
6935

    
6936
						case 'altclass':
6937
							$altclass = $opt[1];
6938
							break;
6939

    
6940
						case 'loop':
6941
							$loop = $opt[1];
6942
							break;
6943

    
6944
						case 'width':
6945
							$width = $opt[1];
6946
							break;
6947

    
6948
						case 'height':
6949
							$height = $opt[1];
6950
							break;
6951

    
6952
						case 'bgcolor':
6953
							$bgcolor = $opt[1];
6954
							break;
6955

    
6956
						case 'mediaplayer':
6957
							$mediaplayer = $opt[1];
6958
							break;
6959

    
6960
						case 'widescreen':
6961
							$widescreen = $opt[1];
6962
							break;
6963
					}
6964
				}
6965
			}
6966
		}
6967

    
6968
		$mime = explode('/', $type, 2);
6969
		$mime = $mime[0];
6970

    
6971
		// Process values for 'auto'
6972
		if ($width === 'auto')
6973
		{
6974
			if ($mime === 'video')
6975
			{
6976
				if ($height === 'auto')
6977
				{
6978
					$width = 480;
6979
				}
6980
				elseif ($widescreen)
6981
				{
6982
					$width = round((intval($height)/9)*16);
6983
				}
6984
				else
6985
				{
6986
					$width = round((intval($height)/3)*4);
6987
				}
6988
			}
6989
			else
6990
			{
6991
				$width = '100%';
6992
			}
6993
		}
6994

    
6995
		if ($height === 'auto')
6996
		{
6997
			if ($mime === 'audio')
6998
			{
6999
				$height = 0;
7000
			}
7001
			elseif ($mime === 'video')
7002
			{
7003
				if ($width === 'auto')
7004
				{
7005
					if ($widescreen)
7006
					{
7007
						$height = 270;
7008
					}
7009
					else
7010
					{
7011
						$height = 360;
7012
					}
7013
				}
7014
				elseif ($widescreen)
7015
				{
7016
					$height = round((intval($width)/16)*9);
7017
				}
7018
				else
7019
				{
7020
					$height = round((intval($width)/4)*3);
7021
				}
7022
			}
7023
			else
7024
			{
7025
				$height = 376;
7026
			}
7027
		}
7028
		elseif ($mime === 'audio')
7029
		{
7030
			$height = 0;
7031
		}
7032

    
7033
		// Set proper placeholder value
7034
		if ($mime === 'audio')
7035
		{
7036
			$placeholder = $audio;
7037
		}
7038
		elseif ($mime === 'video')
7039
		{
7040
			$placeholder = $video;
7041
		}
7042

    
7043
		$embed = '';
7044

    
7045
		// Flash
7046
		if ($handler === 'flash')
7047
		{
7048
			if ($native)
7049
			{
7050
				$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>";
7051
			}
7052
			else
7053
			{
7054
				$embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
7055
			}
7056
		}
7057

    
7058
		// Flash Media Player file types.
7059
		// Preferred handler for MP3 file types.
7060
		elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== ''))
7061
		{
7062
			$height += 20;
7063
			if ($native)
7064
			{
7065
				$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>";
7066
			}
7067
			else
7068
			{
7069
				$embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
7070
			}
7071
		}
7072

    
7073
		// QuickTime 7 file types.  Need to test with QuickTime 6.
7074
		// Only handle MP3's if the Flash Media Player is not present.
7075
		elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === ''))
7076
		{
7077
			$height += 16;
7078
			if ($native)
7079
			{
7080
				if ($placeholder !== '')
7081
				{
7082
					$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>";
7083
				}
7084
				else
7085
				{
7086
					$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>";
7087
				}
7088
			}
7089
			else
7090
			{
7091
				$embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
7092
			}
7093
		}
7094

    
7095
		// Windows Media
7096
		elseif ($handler === 'wmedia')
7097
		{
7098
			$height += 45;
7099
			if ($native)
7100
			{
7101
				$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>";
7102
			}
7103
			else
7104
			{
7105
				$embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
7106
			}
7107
		}
7108

    
7109
		// Everything else
7110
		else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
7111

    
7112
		return $embed;
7113
	}
7114

    
7115
	/**
7116
	 * Get the real media type
7117
	 *
7118
	 * Often, feeds lie to us, necessitating a bit of deeper inspection. This
7119
	 * converts types to their canonical representations based on the file
7120
	 * extension
7121
	 *
7122
	 * @see get_type()
7123
	 * @param bool $find_handler Internal use only, use {@see get_handler()} instead
7124
	 * @return string MIME type
7125
	 */
7126
	public function get_real_type($find_handler = false)
7127
	{
7128
		// Mime-types by handler.
7129
		$types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash
7130
		$types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player
7131
		$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
7132
		$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
7133
		$types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3
7134

    
7135
		if ($this->get_type() !== null)
7136
		{
7137
			$type = strtolower($this->type);
7138
		}
7139
		else
7140
		{
7141
			$type = null;
7142
		}
7143

    
7144
		// If we encounter an unsupported mime-type, check the file extension and guess intelligently.
7145
		if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3)))
7146
		{
7147
			switch (strtolower($this->get_extension()))
7148
			{
7149
				// Audio mime-types
7150
				case 'aac':
7151
				case 'adts':
7152
					$type = 'audio/acc';
7153
					break;
7154

    
7155
				case 'aif':
7156
				case 'aifc':
7157
				case 'aiff':
7158
				case 'cdda':
7159
					$type = 'audio/aiff';
7160
					break;
7161

    
7162
				case 'bwf':
7163
					$type = 'audio/wav';
7164
					break;
7165

    
7166
				case 'kar':
7167
				case 'mid':
7168
				case 'midi':
7169
				case 'smf':
7170
					$type = 'audio/midi';
7171
					break;
7172

    
7173
				case 'm4a':
7174
					$type = 'audio/x-m4a';
7175
					break;
7176

    
7177
				case 'mp3':
7178
				case 'swa':
7179
					$type = 'audio/mp3';
7180
					break;
7181

    
7182
				case 'wav':
7183
					$type = 'audio/wav';
7184
					break;
7185

    
7186
				case 'wax':
7187
					$type = 'audio/x-ms-wax';
7188
					break;
7189

    
7190
				case 'wma':
7191
					$type = 'audio/x-ms-wma';
7192
					break;
7193

    
7194
				// Video mime-types
7195
				case '3gp':
7196
				case '3gpp':
7197
					$type = 'video/3gpp';
7198
					break;
7199

    
7200
				case '3g2':
7201
				case '3gp2':
7202
					$type = 'video/3gpp2';
7203
					break;
7204

    
7205
				case 'asf':
7206
					$type = 'video/x-ms-asf';
7207
					break;
7208

    
7209
				case 'flv':
7210
					$type = 'video/x-flv';
7211
					break;
7212

    
7213
				case 'm1a':
7214
				case 'm1s':
7215
				case 'm1v':
7216
				case 'm15':
7217
				case 'm75':
7218
				case 'mp2':
7219
				case 'mpa':
7220
				case 'mpeg':
7221
				case 'mpg':
7222
				case 'mpm':
7223
				case 'mpv':
7224
					$type = 'video/mpeg';
7225
					break;
7226

    
7227
				case 'm4v':
7228
					$type = 'video/x-m4v';
7229
					break;
7230

    
7231
				case 'mov':
7232
				case 'qt':
7233
					$type = 'video/quicktime';
7234
					break;
7235

    
7236
				case 'mp4':
7237
				case 'mpg4':
7238
					$type = 'video/mp4';
7239
					break;
7240

    
7241
				case 'sdv':
7242
					$type = 'video/sd-video';
7243
					break;
7244

    
7245
				case 'wm':
7246
					$type = 'video/x-ms-wm';
7247
					break;
7248

    
7249
				case 'wmv':
7250
					$type = 'video/x-ms-wmv';
7251
					break;
7252

    
7253
				case 'wvx':
7254
					$type = 'video/x-ms-wvx';
7255
					break;
7256

    
7257
				// Flash mime-types
7258
				case 'spl':
7259
					$type = 'application/futuresplash';
7260
					break;
7261

    
7262
				case 'swf':
7263
					$type = 'application/x-shockwave-flash';
7264
					break;
7265
			}
7266
		}
7267

    
7268
		if ($find_handler)
7269
		{
7270
			if (in_array($type, $types_flash))
7271
			{
7272
				return 'flash';
7273
			}
7274
			elseif (in_array($type, $types_fmedia))
7275
			{
7276
				return 'fmedia';
7277
			}
7278
			elseif (in_array($type, $types_quicktime))
7279
			{
7280
				return 'quicktime';
7281
			}
7282
			elseif (in_array($type, $types_wmedia))
7283
			{
7284
				return 'wmedia';
7285
			}
7286
			elseif (in_array($type, $types_mp3))
7287
			{
7288
				return 'mp3';
7289
			}
7290
			else
7291
			{
7292
				return null;
7293
			}
7294
		}
7295
		else
7296
		{
7297
			return $type;
7298
		}
7299
	}
7300
}
7301

    
7302
/**
7303
 * General SimplePie exception class
7304
 *
7305
 * @package SimplePie
7306
 */
7307
class SimplePie_Exception extends Exception
7308
{
7309
}
7310

    
7311
/**
7312
 * Used for fetching remote files and reading local files
7313
 *
7314
 * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support
7315
 *
7316
 * This class can be overloaded with {@see SimplePie::set_file_class()}
7317
 *
7318
 * @package SimplePie
7319
 * @subpackage HTTP
7320
 * @todo Move to properly supporting RFC2616 (HTTP/1.1)
7321
 */
7322
class SimplePie_File
7323
{
7324
	var $url;
7325
	var $useragent;
7326
	var $success = true;
7327
	var $headers = array();
7328
	var $body;
7329
	var $status_code;
7330
	var $redirects = 0;
7331
	var $error;
7332
	var $method = SIMPLEPIE_FILE_SOURCE_NONE;
7333
	var $permanent_url;
7334

    
7335
	public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = array())
7336
	{
7337
		if (class_exists('idna_convert'))
7338
		{
7339
			$idn = new idna_convert();
7340
			$parsed = SimplePie_Misc::parse_url($url);
7341
			$url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
7342
		}
7343
		$this->url = $url;
7344
		$this->permanent_url = $url;
7345
		$this->useragent = $useragent;
7346
		if (preg_match('/^http(s)?:\/\//i', $url))
7347
		{
7348
			if ($useragent === null)
7349
			{
7350
				$useragent = ini_get('user_agent');
7351
				$this->useragent = $useragent;
7352
			}
7353
			if (!is_array($headers))
7354
			{
7355
				$headers = array();
7356
			}
7357
			if (!$force_fsockopen && function_exists('curl_exec'))
7358
			{
7359
				$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
7360
				$fp = curl_init();
7361
				$headers2 = array();
7362
				foreach ($headers as $key => $value)
7363
				{
7364
					$headers2[] = "$key: $value";
7365
				}
7366
				if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
7367
				{
7368
					curl_setopt($fp, CURLOPT_ENCODING, '');
7369
				}
7370
				curl_setopt($fp, CURLOPT_URL, $url);
7371
				curl_setopt($fp, CURLOPT_HEADER, 1);
7372
				curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
7373
				curl_setopt($fp, CURLOPT_FAILONERROR, 1);
7374
				curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
7375
				curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
7376
				curl_setopt($fp, CURLOPT_REFERER, $url);
7377
				curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
7378
				curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
7379
				if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
7380
				{
7381
					curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
7382
					curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
7383
				}
7384
				foreach ($curl_options as $curl_param => $curl_value) {
7385
					curl_setopt($fp, $curl_param, $curl_value);
7386
				}
7387

    
7388
				$this->headers = curl_exec($fp);
7389
				if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
7390
				{
7391
					curl_setopt($fp, CURLOPT_ENCODING, 'none');
7392
					$this->headers = curl_exec($fp);
7393
				}
7394
				if (curl_errno($fp))
7395
				{
7396
					$this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
7397
					$this->success = false;
7398
				}
7399
				else
7400
				{
7401
					// Use the updated url provided by curl_getinfo after any redirects.
7402
					if ($info = curl_getinfo($fp)) {
7403
						$this->url = $info['url'];
7404
					}
7405
					curl_close($fp);
7406
					$this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
7407
					$this->headers = array_pop($this->headers);
7408
					$parser = new SimplePie_HTTP_Parser($this->headers);
7409
					if ($parser->parse())
7410
					{
7411
						$this->headers = $parser->headers;
7412
						$this->body = trim($parser->body);
7413
						$this->status_code = $parser->status_code;
7414
						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)
7415
						{
7416
							$this->redirects++;
7417
							$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
7418
							$previousStatusCode = $this->status_code;
7419
							$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
7420
							$this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
7421
							return;
7422
						}
7423
					}
7424
				}
7425
			}
7426
			else
7427
			{
7428
				$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
7429
				$url_parts = parse_url($url);
7430
				$socket_host = $url_parts['host'];
7431
				if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
7432
				{
7433
					$socket_host = "ssl://$url_parts[host]";
7434
					$url_parts['port'] = 443;
7435
				}
7436
				if (!isset($url_parts['port']))
7437
				{
7438
					$url_parts['port'] = 80;
7439
				}
7440
				$fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
7441
				if (!$fp)
7442
				{
7443
					$this->error = 'fsockopen error: ' . $errstr;
7444
					$this->success = false;
7445
				}
7446
				else
7447
				{
7448
					stream_set_timeout($fp, $timeout);
7449
					if (isset($url_parts['path']))
7450
					{
7451
						if (isset($url_parts['query']))
7452
						{
7453
							$get = "$url_parts[path]?$url_parts[query]";
7454
						}
7455
						else
7456
						{
7457
							$get = $url_parts['path'];
7458
						}
7459
					}
7460
					else
7461
					{
7462
						$get = '/';
7463
					}
7464
					$out = "GET $get HTTP/1.1\r\n";
7465
					$out .= "Host: $url_parts[host]\r\n";
7466
					$out .= "User-Agent: $useragent\r\n";
7467
					if (extension_loaded('zlib'))
7468
					{
7469
						$out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
7470
					}
7471

    
7472
					if (isset($url_parts['user']) && isset($url_parts['pass']))
7473
					{
7474
						$out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
7475
					}
7476
					foreach ($headers as $key => $value)
7477
					{
7478
						$out .= "$key: $value\r\n";
7479
					}
7480
					$out .= "Connection: Close\r\n\r\n";
7481
					fwrite($fp, $out);
7482

    
7483
					$info = stream_get_meta_data($fp);
7484

    
7485
					$this->headers = '';
7486
					while (!$info['eof'] && !$info['timed_out'])
7487
					{
7488
						$this->headers .= fread($fp, 1160);
7489
						$info = stream_get_meta_data($fp);
7490
					}
7491
					if (!$info['timed_out'])
7492
					{
7493
						$parser = new SimplePie_HTTP_Parser($this->headers);
7494
						if ($parser->parse())
7495
						{
7496
							$this->headers = $parser->headers;
7497
							$this->body = $parser->body;
7498
							$this->status_code = $parser->status_code;
7499
							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)
7500
							{
7501
								$this->redirects++;
7502
								$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
7503
								$previousStatusCode = $this->status_code;
7504
								$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
7505
								$this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
7506
								return;
7507
							}
7508
							if (isset($this->headers['content-encoding']))
7509
							{
7510
								// Hey, we act dumb elsewhere, so let's do that here too
7511
								switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
7512
								{
7513
									case 'gzip':
7514
									case 'x-gzip':
7515
										$decoder = new SimplePie_gzdecode($this->body);
7516
										if (!$decoder->parse())
7517
										{
7518
											$this->error = 'Unable to decode HTTP "gzip" stream';
7519
											$this->success = false;
7520
										}
7521
										else
7522
										{
7523
											$this->body = trim($decoder->data);
7524
										}
7525
										break;
7526

    
7527
									case 'deflate':
7528
										if (($decompressed = gzinflate($this->body)) !== false)
7529
										{
7530
											$this->body = $decompressed;
7531
										}
7532
										else if (($decompressed = gzuncompress($this->body)) !== false)
7533
										{
7534
											$this->body = $decompressed;
7535
										}
7536
										else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false)
7537
										{
7538
											$this->body = $decompressed;
7539
										}
7540
										else
7541
										{
7542
											$this->error = 'Unable to decode HTTP "deflate" stream';
7543
											$this->success = false;
7544
										}
7545
										break;
7546

    
7547
									default:
7548
										$this->error = 'Unknown content coding';
7549
										$this->success = false;
7550
								}
7551
							}
7552
						}
7553
					}
7554
					else
7555
					{
7556
						$this->error = 'fsocket timed out';
7557
						$this->success = false;
7558
					}
7559
					fclose($fp);
7560
				}
7561
			}
7562
		}
7563
		else
7564
		{
7565
			$this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
7566
			if (empty($url) || !($this->body = trim(file_get_contents($url))))
7567
			{
7568
				$this->error = 'file_get_contents could not read the file';
7569
				$this->success = false;
7570
			}
7571
		}
7572
	}
7573
}
7574

    
7575
/**
7576
 * HTTP Response Parser
7577
 *
7578
 * @package SimplePie
7579
 * @subpackage HTTP
7580
 */
7581
class SimplePie_HTTP_Parser
7582
{
7583
	/**
7584
	 * HTTP Version
7585
	 *
7586
	 * @var float
7587
	 */
7588
	public $http_version = 0.0;
7589

    
7590
	/**
7591
	 * Status code
7592
	 *
7593
	 * @var int
7594
	 */
7595
	public $status_code = 0;
7596

    
7597
	/**
7598
	 * Reason phrase
7599
	 *
7600
	 * @var string
7601
	 */
7602
	public $reason = '';
7603

    
7604
	/**
7605
	 * Key/value pairs of the headers
7606
	 *
7607
	 * @var array
7608
	 */
7609
	public $headers = array();
7610

    
7611
	/**
7612
	 * Body of the response
7613
	 *
7614
	 * @var string
7615
	 */
7616
	public $body = '';
7617

    
7618
	/**
7619
	 * Current state of the state machine
7620
	 *
7621
	 * @var string
7622
	 */
7623
	protected $state = 'http_version';
7624

    
7625
	/**
7626
	 * Input data
7627
	 *
7628
	 * @var string
7629
	 */
7630
	protected $data = '';
7631

    
7632
	/**
7633
	 * Input data length (to avoid calling strlen() everytime this is needed)
7634
	 *
7635
	 * @var int
7636
	 */
7637
	protected $data_length = 0;
7638

    
7639
	/**
7640
	 * Current position of the pointer
7641
	 *
7642
	 * @var int
7643
	 */
7644
	protected $position = 0;
7645

    
7646
	/**
7647
	 * Name of the hedaer currently being parsed
7648
	 *
7649
	 * @var string
7650
	 */
7651
	protected $name = '';
7652

    
7653
	/**
7654
	 * Value of the hedaer currently being parsed
7655
	 *
7656
	 * @var string
7657
	 */
7658
	protected $value = '';
7659

    
7660
	/**
7661
	 * Create an instance of the class with the input data
7662
	 *
7663
	 * @param string $data Input data
7664
	 */
7665
	public function __construct($data)
7666
	{
7667
		$this->data = $data;
7668
		$this->data_length = strlen($this->data);
7669
	}
7670

    
7671
	/**
7672
	 * Parse the input data
7673
	 *
7674
	 * @return bool true on success, false on failure
7675
	 */
7676
	public function parse()
7677
	{
7678
		while ($this->state && $this->state !== 'emit' && $this->has_data())
7679
		{
7680
			$state = $this->state;
7681
			$this->$state();
7682
		}
7683
		$this->data = '';
7684
		if ($this->state === 'emit' || $this->state === 'body')
7685
		{
7686
			return true;
7687
		}
7688
		else
7689
		{
7690
			$this->http_version = '';
7691
			$this->status_code = '';
7692
			$this->reason = '';
7693
			$this->headers = array();
7694
			$this->body = '';
7695
			return false;
7696
		}
7697
	}
7698

    
7699
	/**
7700
	 * Check whether there is data beyond the pointer
7701
	 *
7702
	 * @return bool true if there is further data, false if not
7703
	 */
7704
	protected function has_data()
7705
	{
7706
		return (bool) ($this->position < $this->data_length);
7707
	}
7708

    
7709
	/**
7710
	 * See if the next character is LWS
7711
	 *
7712
	 * @return bool true if the next character is LWS, false if not
7713
	 */
7714
	protected function is_linear_whitespace()
7715
	{
7716
		return (bool) ($this->data[$this->position] === "\x09"
7717
			|| $this->data[$this->position] === "\x20"
7718
			|| ($this->data[$this->position] === "\x0A"
7719
				&& isset($this->data[$this->position + 1])
7720
				&& ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
7721
	}
7722

    
7723
	/**
7724
	 * Parse the HTTP version
7725
	 */
7726
	protected function http_version()
7727
	{
7728
		if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
7729
		{
7730
			$len = strspn($this->data, '0123456789.', 5);
7731
			$this->http_version = substr($this->data, 5, $len);
7732
			$this->position += 5 + $len;
7733
			if (substr_count($this->http_version, '.') <= 1)
7734
			{
7735
				$this->http_version = (float) $this->http_version;
7736
				$this->position += strspn($this->data, "\x09\x20", $this->position);
7737
				$this->state = 'status';
7738
			}
7739
			else
7740
			{
7741
				$this->state = false;
7742
			}
7743
		}
7744
		else
7745
		{
7746
			$this->state = false;
7747
		}
7748
	}
7749

    
7750
	/**
7751
	 * Parse the status code
7752
	 */
7753
	protected function status()
7754
	{
7755
		if ($len = strspn($this->data, '0123456789', $this->position))
7756
		{
7757
			$this->status_code = (int) substr($this->data, $this->position, $len);
7758
			$this->position += $len;
7759
			$this->state = 'reason';
7760
		}
7761
		else
7762
		{
7763
			$this->state = false;
7764
		}
7765
	}
7766

    
7767
	/**
7768
	 * Parse the reason phrase
7769
	 */
7770
	protected function reason()
7771
	{
7772
		$len = strcspn($this->data, "\x0A", $this->position);
7773
		$this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
7774
		$this->position += $len + 1;
7775
		$this->state = 'new_line';
7776
	}
7777

    
7778
	/**
7779
	 * Deal with a new line, shifting data around as needed
7780
	 */
7781
	protected function new_line()
7782
	{
7783
		$this->value = trim($this->value, "\x0D\x20");
7784
		if ($this->name !== '' && $this->value !== '')
7785
		{
7786
			$this->name = strtolower($this->name);
7787
			// We should only use the last Content-Type header. c.f. issue #1
7788
			if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
7789
			{
7790
				$this->headers[$this->name] .= ', ' . $this->value;
7791
			}
7792
			else
7793
			{
7794
				$this->headers[$this->name] = $this->value;
7795
			}
7796
		}
7797
		$this->name = '';
7798
		$this->value = '';
7799
		if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
7800
		{
7801
			$this->position += 2;
7802
			$this->state = 'body';
7803
		}
7804
		elseif ($this->data[$this->position] === "\x0A")
7805
		{
7806
			$this->position++;
7807
			$this->state = 'body';
7808
		}
7809
		else
7810
		{
7811
			$this->state = 'name';
7812
		}
7813
	}
7814

    
7815
	/**
7816
	 * Parse a header name
7817
	 */
7818
	protected function name()
7819
	{
7820
		$len = strcspn($this->data, "\x0A:", $this->position);
7821
		if (isset($this->data[$this->position + $len]))
7822
		{
7823
			if ($this->data[$this->position + $len] === "\x0A")
7824
			{
7825
				$this->position += $len;
7826
				$this->state = 'new_line';
7827
			}
7828
			else
7829
			{
7830
				$this->name = substr($this->data, $this->position, $len);
7831
				$this->position += $len + 1;
7832
				$this->state = 'value';
7833
			}
7834
		}
7835
		else
7836
		{
7837
			$this->state = false;
7838
		}
7839
	}
7840

    
7841
	/**
7842
	 * Parse LWS, replacing consecutive LWS characters with a single space
7843
	 */
7844
	protected function linear_whitespace()
7845
	{
7846
		do
7847
		{
7848
			if (substr($this->data, $this->position, 2) === "\x0D\x0A")
7849
			{
7850
				$this->position += 2;
7851
			}
7852
			elseif ($this->data[$this->position] === "\x0A")
7853
			{
7854
				$this->position++;
7855
			}
7856
			$this->position += strspn($this->data, "\x09\x20", $this->position);
7857
		} while ($this->has_data() && $this->is_linear_whitespace());
7858
		$this->value .= "\x20";
7859
	}
7860

    
7861
	/**
7862
	 * See what state to move to while within non-quoted header values
7863
	 */
7864
	protected function value()
7865
	{
7866
		if ($this->is_linear_whitespace())
7867
		{
7868
			$this->linear_whitespace();
7869
		}
7870
		else
7871
		{
7872
			switch ($this->data[$this->position])
7873
			{
7874
				case '"':
7875
					// Workaround for ETags: we have to include the quotes as
7876
					// part of the tag.
7877
					if (strtolower($this->name) === 'etag')
7878
					{
7879
						$this->value .= '"';
7880
						$this->position++;
7881
						$this->state = 'value_char';
7882
						break;
7883
					}
7884
					$this->position++;
7885
					$this->state = 'quote';
7886
					break;
7887

    
7888
				case "\x0A":
7889
					$this->position++;
7890
					$this->state = 'new_line';
7891
					break;
7892

    
7893
				default:
7894
					$this->state = 'value_char';
7895
					break;
7896
			}
7897
		}
7898
	}
7899

    
7900
	/**
7901
	 * Parse a header value while outside quotes
7902
	 */
7903
	protected function value_char()
7904
	{
7905
		$len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
7906
		$this->value .= substr($this->data, $this->position, $len);
7907
		$this->position += $len;
7908
		$this->state = 'value';
7909
	}
7910

    
7911
	/**
7912
	 * See what state to move to while within quoted header values
7913
	 */
7914
	protected function quote()
7915
	{
7916
		if ($this->is_linear_whitespace())
7917
		{
7918
			$this->linear_whitespace();
7919
		}
7920
		else
7921
		{
7922
			switch ($this->data[$this->position])
7923
			{
7924
				case '"':
7925
					$this->position++;
7926
					$this->state = 'value';
7927
					break;
7928

    
7929
				case "\x0A":
7930
					$this->position++;
7931
					$this->state = 'new_line';
7932
					break;
7933

    
7934
				case '\\':
7935
					$this->position++;
7936
					$this->state = 'quote_escaped';
7937
					break;
7938

    
7939
				default:
7940
					$this->state = 'quote_char';
7941
					break;
7942
			}
7943
		}
7944
	}
7945

    
7946
	/**
7947
	 * Parse a header value while within quotes
7948
	 */
7949
	protected function quote_char()
7950
	{
7951
		$len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
7952
		$this->value .= substr($this->data, $this->position, $len);
7953
		$this->position += $len;
7954
		$this->state = 'value';
7955
	}
7956

    
7957
	/**
7958
	 * Parse an escaped character within quotes
7959
	 */
7960
	protected function quote_escaped()
7961
	{
7962
		$this->value .= $this->data[$this->position];
7963
		$this->position++;
7964
		$this->state = 'quote';
7965
	}
7966

    
7967
	/**
7968
	 * Parse the body
7969
	 */
7970
	protected function body()
7971
	{
7972
		$this->body = substr($this->data, $this->position);
7973
		if (!empty($this->headers['transfer-encoding']))
7974
		{
7975
			unset($this->headers['transfer-encoding']);
7976
			$this->state = 'chunked';
7977
		}
7978
		else
7979
		{
7980
			$this->state = 'emit';
7981
		}
7982
	}
7983

    
7984
	/**
7985
	 * Parsed a "Transfer-Encoding: chunked" body
7986
	 */
7987
	protected function chunked()
7988
	{
7989
		if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
7990
		{
7991
			$this->state = 'emit';
7992
			return;
7993
		}
7994

    
7995
		$decoded = '';
7996
		$encoded = $this->body;
7997

    
7998
		while (true)
7999
		{
8000
			$is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
8001
			if (!$is_chunked)
8002
			{
8003
				// Looks like it's not chunked after all
8004
				$this->state = 'emit';
8005
				return;
8006
			}
8007

    
8008
			$length = hexdec(trim($matches[1]));
8009
			if ($length === 0)
8010
			{
8011
				// Ignore trailer headers
8012
				$this->state = 'emit';
8013
				$this->body = $decoded;
8014
				return;
8015
			}
8016

    
8017
			$chunk_length = strlen($matches[0]);
8018
			$decoded .= $part = substr($encoded, $chunk_length, $length);
8019
			$encoded = substr($encoded, $chunk_length + $length + 2);
8020

    
8021
			if (trim($encoded) === '0' || empty($encoded))
8022
			{
8023
				$this->state = 'emit';
8024
				$this->body = $decoded;
8025
				return;
8026
			}
8027
		}
8028
	}
8029
}
8030

    
8031
/**
8032
 * IRI parser/serialiser/normaliser
8033
 *
8034
 * @package SimplePie
8035
 * @subpackage HTTP
8036
 * @author Geoffrey Sneddon
8037
 * @author Steve Minutillo
8038
 * @author Ryan McCue
8039
 * @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue
8040
 * @license http://www.opensource.org/licenses/bsd-license.php
8041
 */
8042
class SimplePie_IRI
8043
{
8044
	/**
8045
	 * Scheme
8046
	 *
8047
	 * @var string
8048
	 */
8049
	protected $scheme = null;
8050

    
8051
	/**
8052
	 * User Information
8053
	 *
8054
	 * @var string
8055
	 */
8056
	protected $iuserinfo = null;
8057

    
8058
	/**
8059
	 * ihost
8060
	 *
8061
	 * @var string
8062
	 */
8063
	protected $ihost = null;
8064

    
8065
	/**
8066
	 * Port
8067
	 *
8068
	 * @var string
8069
	 */
8070
	protected $port = null;
8071

    
8072
	/**
8073
	 * ipath
8074
	 *
8075
	 * @var string
8076
	 */
8077
	protected $ipath = '';
8078

    
8079
	/**
8080
	 * iquery
8081
	 *
8082
	 * @var string
8083
	 */
8084
	protected $iquery = null;
8085

    
8086
	/**
8087
	 * ifragment
8088
	 *
8089
	 * @var string
8090
	 */
8091
	protected $ifragment = null;
8092

    
8093
	/**
8094
	 * Normalization database
8095
	 *
8096
	 * Each key is the scheme, each value is an array with each key as the IRI
8097
	 * part and value as the default value for that part.
8098
	 */
8099
	protected $normalization = array(
8100
		'acap' => array(
8101
			'port' => 674
8102
		),
8103
		'dict' => array(
8104
			'port' => 2628
8105
		),
8106
		'file' => array(
8107
			'ihost' => 'localhost'
8108
		),
8109
		'http' => array(
8110
			'port' => 80,
8111
			'ipath' => '/'
8112
		),
8113
		'https' => array(
8114
			'port' => 443,
8115
			'ipath' => '/'
8116
		),
8117
	);
8118

    
8119
	/**
8120
	 * Return the entire IRI when you try and read the object as a string
8121
	 *
8122
	 * @return string
8123
	 */
8124
	public function __toString()
8125
	{
8126
		return $this->get_iri();
8127
	}
8128

    
8129
	/**
8130
	 * Overload __set() to provide access via properties
8131
	 *
8132
	 * @param string $name Property name
8133
	 * @param mixed $value Property value
8134
	 */
8135
	public function __set($name, $value)
8136
	{
8137
		if (method_exists($this, 'set_' . $name))
8138
		{
8139
			call_user_func(array($this, 'set_' . $name), $value);
8140
		}
8141
		elseif (
8142
			   $name === 'iauthority'
8143
			|| $name === 'iuserinfo'
8144
			|| $name === 'ihost'
8145
			|| $name === 'ipath'
8146
			|| $name === 'iquery'
8147
			|| $name === 'ifragment'
8148
		)
8149
		{
8150
			call_user_func(array($this, 'set_' . substr($name, 1)), $value);
8151
		}
8152
	}
8153

    
8154
	/**
8155
	 * Overload __get() to provide access via properties
8156
	 *
8157
	 * @param string $name Property name
8158
	 * @return mixed
8159
	 */
8160
	public function __get($name)
8161
	{
8162
		// isset() returns false for null, we don't want to do that
8163
		// Also why we use array_key_exists below instead of isset()
8164
		$props = get_object_vars($this);
8165

    
8166
		if (
8167
			$name === 'iri' ||
8168
			$name === 'uri' ||
8169
			$name === 'iauthority' ||
8170
			$name === 'authority'
8171
		)
8172
		{
8173
			$return = $this->{"get_$name"}();
8174
		}
8175
		elseif (array_key_exists($name, $props))
8176
		{
8177
			$return = $this->$name;
8178
		}
8179
		// host -> ihost
8180
		elseif (($prop = 'i' . $name) && array_key_exists($prop, $props))
8181
		{
8182
			$name = $prop;
8183
			$return = $this->$prop;
8184
		}
8185
		// ischeme -> scheme
8186
		elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props))
8187
		{
8188
			$name = $prop;
8189
			$return = $this->$prop;
8190
		}
8191
		else
8192
		{
8193
			trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
8194
			$return = null;
8195
		}
8196

    
8197
		if ($return === null && isset($this->normalization[$this->scheme][$name]))
8198
		{
8199
			return $this->normalization[$this->scheme][$name];
8200
		}
8201
		else
8202
		{
8203
			return $return;
8204
		}
8205
	}
8206

    
8207
	/**
8208
	 * Overload __isset() to provide access via properties
8209
	 *
8210
	 * @param string $name Property name
8211
	 * @return bool
8212
	 */
8213
	public function __isset($name)
8214
	{
8215
		if (method_exists($this, 'get_' . $name) || isset($this->$name))
8216
		{
8217
			return true;
8218
		}
8219
		else
8220
		{
8221
			return false;
8222
		}
8223
	}
8224

    
8225
	/**
8226
	 * Overload __unset() to provide access via properties
8227
	 *
8228
	 * @param string $name Property name
8229
	 */
8230
	public function __unset($name)
8231
	{
8232
		if (method_exists($this, 'set_' . $name))
8233
		{
8234
			call_user_func(array($this, 'set_' . $name), '');
8235
		}
8236
	}
8237

    
8238
	/**
8239
	 * Create a new IRI object, from a specified string
8240
	 *
8241
	 * @param string $iri
8242
	 */
8243
	public function __construct($iri = null)
8244
	{
8245
		$this->set_iri($iri);
8246
	}
8247

    
8248
	/**
8249
	 * Clean up
8250
	 */
8251
	public function __destruct() {
8252
	    $this->set_iri(null, true);
8253
	    $this->set_path(null, true);
8254
	    $this->set_authority(null, true);
8255
	}
8256

    
8257
	/**
8258
	 * Create a new IRI object by resolving a relative IRI
8259
	 *
8260
	 * Returns false if $base is not absolute, otherwise an IRI.
8261
	 *
8262
	 * @param IRI|string $base (Absolute) Base IRI
8263
	 * @param IRI|string $relative Relative IRI
8264
	 * @return IRI|false
8265
	 */
8266
	public static function absolutize($base, $relative)
8267
	{
8268
		if (!($relative instanceof SimplePie_IRI))
8269
		{
8270
			$relative = new SimplePie_IRI($relative);
8271
		}
8272
		if (!$relative->is_valid())
8273
		{
8274
			return false;
8275
		}
8276
		elseif ($relative->scheme !== null)
8277
		{
8278
			return clone $relative;
8279
		}
8280
		else
8281
		{
8282
			if (!($base instanceof SimplePie_IRI))
8283
			{
8284
				$base = new SimplePie_IRI($base);
8285
			}
8286
			if ($base->scheme !== null && $base->is_valid())
8287
			{
8288
				if ($relative->get_iri() !== '')
8289
				{
8290
					if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null)
8291
					{
8292
						$target = clone $relative;
8293
						$target->scheme = $base->scheme;
8294
					}
8295
					else
8296
					{
8297
						$target = new SimplePie_IRI;
8298
						$target->scheme = $base->scheme;
8299
						$target->iuserinfo = $base->iuserinfo;
8300
						$target->ihost = $base->ihost;
8301
						$target->port = $base->port;
8302
						if ($relative->ipath !== '')
8303
						{
8304
							if ($relative->ipath[0] === '/')
8305
							{
8306
								$target->ipath = $relative->ipath;
8307
							}
8308
							elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '')
8309
							{
8310
								$target->ipath = '/' . $relative->ipath;
8311
							}
8312
							elseif (($last_segment = strrpos($base->ipath, '/')) !== false)
8313
							{
8314
								$target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
8315
							}
8316
							else
8317
							{
8318
								$target->ipath = $relative->ipath;
8319
							}
8320
							$target->ipath = $target->remove_dot_segments($target->ipath);
8321
							$target->iquery = $relative->iquery;
8322
						}
8323
						else
8324
						{
8325
							$target->ipath = $base->ipath;
8326
							if ($relative->iquery !== null)
8327
							{
8328
								$target->iquery = $relative->iquery;
8329
							}
8330
							elseif ($base->iquery !== null)
8331
							{
8332
								$target->iquery = $base->iquery;
8333
							}
8334
						}
8335
						$target->ifragment = $relative->ifragment;
8336
					}
8337
				}
8338
				else
8339
				{
8340
					$target = clone $base;
8341
					$target->ifragment = null;
8342
				}
8343
				$target->scheme_normalization();
8344
				return $target;
8345
			}
8346
			else
8347
			{
8348
				return false;
8349
			}
8350
		}
8351
	}
8352

    
8353
	/**
8354
	 * Parse an IRI into scheme/authority/path/query/fragment segments
8355
	 *
8356
	 * @param string $iri
8357
	 * @return array
8358
	 */
8359
	protected function parse_iri($iri)
8360
	{
8361
		$iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
8362
		if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match))
8363
		{
8364
			if ($match[1] === '')
8365
			{
8366
				$match['scheme'] = null;
8367
			}
8368
			if (!isset($match[3]) || $match[3] === '')
8369
			{
8370
				$match['authority'] = null;
8371
			}
8372
			if (!isset($match[5]))
8373
			{
8374
				$match['path'] = '';
8375
			}
8376
			if (!isset($match[6]) || $match[6] === '')
8377
			{
8378
				$match['query'] = null;
8379
			}
8380
			if (!isset($match[8]) || $match[8] === '')
8381
			{
8382
				$match['fragment'] = null;
8383
			}
8384
			return $match;
8385
		}
8386
		else
8387
		{
8388
			// This can occur when a paragraph is accidentally parsed as a URI
8389
			return false;
8390
		}
8391
	}
8392

    
8393
	/**
8394
	 * Remove dot segments from a path
8395
	 *
8396
	 * @param string $input
8397
	 * @return string
8398
	 */
8399
	protected function remove_dot_segments($input)
8400
	{
8401
		$output = '';
8402
		while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
8403
		{
8404
			// A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
8405
			if (strpos($input, '../') === 0)
8406
			{
8407
				$input = substr($input, 3);
8408
			}
8409
			elseif (strpos($input, './') === 0)
8410
			{
8411
				$input = substr($input, 2);
8412
			}
8413
			// 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,
8414
			elseif (strpos($input, '/./') === 0)
8415
			{
8416
				$input = substr($input, 2);
8417
			}
8418
			elseif ($input === '/.')
8419
			{
8420
				$input = '/';
8421
			}
8422
			// 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,
8423
			elseif (strpos($input, '/../') === 0)
8424
			{
8425
				$input = substr($input, 3);
8426
				$output = substr_replace($output, '', strrpos($output, '/'));
8427
			}
8428
			elseif ($input === '/..')
8429
			{
8430
				$input = '/';
8431
				$output = substr_replace($output, '', strrpos($output, '/'));
8432
			}
8433
			// D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
8434
			elseif ($input === '.' || $input === '..')
8435
			{
8436
				$input = '';
8437
			}
8438
			// 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
8439
			elseif (($pos = strpos($input, '/', 1)) !== false)
8440
			{
8441
				$output .= substr($input, 0, $pos);
8442
				$input = substr_replace($input, '', 0, $pos);
8443
			}
8444
			else
8445
			{
8446
				$output .= $input;
8447
				$input = '';
8448
			}
8449
		}
8450
		return $output . $input;
8451
	}
8452

    
8453
	/**
8454
	 * Replace invalid character with percent encoding
8455
	 *
8456
	 * @param string $string Input string
8457
	 * @param string $extra_chars Valid characters not in iunreserved or
8458
	 *                            iprivate (this is ASCII-only)
8459
	 * @param bool $iprivate Allow iprivate
8460
	 * @return string
8461
	 */
8462
	protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
8463
	{
8464
		// Normalize as many pct-encoded sections as possible
8465
		$string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
8466

    
8467
		// Replace invalid percent characters
8468
		$string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
8469

    
8470
		// Add unreserved and % to $extra_chars (the latter is safe because all
8471
		// pct-encoded sections are now valid).
8472
		$extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
8473

    
8474
		// Now replace any bytes that aren't allowed with their pct-encoded versions
8475
		$position = 0;
8476
		$strlen = strlen($string);
8477
		while (($position += strspn($string, $extra_chars, $position)) < $strlen)
8478
		{
8479
			$value = ord($string[$position]);
8480

    
8481
			// Start position
8482
			$start = $position;
8483

    
8484
			// By default we are valid
8485
			$valid = true;
8486

    
8487
			// No one byte sequences are valid due to the while.
8488
			// Two byte sequence:
8489
			if (($value & 0xE0) === 0xC0)
8490
			{
8491
				$character = ($value & 0x1F) << 6;
8492
				$length = 2;
8493
				$remaining = 1;
8494
			}
8495
			// Three byte sequence:
8496
			elseif (($value & 0xF0) === 0xE0)
8497
			{
8498
				$character = ($value & 0x0F) << 12;
8499
				$length = 3;
8500
				$remaining = 2;
8501
			}
8502
			// Four byte sequence:
8503
			elseif (($value & 0xF8) === 0xF0)
8504
			{
8505
				$character = ($value & 0x07) << 18;
8506
				$length = 4;
8507
				$remaining = 3;
8508
			}
8509
			// Invalid byte:
8510
			else
8511
			{
8512
				$valid = false;
8513
				$length = 1;
8514
				$remaining = 0;
8515
			}
8516

    
8517
			if ($remaining)
8518
			{
8519
				if ($position + $length <= $strlen)
8520
				{
8521
					for ($position++; $remaining; $position++)
8522
					{
8523
						$value = ord($string[$position]);
8524

    
8525
						// Check that the byte is valid, then add it to the character:
8526
						if (($value & 0xC0) === 0x80)
8527
						{
8528
							$character |= ($value & 0x3F) << (--$remaining * 6);
8529
						}
8530
						// If it is invalid, count the sequence as invalid and reprocess the current byte:
8531
						else
8532
						{
8533
							$valid = false;
8534
							$position--;
8535
							break;
8536
						}
8537
					}
8538
				}
8539
				else
8540
				{
8541
					$position = $strlen - 1;
8542
					$valid = false;
8543
				}
8544
			}
8545

    
8546
			// Percent encode anything invalid or not in ucschar
8547
			if (
8548
				// Invalid sequences
8549
				!$valid
8550
				// Non-shortest form sequences are invalid
8551
				|| $length > 1 && $character <= 0x7F
8552
				|| $length > 2 && $character <= 0x7FF
8553
				|| $length > 3 && $character <= 0xFFFF
8554
				// Outside of range of ucschar codepoints
8555
				// Noncharacters
8556
				|| ($character & 0xFFFE) === 0xFFFE
8557
				|| $character >= 0xFDD0 && $character <= 0xFDEF
8558
				|| (
8559
					// Everything else not in ucschar
8560
					   $character > 0xD7FF && $character < 0xF900
8561
					|| $character < 0xA0
8562
					|| $character > 0xEFFFD
8563
				)
8564
				&& (
8565
					// Everything not in iprivate, if it applies
8566
					   !$iprivate
8567
					|| $character < 0xE000
8568
					|| $character > 0x10FFFD
8569
				)
8570
			)
8571
			{
8572
				// If we were a character, pretend we weren't, but rather an error.
8573
				if ($valid)
8574
					$position--;
8575

    
8576
				for ($j = $start; $j <= $position; $j++)
8577
				{
8578
					$string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
8579
					$j += 2;
8580
					$position += 2;
8581
					$strlen += 2;
8582
				}
8583
			}
8584
		}
8585

    
8586
		return $string;
8587
	}
8588

    
8589
	/**
8590
	 * Callback function for preg_replace_callback.
8591
	 *
8592
	 * Removes sequences of percent encoded bytes that represent UTF-8
8593
	 * encoded characters in iunreserved
8594
	 *
8595
	 * @param array $match PCRE match
8596
	 * @return string Replacement
8597
	 */
8598
	protected function remove_iunreserved_percent_encoded($match)
8599
	{
8600
		// As we just have valid percent encoded sequences we can just explode
8601
		// and ignore the first member of the returned array (an empty string).
8602
		$bytes = explode('%', $match[0]);
8603

    
8604
		// Initialize the new string (this is what will be returned) and that
8605
		// there are no bytes remaining in the current sequence (unsurprising
8606
		// at the first byte!).
8607
		$string = '';
8608
		$remaining = 0;
8609

    
8610
		// Loop over each and every byte, and set $value to its value
8611
		for ($i = 1, $len = count($bytes); $i < $len; $i++)
8612
		{
8613
			$value = hexdec($bytes[$i]);
8614

    
8615
			// If we're the first byte of sequence:
8616
			if (!$remaining)
8617
			{
8618
				// Start position
8619
				$start = $i;
8620

    
8621
				// By default we are valid
8622
				$valid = true;
8623

    
8624
				// One byte sequence:
8625
				if ($value <= 0x7F)
8626
				{
8627
					$character = $value;
8628
					$length = 1;
8629
				}
8630
				// Two byte sequence:
8631
				elseif (($value & 0xE0) === 0xC0)
8632
				{
8633
					$character = ($value & 0x1F) << 6;
8634
					$length = 2;
8635
					$remaining = 1;
8636
				}
8637
				// Three byte sequence:
8638
				elseif (($value & 0xF0) === 0xE0)
8639
				{
8640
					$character = ($value & 0x0F) << 12;
8641
					$length = 3;
8642
					$remaining = 2;
8643
				}
8644
				// Four byte sequence:
8645
				elseif (($value & 0xF8) === 0xF0)
8646
				{
8647
					$character = ($value & 0x07) << 18;
8648
					$length = 4;
8649
					$remaining = 3;
8650
				}
8651
				// Invalid byte:
8652
				else
8653
				{
8654
					$valid = false;
8655
					$remaining = 0;
8656
				}
8657
			}
8658
			// Continuation byte:
8659
			else
8660
			{
8661
				// Check that the byte is valid, then add it to the character:
8662
				if (($value & 0xC0) === 0x80)
8663
				{
8664
					$remaining--;
8665
					$character |= ($value & 0x3F) << ($remaining * 6);
8666
				}
8667
				// If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
8668
				else
8669
				{
8670
					$valid = false;
8671
					$remaining = 0;
8672
					$i--;
8673
				}
8674
			}
8675

    
8676
			// If we've reached the end of the current byte sequence, append it to Unicode::$data
8677
			if (!$remaining)
8678
			{
8679
				// Percent encode anything invalid or not in iunreserved
8680
				if (
8681
					// Invalid sequences
8682
					!$valid
8683
					// Non-shortest form sequences are invalid
8684
					|| $length > 1 && $character <= 0x7F
8685
					|| $length > 2 && $character <= 0x7FF
8686
					|| $length > 3 && $character <= 0xFFFF
8687
					// Outside of range of iunreserved codepoints
8688
					|| $character < 0x2D
8689
					|| $character > 0xEFFFD
8690
					// Noncharacters
8691
					|| ($character & 0xFFFE) === 0xFFFE
8692
					|| $character >= 0xFDD0 && $character <= 0xFDEF
8693
					// Everything else not in iunreserved (this is all BMP)
8694
					|| $character === 0x2F
8695
					|| $character > 0x39 && $character < 0x41
8696
					|| $character > 0x5A && $character < 0x61
8697
					|| $character > 0x7A && $character < 0x7E
8698
					|| $character > 0x7E && $character < 0xA0
8699
					|| $character > 0xD7FF && $character < 0xF900
8700
				)
8701
				{
8702
					for ($j = $start; $j <= $i; $j++)
8703
					{
8704
						$string .= '%' . strtoupper($bytes[$j]);
8705
					}
8706
				}
8707
				else
8708
				{
8709
					for ($j = $start; $j <= $i; $j++)
8710
					{
8711
						$string .= chr(hexdec($bytes[$j]));
8712
					}
8713
				}
8714
			}
8715
		}
8716

    
8717
		// If we have any bytes left over they are invalid (i.e., we are
8718
		// mid-way through a multi-byte sequence)
8719
		if ($remaining)
8720
		{
8721
			for ($j = $start; $j < $len; $j++)
8722
			{
8723
				$string .= '%' . strtoupper($bytes[$j]);
8724
			}
8725
		}
8726

    
8727
		return $string;
8728
	}
8729

    
8730
	protected function scheme_normalization()
8731
	{
8732
		if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo'])
8733
		{
8734
			$this->iuserinfo = null;
8735
		}
8736
		if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost'])
8737
		{
8738
			$this->ihost = null;
8739
		}
8740
		if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port'])
8741
		{
8742
			$this->port = null;
8743
		}
8744
		if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath'])
8745
		{
8746
			$this->ipath = '';
8747
		}
8748
		if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery'])
8749
		{
8750
			$this->iquery = null;
8751
		}
8752
		if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment'])
8753
		{
8754
			$this->ifragment = null;
8755
		}
8756
	}
8757

    
8758
	/**
8759
	 * Check if the object represents a valid IRI. This needs to be done on each
8760
	 * call as some things change depending on another part of the IRI.
8761
	 *
8762
	 * @return bool
8763
	 */
8764
	public function is_valid()
8765
	{
8766
		if ($this->ipath === '') return true;
8767

    
8768
		$isauthority = $this->iuserinfo !== null || $this->ihost !== null ||
8769
			$this->port !== null;
8770
		if ($isauthority && $this->ipath[0] === '/') return true;
8771

    
8772
		if (!$isauthority && (substr($this->ipath, 0, 2) === '//')) return false;
8773

    
8774
		// Relative urls cannot have a colon in the first path segment (and the
8775
		// slashes themselves are not included so skip the first character).
8776
		if (!$this->scheme && !$isauthority &&
8777
		    strpos($this->ipath, ':') !== false &&
8778
		    strpos($this->ipath, '/', 1) !== false &&
8779
		    strpos($this->ipath, ':') < strpos($this->ipath, '/', 1)) return false;
8780

    
8781
		return true;
8782
	}
8783

    
8784
	/**
8785
	 * Set the entire IRI. Returns true on success, false on failure (if there
8786
	 * are any invalid characters).
8787
	 *
8788
	 * @param string $iri
8789
	 * @return bool
8790
	 */
8791
	public function set_iri($iri, $clear_cache = false)
8792
	{
8793
		static $cache;
8794
		if ($clear_cache) 
8795
		{
8796
			$cache = null;
8797
			return;
8798
		}
8799
		if (!$cache)
8800
		{
8801
			$cache = array();
8802
		}
8803

    
8804
		if ($iri === null)
8805
		{
8806
			return true;
8807
		}
8808
		elseif (isset($cache[$iri]))
8809
		{
8810
			list($this->scheme,
8811
				 $this->iuserinfo,
8812
				 $this->ihost,
8813
				 $this->port,
8814
				 $this->ipath,
8815
				 $this->iquery,
8816
				 $this->ifragment,
8817
				 $return) = $cache[$iri];
8818
			return $return;
8819
		}
8820
		else
8821
		{
8822
			$parsed = $this->parse_iri((string) $iri);
8823
			if (!$parsed)
8824
			{
8825
				return false;
8826
			}
8827

    
8828
			$return = $this->set_scheme($parsed['scheme'])
8829
				&& $this->set_authority($parsed['authority'])
8830
				&& $this->set_path($parsed['path'])
8831
				&& $this->set_query($parsed['query'])
8832
				&& $this->set_fragment($parsed['fragment']);
8833

    
8834
			$cache[$iri] = array($this->scheme,
8835
								 $this->iuserinfo,
8836
								 $this->ihost,
8837
								 $this->port,
8838
								 $this->ipath,
8839
								 $this->iquery,
8840
								 $this->ifragment,
8841
								 $return);
8842
			return $return;
8843
		}
8844
	}
8845

    
8846
	/**
8847
	 * Set the scheme. Returns true on success, false on failure (if there are
8848
	 * any invalid characters).
8849
	 *
8850
	 * @param string $scheme
8851
	 * @return bool
8852
	 */
8853
	public function set_scheme($scheme)
8854
	{
8855
		if ($scheme === null)
8856
		{
8857
			$this->scheme = null;
8858
		}
8859
		elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme))
8860
		{
8861
			$this->scheme = null;
8862
			return false;
8863
		}
8864
		else
8865
		{
8866
			$this->scheme = strtolower($scheme);
8867
		}
8868
		return true;
8869
	}
8870

    
8871
	/**
8872
	 * Set the authority. Returns true on success, false on failure (if there are
8873
	 * any invalid characters).
8874
	 *
8875
	 * @param string $authority
8876
	 * @return bool
8877
	 */
8878
	public function set_authority($authority, $clear_cache = false)
8879
	{
8880
		static $cache;
8881
		if ($clear_cache)
8882
		{
8883
			$cache = null;
8884
			return;
8885
		}
8886
		if (!$cache)
8887
			$cache = array();
8888

    
8889
		if ($authority === null)
8890
		{
8891
			$this->iuserinfo = null;
8892
			$this->ihost = null;
8893
			$this->port = null;
8894
			return true;
8895
		}
8896
		elseif (isset($cache[$authority]))
8897
		{
8898
			list($this->iuserinfo,
8899
				 $this->ihost,
8900
				 $this->port,
8901
				 $return) = $cache[$authority];
8902

    
8903
			return $return;
8904
		}
8905
		else
8906
		{
8907
			$remaining = $authority;
8908
			if (($iuserinfo_end = strrpos($remaining, '@')) !== false)
8909
			{
8910
				$iuserinfo = substr($remaining, 0, $iuserinfo_end);
8911
				$remaining = substr($remaining, $iuserinfo_end + 1);
8912
			}
8913
			else
8914
			{
8915
				$iuserinfo = null;
8916
			}
8917
			if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false)
8918
			{
8919
				if (($port = substr($remaining, $port_start + 1)) === false)
8920
				{
8921
					$port = null;
8922
				}
8923
				$remaining = substr($remaining, 0, $port_start);
8924
			}
8925
			else
8926
			{
8927
				$port = null;
8928
			}
8929

    
8930
			$return = $this->set_userinfo($iuserinfo) &&
8931
					  $this->set_host($remaining) &&
8932
					  $this->set_port($port);
8933

    
8934
			$cache[$authority] = array($this->iuserinfo,
8935
									   $this->ihost,
8936
									   $this->port,
8937
									   $return);
8938

    
8939
			return $return;
8940
		}
8941
	}
8942

    
8943
	/**
8944
	 * Set the iuserinfo.
8945
	 *
8946
	 * @param string $iuserinfo
8947
	 * @return bool
8948
	 */
8949
	public function set_userinfo($iuserinfo)
8950
	{
8951
		if ($iuserinfo === null)
8952
		{
8953
			$this->iuserinfo = null;
8954
		}
8955
		else
8956
		{
8957
			$this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
8958
			$this->scheme_normalization();
8959
		}
8960

    
8961
		return true;
8962
	}
8963

    
8964
	/**
8965
	 * Set the ihost. Returns true on success, false on failure (if there are
8966
	 * any invalid characters).
8967
	 *
8968
	 * @param string $ihost
8969
	 * @return bool
8970
	 */
8971
	public function set_host($ihost)
8972
	{
8973
		if ($ihost === null)
8974
		{
8975
			$this->ihost = null;
8976
			return true;
8977
		}
8978
		elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']')
8979
		{
8980
			if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1)))
8981
			{
8982
				$this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']';
8983
			}
8984
			else
8985
			{
8986
				$this->ihost = null;
8987
				return false;
8988
			}
8989
		}
8990
		else
8991
		{
8992
			$ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
8993

    
8994
			// Lowercase, but ignore pct-encoded sections (as they should
8995
			// remain uppercase). This must be done after the previous step
8996
			// as that can add unescaped characters.
8997
			$position = 0;
8998
			$strlen = strlen($ihost);
8999
			while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen)
9000
			{
9001
				if ($ihost[$position] === '%')
9002
				{
9003
					$position += 3;
9004
				}
9005
				else
9006
				{
9007
					$ihost[$position] = strtolower($ihost[$position]);
9008
					$position++;
9009
				}
9010
			}
9011

    
9012
			$this->ihost = $ihost;
9013
		}
9014

    
9015
		$this->scheme_normalization();
9016

    
9017
		return true;
9018
	}
9019

    
9020
	/**
9021
	 * Set the port. Returns true on success, false on failure (if there are
9022
	 * any invalid characters).
9023
	 *
9024
	 * @param string $port
9025
	 * @return bool
9026
	 */
9027
	public function set_port($port)
9028
	{
9029
		if ($port === null)
9030
		{
9031
			$this->port = null;
9032
			return true;
9033
		}
9034
		elseif (strspn($port, '0123456789') === strlen($port))
9035
		{
9036
			$this->port = (int) $port;
9037
			$this->scheme_normalization();
9038
			return true;
9039
		}
9040
		else
9041
		{
9042
			$this->port = null;
9043
			return false;
9044
		}
9045
	}
9046

    
9047
	/**
9048
	 * Set the ipath.
9049
	 *
9050
	 * @param string $ipath
9051
	 * @return bool
9052
	 */
9053
	public function set_path($ipath, $clear_cache = false)
9054
	{
9055
		static $cache;
9056
		if ($clear_cache) 
9057
		{
9058
			$cache = null;
9059
			return;
9060
		}
9061
		if (!$cache)
9062
		{
9063
			$cache = array();
9064
		}
9065

    
9066
		$ipath = (string) $ipath;
9067

    
9068
		if (isset($cache[$ipath]))
9069
		{
9070
			$this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
9071
		}
9072
		else
9073
		{
9074
			$valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
9075
			$removed = $this->remove_dot_segments($valid);
9076

    
9077
			$cache[$ipath] = array($valid, $removed);
9078
			$this->ipath =  ($this->scheme !== null) ? $removed : $valid;
9079
		}
9080

    
9081
		$this->scheme_normalization();
9082
		return true;
9083
	}
9084

    
9085
	/**
9086
	 * Set the iquery.
9087
	 *
9088
	 * @param string $iquery
9089
	 * @return bool
9090
	 */
9091
	public function set_query($iquery)
9092
	{
9093
		if ($iquery === null)
9094
		{
9095
			$this->iquery = null;
9096
		}
9097
		else
9098
		{
9099
			$this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
9100
			$this->scheme_normalization();
9101
		}
9102
		return true;
9103
	}
9104

    
9105
	/**
9106
	 * Set the ifragment.
9107
	 *
9108
	 * @param string $ifragment
9109
	 * @return bool
9110
	 */
9111
	public function set_fragment($ifragment)
9112
	{
9113
		if ($ifragment === null)
9114
		{
9115
			$this->ifragment = null;
9116
		}
9117
		else
9118
		{
9119
			$this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
9120
			$this->scheme_normalization();
9121
		}
9122
		return true;
9123
	}
9124

    
9125
	/**
9126
	 * Convert an IRI to a URI (or parts thereof)
9127
	 *
9128
	 * @return string
9129
	 */
9130
	public function to_uri($string)
9131
	{
9132
		static $non_ascii;
9133
		if (!$non_ascii)
9134
		{
9135
			$non_ascii = implode('', range("\x80", "\xFF"));
9136
		}
9137

    
9138
		$position = 0;
9139
		$strlen = strlen($string);
9140
		while (($position += strcspn($string, $non_ascii, $position)) < $strlen)
9141
		{
9142
			$string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
9143
			$position += 3;
9144
			$strlen += 2;
9145
		}
9146

    
9147
		return $string;
9148
	}
9149

    
9150
	/**
9151
	 * Get the complete IRI
9152
	 *
9153
	 * @return string
9154
	 */
9155
	public function get_iri()
9156
	{
9157
		if (!$this->is_valid())
9158
		{
9159
			return false;
9160
		}
9161

    
9162
		$iri = '';
9163
		if ($this->scheme !== null)
9164
		{
9165
			$iri .= $this->scheme . ':';
9166
		}
9167
		if (($iauthority = $this->get_iauthority()) !== null)
9168
		{
9169
			$iri .= '//' . $iauthority;
9170
		}
9171
		if ($this->ipath !== '')
9172
		{
9173
			$iri .= $this->ipath;
9174
		}
9175
		elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '')
9176
		{
9177
			$iri .= $this->normalization[$this->scheme]['ipath'];
9178
		}
9179
		if ($this->iquery !== null)
9180
		{
9181
			$iri .= '?' . $this->iquery;
9182
		}
9183
		if ($this->ifragment !== null)
9184
		{
9185
			$iri .= '#' . $this->ifragment;
9186
		}
9187

    
9188
		return $iri;
9189
	}
9190

    
9191
	/**
9192
	 * Get the complete URI
9193
	 *
9194
	 * @return string
9195
	 */
9196
	public function get_uri()
9197
	{
9198
		return $this->to_uri($this->get_iri());
9199
	}
9200

    
9201
	/**
9202
	 * Get the complete iauthority
9203
	 *
9204
	 * @return string
9205
	 */
9206
	protected function get_iauthority()
9207
	{
9208
		if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null)
9209
		{
9210
			$iauthority = '';
9211
			if ($this->iuserinfo !== null)
9212
			{
9213
				$iauthority .= $this->iuserinfo . '@';
9214
			}
9215
			if ($this->ihost !== null)
9216
			{
9217
				$iauthority .= $this->ihost;
9218
			}
9219
			if ($this->port !== null)
9220
			{
9221
				$iauthority .= ':' . $this->port;
9222
			}
9223
			return $iauthority;
9224
		}
9225
		else
9226
		{
9227
			return null;
9228
		}
9229
	}
9230

    
9231
	/**
9232
	 * Get the complete authority
9233
	 *
9234
	 * @return string
9235
	 */
9236
	protected function get_authority()
9237
	{
9238
		$iauthority = $this->get_iauthority();
9239
		if (is_string($iauthority))
9240
			return $this->to_uri($iauthority);
9241
		else
9242
			return $iauthority;
9243
	}
9244
}
9245

    
9246
/**
9247
 * Manages all item-related data
9248
 *
9249
 * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()}
9250
 *
9251
 * This class can be overloaded with {@see SimplePie::set_item_class()}
9252
 *
9253
 * @package SimplePie
9254
 * @subpackage API
9255
 */
9256
class SimplePie_Item
9257
{
9258
	/**
9259
	 * Parent feed
9260
	 *
9261
	 * @access private
9262
	 * @var SimplePie
9263
	 */
9264
	var $feed;
9265

    
9266
	/**
9267
	 * Raw data
9268
	 *
9269
	 * @access private
9270
	 * @var array
9271
	 */
9272
	var $data = array();
9273

    
9274
	/**
9275
	 * Registry object
9276
	 *
9277
	 * @see set_registry
9278
	 * @var SimplePie_Registry
9279
	 */
9280
	protected $registry;
9281

    
9282
	/**
9283
	 * Create a new item object
9284
	 *
9285
	 * This is usually used by {@see SimplePie::get_items} and
9286
	 * {@see SimplePie::get_item}. Avoid creating this manually.
9287
	 *
9288
	 * @param SimplePie $feed Parent feed
9289
	 * @param array $data Raw data
9290
	 */
9291
	public function __construct($feed, $data)
9292
	{
9293
		$this->feed = $feed;
9294
		$this->data = $data;
9295
	}
9296

    
9297
	/**
9298
	 * Set the registry handler
9299
	 *
9300
	 * This is usually used by {@see SimplePie_Registry::create}
9301
	 *
9302
	 * @since 1.3
9303
	 * @param SimplePie_Registry $registry
9304
	 */
9305
	public function set_registry(SimplePie_Registry $registry)
9306
	{
9307
		$this->registry = $registry;
9308
	}
9309

    
9310
	/**
9311
	 * Get a string representation of the item
9312
	 *
9313
	 * @return string
9314
	 */
9315
	public function __toString()
9316
	{
9317
		return md5(serialize($this->data));
9318
	}
9319

    
9320
	/**
9321
	 * Remove items that link back to this before destroying this object
9322
	 */
9323
	public function __destruct()
9324
	{
9325
		if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
9326
		{
9327
			unset($this->feed);
9328
		}
9329
	}
9330

    
9331
	/**
9332
	 * Get data for an item-level element
9333
	 *
9334
	 * This method allows you to get access to ANY element/attribute that is a
9335
	 * sub-element of the item/entry tag.
9336
	 *
9337
	 * See {@see SimplePie::get_feed_tags()} for a description of the return value
9338
	 *
9339
	 * @since 1.0
9340
	 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
9341
	 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
9342
	 * @param string $tag Tag name
9343
	 * @return array
9344
	 */
9345
	public function get_item_tags($namespace, $tag)
9346
	{
9347
		if (isset($this->data['child'][$namespace][$tag]))
9348
		{
9349
			return $this->data['child'][$namespace][$tag];
9350
		}
9351
		else
9352
		{
9353
			return null;
9354
		}
9355
	}
9356

    
9357
	/**
9358
	 * Get the base URL value from the parent feed
9359
	 *
9360
	 * Uses `<xml:base>`
9361
	 *
9362
	 * @param array $element
9363
	 * @return string
9364
	 */
9365
	public function get_base($element = array())
9366
	{
9367
		return $this->feed->get_base($element);
9368
	}
9369

    
9370
	/**
9371
	 * Sanitize feed data
9372
	 *
9373
	 * @access private
9374
	 * @see SimplePie::sanitize()
9375
	 * @param string $data Data to sanitize
9376
	 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
9377
	 * @param string $base Base URL to resolve URLs against
9378
	 * @return string Sanitized data
9379
	 */
9380
	public function sanitize($data, $type, $base = '')
9381
	{
9382
		return $this->feed->sanitize($data, $type, $base);
9383
	}
9384

    
9385
	/**
9386
	 * Get the parent feed
9387
	 *
9388
	 * Note: this may not work as you think for multifeeds!
9389
	 *
9390
	 * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
9391
	 * @since 1.0
9392
	 * @return SimplePie
9393
	 */
9394
	public function get_feed()
9395
	{
9396
		return $this->feed;
9397
	}
9398

    
9399
	/**
9400
	 * Get the unique identifier for the item
9401
	 *
9402
	 * This is usually used when writing code to check for new items in a feed.
9403
	 *
9404
	 * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
9405
	 * for RDF. If none of these are supplied (or `$hash` is true), creates an
9406
	 * MD5 hash based on the permalink, title and content.
9407
	 *
9408
	 * @since Beta 2
9409
	 * @param boolean $hash Should we force using a hash instead of the supplied ID?
9410
	 * @return string
9411
	 */
9412
	public function get_id($hash = false, $fn = '')
9413
	{
9414
		if (!$hash)
9415
		{
9416
			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
9417
			{
9418
				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9419
			}
9420
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
9421
			{
9422
				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9423
			}
9424
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9425
			{
9426
				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9427
			}
9428
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
9429
			{
9430
				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9431
			}
9432
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
9433
			{
9434
				return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9435
			}
9436
			elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about']))
9437
			{
9438
				return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT);
9439
			}
9440
		}
9441
		if ($fn === '' || !is_callable($fn)) $fn = 'md5';
9442
		return call_user_func($fn,
9443
		       $this->get_permalink().$this->get_title().$this->get_content());
9444
	}
9445

    
9446
	/**
9447
	 * Get the title of the item
9448
	 *
9449
	 * Uses `<atom:title>`, `<title>` or `<dc:title>`
9450
	 *
9451
	 * @since Beta 2 (previously called `get_item_title` since 0.8)
9452
	 * @return string|null
9453
	 */
9454
	public function get_title()
9455
	{
9456
		if (!isset($this->data['title']))
9457
		{
9458
			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
9459
			{
9460
				$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]));
9461
			}
9462
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
9463
			{
9464
				$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]));
9465
			}
9466
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
9467
			{
9468
				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9469
			}
9470
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
9471
			{
9472
				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9473
			}
9474
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
9475
			{
9476
				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9477
			}
9478
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
9479
			{
9480
				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9481
			}
9482
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
9483
			{
9484
				$this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9485
			}
9486
			else
9487
			{
9488
				$this->data['title'] = null;
9489
			}
9490
		}
9491
		return $this->data['title'];
9492
	}
9493

    
9494
	/**
9495
	 * Get the content for the item
9496
	 *
9497
	 * Prefers summaries over full content , but will return full content if a
9498
	 * summary does not exist.
9499
	 *
9500
	 * To prefer full content instead, use {@see get_content}
9501
	 *
9502
	 * Uses `<atom:summary>`, `<description>`, `<dc:description>` or
9503
	 * `<itunes:subtitle>`
9504
	 *
9505
	 * @since 0.8
9506
	 * @param boolean $description_only Should we avoid falling back to the content?
9507
	 * @return string|null
9508
	 */
9509
	public function get_description($description_only = false)
9510
	{
9511
		if (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) &&
9512
		    ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
9513
		{
9514
			return $return;
9515
		}
9516
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) &&
9517
		        ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
9518
		{
9519
			return $return;
9520
		}
9521
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) &&
9522
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($tags[0]))))
9523
		{
9524
			return $return;
9525
		}
9526
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) &&
9527
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0]))))
9528
		{
9529
			return $return;
9530
		}
9531
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) &&
9532
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)))
9533
		{
9534
			return $return;
9535
		}
9536
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) &&
9537
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)))
9538
		{
9539
			return $return;
9540
		}
9541
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) &&
9542
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0]))))
9543
		{
9544
			return $return;
9545
		}
9546
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) &&
9547
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)))
9548
		{
9549
			return $return;
9550
		}
9551
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) &&
9552
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML)))
9553
		{
9554
			return $return;
9555
		}
9556

    
9557
		elseif (!$description_only)
9558
		{
9559
			return $this->get_content(true);
9560
		}
9561
		else
9562
		{
9563
			return null;
9564
		}
9565
	}
9566

    
9567
	/**
9568
	 * Get the content for the item
9569
	 *
9570
	 * Prefers full content over summaries, but will return a summary if full
9571
	 * content does not exist.
9572
	 *
9573
	 * To prefer summaries instead, use {@see get_description}
9574
	 *
9575
	 * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
9576
	 *
9577
	 * @since 1.0
9578
	 * @param boolean $content_only Should we avoid falling back to the description?
9579
	 * @return string|null
9580
	 */
9581
	public function get_content($content_only = false)
9582
	{
9583
		if (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) &&
9584
		    ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
9585
		{
9586
			return $return;
9587
		}
9588
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) &&
9589
		        ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
9590
		{
9591
			return $return;
9592
		}
9593
		elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) &&
9594
		        ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0]))))
9595
		{
9596
			return $return;
9597
		}
9598
		elseif (!$content_only)
9599
		{
9600
			return $this->get_description(true);
9601
		}
9602
		else
9603
		{
9604
			return null;
9605
		}
9606
	}
9607
	
9608
	/**
9609
	 * Get the media:thumbnail of the item
9610
	 *
9611
	 * Uses `<media:thumbnail>`
9612
	 *
9613
	 * 
9614
	 * @return array|null
9615
	 */
9616
	public function get_thumbnail()
9617
	{
9618
		if (!isset($this->data['thumbnail']))
9619
		{
9620
			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
9621
			{
9622
				$this->data['thumbnail'] = $return[0]['attribs'][''];
9623
			}
9624
			else
9625
			{
9626
				$this->data['thumbnail'] = null;
9627
			}
9628
		}
9629
		return $this->data['thumbnail'];
9630
	}	
9631

    
9632
	/**
9633
	 * Get a category for the item
9634
	 *
9635
	 * @since Beta 3 (previously called `get_categories()` since Beta 2)
9636
	 * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
9637
	 * @return SimplePie_Category|null
9638
	 */
9639
	public function get_category($key = 0)
9640
	{
9641
		$categories = $this->get_categories();
9642
		if (isset($categories[$key]))
9643
		{
9644
			return $categories[$key];
9645
		}
9646
		else
9647
		{
9648
			return null;
9649
		}
9650
	}
9651

    
9652
	/**
9653
	 * Get all categories for the item
9654
	 *
9655
	 * Uses `<atom:category>`, `<category>` or `<dc:subject>`
9656
	 *
9657
	 * @since Beta 3
9658
	 * @return SimplePie_Category[]|null List of {@see SimplePie_Category} objects
9659
	 */
9660
	public function get_categories()
9661
	{
9662
		$categories = array();
9663

    
9664
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
9665
		{
9666
			$term = null;
9667
			$scheme = null;
9668
			$label = null;
9669
			if (isset($category['attribs']['']['term']))
9670
			{
9671
				$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_HTML);
9672
			}
9673
			if (isset($category['attribs']['']['scheme']))
9674
			{
9675
				$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_HTML);
9676
			}
9677
			if (isset($category['attribs']['']['label']))
9678
			{
9679
				$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_HTML);
9680
			}
9681
			$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
9682
		}
9683
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
9684
		{
9685
			// This is really the label, but keep this as the term also for BC.
9686
			// Label will also work on retrieving because that falls back to term.
9687
			$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_HTML);
9688
			if (isset($category['attribs']['']['domain']))
9689
			{
9690
				$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_HTML);
9691
			}
9692
			else
9693
			{
9694
				$scheme = null;
9695
			}
9696
			$categories[] = $this->registry->create('Category', array($term, $scheme, null));
9697
		}
9698
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
9699
		{
9700
			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null));
9701
		}
9702
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
9703
		{
9704
			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null));
9705
		}
9706

    
9707
		if (!empty($categories))
9708
		{
9709
			return array_unique($categories);
9710
		}
9711
		else
9712
		{
9713
			return null;
9714
		}
9715
	}
9716

    
9717
	/**
9718
	 * Get an author for the item
9719
	 *
9720
	 * @since Beta 2
9721
	 * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
9722
	 * @return SimplePie_Author|null
9723
	 */
9724
	public function get_author($key = 0)
9725
	{
9726
		$authors = $this->get_authors();
9727
		if (isset($authors[$key]))
9728
		{
9729
			return $authors[$key];
9730
		}
9731
		else
9732
		{
9733
			return null;
9734
		}
9735
	}
9736

    
9737
	/**
9738
	 * Get a contributor for the item
9739
	 *
9740
	 * @since 1.1
9741
	 * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
9742
	 * @return SimplePie_Author|null
9743
	 */
9744
	public function get_contributor($key = 0)
9745
	{
9746
		$contributors = $this->get_contributors();
9747
		if (isset($contributors[$key]))
9748
		{
9749
			return $contributors[$key];
9750
		}
9751
		else
9752
		{
9753
			return null;
9754
		}
9755
	}
9756

    
9757
	/**
9758
	 * Get all contributors for the item
9759
	 *
9760
	 * Uses `<atom:contributor>`
9761
	 *
9762
	 * @since 1.1
9763
	 * @return array|null List of {@see SimplePie_Author} objects
9764
	 */
9765
	public function get_contributors()
9766
	{
9767
		$contributors = array();
9768
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
9769
		{
9770
			$name = null;
9771
			$uri = null;
9772
			$email = null;
9773
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9774
			{
9775
				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9776
			}
9777
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9778
			{
9779
				$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]));
9780
			}
9781
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9782
			{
9783
				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9784
			}
9785
			if ($name !== null || $email !== null || $uri !== null)
9786
			{
9787
				$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
9788
			}
9789
		}
9790
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
9791
		{
9792
			$name = null;
9793
			$url = null;
9794
			$email = null;
9795
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9796
			{
9797
				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9798
			}
9799
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9800
			{
9801
				$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]));
9802
			}
9803
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9804
			{
9805
				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9806
			}
9807
			if ($name !== null || $email !== null || $url !== null)
9808
			{
9809
				$contributors[] = $this->registry->create('Author', array($name, $url, $email));
9810
			}
9811
		}
9812

    
9813
		if (!empty($contributors))
9814
		{
9815
			return array_unique($contributors);
9816
		}
9817
		else
9818
		{
9819
			return null;
9820
		}
9821
	}
9822

    
9823
	/**
9824
	 * Get all authors for the item
9825
	 *
9826
	 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
9827
	 *
9828
	 * @since Beta 2
9829
	 * @return array|null List of {@see SimplePie_Author} objects
9830
	 */
9831
	public function get_authors()
9832
	{
9833
		$authors = array();
9834
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
9835
		{
9836
			$name = null;
9837
			$uri = null;
9838
			$email = null;
9839
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9840
			{
9841
				$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9842
			}
9843
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9844
			{
9845
				$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]));
9846
			}
9847
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9848
			{
9849
				$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9850
			}
9851
			if ($name !== null || $email !== null || $uri !== null)
9852
			{
9853
				$authors[] = $this->registry->create('Author', array($name, $uri, $email));
9854
			}
9855
		}
9856
		if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
9857
		{
9858
			$name = null;
9859
			$url = null;
9860
			$email = null;
9861
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9862
			{
9863
				$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9864
			}
9865
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9866
			{
9867
				$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]));
9868
			}
9869
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9870
			{
9871
				$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9872
			}
9873
			if ($name !== null || $email !== null || $url !== null)
9874
			{
9875
				$authors[] = $this->registry->create('Author', array($name, $url, $email));
9876
			}
9877
		}
9878
		if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
9879
		{
9880
			$authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_HTML)));
9881
		}
9882
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
9883
		{
9884
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null));
9885
		}
9886
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
9887
		{
9888
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null));
9889
		}
9890
		foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
9891
		{
9892
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null));
9893
		}
9894

    
9895
		if (!empty($authors))
9896
		{
9897
			return array_unique($authors);
9898
		}
9899
		elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
9900
		{
9901
			return $authors;
9902
		}
9903
		elseif ($authors = $this->feed->get_authors())
9904
		{
9905
			return $authors;
9906
		}
9907
		else
9908
		{
9909
			return null;
9910
		}
9911
	}
9912

    
9913
	/**
9914
	 * Get the copyright info for the item
9915
	 *
9916
	 * Uses `<atom:rights>` or `<dc:rights>`
9917
	 *
9918
	 * @since 1.1
9919
	 * @return string
9920
	 */
9921
	public function get_copyright()
9922
	{
9923
		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
9924
		{
9925
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9926
		}
9927
		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
9928
		{
9929
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9930
		}
9931
		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
9932
		{
9933
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9934
		}
9935
		else
9936
		{
9937
			return null;
9938
		}
9939
	}
9940

    
9941
	/**
9942
	 * Get the posting date/time for the item
9943
	 *
9944
	 * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
9945
	 * `<atom:modified>`, `<pubDate>` or `<dc:date>`
9946
	 *
9947
	 * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9948
	 * {@see get_gmdate}
9949
	 *
9950
	 * @since Beta 2 (previously called `get_item_date` since 0.8)
9951
	 *
9952
	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9953
	 * @return int|string|null
9954
	 */
9955
	public function get_date($date_format = 'j F Y, g:i a')
9956
	{
9957
		if (!isset($this->data['date']))
9958
		{
9959
			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
9960
			{
9961
				$this->data['date']['raw'] = $return[0]['data'];
9962
			}
9963
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
9964
			{
9965
				$this->data['date']['raw'] = $return[0]['data'];
9966
			}
9967
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
9968
			{
9969
				$this->data['date']['raw'] = $return[0]['data'];
9970
			}
9971
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
9972
			{
9973
				$this->data['date']['raw'] = $return[0]['data'];
9974
			}
9975
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9976
			{
9977
				$this->data['date']['raw'] = $return[0]['data'];
9978
			}
9979
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
9980
			{
9981
				$this->data['date']['raw'] = $return[0]['data'];
9982
			}
9983
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
9984
			{
9985
				$this->data['date']['raw'] = $return[0]['data'];
9986
			}
9987
			elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
9988
			{
9989
				$this->data['date']['raw'] = $return[0]['data'];
9990
			}
9991

    
9992
			if (!empty($this->data['date']['raw']))
9993
			{
9994
				$parser = $this->registry->call('Parse_Date', 'get');
9995
				$this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
9996
			}
9997
			else
9998
			{
9999
				$this->data['date'] = null;
10000
			}
10001
		}
10002
		if ($this->data['date'])
10003
		{
10004
			$date_format = (string) $date_format;
10005
			switch ($date_format)
10006
			{
10007
				case '':
10008
					return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
10009

    
10010
				case 'U':
10011
					return $this->data['date']['parsed'];
10012

    
10013
				default:
10014
					return date($date_format, $this->data['date']['parsed']);
10015
			}
10016
		}
10017
		else
10018
		{
10019
			return null;
10020
		}
10021
	}
10022

    
10023
	/**
10024
	 * Get the update date/time for the item
10025
	 *
10026
	 * Uses `<atom:updated>`
10027
	 *
10028
	 * Note: obeys PHP's timezone setting. To get a UTC date/time, use
10029
	 * {@see get_gmdate}
10030
	 *
10031
	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
10032
	 * @return int|string|null
10033
	 */
10034
	public function get_updated_date($date_format = 'j F Y, g:i a')
10035
	{
10036
		if (!isset($this->data['updated']))
10037
		{
10038
			if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
10039
			{
10040
				$this->data['updated']['raw'] = $return[0]['data'];
10041
			}
10042

    
10043
			if (!empty($this->data['updated']['raw']))
10044
			{
10045
				$parser = $this->registry->call('Parse_Date', 'get');
10046
				$this->data['updated']['parsed'] = $parser->parse($this->data['updated']['raw']);
10047
			}
10048
			else
10049
			{
10050
				$this->data['updated'] = null;
10051
			}
10052
		}
10053
		if ($this->data['updated'])
10054
		{
10055
			$date_format = (string) $date_format;
10056
			switch ($date_format)
10057
			{
10058
				case '':
10059
					return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
10060

    
10061
				case 'U':
10062
					return $this->data['updated']['parsed'];
10063

    
10064
				default:
10065
					return date($date_format, $this->data['updated']['parsed']);
10066
			}
10067
		}
10068
		else
10069
		{
10070
			return null;
10071
		}
10072
	}
10073

    
10074
	/**
10075
	 * Get the localized posting date/time for the item
10076
	 *
10077
	 * Returns the date formatted in the localized language. To display in
10078
	 * languages other than the server's default, you need to change the locale
10079
	 * with {@link http://php.net/setlocale setlocale()}. The available
10080
	 * localizations depend on which ones are installed on your web server.
10081
	 *
10082
	 * @since 1.0
10083
	 *
10084
	 * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
10085
	 * @return int|string|null
10086
	 */
10087
	public function get_local_date($date_format = '%c')
10088
	{
10089
		if (!$date_format)
10090
		{
10091
			return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
10092
		}
10093
		elseif (($date = $this->get_date('U')) !== null && $date !== false)
10094
		{
10095
			return strftime($date_format, $date);
10096
		}
10097
		else
10098
		{
10099
			return null;
10100
		}
10101
	}
10102

    
10103
	/**
10104
	 * Get the posting date/time for the item (UTC time)
10105
	 *
10106
	 * @see get_date
10107
	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
10108
	 * @return int|string|null
10109
	 */
10110
	public function get_gmdate($date_format = 'j F Y, g:i a')
10111
	{
10112
		$date = $this->get_date('U');
10113
		if ($date === null)
10114
		{
10115
			return null;
10116
		}
10117

    
10118
		return gmdate($date_format, $date);
10119
	}
10120

    
10121
	/**
10122
	 * Get the update date/time for the item (UTC time)
10123
	 *
10124
	 * @see get_updated_date
10125
	 * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
10126
	 * @return int|string|null
10127
	 */
10128
	public function get_updated_gmdate($date_format = 'j F Y, g:i a')
10129
	{
10130
		$date = $this->get_updated_date('U');
10131
		if ($date === null)
10132
		{
10133
			return null;
10134
		}
10135

    
10136
		return gmdate($date_format, $date);
10137
	}
10138

    
10139
	/**
10140
	 * Get the permalink for the item
10141
	 *
10142
	 * Returns the first link available with a relationship of "alternate".
10143
	 * Identical to {@see get_link()} with key 0
10144
	 *
10145
	 * @see get_link
10146
	 * @since 0.8
10147
	 * @return string|null Permalink URL
10148
	 */
10149
	public function get_permalink()
10150
	{
10151
		$link = $this->get_link();
10152
		$enclosure = $this->get_enclosure(0);
10153
		if ($link !== null)
10154
		{
10155
			return $link;
10156
		}
10157
		elseif ($enclosure !== null)
10158
		{
10159
			return $enclosure->get_link();
10160
		}
10161
		else
10162
		{
10163
			return null;
10164
		}
10165
	}
10166

    
10167
	/**
10168
	 * Get a single link for the item
10169
	 *
10170
	 * @since Beta 3
10171
	 * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
10172
	 * @param string $rel The relationship of the link to return
10173
	 * @return string|null Link URL
10174
	 */
10175
	public function get_link($key = 0, $rel = 'alternate')
10176
	{
10177
		$links = $this->get_links($rel);
10178
		if ($links[$key] !== null)
10179
		{
10180
			return $links[$key];
10181
		}
10182
		else
10183
		{
10184
			return null;
10185
		}
10186
	}
10187

    
10188
	/**
10189
	 * Get all links for the item
10190
	 *
10191
	 * Uses `<atom:link>`, `<link>` or `<guid>`
10192
	 *
10193
	 * @since Beta 2
10194
	 * @param string $rel The relationship of links to return
10195
	 * @return array|null Links found for the item (strings)
10196
	 */
10197
	public function get_links($rel = 'alternate')
10198
	{
10199
		if (!isset($this->data['links']))
10200
		{
10201
			$this->data['links'] = array();
10202
			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
10203
			{
10204
				if (isset($link['attribs']['']['href']))
10205
				{
10206
					$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
10207
					$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
10208

    
10209
				}
10210
			}
10211
			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
10212
			{
10213
				if (isset($link['attribs']['']['href']))
10214
				{
10215
					$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
10216
					$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
10217
				}
10218
			}
10219
			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
10220
			{
10221
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
10222
			}
10223
			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
10224
			{
10225
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
10226
			}
10227
			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
10228
			{
10229
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
10230
			}
10231
			if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
10232
			{
10233
				if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
10234
				{
10235
					$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
10236
				}
10237
			}
10238

    
10239
			$keys = array_keys($this->data['links']);
10240
			foreach ($keys as $key)
10241
			{
10242
				if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
10243
				{
10244
					if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
10245
					{
10246
						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
10247
						$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
10248
					}
10249
					else
10250
					{
10251
						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
10252
					}
10253
				}
10254
				elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
10255
				{
10256
					$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
10257
				}
10258
				$this->data['links'][$key] = array_unique($this->data['links'][$key]);
10259
			}
10260
		}
10261
		if (isset($this->data['links'][$rel]))
10262
		{
10263
			return $this->data['links'][$rel];
10264
		}
10265
		else
10266
		{
10267
			return null;
10268
		}
10269
	}
10270

    
10271
	/**
10272
	 * Get an enclosure from the item
10273
	 *
10274
	 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10275
	 *
10276
	 * @since Beta 2
10277
	 * @todo Add ability to prefer one type of content over another (in a media group).
10278
	 * @param int $key The enclosure that you want to return.  Remember that arrays begin with 0, not 1
10279
	 * @return SimplePie_Enclosure|null
10280
	 */
10281
	public function get_enclosure($key = 0, $prefer = null)
10282
	{
10283
		$enclosures = $this->get_enclosures();
10284
		if (isset($enclosures[$key]))
10285
		{
10286
			return $enclosures[$key];
10287
		}
10288
		else
10289
		{
10290
			return null;
10291
		}
10292
	}
10293

    
10294
	/**
10295
	 * Get all available enclosures (podcasts, etc.)
10296
	 *
10297
	 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10298
	 *
10299
	 * At this point, we're pretty much assuming that all enclosures for an item
10300
	 * are the same content.  Anything else is too complicated to
10301
	 * properly support.
10302
	 *
10303
	 * @since Beta 2
10304
	 * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
10305
	 * @todo If an element exists at a level, but its value is empty, we should fall back to the value from the parent (if it exists).
10306
	 * @return SimplePie_Enclosure[]|null List of SimplePie_Enclosure items
10307
	 */
10308
	public function get_enclosures()
10309
	{
10310
		if (!isset($this->data['enclosures']))
10311
		{
10312
			$this->data['enclosures'] = array();
10313

    
10314
			// Elements
10315
			$captions_parent = null;
10316
			$categories_parent = null;
10317
			$copyrights_parent = null;
10318
			$credits_parent = null;
10319
			$description_parent = null;
10320
			$duration_parent = null;
10321
			$hashes_parent = null;
10322
			$keywords_parent = null;
10323
			$player_parent = null;
10324
			$ratings_parent = null;
10325
			$restrictions_parent = null;
10326
			$thumbnails_parent = null;
10327
			$title_parent = null;
10328

    
10329
			// Let's do the channel and item-level ones first, and just re-use them if we need to.
10330
			$parent = $this->get_feed();
10331

    
10332
			// CAPTIONS
10333
			if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10334
			{
10335
				foreach ($captions as $caption)
10336
				{
10337
					$caption_type = null;
10338
					$caption_lang = null;
10339
					$caption_startTime = null;
10340
					$caption_endTime = null;
10341
					$caption_text = null;
10342
					if (isset($caption['attribs']['']['type']))
10343
					{
10344
						$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10345
					}
10346
					if (isset($caption['attribs']['']['lang']))
10347
					{
10348
						$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10349
					}
10350
					if (isset($caption['attribs']['']['start']))
10351
					{
10352
						$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10353
					}
10354
					if (isset($caption['attribs']['']['end']))
10355
					{
10356
						$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10357
					}
10358
					if (isset($caption['data']))
10359
					{
10360
						$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10361
					}
10362
					$captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10363
				}
10364
			}
10365
			elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10366
			{
10367
				foreach ($captions as $caption)
10368
				{
10369
					$caption_type = null;
10370
					$caption_lang = null;
10371
					$caption_startTime = null;
10372
					$caption_endTime = null;
10373
					$caption_text = null;
10374
					if (isset($caption['attribs']['']['type']))
10375
					{
10376
						$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10377
					}
10378
					if (isset($caption['attribs']['']['lang']))
10379
					{
10380
						$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10381
					}
10382
					if (isset($caption['attribs']['']['start']))
10383
					{
10384
						$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10385
					}
10386
					if (isset($caption['attribs']['']['end']))
10387
					{
10388
						$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10389
					}
10390
					if (isset($caption['data']))
10391
					{
10392
						$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10393
					}
10394
					$captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10395
				}
10396
			}
10397
			if (is_array($captions_parent))
10398
			{
10399
				$captions_parent = array_values(array_unique($captions_parent));
10400
			}
10401

    
10402
			// CATEGORIES
10403
			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10404
			{
10405
				$term = null;
10406
				$scheme = null;
10407
				$label = null;
10408
				if (isset($category['data']))
10409
				{
10410
					$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10411
				}
10412
				if (isset($category['attribs']['']['scheme']))
10413
				{
10414
					$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10415
				}
10416
				else
10417
				{
10418
					$scheme = 'http://search.yahoo.com/mrss/category_schema';
10419
				}
10420
				if (isset($category['attribs']['']['label']))
10421
				{
10422
					$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10423
				}
10424
				$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10425
			}
10426
			foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10427
			{
10428
				$term = null;
10429
				$scheme = null;
10430
				$label = null;
10431
				if (isset($category['data']))
10432
				{
10433
					$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10434
				}
10435
				if (isset($category['attribs']['']['scheme']))
10436
				{
10437
					$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10438
				}
10439
				else
10440
				{
10441
					$scheme = 'http://search.yahoo.com/mrss/category_schema';
10442
				}
10443
				if (isset($category['attribs']['']['label']))
10444
				{
10445
					$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10446
				}
10447
				$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10448
			}
10449
			foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
10450
			{
10451
				$term = null;
10452
				$scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
10453
				$label = null;
10454
				if (isset($category['attribs']['']['text']))
10455
				{
10456
					$label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10457
				}
10458
				$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10459

    
10460
				if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
10461
				{
10462
					foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
10463
					{
10464
						if (isset($subcategory['attribs']['']['text']))
10465
						{
10466
							$label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10467
						}
10468
						$categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10469
					}
10470
				}
10471
			}
10472
			if (is_array($categories_parent))
10473
			{
10474
				$categories_parent = array_values(array_unique($categories_parent));
10475
			}
10476

    
10477
			// COPYRIGHT
10478
			if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10479
			{
10480
				$copyright_url = null;
10481
				$copyright_label = null;
10482
				if (isset($copyright[0]['attribs']['']['url']))
10483
				{
10484
					$copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10485
				}
10486
				if (isset($copyright[0]['data']))
10487
				{
10488
					$copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10489
				}
10490
				$copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10491
			}
10492
			elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10493
			{
10494
				$copyright_url = null;
10495
				$copyright_label = null;
10496
				if (isset($copyright[0]['attribs']['']['url']))
10497
				{
10498
					$copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10499
				}
10500
				if (isset($copyright[0]['data']))
10501
				{
10502
					$copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10503
				}
10504
				$copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10505
			}
10506

    
10507
			// CREDITS
10508
			if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10509
			{
10510
				foreach ($credits as $credit)
10511
				{
10512
					$credit_role = null;
10513
					$credit_scheme = null;
10514
					$credit_name = null;
10515
					if (isset($credit['attribs']['']['role']))
10516
					{
10517
						$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10518
					}
10519
					if (isset($credit['attribs']['']['scheme']))
10520
					{
10521
						$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10522
					}
10523
					else
10524
					{
10525
						$credit_scheme = 'urn:ebu';
10526
					}
10527
					if (isset($credit['data']))
10528
					{
10529
						$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10530
					}
10531
					$credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10532
				}
10533
			}
10534
			elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10535
			{
10536
				foreach ($credits as $credit)
10537
				{
10538
					$credit_role = null;
10539
					$credit_scheme = null;
10540
					$credit_name = null;
10541
					if (isset($credit['attribs']['']['role']))
10542
					{
10543
						$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10544
					}
10545
					if (isset($credit['attribs']['']['scheme']))
10546
					{
10547
						$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10548
					}
10549
					else
10550
					{
10551
						$credit_scheme = 'urn:ebu';
10552
					}
10553
					if (isset($credit['data']))
10554
					{
10555
						$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10556
					}
10557
					$credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10558
				}
10559
			}
10560
			if (is_array($credits_parent))
10561
			{
10562
				$credits_parent = array_values(array_unique($credits_parent));
10563
			}
10564

    
10565
			// DESCRIPTION
10566
			if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10567
			{
10568
				if (isset($description_parent[0]['data']))
10569
				{
10570
					$description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10571
				}
10572
			}
10573
			elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10574
			{
10575
				if (isset($description_parent[0]['data']))
10576
				{
10577
					$description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10578
				}
10579
			}
10580

    
10581
			// DURATION
10582
			if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
10583
			{
10584
				$seconds = null;
10585
				$minutes = null;
10586
				$hours = null;
10587
				if (isset($duration_parent[0]['data']))
10588
				{
10589
					$temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10590
					if (sizeof($temp) > 0)
10591
					{
10592
						$seconds = (int) array_pop($temp);
10593
					}
10594
					if (sizeof($temp) > 0)
10595
					{
10596
						$minutes = (int) array_pop($temp);
10597
						$seconds += $minutes * 60;
10598
					}
10599
					if (sizeof($temp) > 0)
10600
					{
10601
						$hours = (int) array_pop($temp);
10602
						$seconds += $hours * 3600;
10603
					}
10604
					unset($temp);
10605
					$duration_parent = $seconds;
10606
				}
10607
			}
10608

    
10609
			// HASHES
10610
			if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10611
			{
10612
				foreach ($hashes_iterator as $hash)
10613
				{
10614
					$value = null;
10615
					$algo = null;
10616
					if (isset($hash['data']))
10617
					{
10618
						$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10619
					}
10620
					if (isset($hash['attribs']['']['algo']))
10621
					{
10622
						$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10623
					}
10624
					else
10625
					{
10626
						$algo = 'md5';
10627
					}
10628
					$hashes_parent[] = $algo.':'.$value;
10629
				}
10630
			}
10631
			elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10632
			{
10633
				foreach ($hashes_iterator as $hash)
10634
				{
10635
					$value = null;
10636
					$algo = null;
10637
					if (isset($hash['data']))
10638
					{
10639
						$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10640
					}
10641
					if (isset($hash['attribs']['']['algo']))
10642
					{
10643
						$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10644
					}
10645
					else
10646
					{
10647
						$algo = 'md5';
10648
					}
10649
					$hashes_parent[] = $algo.':'.$value;
10650
				}
10651
			}
10652
			if (is_array($hashes_parent))
10653
			{
10654
				$hashes_parent = array_values(array_unique($hashes_parent));
10655
			}
10656

    
10657
			// KEYWORDS
10658
			if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10659
			{
10660
				if (isset($keywords[0]['data']))
10661
				{
10662
					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10663
					foreach ($temp as $word)
10664
					{
10665
						$keywords_parent[] = trim($word);
10666
					}
10667
				}
10668
				unset($temp);
10669
			}
10670
			elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10671
			{
10672
				if (isset($keywords[0]['data']))
10673
				{
10674
					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10675
					foreach ($temp as $word)
10676
					{
10677
						$keywords_parent[] = trim($word);
10678
					}
10679
				}
10680
				unset($temp);
10681
			}
10682
			elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10683
			{
10684
				if (isset($keywords[0]['data']))
10685
				{
10686
					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10687
					foreach ($temp as $word)
10688
					{
10689
						$keywords_parent[] = trim($word);
10690
					}
10691
				}
10692
				unset($temp);
10693
			}
10694
			elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10695
			{
10696
				if (isset($keywords[0]['data']))
10697
				{
10698
					$temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10699
					foreach ($temp as $word)
10700
					{
10701
						$keywords_parent[] = trim($word);
10702
					}
10703
				}
10704
				unset($temp);
10705
			}
10706
			if (is_array($keywords_parent))
10707
			{
10708
				$keywords_parent = array_values(array_unique($keywords_parent));
10709
			}
10710

    
10711
			// PLAYER
10712
			if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10713
			{
10714
				if (isset($player_parent[0]['attribs']['']['url']))
10715
				{
10716
					$player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10717
				}
10718
			}
10719
			elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10720
			{
10721
				if (isset($player_parent[0]['attribs']['']['url']))
10722
				{
10723
					$player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10724
				}
10725
			}
10726

    
10727
			// RATINGS
10728
			if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10729
			{
10730
				foreach ($ratings as $rating)
10731
				{
10732
					$rating_scheme = null;
10733
					$rating_value = null;
10734
					if (isset($rating['attribs']['']['scheme']))
10735
					{
10736
						$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10737
					}
10738
					else
10739
					{
10740
						$rating_scheme = 'urn:simple';
10741
					}
10742
					if (isset($rating['data']))
10743
					{
10744
						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10745
					}
10746
					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10747
				}
10748
			}
10749
			elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10750
			{
10751
				foreach ($ratings as $rating)
10752
				{
10753
					$rating_scheme = 'urn:itunes';
10754
					$rating_value = null;
10755
					if (isset($rating['data']))
10756
					{
10757
						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10758
					}
10759
					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10760
				}
10761
			}
10762
			elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10763
			{
10764
				foreach ($ratings as $rating)
10765
				{
10766
					$rating_scheme = null;
10767
					$rating_value = null;
10768
					if (isset($rating['attribs']['']['scheme']))
10769
					{
10770
						$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10771
					}
10772
					else
10773
					{
10774
						$rating_scheme = 'urn:simple';
10775
					}
10776
					if (isset($rating['data']))
10777
					{
10778
						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10779
					}
10780
					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10781
				}
10782
			}
10783
			elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10784
			{
10785
				foreach ($ratings as $rating)
10786
				{
10787
					$rating_scheme = 'urn:itunes';
10788
					$rating_value = null;
10789
					if (isset($rating['data']))
10790
					{
10791
						$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10792
					}
10793
					$ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10794
				}
10795
			}
10796
			if (is_array($ratings_parent))
10797
			{
10798
				$ratings_parent = array_values(array_unique($ratings_parent));
10799
			}
10800

    
10801
			// RESTRICTIONS
10802
			if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10803
			{
10804
				foreach ($restrictions as $restriction)
10805
				{
10806
					$restriction_relationship = null;
10807
					$restriction_type = null;
10808
					$restriction_value = null;
10809
					if (isset($restriction['attribs']['']['relationship']))
10810
					{
10811
						$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10812
					}
10813
					if (isset($restriction['attribs']['']['type']))
10814
					{
10815
						$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10816
					}
10817
					if (isset($restriction['data']))
10818
					{
10819
						$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10820
					}
10821
					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10822
				}
10823
			}
10824
			elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10825
			{
10826
				foreach ($restrictions as $restriction)
10827
				{
10828
					$restriction_relationship = 'allow';
10829
					$restriction_type = null;
10830
					$restriction_value = 'itunes';
10831
					if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10832
					{
10833
						$restriction_relationship = 'deny';
10834
					}
10835
					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10836
				}
10837
			}
10838
			elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10839
			{
10840
				foreach ($restrictions as $restriction)
10841
				{
10842
					$restriction_relationship = null;
10843
					$restriction_type = null;
10844
					$restriction_value = null;
10845
					if (isset($restriction['attribs']['']['relationship']))
10846
					{
10847
						$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10848
					}
10849
					if (isset($restriction['attribs']['']['type']))
10850
					{
10851
						$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10852
					}
10853
					if (isset($restriction['data']))
10854
					{
10855
						$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10856
					}
10857
					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10858
				}
10859
			}
10860
			elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10861
			{
10862
				foreach ($restrictions as $restriction)
10863
				{
10864
					$restriction_relationship = 'allow';
10865
					$restriction_type = null;
10866
					$restriction_value = 'itunes';
10867
					if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10868
					{
10869
						$restriction_relationship = 'deny';
10870
					}
10871
					$restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10872
				}
10873
			}
10874
			if (is_array($restrictions_parent))
10875
			{
10876
				$restrictions_parent = array_values(array_unique($restrictions_parent));
10877
			}
10878
			else
10879
			{
10880
				$restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default'));
10881
			}
10882

    
10883
			// THUMBNAILS
10884
			if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10885
			{
10886
				foreach ($thumbnails as $thumbnail)
10887
				{
10888
					if (isset($thumbnail['attribs']['']['url']))
10889
					{
10890
						$thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10891
					}
10892
				}
10893
			}
10894
			elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10895
			{
10896
				foreach ($thumbnails as $thumbnail)
10897
				{
10898
					if (isset($thumbnail['attribs']['']['url']))
10899
					{
10900
						$thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10901
					}
10902
				}
10903
			}
10904

    
10905
			// TITLES
10906
			if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10907
			{
10908
				if (isset($title_parent[0]['data']))
10909
				{
10910
					$title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10911
				}
10912
			}
10913
			elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10914
			{
10915
				if (isset($title_parent[0]['data']))
10916
				{
10917
					$title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10918
				}
10919
			}
10920

    
10921
			// Clear the memory
10922
			unset($parent);
10923

    
10924
			// Attributes
10925
			$bitrate = null;
10926
			$channels = null;
10927
			$duration = null;
10928
			$expression = null;
10929
			$framerate = null;
10930
			$height = null;
10931
			$javascript = null;
10932
			$lang = null;
10933
			$length = null;
10934
			$medium = null;
10935
			$samplingrate = null;
10936
			$type = null;
10937
			$url = null;
10938
			$width = null;
10939

    
10940
			// Elements
10941
			$captions = null;
10942
			$categories = null;
10943
			$copyrights = null;
10944
			$credits = null;
10945
			$description = null;
10946
			$hashes = null;
10947
			$keywords = null;
10948
			$player = null;
10949
			$ratings = null;
10950
			$restrictions = null;
10951
			$thumbnails = null;
10952
			$title = null;
10953

    
10954
			// If we have media:group tags, loop through them.
10955
			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
10956
			{
10957
				if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
10958
				{
10959
					// If we have media:content tags, loop through them.
10960
					foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
10961
					{
10962
						if (isset($content['attribs']['']['url']))
10963
						{
10964
							// Attributes
10965
							$bitrate = null;
10966
							$channels = null;
10967
							$duration = null;
10968
							$expression = null;
10969
							$framerate = null;
10970
							$height = null;
10971
							$javascript = null;
10972
							$lang = null;
10973
							$length = null;
10974
							$medium = null;
10975
							$samplingrate = null;
10976
							$type = null;
10977
							$url = null;
10978
							$width = null;
10979

    
10980
							// Elements
10981
							$captions = null;
10982
							$categories = null;
10983
							$copyrights = null;
10984
							$credits = null;
10985
							$description = null;
10986
							$hashes = null;
10987
							$keywords = null;
10988
							$player = null;
10989
							$ratings = null;
10990
							$restrictions = null;
10991
							$thumbnails = null;
10992
							$title = null;
10993

    
10994
							// Start checking the attributes of media:content
10995
							if (isset($content['attribs']['']['bitrate']))
10996
							{
10997
								$bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10998
							}
10999
							if (isset($content['attribs']['']['channels']))
11000
							{
11001
								$channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
11002
							}
11003
							if (isset($content['attribs']['']['duration']))
11004
							{
11005
								$duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
11006
							}
11007
							else
11008
							{
11009
								$duration = $duration_parent;
11010
							}
11011
							if (isset($content['attribs']['']['expression']))
11012
							{
11013
								$expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
11014
							}
11015
							if (isset($content['attribs']['']['framerate']))
11016
							{
11017
								$framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
11018
							}
11019
							if (isset($content['attribs']['']['height']))
11020
							{
11021
								$height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
11022
							}
11023
							if (isset($content['attribs']['']['lang']))
11024
							{
11025
								$lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11026
							}
11027
							if (isset($content['attribs']['']['fileSize']))
11028
							{
11029
								$length = ceil($content['attribs']['']['fileSize']);
11030
							}
11031
							if (isset($content['attribs']['']['medium']))
11032
							{
11033
								$medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
11034
							}
11035
							if (isset($content['attribs']['']['samplingrate']))
11036
							{
11037
								$samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11038
							}
11039
							if (isset($content['attribs']['']['type']))
11040
							{
11041
								$type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11042
							}
11043
							if (isset($content['attribs']['']['width']))
11044
							{
11045
								$width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
11046
							}
11047
							$url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11048

    
11049
							// Checking the other optional media: elements. Priority: media:content, media:group, item, channel
11050

    
11051
							// CAPTIONS
11052
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
11053
							{
11054
								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
11055
								{
11056
									$caption_type = null;
11057
									$caption_lang = null;
11058
									$caption_startTime = null;
11059
									$caption_endTime = null;
11060
									$caption_text = null;
11061
									if (isset($caption['attribs']['']['type']))
11062
									{
11063
										$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11064
									}
11065
									if (isset($caption['attribs']['']['lang']))
11066
									{
11067
										$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11068
									}
11069
									if (isset($caption['attribs']['']['start']))
11070
									{
11071
										$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
11072
									}
11073
									if (isset($caption['attribs']['']['end']))
11074
									{
11075
										$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
11076
									}
11077
									if (isset($caption['data']))
11078
									{
11079
										$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11080
									}
11081
									$captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
11082
								}
11083
								if (is_array($captions))
11084
								{
11085
									$captions = array_values(array_unique($captions));
11086
								}
11087
							}
11088
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
11089
							{
11090
								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
11091
								{
11092
									$caption_type = null;
11093
									$caption_lang = null;
11094
									$caption_startTime = null;
11095
									$caption_endTime = null;
11096
									$caption_text = null;
11097
									if (isset($caption['attribs']['']['type']))
11098
									{
11099
										$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11100
									}
11101
									if (isset($caption['attribs']['']['lang']))
11102
									{
11103
										$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11104
									}
11105
									if (isset($caption['attribs']['']['start']))
11106
									{
11107
										$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
11108
									}
11109
									if (isset($caption['attribs']['']['end']))
11110
									{
11111
										$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
11112
									}
11113
									if (isset($caption['data']))
11114
									{
11115
										$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11116
									}
11117
									$captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
11118
								}
11119
								if (is_array($captions))
11120
								{
11121
									$captions = array_values(array_unique($captions));
11122
								}
11123
							}
11124
							else
11125
							{
11126
								$captions = $captions_parent;
11127
							}
11128

    
11129
							// CATEGORIES
11130
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
11131
							{
11132
								foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
11133
								{
11134
									$term = null;
11135
									$scheme = null;
11136
									$label = null;
11137
									if (isset($category['data']))
11138
									{
11139
										$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11140
									}
11141
									if (isset($category['attribs']['']['scheme']))
11142
									{
11143
										$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11144
									}
11145
									else
11146
									{
11147
										$scheme = 'http://search.yahoo.com/mrss/category_schema';
11148
									}
11149
									if (isset($category['attribs']['']['label']))
11150
									{
11151
										$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
11152
									}
11153
									$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
11154
								}
11155
							}
11156
							if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
11157
							{
11158
								foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
11159
								{
11160
									$term = null;
11161
									$scheme = null;
11162
									$label = null;
11163
									if (isset($category['data']))
11164
									{
11165
										$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11166
									}
11167
									if (isset($category['attribs']['']['scheme']))
11168
									{
11169
										$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11170
									}
11171
									else
11172
									{
11173
										$scheme = 'http://search.yahoo.com/mrss/category_schema';
11174
									}
11175
									if (isset($category['attribs']['']['label']))
11176
									{
11177
										$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
11178
									}
11179
									$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
11180
								}
11181
							}
11182
							if (is_array($categories) && is_array($categories_parent))
11183
							{
11184
								$categories = array_values(array_unique(array_merge($categories, $categories_parent)));
11185
							}
11186
							elseif (is_array($categories))
11187
							{
11188
								$categories = array_values(array_unique($categories));
11189
							}
11190
							elseif (is_array($categories_parent))
11191
							{
11192
								$categories = array_values(array_unique($categories_parent));
11193
							}
11194

    
11195
							// COPYRIGHTS
11196
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
11197
							{
11198
								$copyright_url = null;
11199
								$copyright_label = null;
11200
								if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
11201
								{
11202
									$copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
11203
								}
11204
								if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
11205
								{
11206
									$copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11207
								}
11208
								$copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
11209
							}
11210
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
11211
							{
11212
								$copyright_url = null;
11213
								$copyright_label = null;
11214
								if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
11215
								{
11216
									$copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
11217
								}
11218
								if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
11219
								{
11220
									$copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11221
								}
11222
								$copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
11223
							}
11224
							else
11225
							{
11226
								$copyrights = $copyrights_parent;
11227
							}
11228

    
11229
							// CREDITS
11230
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11231
							{
11232
								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11233
								{
11234
									$credit_role = null;
11235
									$credit_scheme = null;
11236
									$credit_name = null;
11237
									if (isset($credit['attribs']['']['role']))
11238
									{
11239
										$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11240
									}
11241
									if (isset($credit['attribs']['']['scheme']))
11242
									{
11243
										$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11244
									}
11245
									else
11246
									{
11247
										$credit_scheme = 'urn:ebu';
11248
									}
11249
									if (isset($credit['data']))
11250
									{
11251
										$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11252
									}
11253
									$credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11254
								}
11255
								if (is_array($credits))
11256
								{
11257
									$credits = array_values(array_unique($credits));
11258
								}
11259
							}
11260
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11261
							{
11262
								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11263
								{
11264
									$credit_role = null;
11265
									$credit_scheme = null;
11266
									$credit_name = null;
11267
									if (isset($credit['attribs']['']['role']))
11268
									{
11269
										$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11270
									}
11271
									if (isset($credit['attribs']['']['scheme']))
11272
									{
11273
										$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11274
									}
11275
									else
11276
									{
11277
										$credit_scheme = 'urn:ebu';
11278
									}
11279
									if (isset($credit['data']))
11280
									{
11281
										$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11282
									}
11283
									$credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11284
								}
11285
								if (is_array($credits))
11286
								{
11287
									$credits = array_values(array_unique($credits));
11288
								}
11289
							}
11290
							else
11291
							{
11292
								$credits = $credits_parent;
11293
							}
11294

    
11295
							// DESCRIPTION
11296
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11297
							{
11298
								$description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11299
							}
11300
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11301
							{
11302
								$description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11303
							}
11304
							else
11305
							{
11306
								$description = $description_parent;
11307
							}
11308

    
11309
							// HASHES
11310
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11311
							{
11312
								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11313
								{
11314
									$value = null;
11315
									$algo = null;
11316
									if (isset($hash['data']))
11317
									{
11318
										$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11319
									}
11320
									if (isset($hash['attribs']['']['algo']))
11321
									{
11322
										$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11323
									}
11324
									else
11325
									{
11326
										$algo = 'md5';
11327
									}
11328
									$hashes[] = $algo.':'.$value;
11329
								}
11330
								if (is_array($hashes))
11331
								{
11332
									$hashes = array_values(array_unique($hashes));
11333
								}
11334
							}
11335
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11336
							{
11337
								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11338
								{
11339
									$value = null;
11340
									$algo = null;
11341
									if (isset($hash['data']))
11342
									{
11343
										$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11344
									}
11345
									if (isset($hash['attribs']['']['algo']))
11346
									{
11347
										$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11348
									}
11349
									else
11350
									{
11351
										$algo = 'md5';
11352
									}
11353
									$hashes[] = $algo.':'.$value;
11354
								}
11355
								if (is_array($hashes))
11356
								{
11357
									$hashes = array_values(array_unique($hashes));
11358
								}
11359
							}
11360
							else
11361
							{
11362
								$hashes = $hashes_parent;
11363
							}
11364

    
11365
							// KEYWORDS
11366
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11367
							{
11368
								if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11369
								{
11370
									$temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11371
									foreach ($temp as $word)
11372
									{
11373
										$keywords[] = trim($word);
11374
									}
11375
									unset($temp);
11376
								}
11377
								if (is_array($keywords))
11378
								{
11379
									$keywords = array_values(array_unique($keywords));
11380
								}
11381
							}
11382
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11383
							{
11384
								if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11385
								{
11386
									$temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11387
									foreach ($temp as $word)
11388
									{
11389
										$keywords[] = trim($word);
11390
									}
11391
									unset($temp);
11392
								}
11393
								if (is_array($keywords))
11394
								{
11395
									$keywords = array_values(array_unique($keywords));
11396
								}
11397
							}
11398
							else
11399
							{
11400
								$keywords = $keywords_parent;
11401
							}
11402

    
11403
							// PLAYER
11404
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11405
							{
11406
								$player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11407
							}
11408
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11409
							{
11410
								$player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11411
							}
11412
							else
11413
							{
11414
								$player = $player_parent;
11415
							}
11416

    
11417
							// RATINGS
11418
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11419
							{
11420
								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11421
								{
11422
									$rating_scheme = null;
11423
									$rating_value = null;
11424
									if (isset($rating['attribs']['']['scheme']))
11425
									{
11426
										$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11427
									}
11428
									else
11429
									{
11430
										$rating_scheme = 'urn:simple';
11431
									}
11432
									if (isset($rating['data']))
11433
									{
11434
										$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11435
									}
11436
									$ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11437
								}
11438
								if (is_array($ratings))
11439
								{
11440
									$ratings = array_values(array_unique($ratings));
11441
								}
11442
							}
11443
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11444
							{
11445
								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11446
								{
11447
									$rating_scheme = null;
11448
									$rating_value = null;
11449
									if (isset($rating['attribs']['']['scheme']))
11450
									{
11451
										$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11452
									}
11453
									else
11454
									{
11455
										$rating_scheme = 'urn:simple';
11456
									}
11457
									if (isset($rating['data']))
11458
									{
11459
										$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11460
									}
11461
									$ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11462
								}
11463
								if (is_array($ratings))
11464
								{
11465
									$ratings = array_values(array_unique($ratings));
11466
								}
11467
							}
11468
							else
11469
							{
11470
								$ratings = $ratings_parent;
11471
							}
11472

    
11473
							// RESTRICTIONS
11474
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11475
							{
11476
								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11477
								{
11478
									$restriction_relationship = null;
11479
									$restriction_type = null;
11480
									$restriction_value = null;
11481
									if (isset($restriction['attribs']['']['relationship']))
11482
									{
11483
										$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11484
									}
11485
									if (isset($restriction['attribs']['']['type']))
11486
									{
11487
										$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11488
									}
11489
									if (isset($restriction['data']))
11490
									{
11491
										$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11492
									}
11493
									$restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11494
								}
11495
								if (is_array($restrictions))
11496
								{
11497
									$restrictions = array_values(array_unique($restrictions));
11498
								}
11499
							}
11500
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11501
							{
11502
								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11503
								{
11504
									$restriction_relationship = null;
11505
									$restriction_type = null;
11506
									$restriction_value = null;
11507
									if (isset($restriction['attribs']['']['relationship']))
11508
									{
11509
										$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11510
									}
11511
									if (isset($restriction['attribs']['']['type']))
11512
									{
11513
										$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11514
									}
11515
									if (isset($restriction['data']))
11516
									{
11517
										$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11518
									}
11519
									$restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11520
								}
11521
								if (is_array($restrictions))
11522
								{
11523
									$restrictions = array_values(array_unique($restrictions));
11524
								}
11525
							}
11526
							else
11527
							{
11528
								$restrictions = $restrictions_parent;
11529
							}
11530

    
11531
							// THUMBNAILS
11532
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11533
							{
11534
								foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11535
								{
11536
									$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11537
								}
11538
								if (is_array($thumbnails))
11539
								{
11540
									$thumbnails = array_values(array_unique($thumbnails));
11541
								}
11542
							}
11543
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11544
							{
11545
								foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11546
								{
11547
									$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11548
								}
11549
								if (is_array($thumbnails))
11550
								{
11551
									$thumbnails = array_values(array_unique($thumbnails));
11552
								}
11553
							}
11554
							else
11555
							{
11556
								$thumbnails = $thumbnails_parent;
11557
							}
11558

    
11559
							// TITLES
11560
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11561
							{
11562
								$title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11563
							}
11564
							elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11565
							{
11566
								$title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11567
							}
11568
							else
11569
							{
11570
								$title = $title_parent;
11571
							}
11572

    
11573
							$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));
11574
						}
11575
					}
11576
				}
11577
			}
11578

    
11579
			// If we have standalone media:content tags, loop through them.
11580
			if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
11581
			{
11582
				foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
11583
				{
11584
					if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11585
					{
11586
						// Attributes
11587
						$bitrate = null;
11588
						$channels = null;
11589
						$duration = null;
11590
						$expression = null;
11591
						$framerate = null;
11592
						$height = null;
11593
						$javascript = null;
11594
						$lang = null;
11595
						$length = null;
11596
						$medium = null;
11597
						$samplingrate = null;
11598
						$type = null;
11599
						$url = null;
11600
						$width = null;
11601

    
11602
						// Elements
11603
						$captions = null;
11604
						$categories = null;
11605
						$copyrights = null;
11606
						$credits = null;
11607
						$description = null;
11608
						$hashes = null;
11609
						$keywords = null;
11610
						$player = null;
11611
						$ratings = null;
11612
						$restrictions = null;
11613
						$thumbnails = null;
11614
						$title = null;
11615

    
11616
						// Start checking the attributes of media:content
11617
						if (isset($content['attribs']['']['bitrate']))
11618
						{
11619
							$bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11620
						}
11621
						if (isset($content['attribs']['']['channels']))
11622
						{
11623
							$channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
11624
						}
11625
						if (isset($content['attribs']['']['duration']))
11626
						{
11627
							$duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
11628
						}
11629
						else
11630
						{
11631
							$duration = $duration_parent;
11632
						}
11633
						if (isset($content['attribs']['']['expression']))
11634
						{
11635
							$expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
11636
						}
11637
						if (isset($content['attribs']['']['framerate']))
11638
						{
11639
							$framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
11640
						}
11641
						if (isset($content['attribs']['']['height']))
11642
						{
11643
							$height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
11644
						}
11645
						if (isset($content['attribs']['']['lang']))
11646
						{
11647
							$lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11648
						}
11649
						if (isset($content['attribs']['']['fileSize']))
11650
						{
11651
							$length = ceil($content['attribs']['']['fileSize']);
11652
						}
11653
						if (isset($content['attribs']['']['medium']))
11654
						{
11655
							$medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
11656
						}
11657
						if (isset($content['attribs']['']['samplingrate']))
11658
						{
11659
							$samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11660
						}
11661
						if (isset($content['attribs']['']['type']))
11662
						{
11663
							$type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11664
						}
11665
						if (isset($content['attribs']['']['width']))
11666
						{
11667
							$width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
11668
						}
11669
						if (isset($content['attribs']['']['url']))
11670
						{
11671
							$url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11672
						}
11673
						// Checking the other optional media: elements. Priority: media:content, media:group, item, channel
11674

    
11675
						// CAPTIONS
11676
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
11677
						{
11678
							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
11679
							{
11680
								$caption_type = null;
11681
								$caption_lang = null;
11682
								$caption_startTime = null;
11683
								$caption_endTime = null;
11684
								$caption_text = null;
11685
								if (isset($caption['attribs']['']['type']))
11686
								{
11687
									$caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11688
								}
11689
								if (isset($caption['attribs']['']['lang']))
11690
								{
11691
									$caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11692
								}
11693
								if (isset($caption['attribs']['']['start']))
11694
								{
11695
									$caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
11696
								}
11697
								if (isset($caption['attribs']['']['end']))
11698
								{
11699
									$caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
11700
								}
11701
								if (isset($caption['data']))
11702
								{
11703
									$caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11704
								}
11705
								$captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
11706
							}
11707
							if (is_array($captions))
11708
							{
11709
								$captions = array_values(array_unique($captions));
11710
							}
11711
						}
11712
						else
11713
						{
11714
							$captions = $captions_parent;
11715
						}
11716

    
11717
						// CATEGORIES
11718
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
11719
						{
11720
							foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
11721
							{
11722
								$term = null;
11723
								$scheme = null;
11724
								$label = null;
11725
								if (isset($category['data']))
11726
								{
11727
									$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11728
								}
11729
								if (isset($category['attribs']['']['scheme']))
11730
								{
11731
									$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11732
								}
11733
								else
11734
								{
11735
									$scheme = 'http://search.yahoo.com/mrss/category_schema';
11736
								}
11737
								if (isset($category['attribs']['']['label']))
11738
								{
11739
									$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
11740
								}
11741
								$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
11742
							}
11743
						}
11744
						if (is_array($categories) && is_array($categories_parent))
11745
						{
11746
							$categories = array_values(array_unique(array_merge($categories, $categories_parent)));
11747
						}
11748
						elseif (is_array($categories))
11749
						{
11750
							$categories = array_values(array_unique($categories));
11751
						}
11752
						elseif (is_array($categories_parent))
11753
						{
11754
							$categories = array_values(array_unique($categories_parent));
11755
						}
11756
						else
11757
						{
11758
							$categories = null;
11759
						}
11760

    
11761
						// COPYRIGHTS
11762
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
11763
						{
11764
							$copyright_url = null;
11765
							$copyright_label = null;
11766
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
11767
							{
11768
								$copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
11769
							}
11770
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
11771
							{
11772
								$copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11773
							}
11774
							$copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
11775
						}
11776
						else
11777
						{
11778
							$copyrights = $copyrights_parent;
11779
						}
11780

    
11781
						// CREDITS
11782
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11783
						{
11784
							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11785
							{
11786
								$credit_role = null;
11787
								$credit_scheme = null;
11788
								$credit_name = null;
11789
								if (isset($credit['attribs']['']['role']))
11790
								{
11791
									$credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11792
								}
11793
								if (isset($credit['attribs']['']['scheme']))
11794
								{
11795
									$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11796
								}
11797
								else
11798
								{
11799
									$credit_scheme = 'urn:ebu';
11800
								}
11801
								if (isset($credit['data']))
11802
								{
11803
									$credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11804
								}
11805
								$credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11806
							}
11807
							if (is_array($credits))
11808
							{
11809
								$credits = array_values(array_unique($credits));
11810
							}
11811
						}
11812
						else
11813
						{
11814
							$credits = $credits_parent;
11815
						}
11816

    
11817
						// DESCRIPTION
11818
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11819
						{
11820
							$description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11821
						}
11822
						else
11823
						{
11824
							$description = $description_parent;
11825
						}
11826

    
11827
						// HASHES
11828
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11829
						{
11830
							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11831
							{
11832
								$value = null;
11833
								$algo = null;
11834
								if (isset($hash['data']))
11835
								{
11836
									$value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11837
								}
11838
								if (isset($hash['attribs']['']['algo']))
11839
								{
11840
									$algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11841
								}
11842
								else
11843
								{
11844
									$algo = 'md5';
11845
								}
11846
								$hashes[] = $algo.':'.$value;
11847
							}
11848
							if (is_array($hashes))
11849
							{
11850
								$hashes = array_values(array_unique($hashes));
11851
							}
11852
						}
11853
						else
11854
						{
11855
							$hashes = $hashes_parent;
11856
						}
11857

    
11858
						// KEYWORDS
11859
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11860
						{
11861
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11862
							{
11863
								$temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11864
								foreach ($temp as $word)
11865
								{
11866
									$keywords[] = trim($word);
11867
								}
11868
								unset($temp);
11869
							}
11870
							if (is_array($keywords))
11871
							{
11872
								$keywords = array_values(array_unique($keywords));
11873
							}
11874
						}
11875
						else
11876
						{
11877
							$keywords = $keywords_parent;
11878
						}
11879

    
11880
						// PLAYER
11881
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11882
						{
11883
							if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'])) {
11884
								$player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11885
							}
11886
						}
11887
						else
11888
						{
11889
							$player = $player_parent;
11890
						}
11891

    
11892
						// RATINGS
11893
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11894
						{
11895
							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11896
							{
11897
								$rating_scheme = null;
11898
								$rating_value = null;
11899
								if (isset($rating['attribs']['']['scheme']))
11900
								{
11901
									$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11902
								}
11903
								else
11904
								{
11905
									$rating_scheme = 'urn:simple';
11906
								}
11907
								if (isset($rating['data']))
11908
								{
11909
									$rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11910
								}
11911
								$ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11912
							}
11913
							if (is_array($ratings))
11914
							{
11915
								$ratings = array_values(array_unique($ratings));
11916
							}
11917
						}
11918
						else
11919
						{
11920
							$ratings = $ratings_parent;
11921
						}
11922

    
11923
						// RESTRICTIONS
11924
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11925
						{
11926
							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11927
							{
11928
								$restriction_relationship = null;
11929
								$restriction_type = null;
11930
								$restriction_value = null;
11931
								if (isset($restriction['attribs']['']['relationship']))
11932
								{
11933
									$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11934
								}
11935
								if (isset($restriction['attribs']['']['type']))
11936
								{
11937
									$restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11938
								}
11939
								if (isset($restriction['data']))
11940
								{
11941
									$restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11942
								}
11943
								$restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11944
							}
11945
							if (is_array($restrictions))
11946
							{
11947
								$restrictions = array_values(array_unique($restrictions));
11948
							}
11949
						}
11950
						else
11951
						{
11952
							$restrictions = $restrictions_parent;
11953
						}
11954

    
11955
						// THUMBNAILS
11956
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11957
						{
11958
							foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11959
							{
11960
								if (isset($thumbnail['attribs']['']['url'])) {
11961
									$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11962
								}
11963
							}
11964
							if (is_array($thumbnails))
11965
							{
11966
								$thumbnails = array_values(array_unique($thumbnails));
11967
							}
11968
						}
11969
						else
11970
						{
11971
							$thumbnails = $thumbnails_parent;
11972
						}
11973

    
11974
						// TITLES
11975
						if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11976
						{
11977
							$title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11978
						}
11979
						else
11980
						{
11981
							$title = $title_parent;
11982
						}
11983

    
11984
						$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));
11985
					}
11986
				}
11987
			}
11988

    
11989
			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
11990
			{
11991
				if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11992
				{
11993
					// Attributes
11994
					$bitrate = null;
11995
					$channels = null;
11996
					$duration = null;
11997
					$expression = null;
11998
					$framerate = null;
11999
					$height = null;
12000
					$javascript = null;
12001
					$lang = null;
12002
					$length = null;
12003
					$medium = null;
12004
					$samplingrate = null;
12005
					$type = null;
12006
					$url = null;
12007
					$width = null;
12008

    
12009
					$url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
12010
					if (isset($link['attribs']['']['type']))
12011
					{
12012
						$type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
12013
					}
12014
					if (isset($link['attribs']['']['length']))
12015
					{
12016
						$length = ceil($link['attribs']['']['length']);
12017
					}
12018

    
12019
					// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
12020
					$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));
12021
				}
12022
			}
12023

    
12024
			foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
12025
			{
12026
				if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
12027
				{
12028
					// Attributes
12029
					$bitrate = null;
12030
					$channels = null;
12031
					$duration = null;
12032
					$expression = null;
12033
					$framerate = null;
12034
					$height = null;
12035
					$javascript = null;
12036
					$lang = null;
12037
					$length = null;
12038
					$medium = null;
12039
					$samplingrate = null;
12040
					$type = null;
12041
					$url = null;
12042
					$width = null;
12043

    
12044
					$url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
12045
					if (isset($link['attribs']['']['type']))
12046
					{
12047
						$type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
12048
					}
12049
					if (isset($link['attribs']['']['length']))
12050
					{
12051
						$length = ceil($link['attribs']['']['length']);
12052
					}
12053

    
12054
					// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
12055
					$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));
12056
				}
12057
			}
12058

    
12059
			if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
12060
			{
12061
				if (isset($enclosure[0]['attribs']['']['url']))
12062
				{
12063
					// Attributes
12064
					$bitrate = null;
12065
					$channels = null;
12066
					$duration = null;
12067
					$expression = null;
12068
					$framerate = null;
12069
					$height = null;
12070
					$javascript = null;
12071
					$lang = null;
12072
					$length = null;
12073
					$medium = null;
12074
					$samplingrate = null;
12075
					$type = null;
12076
					$url = null;
12077
					$width = null;
12078

    
12079
					$url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
12080
					if (isset($enclosure[0]['attribs']['']['type']))
12081
					{
12082
						$type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
12083
					}
12084
					if (isset($enclosure[0]['attribs']['']['length']))
12085
					{
12086
						$length = ceil($enclosure[0]['attribs']['']['length']);
12087
					}
12088

    
12089
					// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
12090
					$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));
12091
				}
12092
			}
12093

    
12094
			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))
12095
			{
12096
				// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
12097
				$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));
12098
			}
12099

    
12100
			$this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
12101
		}
12102
		if (!empty($this->data['enclosures']))
12103
		{
12104
			return $this->data['enclosures'];
12105
		}
12106
		else
12107
		{
12108
			return null;
12109
		}
12110
	}
12111

    
12112
	/**
12113
	 * Get the latitude coordinates for the item
12114
	 *
12115
	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
12116
	 *
12117
	 * Uses `<geo:lat>` or `<georss:point>`
12118
	 *
12119
	 * @since 1.0
12120
	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
12121
	 * @link http://www.georss.org/ GeoRSS
12122
	 * @return string|null
12123
	 */
12124
	public function get_latitude()
12125
	{
12126
		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
12127
		{
12128
			return (float) $return[0]['data'];
12129
		}
12130
		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))
12131
		{
12132
			return (float) $match[1];
12133
		}
12134
		else
12135
		{
12136
			return null;
12137
		}
12138
	}
12139

    
12140
	/**
12141
	 * Get the longitude coordinates for the item
12142
	 *
12143
	 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
12144
	 *
12145
	 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
12146
	 *
12147
	 * @since 1.0
12148
	 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
12149
	 * @link http://www.georss.org/ GeoRSS
12150
	 * @return string|null
12151
	 */
12152
	public function get_longitude()
12153
	{
12154
		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
12155
		{
12156
			return (float) $return[0]['data'];
12157
		}
12158
		elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
12159
		{
12160
			return (float) $return[0]['data'];
12161
		}
12162
		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))
12163
		{
12164
			return (float) $match[2];
12165
		}
12166
		else
12167
		{
12168
			return null;
12169
		}
12170
	}
12171

    
12172
	/**
12173
	 * Get the `<atom:source>` for the item
12174
	 *
12175
	 * @since 1.1
12176
	 * @return SimplePie_Source|null
12177
	 */
12178
	public function get_source()
12179
	{
12180
		if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
12181
		{
12182
			return $this->registry->create('Source', array($this, $return[0]));
12183
		}
12184
		else
12185
		{
12186
			return null;
12187
		}
12188
	}
12189
}
12190

    
12191
/**
12192
 * Used for feed auto-discovery
12193
 *
12194
 *
12195
 * This class can be overloaded with {@see SimplePie::set_locator_class()}
12196
 *
12197
 * @package SimplePie
12198
 */
12199
class SimplePie_Locator
12200
{
12201
	var $useragent;
12202
	var $timeout;
12203
	var $file;
12204
	var $local = array();
12205
	var $elsewhere = array();
12206
	var $cached_entities = array();
12207
	var $http_base;
12208
	var $base;
12209
	var $base_location = 0;
12210
	var $checked_feeds = 0;
12211
	var $max_checked_feeds = 10;
12212
	protected $registry;
12213

    
12214
	public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10)
12215
	{
12216
		$this->file = $file;
12217
		$this->useragent = $useragent;
12218
		$this->timeout = $timeout;
12219
		$this->max_checked_feeds = $max_checked_feeds;
12220

    
12221
		if (class_exists('DOMDocument'))
12222
		{
12223
			$this->dom = new DOMDocument();
12224

    
12225
			set_error_handler(array('SimplePie_Misc', 'silence_errors'));
12226
			$this->dom->loadHTML($this->file->body);
12227
			restore_error_handler();
12228
		}
12229
		else
12230
		{
12231
			$this->dom = null;
12232
		}
12233
	}
12234

    
12235
	public function set_registry(SimplePie_Registry $registry)
12236
	{
12237
		$this->registry = $registry;
12238
	}
12239

    
12240
	public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
12241
	{
12242
		if ($this->is_feed($this->file))
12243
		{
12244
			return $this->file;
12245
		}
12246

    
12247
		if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
12248
		{
12249
			$sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
12250
			if ($sniffer->get_type() !== 'text/html')
12251
			{
12252
				return null;
12253
			}
12254
		}
12255

    
12256
		if ($type & ~SIMPLEPIE_LOCATOR_NONE)
12257
		{
12258
			$this->get_base();
12259
		}
12260

    
12261
		if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
12262
		{
12263
			return $working[0];
12264
		}
12265

    
12266
		if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
12267
		{
12268
			if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
12269
			{
12270
				return $working[0];
12271
			}
12272

    
12273
			if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
12274
			{
12275
				return $working[0];
12276
			}
12277

    
12278
			if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
12279
			{
12280
				return $working[0];
12281
			}
12282

    
12283
			if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
12284
			{
12285
				return $working[0];
12286
			}
12287
		}
12288
		return null;
12289
	}
12290

    
12291
	public function is_feed($file, $check_html = false)
12292
	{
12293
		if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
12294
		{
12295
			$sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
12296
			$sniffed = $sniffer->get_type();
12297
			$mime_types = array('application/rss+xml', 'application/rdf+xml',
12298
			                    'text/rdf', 'application/atom+xml', 'text/xml',
12299
			                    'application/xml', 'application/x-rss+xml');
12300
			if ($check_html)
12301
			{
12302
				$mime_types[] = 'text/html';
12303
			}
12304
			if (in_array($sniffed, $mime_types))
12305
			{
12306
				return true;
12307
			}
12308
			else
12309
			{
12310
				return false;
12311
			}
12312
		}
12313
		elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
12314
		{
12315
			return true;
12316
		}
12317
		else
12318
		{
12319
			return false;
12320
		}
12321
	}
12322

    
12323
	public function get_base()
12324
	{
12325
		if ($this->dom === null)
12326
		{
12327
			throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12328
		}
12329
		$this->http_base = $this->file->url;
12330
		$this->base = $this->http_base;
12331
		$elements = $this->dom->getElementsByTagName('base');
12332
		foreach ($elements as $element)
12333
		{
12334
			if ($element->hasAttribute('href'))
12335
			{
12336
				$base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
12337
				if ($base === false)
12338
				{
12339
					continue;
12340
				}
12341
				$this->base = $base;
12342
				$this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
12343
				break;
12344
			}
12345
		}
12346
	}
12347

    
12348
	public function autodiscovery()
12349
	{
12350
		$done = array();
12351
		$feeds = array();
12352
		$feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
12353
		$feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
12354
		$feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
12355

    
12356
		if (!empty($feeds))
12357
		{
12358
			return array_values($feeds);
12359
		}
12360
		else
12361
		{
12362
			return null;
12363
		}
12364
	}
12365

    
12366
	protected function search_elements_by_tag($name, &$done, $feeds)
12367
	{
12368
		if ($this->dom === null)
12369
		{
12370
			throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12371
		}
12372

    
12373
		$links = $this->dom->getElementsByTagName($name);
12374
		foreach ($links as $link)
12375
		{
12376
			if ($this->checked_feeds === $this->max_checked_feeds)
12377
			{
12378
				break;
12379
			}
12380
			if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
12381
			{
12382
				$rel = array_unique($this->registry->call('Misc', 'space_separated_tokens', array(strtolower($link->getAttribute('rel')))));
12383
				$line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
12384

    
12385
				if ($this->base_location < $line)
12386
				{
12387
					$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12388
				}
12389
				else
12390
				{
12391
					$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12392
				}
12393
				if ($href === false)
12394
				{
12395
					continue;
12396
				}
12397

    
12398
				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('text/html', 'application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
12399
				{
12400
					$this->checked_feeds++;
12401
					$headers = array(
12402
						'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',
12403
					);
12404
					$feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent));
12405
					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, true))
12406
					{
12407
						$feeds[$href] = $feed;
12408
					}
12409
				}
12410
				$done[] = $href;
12411
			}
12412
		}
12413

    
12414
		return $feeds;
12415
	}
12416

    
12417
	public function get_links()
12418
	{
12419
		if ($this->dom === null)
12420
		{
12421
			throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12422
		}
12423

    
12424
		$links = $this->dom->getElementsByTagName('a');
12425
		foreach ($links as $link)
12426
		{
12427
			if ($link->hasAttribute('href'))
12428
			{
12429
				$href = trim($link->getAttribute('href'));
12430
				$parsed = $this->registry->call('Misc', 'parse_url', array($href));
12431
				if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme']))
12432
				{
12433
					if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo())
12434
					{
12435
						$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12436
					}
12437
					else
12438
					{
12439
						$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12440
					}
12441
					if ($href === false)
12442
					{
12443
						continue;
12444
					}
12445

    
12446
					$current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
12447

    
12448
					if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
12449
					{
12450
						$this->local[] = $href;
12451
					}
12452
					else
12453
					{
12454
						$this->elsewhere[] = $href;
12455
					}
12456
				}
12457
			}
12458
		}
12459
		$this->local = array_unique($this->local);
12460
		$this->elsewhere = array_unique($this->elsewhere);
12461
		if (!empty($this->local) || !empty($this->elsewhere))
12462
		{
12463
			return true;
12464
		}
12465
		return null;
12466
	}
12467

    
12468
	public function get_rel_link($rel)
12469
	{
12470
		if ($this->dom === null)
12471
		{
12472
			throw new SimplePie_Exception('DOMDocument not found, unable to use '.
12473
			                              'locator');
12474
		}
12475
		if (!class_exists('DOMXpath'))
12476
		{
12477
			throw new SimplePie_Exception('DOMXpath not found, unable to use '.
12478
			                              'get_rel_link');
12479
		}
12480

    
12481
		$xpath = new DOMXpath($this->dom);
12482
		$query = '//a[@rel and @href] | //link[@rel and @href]';
12483
		foreach ($xpath->query($query) as $link)
12484
		{
12485
			$href = trim($link->getAttribute('href'));
12486
			$parsed = $this->registry->call('Misc', 'parse_url', array($href));
12487
			if ($parsed['scheme'] === '' ||
12488
			    preg_match('/^https?$/i', $parsed['scheme']))
12489
			{
12490
				if (method_exists($link, 'getLineNo') &&
12491
				    $this->base_location < $link->getLineNo())
12492
				{
12493
					$href =
12494
						$this->registry->call('Misc', 'absolutize_url',
12495
						                      array(trim($link->getAttribute('href')),
12496
						                            $this->base));
12497
				}
12498
				else
12499
				{
12500
					$href =
12501
						$this->registry->call('Misc', 'absolutize_url',
12502
						                      array(trim($link->getAttribute('href')),
12503
						                            $this->http_base));
12504
				}
12505
				if ($href === false)
12506
				{
12507
					return null;
12508
				}
12509
				$rel_values = explode(' ', strtolower($link->getAttribute('rel')));
12510
				if (in_array($rel, $rel_values))
12511
				{
12512
					return $href;
12513
				}
12514
			}
12515
		}
12516
		return null;
12517
	}
12518

    
12519
	public function extension(&$array)
12520
	{
12521
		foreach ($array as $key => $value)
12522
		{
12523
			if ($this->checked_feeds === $this->max_checked_feeds)
12524
			{
12525
				break;
12526
			}
12527
			if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
12528
			{
12529
				$this->checked_feeds++;
12530

    
12531
				$headers = array(
12532
					'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',
12533
				);
12534
				$feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent));
12535
				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))
12536
				{
12537
					return array($feed);
12538
				}
12539
				else
12540
				{
12541
					unset($array[$key]);
12542
				}
12543
			}
12544
		}
12545
		return null;
12546
	}
12547

    
12548
	public function body(&$array)
12549
	{
12550
		foreach ($array as $key => $value)
12551
		{
12552
			if ($this->checked_feeds === $this->max_checked_feeds)
12553
			{
12554
				break;
12555
			}
12556
			if (preg_match('/(rss|rdf|atom|xml)/i', $value))
12557
			{
12558
				$this->checked_feeds++;
12559
				$headers = array(
12560
					'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',
12561
				);
12562
				$feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent));
12563
				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))
12564
				{
12565
					return array($feed);
12566
				}
12567
				else
12568
				{
12569
					unset($array[$key]);
12570
				}
12571
			}
12572
		}
12573
		return null;
12574
	}
12575
}
12576

    
12577
/**
12578
 * Miscellanous utilities
12579
 *
12580
 * @package SimplePie
12581
 */
12582
class SimplePie_Misc
12583
{
12584
	public static function time_hms($seconds)
12585
	{
12586
		$time = '';
12587

    
12588
		$hours = floor($seconds / 3600);
12589
		$remainder = $seconds % 3600;
12590
		if ($hours > 0)
12591
		{
12592
			$time .= $hours.':';
12593
		}
12594

    
12595
		$minutes = floor($remainder / 60);
12596
		$seconds = $remainder % 60;
12597
		if ($minutes < 10 && $hours > 0)
12598
		{
12599
			$minutes = '0' . $minutes;
12600
		}
12601
		if ($seconds < 10)
12602
		{
12603
			$seconds = '0' . $seconds;
12604
		}
12605

    
12606
		$time .= $minutes.':';
12607
		$time .= $seconds;
12608

    
12609
		return $time;
12610
	}
12611

    
12612
	public static function absolutize_url($relative, $base)
12613
	{
12614
		$iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
12615
		if ($iri === false)
12616
		{
12617
			return false;
12618
		}
12619
		return $iri->get_uri();
12620
	}
12621

    
12622
	/**
12623
	 * Get a HTML/XML element from a HTML string
12624
	 *
12625
	 * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!)
12626
	 * @param string $realname Element name (including namespace prefix if applicable)
12627
	 * @param string $string HTML document
12628
	 * @return array
12629
	 */
12630
	public static function get_element($realname, $string)
12631
	{
12632
		$return = array();
12633
		$name = preg_quote($realname, '/');
12634
		if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
12635
		{
12636
			for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
12637
			{
12638
				$return[$i]['tag'] = $realname;
12639
				$return[$i]['full'] = $matches[$i][0][0];
12640
				$return[$i]['offset'] = $matches[$i][0][1];
12641
				if (strlen($matches[$i][3][0]) <= 2)
12642
				{
12643
					$return[$i]['self_closing'] = true;
12644
				}
12645
				else
12646
				{
12647
					$return[$i]['self_closing'] = false;
12648
					$return[$i]['content'] = $matches[$i][4][0];
12649
				}
12650
				$return[$i]['attribs'] = array();
12651
				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))
12652
				{
12653
					for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
12654
					{
12655
						if (count($attribs[$j]) === 2)
12656
						{
12657
							$attribs[$j][2] = $attribs[$j][1];
12658
						}
12659
						$return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]));
12660
					}
12661
				}
12662
			}
12663
		}
12664
		return $return;
12665
	}
12666

    
12667
	public static function element_implode($element)
12668
	{
12669
		$full = "<$element[tag]";
12670
		foreach ($element['attribs'] as $key => $value)
12671
		{
12672
			$key = strtolower($key);
12673
			$full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"';
12674
		}
12675
		if ($element['self_closing'])
12676
		{
12677
			$full .= ' />';
12678
		}
12679
		else
12680
		{
12681
			$full .= ">$element[content]</$element[tag]>";
12682
		}
12683
		return $full;
12684
	}
12685

    
12686
	public static function error($message, $level, $file, $line)
12687
	{
12688
		if ((ini_get('error_reporting') & $level) > 0)
12689
		{
12690
			switch ($level)
12691
			{
12692
				case E_USER_ERROR:
12693
					$note = 'PHP Error';
12694
					break;
12695
				case E_USER_WARNING:
12696
					$note = 'PHP Warning';
12697
					break;
12698
				case E_USER_NOTICE:
12699
					$note = 'PHP Notice';
12700
					break;
12701
				default:
12702
					$note = 'Unknown Error';
12703
					break;
12704
			}
12705

    
12706
			$log_error = true;
12707
			if (!function_exists('error_log'))
12708
			{
12709
				$log_error = false;
12710
			}
12711

    
12712
			$log_file = @ini_get('error_log');
12713
			if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file))
12714
			{
12715
				$log_error = false;
12716
			}
12717

    
12718
			if ($log_error)
12719
			{
12720
				@error_log("$note: $message in $file on line $line", 0);
12721
			}
12722
		}
12723

    
12724
		return $message;
12725
	}
12726

    
12727
	public static function fix_protocol($url, $http = 1)
12728
	{
12729
		$url = SimplePie_Misc::normalize_url($url);
12730
		$parsed = SimplePie_Misc::parse_url($url);
12731
		if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
12732
		{
12733
			return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
12734
		}
12735

    
12736
		if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
12737
		{
12738
			return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
12739
		}
12740

    
12741
		if ($http === 2 && $parsed['scheme'] !== '')
12742
		{
12743
			return "feed:$url";
12744
		}
12745
		elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
12746
		{
12747
			return substr_replace($url, 'podcast', 0, 4);
12748
		}
12749
		elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
12750
		{
12751
			return substr_replace($url, 'itpc', 0, 4);
12752
		}
12753
		else
12754
		{
12755
			return $url;
12756
		}
12757
	}
12758

    
12759
	public static function array_merge_recursive($array1, $array2)
12760
	{
12761
		foreach ($array2 as $key => $value)
12762
		{
12763
			if (is_array($value))
12764
			{
12765
				$array1[$key] = SimplePie_Misc::array_merge_recursive($array1[$key], $value);
12766
			}
12767
			else
12768
			{
12769
				$array1[$key] = $value;
12770
			}            
12771
		}
12772
		
12773
		return $array1;
12774
	}
12775

    
12776
	public static function parse_url($url)
12777
	{
12778
		$iri = new SimplePie_IRI($url);
12779
		return array(
12780
			'scheme' => (string) $iri->scheme,
12781
			'authority' => (string) $iri->authority,
12782
			'path' => (string) $iri->path,
12783
			'query' => (string) $iri->query,
12784
			'fragment' => (string) $iri->fragment
12785
		);
12786
	}
12787

    
12788
	public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
12789
	{
12790
		$iri = new SimplePie_IRI('');
12791
		$iri->scheme = $scheme;
12792
		$iri->authority = $authority;
12793
		$iri->path = $path;
12794
		$iri->query = $query;
12795
		$iri->fragment = $fragment;
12796
		return $iri->get_uri();
12797
	}
12798

    
12799
	public static function normalize_url($url)
12800
	{
12801
		$iri = new SimplePie_IRI($url);
12802
		return $iri->get_uri();
12803
	}
12804

    
12805
	public static function percent_encoding_normalization($match)
12806
	{
12807
		$integer = hexdec($match[1]);
12808
		if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
12809
		{
12810
			return chr($integer);
12811
		}
12812
		else
12813
		{
12814
			return strtoupper($match[0]);
12815
		}
12816
	}
12817

    
12818
	/**
12819
	 * Converts a Windows-1252 encoded string to a UTF-8 encoded string
12820
	 *
12821
	 * @static
12822
	 * @param string $string Windows-1252 encoded string
12823
	 * @return string UTF-8 encoded string
12824
	 */
12825
	public static function windows_1252_to_utf8($string)
12826
	{
12827
		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");
12828

    
12829
		return strtr($string, $convert_table);
12830
	}
12831

    
12832
	/**
12833
	 * Change a string from one encoding to another
12834
	 *
12835
	 * @param string $data Raw data in $input encoding
12836
	 * @param string $input Encoding of $data
12837
	 * @param string $output Encoding you want
12838
	 * @return string|boolean False if we can't convert it
12839
	 */
12840
	public static function change_encoding($data, $input, $output)
12841
	{
12842
		$input = SimplePie_Misc::encoding($input);
12843
		$output = SimplePie_Misc::encoding($output);
12844

    
12845
		// We fail to fail on non US-ASCII bytes
12846
		if ($input === 'US-ASCII')
12847
		{
12848
			static $non_ascii_octects = '';
12849
			if (!$non_ascii_octects)
12850
			{
12851
				for ($i = 0x80; $i <= 0xFF; $i++)
12852
				{
12853
					$non_ascii_octects .= chr($i);
12854
				}
12855
			}
12856
			$data = substr($data, 0, strcspn($data, $non_ascii_octects));
12857
		}
12858

    
12859
		// This is first, as behaviour of this is completely predictable
12860
		if ($input === 'windows-1252' && $output === 'UTF-8')
12861
		{
12862
			return SimplePie_Misc::windows_1252_to_utf8($data);
12863
		}
12864
		// This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
12865
		elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output)))
12866
		{
12867
			return $return;
12868
 		}
12869
		// This is third, as behaviour of this varies with OS userland and PHP version
12870
		elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output)))
12871
		{
12872
			return $return;
12873
		}
12874
		// This is last, as behaviour of this varies with OS userland and PHP version
12875
		elseif (class_exists('\UConverter') && ($return = SimplePie_Misc::change_encoding_uconverter($data, $input, $output)))
12876
		{
12877
			return $return;
12878
		}
12879
		// If we can't do anything, just fail
12880
		else
12881
		{
12882
			return false;
12883
		}
12884
	}
12885

    
12886
	protected static function change_encoding_mbstring($data, $input, $output)
12887
	{
12888
		if ($input === 'windows-949')
12889
		{
12890
			$input = 'EUC-KR';
12891
		}
12892
		if ($output === 'windows-949')
12893
		{
12894
			$output = 'EUC-KR';
12895
		}
12896
		if ($input === 'Windows-31J')
12897
		{
12898
			$input = 'SJIS';
12899
		}
12900
		if ($output === 'Windows-31J')
12901
		{
12902
			$output = 'SJIS';
12903
		}
12904

    
12905
		// Check that the encoding is supported
12906
		if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
12907
		{
12908
			return false;
12909
		}
12910
		if (!in_array($input, mb_list_encodings()))
12911
		{
12912
			return false;
12913
		}
12914

    
12915
		// Let's do some conversion
12916
		if ($return = @mb_convert_encoding($data, $output, $input))
12917
		{
12918
			return $return;
12919
		}
12920

    
12921
		return false;
12922
	}
12923

    
12924
	protected static function change_encoding_iconv($data, $input, $output)
12925
	{
12926
		return @iconv($input, $output, $data);
12927
	}
12928

    
12929
	/**
12930
	 * @param string $data
12931
	 * @param string $input
12932
	 * @param string $output
12933
	 * @return string|false
12934
	 */
12935
	protected static function change_encoding_uconverter($data, $input, $output)
12936
	{
12937
		return @\UConverter::transcode($data, $output, $input);
12938
	}
12939

    
12940
	/**
12941
	 * Normalize an encoding name
12942
	 *
12943
	 * This is automatically generated by create.php
12944
	 *
12945
	 * To generate it, run `php create.php` on the command line, and copy the
12946
	 * output to replace this function.
12947
	 *
12948
	 * @param string $charset Character set to standardise
12949
	 * @return string Standardised name
12950
	 */
12951
	public static function encoding($charset)
12952
	{
12953
		// Normalization from UTS #22
12954
		switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
12955
		{
12956
			case 'adobestandardencoding':
12957
			case 'csadobestandardencoding':
12958
				return 'Adobe-Standard-Encoding';
12959

    
12960
			case 'adobesymbolencoding':
12961
			case 'cshppsmath':
12962
				return 'Adobe-Symbol-Encoding';
12963

    
12964
			case 'ami1251':
12965
			case 'amiga1251':
12966
				return 'Amiga-1251';
12967

    
12968
			case 'ansix31101983':
12969
			case 'csat5001983':
12970
			case 'csiso99naplps':
12971
			case 'isoir99':
12972
			case 'naplps':
12973
				return 'ANSI_X3.110-1983';
12974

    
12975
			case 'arabic7':
12976
			case 'asmo449':
12977
			case 'csiso89asmo449':
12978
			case 'iso9036':
12979
			case 'isoir89':
12980
				return 'ASMO_449';
12981

    
12982
			case 'big5':
12983
			case 'csbig5':
12984
				return 'Big5';
12985

    
12986
			case 'big5hkscs':
12987
				return 'Big5-HKSCS';
12988

    
12989
			case 'bocu1':
12990
			case 'csbocu1':
12991
				return 'BOCU-1';
12992

    
12993
			case 'brf':
12994
			case 'csbrf':
12995
				return 'BRF';
12996

    
12997
			case 'bs4730':
12998
			case 'csiso4unitedkingdom':
12999
			case 'gb':
13000
			case 'iso646gb':
13001
			case 'isoir4':
13002
			case 'uk':
13003
				return 'BS_4730';
13004

    
13005
			case 'bsviewdata':
13006
			case 'csiso47bsviewdata':
13007
			case 'isoir47':
13008
				return 'BS_viewdata';
13009

    
13010
			case 'cesu8':
13011
			case 'cscesu8':
13012
				return 'CESU-8';
13013

    
13014
			case 'ca':
13015
			case 'csa71':
13016
			case 'csaz243419851':
13017
			case 'csiso121canadian1':
13018
			case 'iso646ca':
13019
			case 'isoir121':
13020
				return 'CSA_Z243.4-1985-1';
13021

    
13022
			case 'csa72':
13023
			case 'csaz243419852':
13024
			case 'csiso122canadian2':
13025
			case 'iso646ca2':
13026
			case 'isoir122':
13027
				return 'CSA_Z243.4-1985-2';
13028

    
13029
			case 'csaz24341985gr':
13030
			case 'csiso123csaz24341985gr':
13031
			case 'isoir123':
13032
				return 'CSA_Z243.4-1985-gr';
13033

    
13034
			case 'csiso139csn369103':
13035
			case 'csn369103':
13036
			case 'isoir139':
13037
				return 'CSN_369103';
13038

    
13039
			case 'csdecmcs':
13040
			case 'dec':
13041
			case 'decmcs':
13042
				return 'DEC-MCS';
13043

    
13044
			case 'csiso21german':
13045
			case 'de':
13046
			case 'din66003':
13047
			case 'iso646de':
13048
			case 'isoir21':
13049
				return 'DIN_66003';
13050

    
13051
			case 'csdkus':
13052
			case 'dkus':
13053
				return 'dk-us';
13054

    
13055
			case 'csiso646danish':
13056
			case 'dk':
13057
			case 'ds2089':
13058
			case 'iso646dk':
13059
				return 'DS_2089';
13060

    
13061
			case 'csibmebcdicatde':
13062
			case 'ebcdicatde':
13063
				return 'EBCDIC-AT-DE';
13064

    
13065
			case 'csebcdicatdea':
13066
			case 'ebcdicatdea':
13067
				return 'EBCDIC-AT-DE-A';
13068

    
13069
			case 'csebcdiccafr':
13070
			case 'ebcdiccafr':
13071
				return 'EBCDIC-CA-FR';
13072

    
13073
			case 'csebcdicdkno':
13074
			case 'ebcdicdkno':
13075
				return 'EBCDIC-DK-NO';
13076

    
13077
			case 'csebcdicdknoa':
13078
			case 'ebcdicdknoa':
13079
				return 'EBCDIC-DK-NO-A';
13080

    
13081
			case 'csebcdices':
13082
			case 'ebcdices':
13083
				return 'EBCDIC-ES';
13084

    
13085
			case 'csebcdicesa':
13086
			case 'ebcdicesa':
13087
				return 'EBCDIC-ES-A';
13088

    
13089
			case 'csebcdicess':
13090
			case 'ebcdicess':
13091
				return 'EBCDIC-ES-S';
13092

    
13093
			case 'csebcdicfise':
13094
			case 'ebcdicfise':
13095
				return 'EBCDIC-FI-SE';
13096

    
13097
			case 'csebcdicfisea':
13098
			case 'ebcdicfisea':
13099
				return 'EBCDIC-FI-SE-A';
13100

    
13101
			case 'csebcdicfr':
13102
			case 'ebcdicfr':
13103
				return 'EBCDIC-FR';
13104

    
13105
			case 'csebcdicit':
13106
			case 'ebcdicit':
13107
				return 'EBCDIC-IT';
13108

    
13109
			case 'csebcdicpt':
13110
			case 'ebcdicpt':
13111
				return 'EBCDIC-PT';
13112

    
13113
			case 'csebcdicuk':
13114
			case 'ebcdicuk':
13115
				return 'EBCDIC-UK';
13116

    
13117
			case 'csebcdicus':
13118
			case 'ebcdicus':
13119
				return 'EBCDIC-US';
13120

    
13121
			case 'csiso111ecmacyrillic':
13122
			case 'ecmacyrillic':
13123
			case 'isoir111':
13124
			case 'koi8e':
13125
				return 'ECMA-cyrillic';
13126

    
13127
			case 'csiso17spanish':
13128
			case 'es':
13129
			case 'iso646es':
13130
			case 'isoir17':
13131
				return 'ES';
13132

    
13133
			case 'csiso85spanish2':
13134
			case 'es2':
13135
			case 'iso646es2':
13136
			case 'isoir85':
13137
				return 'ES2';
13138

    
13139
			case 'cseucpkdfmtjapanese':
13140
			case 'eucjp':
13141
			case 'extendedunixcodepackedformatforjapanese':
13142
				return 'EUC-JP';
13143

    
13144
			case 'cseucfixwidjapanese':
13145
			case 'extendedunixcodefixedwidthforjapanese':
13146
				return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
13147

    
13148
			case 'gb18030':
13149
				return 'GB18030';
13150

    
13151
			case 'chinese':
13152
			case 'cp936':
13153
			case 'csgb2312':
13154
			case 'csiso58gb231280':
13155
			case 'gb2312':
13156
			case 'gb231280':
13157
			case 'gbk':
13158
			case 'isoir58':
13159
			case 'ms936':
13160
			case 'windows936':
13161
				return 'GBK';
13162

    
13163
			case 'cn':
13164
			case 'csiso57gb1988':
13165
			case 'gb198880':
13166
			case 'iso646cn':
13167
			case 'isoir57':
13168
				return 'GB_1988-80';
13169

    
13170
			case 'csiso153gost1976874':
13171
			case 'gost1976874':
13172
			case 'isoir153':
13173
			case 'stsev35888':
13174
				return 'GOST_19768-74';
13175

    
13176
			case 'csiso150':
13177
			case 'csiso150greekccitt':
13178
			case 'greekccitt':
13179
			case 'isoir150':
13180
				return 'greek-ccitt';
13181

    
13182
			case 'csiso88greek7':
13183
			case 'greek7':
13184
			case 'isoir88':
13185
				return 'greek7';
13186

    
13187
			case 'csiso18greek7old':
13188
			case 'greek7old':
13189
			case 'isoir18':
13190
				return 'greek7-old';
13191

    
13192
			case 'cshpdesktop':
13193
			case 'hpdesktop':
13194
				return 'HP-DeskTop';
13195

    
13196
			case 'cshplegal':
13197
			case 'hplegal':
13198
				return 'HP-Legal';
13199

    
13200
			case 'cshpmath8':
13201
			case 'hpmath8':
13202
				return 'HP-Math8';
13203

    
13204
			case 'cshppifont':
13205
			case 'hppifont':
13206
				return 'HP-Pi-font';
13207

    
13208
			case 'cshproman8':
13209
			case 'hproman8':
13210
			case 'r8':
13211
			case 'roman8':
13212
				return 'hp-roman8';
13213

    
13214
			case 'hzgb2312':
13215
				return 'HZ-GB-2312';
13216

    
13217
			case 'csibmsymbols':
13218
			case 'ibmsymbols':
13219
				return 'IBM-Symbols';
13220

    
13221
			case 'csibmthai':
13222
			case 'ibmthai':
13223
				return 'IBM-Thai';
13224

    
13225
			case 'cp37':
13226
			case 'csibm37':
13227
			case 'ebcdiccpca':
13228
			case 'ebcdiccpnl':
13229
			case 'ebcdiccpus':
13230
			case 'ebcdiccpwt':
13231
			case 'ibm37':
13232
				return 'IBM037';
13233

    
13234
			case 'cp38':
13235
			case 'csibm38':
13236
			case 'ebcdicint':
13237
			case 'ibm38':
13238
				return 'IBM038';
13239

    
13240
			case 'cp273':
13241
			case 'csibm273':
13242
			case 'ibm273':
13243
				return 'IBM273';
13244

    
13245
			case 'cp274':
13246
			case 'csibm274':
13247
			case 'ebcdicbe':
13248
			case 'ibm274':
13249
				return 'IBM274';
13250

    
13251
			case 'cp275':
13252
			case 'csibm275':
13253
			case 'ebcdicbr':
13254
			case 'ibm275':
13255
				return 'IBM275';
13256

    
13257
			case 'csibm277':
13258
			case 'ebcdiccpdk':
13259
			case 'ebcdiccpno':
13260
			case 'ibm277':
13261
				return 'IBM277';
13262

    
13263
			case 'cp278':
13264
			case 'csibm278':
13265
			case 'ebcdiccpfi':
13266
			case 'ebcdiccpse':
13267
			case 'ibm278':
13268
				return 'IBM278';
13269

    
13270
			case 'cp280':
13271
			case 'csibm280':
13272
			case 'ebcdiccpit':
13273
			case 'ibm280':
13274
				return 'IBM280';
13275

    
13276
			case 'cp281':
13277
			case 'csibm281':
13278
			case 'ebcdicjpe':
13279
			case 'ibm281':
13280
				return 'IBM281';
13281

    
13282
			case 'cp284':
13283
			case 'csibm284':
13284
			case 'ebcdiccpes':
13285
			case 'ibm284':
13286
				return 'IBM284';
13287

    
13288
			case 'cp285':
13289
			case 'csibm285':
13290
			case 'ebcdiccpgb':
13291
			case 'ibm285':
13292
				return 'IBM285';
13293

    
13294
			case 'cp290':
13295
			case 'csibm290':
13296
			case 'ebcdicjpkana':
13297
			case 'ibm290':
13298
				return 'IBM290';
13299

    
13300
			case 'cp297':
13301
			case 'csibm297':
13302
			case 'ebcdiccpfr':
13303
			case 'ibm297':
13304
				return 'IBM297';
13305

    
13306
			case 'cp420':
13307
			case 'csibm420':
13308
			case 'ebcdiccpar1':
13309
			case 'ibm420':
13310
				return 'IBM420';
13311

    
13312
			case 'cp423':
13313
			case 'csibm423':
13314
			case 'ebcdiccpgr':
13315
			case 'ibm423':
13316
				return 'IBM423';
13317

    
13318
			case 'cp424':
13319
			case 'csibm424':
13320
			case 'ebcdiccphe':
13321
			case 'ibm424':
13322
				return 'IBM424';
13323

    
13324
			case '437':
13325
			case 'cp437':
13326
			case 'cspc8codepage437':
13327
			case 'ibm437':
13328
				return 'IBM437';
13329

    
13330
			case 'cp500':
13331
			case 'csibm500':
13332
			case 'ebcdiccpbe':
13333
			case 'ebcdiccpch':
13334
			case 'ibm500':
13335
				return 'IBM500';
13336

    
13337
			case 'cp775':
13338
			case 'cspc775baltic':
13339
			case 'ibm775':
13340
				return 'IBM775';
13341

    
13342
			case '850':
13343
			case 'cp850':
13344
			case 'cspc850multilingual':
13345
			case 'ibm850':
13346
				return 'IBM850';
13347

    
13348
			case '851':
13349
			case 'cp851':
13350
			case 'csibm851':
13351
			case 'ibm851':
13352
				return 'IBM851';
13353

    
13354
			case '852':
13355
			case 'cp852':
13356
			case 'cspcp852':
13357
			case 'ibm852':
13358
				return 'IBM852';
13359

    
13360
			case '855':
13361
			case 'cp855':
13362
			case 'csibm855':
13363
			case 'ibm855':
13364
				return 'IBM855';
13365

    
13366
			case '857':
13367
			case 'cp857':
13368
			case 'csibm857':
13369
			case 'ibm857':
13370
				return 'IBM857';
13371

    
13372
			case 'ccsid858':
13373
			case 'cp858':
13374
			case 'ibm858':
13375
			case 'pcmultilingual850euro':
13376
				return 'IBM00858';
13377

    
13378
			case '860':
13379
			case 'cp860':
13380
			case 'csibm860':
13381
			case 'ibm860':
13382
				return 'IBM860';
13383

    
13384
			case '861':
13385
			case 'cp861':
13386
			case 'cpis':
13387
			case 'csibm861':
13388
			case 'ibm861':
13389
				return 'IBM861';
13390

    
13391
			case '862':
13392
			case 'cp862':
13393
			case 'cspc862latinhebrew':
13394
			case 'ibm862':
13395
				return 'IBM862';
13396

    
13397
			case '863':
13398
			case 'cp863':
13399
			case 'csibm863':
13400
			case 'ibm863':
13401
				return 'IBM863';
13402

    
13403
			case 'cp864':
13404
			case 'csibm864':
13405
			case 'ibm864':
13406
				return 'IBM864';
13407

    
13408
			case '865':
13409
			case 'cp865':
13410
			case 'csibm865':
13411
			case 'ibm865':
13412
				return 'IBM865';
13413

    
13414
			case '866':
13415
			case 'cp866':
13416
			case 'csibm866':
13417
			case 'ibm866':
13418
				return 'IBM866';
13419

    
13420
			case 'cp868':
13421
			case 'cpar':
13422
			case 'csibm868':
13423
			case 'ibm868':
13424
				return 'IBM868';
13425

    
13426
			case '869':
13427
			case 'cp869':
13428
			case 'cpgr':
13429
			case 'csibm869':
13430
			case 'ibm869':
13431
				return 'IBM869';
13432

    
13433
			case 'cp870':
13434
			case 'csibm870':
13435
			case 'ebcdiccproece':
13436
			case 'ebcdiccpyu':
13437
			case 'ibm870':
13438
				return 'IBM870';
13439

    
13440
			case 'cp871':
13441
			case 'csibm871':
13442
			case 'ebcdiccpis':
13443
			case 'ibm871':
13444
				return 'IBM871';
13445

    
13446
			case 'cp880':
13447
			case 'csibm880':
13448
			case 'ebcdiccyrillic':
13449
			case 'ibm880':
13450
				return 'IBM880';
13451

    
13452
			case 'cp891':
13453
			case 'csibm891':
13454
			case 'ibm891':
13455
				return 'IBM891';
13456

    
13457
			case 'cp903':
13458
			case 'csibm903':
13459
			case 'ibm903':
13460
				return 'IBM903';
13461

    
13462
			case '904':
13463
			case 'cp904':
13464
			case 'csibbm904':
13465
			case 'ibm904':
13466
				return 'IBM904';
13467

    
13468
			case 'cp905':
13469
			case 'csibm905':
13470
			case 'ebcdiccptr':
13471
			case 'ibm905':
13472
				return 'IBM905';
13473

    
13474
			case 'cp918':
13475
			case 'csibm918':
13476
			case 'ebcdiccpar2':
13477
			case 'ibm918':
13478
				return 'IBM918';
13479

    
13480
			case 'ccsid924':
13481
			case 'cp924':
13482
			case 'ebcdiclatin9euro':
13483
			case 'ibm924':
13484
				return 'IBM00924';
13485

    
13486
			case 'cp1026':
13487
			case 'csibm1026':
13488
			case 'ibm1026':
13489
				return 'IBM1026';
13490

    
13491
			case 'ibm1047':
13492
				return 'IBM1047';
13493

    
13494
			case 'ccsid1140':
13495
			case 'cp1140':
13496
			case 'ebcdicus37euro':
13497
			case 'ibm1140':
13498
				return 'IBM01140';
13499

    
13500
			case 'ccsid1141':
13501
			case 'cp1141':
13502
			case 'ebcdicde273euro':
13503
			case 'ibm1141':
13504
				return 'IBM01141';
13505

    
13506
			case 'ccsid1142':
13507
			case 'cp1142':
13508
			case 'ebcdicdk277euro':
13509
			case 'ebcdicno277euro':
13510
			case 'ibm1142':
13511
				return 'IBM01142';
13512

    
13513
			case 'ccsid1143':
13514
			case 'cp1143':
13515
			case 'ebcdicfi278euro':
13516
			case 'ebcdicse278euro':
13517
			case 'ibm1143':
13518
				return 'IBM01143';
13519

    
13520
			case 'ccsid1144':
13521
			case 'cp1144':
13522
			case 'ebcdicit280euro':
13523
			case 'ibm1144':
13524
				return 'IBM01144';
13525

    
13526
			case 'ccsid1145':
13527
			case 'cp1145':
13528
			case 'ebcdices284euro':
13529
			case 'ibm1145':
13530
				return 'IBM01145';
13531

    
13532
			case 'ccsid1146':
13533
			case 'cp1146':
13534
			case 'ebcdicgb285euro':
13535
			case 'ibm1146':
13536
				return 'IBM01146';
13537

    
13538
			case 'ccsid1147':
13539
			case 'cp1147':
13540
			case 'ebcdicfr297euro':
13541
			case 'ibm1147':
13542
				return 'IBM01147';
13543

    
13544
			case 'ccsid1148':
13545
			case 'cp1148':
13546
			case 'ebcdicinternational500euro':
13547
			case 'ibm1148':
13548
				return 'IBM01148';
13549

    
13550
			case 'ccsid1149':
13551
			case 'cp1149':
13552
			case 'ebcdicis871euro':
13553
			case 'ibm1149':
13554
				return 'IBM01149';
13555

    
13556
			case 'csiso143iecp271':
13557
			case 'iecp271':
13558
			case 'isoir143':
13559
				return 'IEC_P27-1';
13560

    
13561
			case 'csiso49inis':
13562
			case 'inis':
13563
			case 'isoir49':
13564
				return 'INIS';
13565

    
13566
			case 'csiso50inis8':
13567
			case 'inis8':
13568
			case 'isoir50':
13569
				return 'INIS-8';
13570

    
13571
			case 'csiso51iniscyrillic':
13572
			case 'iniscyrillic':
13573
			case 'isoir51':
13574
				return 'INIS-cyrillic';
13575

    
13576
			case 'csinvariant':
13577
			case 'invariant':
13578
				return 'INVARIANT';
13579

    
13580
			case 'iso2022cn':
13581
				return 'ISO-2022-CN';
13582

    
13583
			case 'iso2022cnext':
13584
				return 'ISO-2022-CN-EXT';
13585

    
13586
			case 'csiso2022jp':
13587
			case 'iso2022jp':
13588
				return 'ISO-2022-JP';
13589

    
13590
			case 'csiso2022jp2':
13591
			case 'iso2022jp2':
13592
				return 'ISO-2022-JP-2';
13593

    
13594
			case 'csiso2022kr':
13595
			case 'iso2022kr':
13596
				return 'ISO-2022-KR';
13597

    
13598
			case 'cswindows30latin1':
13599
			case 'iso88591windows30latin1':
13600
				return 'ISO-8859-1-Windows-3.0-Latin-1';
13601

    
13602
			case 'cswindows31latin1':
13603
			case 'iso88591windows31latin1':
13604
				return 'ISO-8859-1-Windows-3.1-Latin-1';
13605

    
13606
			case 'csisolatin2':
13607
			case 'iso88592':
13608
			case 'iso885921987':
13609
			case 'isoir101':
13610
			case 'l2':
13611
			case 'latin2':
13612
				return 'ISO-8859-2';
13613

    
13614
			case 'cswindows31latin2':
13615
			case 'iso88592windowslatin2':
13616
				return 'ISO-8859-2-Windows-Latin-2';
13617

    
13618
			case 'csisolatin3':
13619
			case 'iso88593':
13620
			case 'iso885931988':
13621
			case 'isoir109':
13622
			case 'l3':
13623
			case 'latin3':
13624
				return 'ISO-8859-3';
13625

    
13626
			case 'csisolatin4':
13627
			case 'iso88594':
13628
			case 'iso885941988':
13629
			case 'isoir110':
13630
			case 'l4':
13631
			case 'latin4':
13632
				return 'ISO-8859-4';
13633

    
13634
			case 'csisolatincyrillic':
13635
			case 'cyrillic':
13636
			case 'iso88595':
13637
			case 'iso885951988':
13638
			case 'isoir144':
13639
				return 'ISO-8859-5';
13640

    
13641
			case 'arabic':
13642
			case 'asmo708':
13643
			case 'csisolatinarabic':
13644
			case 'ecma114':
13645
			case 'iso88596':
13646
			case 'iso885961987':
13647
			case 'isoir127':
13648
				return 'ISO-8859-6';
13649

    
13650
			case 'csiso88596e':
13651
			case 'iso88596e':
13652
				return 'ISO-8859-6-E';
13653

    
13654
			case 'csiso88596i':
13655
			case 'iso88596i':
13656
				return 'ISO-8859-6-I';
13657

    
13658
			case 'csisolatingreek':
13659
			case 'ecma118':
13660
			case 'elot928':
13661
			case 'greek':
13662
			case 'greek8':
13663
			case 'iso88597':
13664
			case 'iso885971987':
13665
			case 'isoir126':
13666
				return 'ISO-8859-7';
13667

    
13668
			case 'csisolatinhebrew':
13669
			case 'hebrew':
13670
			case 'iso88598':
13671
			case 'iso885981988':
13672
			case 'isoir138':
13673
				return 'ISO-8859-8';
13674

    
13675
			case 'csiso88598e':
13676
			case 'iso88598e':
13677
				return 'ISO-8859-8-E';
13678

    
13679
			case 'csiso88598i':
13680
			case 'iso88598i':
13681
				return 'ISO-8859-8-I';
13682

    
13683
			case 'cswindows31latin5':
13684
			case 'iso88599windowslatin5':
13685
				return 'ISO-8859-9-Windows-Latin-5';
13686

    
13687
			case 'csisolatin6':
13688
			case 'iso885910':
13689
			case 'iso8859101992':
13690
			case 'isoir157':
13691
			case 'l6':
13692
			case 'latin6':
13693
				return 'ISO-8859-10';
13694

    
13695
			case 'iso885913':
13696
				return 'ISO-8859-13';
13697

    
13698
			case 'iso885914':
13699
			case 'iso8859141998':
13700
			case 'isoceltic':
13701
			case 'isoir199':
13702
			case 'l8':
13703
			case 'latin8':
13704
				return 'ISO-8859-14';
13705

    
13706
			case 'iso885915':
13707
			case 'latin9':
13708
				return 'ISO-8859-15';
13709

    
13710
			case 'iso885916':
13711
			case 'iso8859162001':
13712
			case 'isoir226':
13713
			case 'l10':
13714
			case 'latin10':
13715
				return 'ISO-8859-16';
13716

    
13717
			case 'iso10646j1':
13718
				return 'ISO-10646-J-1';
13719

    
13720
			case 'csunicode':
13721
			case 'iso10646ucs2':
13722
				return 'ISO-10646-UCS-2';
13723

    
13724
			case 'csucs4':
13725
			case 'iso10646ucs4':
13726
				return 'ISO-10646-UCS-4';
13727

    
13728
			case 'csunicodeascii':
13729
			case 'iso10646ucsbasic':
13730
				return 'ISO-10646-UCS-Basic';
13731

    
13732
			case 'csunicodelatin1':
13733
			case 'iso10646':
13734
			case 'iso10646unicodelatin1':
13735
				return 'ISO-10646-Unicode-Latin1';
13736

    
13737
			case 'csiso10646utf1':
13738
			case 'iso10646utf1':
13739
				return 'ISO-10646-UTF-1';
13740

    
13741
			case 'csiso115481':
13742
			case 'iso115481':
13743
			case 'isotr115481':
13744
				return 'ISO-11548-1';
13745

    
13746
			case 'csiso90':
13747
			case 'isoir90':
13748
				return 'iso-ir-90';
13749

    
13750
			case 'csunicodeibm1261':
13751
			case 'isounicodeibm1261':
13752
				return 'ISO-Unicode-IBM-1261';
13753

    
13754
			case 'csunicodeibm1264':
13755
			case 'isounicodeibm1264':
13756
				return 'ISO-Unicode-IBM-1264';
13757

    
13758
			case 'csunicodeibm1265':
13759
			case 'isounicodeibm1265':
13760
				return 'ISO-Unicode-IBM-1265';
13761

    
13762
			case 'csunicodeibm1268':
13763
			case 'isounicodeibm1268':
13764
				return 'ISO-Unicode-IBM-1268';
13765

    
13766
			case 'csunicodeibm1276':
13767
			case 'isounicodeibm1276':
13768
				return 'ISO-Unicode-IBM-1276';
13769

    
13770
			case 'csiso646basic1983':
13771
			case 'iso646basic1983':
13772
			case 'ref':
13773
				return 'ISO_646.basic:1983';
13774

    
13775
			case 'csiso2intlrefversion':
13776
			case 'irv':
13777
			case 'iso646irv1983':
13778
			case 'isoir2':
13779
				return 'ISO_646.irv:1983';
13780

    
13781
			case 'csiso2033':
13782
			case 'e13b':
13783
			case 'iso20331983':
13784
			case 'isoir98':
13785
				return 'ISO_2033-1983';
13786

    
13787
			case 'csiso5427cyrillic':
13788
			case 'iso5427':
13789
			case 'isoir37':
13790
				return 'ISO_5427';
13791

    
13792
			case 'iso5427cyrillic1981':
13793
			case 'iso54271981':
13794
			case 'isoir54':
13795
				return 'ISO_5427:1981';
13796

    
13797
			case 'csiso5428greek':
13798
			case 'iso54281980':
13799
			case 'isoir55':
13800
				return 'ISO_5428:1980';
13801

    
13802
			case 'csiso6937add':
13803
			case 'iso6937225':
13804
			case 'isoir152':
13805
				return 'ISO_6937-2-25';
13806

    
13807
			case 'csisotextcomm':
13808
			case 'iso69372add':
13809
			case 'isoir142':
13810
				return 'ISO_6937-2-add';
13811

    
13812
			case 'csiso8859supp':
13813
			case 'iso8859supp':
13814
			case 'isoir154':
13815
			case 'latin125':
13816
				return 'ISO_8859-supp';
13817

    
13818
			case 'csiso10367box':
13819
			case 'iso10367box':
13820
			case 'isoir155':
13821
				return 'ISO_10367-box';
13822

    
13823
			case 'csiso15italian':
13824
			case 'iso646it':
13825
			case 'isoir15':
13826
			case 'it':
13827
				return 'IT';
13828

    
13829
			case 'csiso13jisc6220jp':
13830
			case 'isoir13':
13831
			case 'jisc62201969':
13832
			case 'jisc62201969jp':
13833
			case 'katakana':
13834
			case 'x2017':
13835
				return 'JIS_C6220-1969-jp';
13836

    
13837
			case 'csiso14jisc6220ro':
13838
			case 'iso646jp':
13839
			case 'isoir14':
13840
			case 'jisc62201969ro':
13841
			case 'jp':
13842
				return 'JIS_C6220-1969-ro';
13843

    
13844
			case 'csiso42jisc62261978':
13845
			case 'isoir42':
13846
			case 'jisc62261978':
13847
				return 'JIS_C6226-1978';
13848

    
13849
			case 'csiso87jisx208':
13850
			case 'isoir87':
13851
			case 'jisc62261983':
13852
			case 'jisx2081983':
13853
			case 'x208':
13854
				return 'JIS_C6226-1983';
13855

    
13856
			case 'csiso91jisc62291984a':
13857
			case 'isoir91':
13858
			case 'jisc62291984a':
13859
			case 'jpocra':
13860
				return 'JIS_C6229-1984-a';
13861

    
13862
			case 'csiso92jisc62991984b':
13863
			case 'iso646jpocrb':
13864
			case 'isoir92':
13865
			case 'jisc62291984b':
13866
			case 'jpocrb':
13867
				return 'JIS_C6229-1984-b';
13868

    
13869
			case 'csiso93jis62291984badd':
13870
			case 'isoir93':
13871
			case 'jisc62291984badd':
13872
			case 'jpocrbadd':
13873
				return 'JIS_C6229-1984-b-add';
13874

    
13875
			case 'csiso94jis62291984hand':
13876
			case 'isoir94':
13877
			case 'jisc62291984hand':
13878
			case 'jpocrhand':
13879
				return 'JIS_C6229-1984-hand';
13880

    
13881
			case 'csiso95jis62291984handadd':
13882
			case 'isoir95':
13883
			case 'jisc62291984handadd':
13884
			case 'jpocrhandadd':
13885
				return 'JIS_C6229-1984-hand-add';
13886

    
13887
			case 'csiso96jisc62291984kana':
13888
			case 'isoir96':
13889
			case 'jisc62291984kana':
13890
				return 'JIS_C6229-1984-kana';
13891

    
13892
			case 'csjisencoding':
13893
			case 'jisencoding':
13894
				return 'JIS_Encoding';
13895

    
13896
			case 'cshalfwidthkatakana':
13897
			case 'jisx201':
13898
			case 'x201':
13899
				return 'JIS_X0201';
13900

    
13901
			case 'csiso159jisx2121990':
13902
			case 'isoir159':
13903
			case 'jisx2121990':
13904
			case 'x212':
13905
				return 'JIS_X0212-1990';
13906

    
13907
			case 'csiso141jusib1002':
13908
			case 'iso646yu':
13909
			case 'isoir141':
13910
			case 'js':
13911
			case 'jusib1002':
13912
			case 'yu':
13913
				return 'JUS_I.B1.002';
13914

    
13915
			case 'csiso147macedonian':
13916
			case 'isoir147':
13917
			case 'jusib1003mac':
13918
			case 'macedonian':
13919
				return 'JUS_I.B1.003-mac';
13920

    
13921
			case 'csiso146serbian':
13922
			case 'isoir146':
13923
			case 'jusib1003serb':
13924
			case 'serbian':
13925
				return 'JUS_I.B1.003-serb';
13926

    
13927
			case 'koi7switched':
13928
				return 'KOI7-switched';
13929

    
13930
			case 'cskoi8r':
13931
			case 'koi8r':
13932
				return 'KOI8-R';
13933

    
13934
			case 'koi8u':
13935
				return 'KOI8-U';
13936

    
13937
			case 'csksc5636':
13938
			case 'iso646kr':
13939
			case 'ksc5636':
13940
				return 'KSC5636';
13941

    
13942
			case 'cskz1048':
13943
			case 'kz1048':
13944
			case 'rk1048':
13945
			case 'strk10482002':
13946
				return 'KZ-1048';
13947

    
13948
			case 'csiso19latingreek':
13949
			case 'isoir19':
13950
			case 'latingreek':
13951
				return 'latin-greek';
13952

    
13953
			case 'csiso27latingreek1':
13954
			case 'isoir27':
13955
			case 'latingreek1':
13956
				return 'Latin-greek-1';
13957

    
13958
			case 'csiso158lap':
13959
			case 'isoir158':
13960
			case 'lap':
13961
			case 'latinlap':
13962
				return 'latin-lap';
13963

    
13964
			case 'csmacintosh':
13965
			case 'mac':
13966
			case 'macintosh':
13967
				return 'macintosh';
13968

    
13969
			case 'csmicrosoftpublishing':
13970
			case 'microsoftpublishing':
13971
				return 'Microsoft-Publishing';
13972

    
13973
			case 'csmnem':
13974
			case 'mnem':
13975
				return 'MNEM';
13976

    
13977
			case 'csmnemonic':
13978
			case 'mnemonic':
13979
				return 'MNEMONIC';
13980

    
13981
			case 'csiso86hungarian':
13982
			case 'hu':
13983
			case 'iso646hu':
13984
			case 'isoir86':
13985
			case 'msz77953':
13986
				return 'MSZ_7795.3';
13987

    
13988
			case 'csnatsdano':
13989
			case 'isoir91':
13990
			case 'natsdano':
13991
				return 'NATS-DANO';
13992

    
13993
			case 'csnatsdanoadd':
13994
			case 'isoir92':
13995
			case 'natsdanoadd':
13996
				return 'NATS-DANO-ADD';
13997

    
13998
			case 'csnatssefi':
13999
			case 'isoir81':
14000
			case 'natssefi':
14001
				return 'NATS-SEFI';
14002

    
14003
			case 'csnatssefiadd':
14004
			case 'isoir82':
14005
			case 'natssefiadd':
14006
				return 'NATS-SEFI-ADD';
14007

    
14008
			case 'csiso151cuba':
14009
			case 'cuba':
14010
			case 'iso646cu':
14011
			case 'isoir151':
14012
			case 'ncnc1081':
14013
				return 'NC_NC00-10:81';
14014

    
14015
			case 'csiso69french':
14016
			case 'fr':
14017
			case 'iso646fr':
14018
			case 'isoir69':
14019
			case 'nfz62010':
14020
				return 'NF_Z_62-010';
14021

    
14022
			case 'csiso25french':
14023
			case 'iso646fr1':
14024
			case 'isoir25':
14025
			case 'nfz620101973':
14026
				return 'NF_Z_62-010_(1973)';
14027

    
14028
			case 'csiso60danishnorwegian':
14029
			case 'csiso60norwegian1':
14030
			case 'iso646no':
14031
			case 'isoir60':
14032
			case 'no':
14033
			case 'ns45511':
14034
				return 'NS_4551-1';
14035

    
14036
			case 'csiso61norwegian2':
14037
			case 'iso646no2':
14038
			case 'isoir61':
14039
			case 'no2':
14040
			case 'ns45512':
14041
				return 'NS_4551-2';
14042

    
14043
			case 'osdebcdicdf3irv':
14044
				return 'OSD_EBCDIC_DF03_IRV';
14045

    
14046
			case 'osdebcdicdf41':
14047
				return 'OSD_EBCDIC_DF04_1';
14048

    
14049
			case 'osdebcdicdf415':
14050
				return 'OSD_EBCDIC_DF04_15';
14051

    
14052
			case 'cspc8danishnorwegian':
14053
			case 'pc8danishnorwegian':
14054
				return 'PC8-Danish-Norwegian';
14055

    
14056
			case 'cspc8turkish':
14057
			case 'pc8turkish':
14058
				return 'PC8-Turkish';
14059

    
14060
			case 'csiso16portuguese':
14061
			case 'iso646pt':
14062
			case 'isoir16':
14063
			case 'pt':
14064
				return 'PT';
14065

    
14066
			case 'csiso84portuguese2':
14067
			case 'iso646pt2':
14068
			case 'isoir84':
14069
			case 'pt2':
14070
				return 'PT2';
14071

    
14072
			case 'cp154':
14073
			case 'csptcp154':
14074
			case 'cyrillicasian':
14075
			case 'pt154':
14076
			case 'ptcp154':
14077
				return 'PTCP154';
14078

    
14079
			case 'scsu':
14080
				return 'SCSU';
14081

    
14082
			case 'csiso10swedish':
14083
			case 'fi':
14084
			case 'iso646fi':
14085
			case 'iso646se':
14086
			case 'isoir10':
14087
			case 'se':
14088
			case 'sen850200b':
14089
				return 'SEN_850200_B';
14090

    
14091
			case 'csiso11swedishfornames':
14092
			case 'iso646se2':
14093
			case 'isoir11':
14094
			case 'se2':
14095
			case 'sen850200c':
14096
				return 'SEN_850200_C';
14097

    
14098
			case 'csiso102t617bit':
14099
			case 'isoir102':
14100
			case 't617bit':
14101
				return 'T.61-7bit';
14102

    
14103
			case 'csiso103t618bit':
14104
			case 'isoir103':
14105
			case 't61':
14106
			case 't618bit':
14107
				return 'T.61-8bit';
14108

    
14109
			case 'csiso128t101g2':
14110
			case 'isoir128':
14111
			case 't101g2':
14112
				return 'T.101-G2';
14113

    
14114
			case 'cstscii':
14115
			case 'tscii':
14116
				return 'TSCII';
14117

    
14118
			case 'csunicode11':
14119
			case 'unicode11':
14120
				return 'UNICODE-1-1';
14121

    
14122
			case 'csunicode11utf7':
14123
			case 'unicode11utf7':
14124
				return 'UNICODE-1-1-UTF-7';
14125

    
14126
			case 'csunknown8bit':
14127
			case 'unknown8bit':
14128
				return 'UNKNOWN-8BIT';
14129

    
14130
			case 'ansix341968':
14131
			case 'ansix341986':
14132
			case 'ascii':
14133
			case 'cp367':
14134
			case 'csascii':
14135
			case 'ibm367':
14136
			case 'iso646irv1991':
14137
			case 'iso646us':
14138
			case 'isoir6':
14139
			case 'us':
14140
			case 'usascii':
14141
				return 'US-ASCII';
14142

    
14143
			case 'csusdk':
14144
			case 'usdk':
14145
				return 'us-dk';
14146

    
14147
			case 'utf7':
14148
				return 'UTF-7';
14149

    
14150
			case 'utf8':
14151
				return 'UTF-8';
14152

    
14153
			case 'utf16':
14154
				return 'UTF-16';
14155

    
14156
			case 'utf16be':
14157
				return 'UTF-16BE';
14158

    
14159
			case 'utf16le':
14160
				return 'UTF-16LE';
14161

    
14162
			case 'utf32':
14163
				return 'UTF-32';
14164

    
14165
			case 'utf32be':
14166
				return 'UTF-32BE';
14167

    
14168
			case 'utf32le':
14169
				return 'UTF-32LE';
14170

    
14171
			case 'csventurainternational':
14172
			case 'venturainternational':
14173
				return 'Ventura-International';
14174

    
14175
			case 'csventuramath':
14176
			case 'venturamath':
14177
				return 'Ventura-Math';
14178

    
14179
			case 'csventuraus':
14180
			case 'venturaus':
14181
				return 'Ventura-US';
14182

    
14183
			case 'csiso70videotexsupp1':
14184
			case 'isoir70':
14185
			case 'videotexsuppl':
14186
				return 'videotex-suppl';
14187

    
14188
			case 'csviqr':
14189
			case 'viqr':
14190
				return 'VIQR';
14191

    
14192
			case 'csviscii':
14193
			case 'viscii':
14194
				return 'VISCII';
14195

    
14196
			case 'csshiftjis':
14197
			case 'cswindows31j':
14198
			case 'mskanji':
14199
			case 'shiftjis':
14200
			case 'windows31j':
14201
				return 'Windows-31J';
14202

    
14203
			case 'iso885911':
14204
			case 'tis620':
14205
				return 'windows-874';
14206

    
14207
			case 'cseuckr':
14208
			case 'csksc56011987':
14209
			case 'euckr':
14210
			case 'isoir149':
14211
			case 'korean':
14212
			case 'ksc5601':
14213
			case 'ksc56011987':
14214
			case 'ksc56011989':
14215
			case 'windows949':
14216
				return 'windows-949';
14217

    
14218
			case 'windows1250':
14219
				return 'windows-1250';
14220

    
14221
			case 'windows1251':
14222
				return 'windows-1251';
14223

    
14224
			case 'cp819':
14225
			case 'csisolatin1':
14226
			case 'ibm819':
14227
			case 'iso88591':
14228
			case 'iso885911987':
14229
			case 'isoir100':
14230
			case 'l1':
14231
			case 'latin1':
14232
			case 'windows1252':
14233
				return 'windows-1252';
14234

    
14235
			case 'windows1253':
14236
				return 'windows-1253';
14237

    
14238
			case 'csisolatin5':
14239
			case 'iso88599':
14240
			case 'iso885991989':
14241
			case 'isoir148':
14242
			case 'l5':
14243
			case 'latin5':
14244
			case 'windows1254':
14245
				return 'windows-1254';
14246

    
14247
			case 'windows1255':
14248
				return 'windows-1255';
14249

    
14250
			case 'windows1256':
14251
				return 'windows-1256';
14252

    
14253
			case 'windows1257':
14254
				return 'windows-1257';
14255

    
14256
			case 'windows1258':
14257
				return 'windows-1258';
14258

    
14259
			default:
14260
				return $charset;
14261
		}
14262
	}
14263

    
14264
	public static function get_curl_version()
14265
	{
14266
		if (is_array($curl = curl_version()))
14267
		{
14268
			$curl = $curl['version'];
14269
		}
14270
		elseif (substr($curl, 0, 5) === 'curl/')
14271
		{
14272
			$curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
14273
		}
14274
		elseif (substr($curl, 0, 8) === 'libcurl/')
14275
		{
14276
			$curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
14277
		}
14278
		else
14279
		{
14280
			$curl = 0;
14281
		}
14282
		return $curl;
14283
	}
14284

    
14285
	/**
14286
	 * Strip HTML comments
14287
	 *
14288
	 * @param string $data Data to strip comments from
14289
	 * @return string Comment stripped string
14290
	 */
14291
	public static function strip_comments($data)
14292
	{
14293
		$output = '';
14294
		while (($start = strpos($data, '<!--')) !== false)
14295
		{
14296
			$output .= substr($data, 0, $start);
14297
			if (($end = strpos($data, '-->', $start)) !== false)
14298
			{
14299
				$data = substr_replace($data, '', 0, $end + 3);
14300
			}
14301
			else
14302
			{
14303
				$data = '';
14304
			}
14305
		}
14306
		return $output . $data;
14307
	}
14308

    
14309
	public static function parse_date($dt)
14310
	{
14311
		$parser = SimplePie_Parse_Date::get();
14312
		return $parser->parse($dt);
14313
	}
14314

    
14315
	/**
14316
	 * Decode HTML entities
14317
	 *
14318
	 * @deprecated Use DOMDocument instead
14319
	 * @param string $data Input data
14320
	 * @return string Output data
14321
	 */
14322
	public static function entities_decode($data)
14323
	{
14324
		$decoder = new SimplePie_Decode_HTML_Entities($data);
14325
		return $decoder->parse();
14326
	}
14327

    
14328
	/**
14329
	 * Remove RFC822 comments
14330
	 *
14331
	 * @param string $data Data to strip comments from
14332
	 * @return string Comment stripped string
14333
	 */
14334
	public static function uncomment_rfc822($string)
14335
	{
14336
		$string = (string) $string;
14337
		$position = 0;
14338
		$length = strlen($string);
14339
		$depth = 0;
14340

    
14341
		$output = '';
14342

    
14343
		while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
14344
		{
14345
			$output .= substr($string, $position, $pos - $position);
14346
			$position = $pos + 1;
14347
			if ($string[$pos - 1] !== '\\')
14348
			{
14349
				$depth++;
14350
				while ($depth && $position < $length)
14351
				{
14352
					$position += strcspn($string, '()', $position);
14353
					if ($string[$position - 1] === '\\')
14354
					{
14355
						$position++;
14356
						continue;
14357
					}
14358
					elseif (isset($string[$position]))
14359
					{
14360
						switch ($string[$position])
14361
						{
14362
							case '(':
14363
								$depth++;
14364
								break;
14365

    
14366
							case ')':
14367
								$depth--;
14368
								break;
14369
						}
14370
						$position++;
14371
					}
14372
					else
14373
					{
14374
						break;
14375
					}
14376
				}
14377
			}
14378
			else
14379
			{
14380
				$output .= '(';
14381
			}
14382
		}
14383
		$output .= substr($string, $position);
14384

    
14385
		return $output;
14386
	}
14387

    
14388
	public static function parse_mime($mime)
14389
	{
14390
		if (($pos = strpos($mime, ';')) === false)
14391
		{
14392
			return trim($mime);
14393
		}
14394
		else
14395
		{
14396
			return trim(substr($mime, 0, $pos));
14397
		}
14398
	}
14399

    
14400
	public static function atom_03_construct_type($attribs)
14401
	{
14402
		if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
14403
		{
14404
			$mode = SIMPLEPIE_CONSTRUCT_BASE64;
14405
		}
14406
		else
14407
		{
14408
			$mode = SIMPLEPIE_CONSTRUCT_NONE;
14409
		}
14410
		if (isset($attribs['']['type']))
14411
		{
14412
			switch (strtolower(trim($attribs['']['type'])))
14413
			{
14414
				case 'text':
14415
				case 'text/plain':
14416
					return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14417

    
14418
				case 'html':
14419
				case 'text/html':
14420
					return SIMPLEPIE_CONSTRUCT_HTML | $mode;
14421

    
14422
				case 'xhtml':
14423
				case 'application/xhtml+xml':
14424
					return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
14425

    
14426
				default:
14427
					return SIMPLEPIE_CONSTRUCT_NONE | $mode;
14428
			}
14429
		}
14430
		else
14431
		{
14432
			return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14433
		}
14434
	}
14435

    
14436
	public static function atom_10_construct_type($attribs)
14437
	{
14438
		if (isset($attribs['']['type']))
14439
		{
14440
			switch (strtolower(trim($attribs['']['type'])))
14441
			{
14442
				case 'text':
14443
					return SIMPLEPIE_CONSTRUCT_TEXT;
14444

    
14445
				case 'html':
14446
					return SIMPLEPIE_CONSTRUCT_HTML;
14447

    
14448
				case 'xhtml':
14449
					return SIMPLEPIE_CONSTRUCT_XHTML;
14450

    
14451
				default:
14452
					return SIMPLEPIE_CONSTRUCT_NONE;
14453
			}
14454
		}
14455
		return SIMPLEPIE_CONSTRUCT_TEXT;
14456
	}
14457

    
14458
	public static function atom_10_content_construct_type($attribs)
14459
	{
14460
		if (isset($attribs['']['type']))
14461
		{
14462
			$type = strtolower(trim($attribs['']['type']));
14463
			switch ($type)
14464
			{
14465
				case 'text':
14466
					return SIMPLEPIE_CONSTRUCT_TEXT;
14467

    
14468
				case 'html':
14469
					return SIMPLEPIE_CONSTRUCT_HTML;
14470

    
14471
				case 'xhtml':
14472
					return SIMPLEPIE_CONSTRUCT_XHTML;
14473
			}
14474
			if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
14475
			{
14476
				return SIMPLEPIE_CONSTRUCT_NONE;
14477
			}
14478
			else
14479
			{
14480
				return SIMPLEPIE_CONSTRUCT_BASE64;
14481
			}
14482
		}
14483
		else
14484
		{
14485
			return SIMPLEPIE_CONSTRUCT_TEXT;
14486
		}
14487
	}
14488

    
14489
	public static function is_isegment_nz_nc($string)
14490
	{
14491
		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);
14492
	}
14493

    
14494
	public static function space_separated_tokens($string)
14495
	{
14496
		$space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
14497
		$string_length = strlen($string);
14498

    
14499
		$position = strspn($string, $space_characters);
14500
		$tokens = array();
14501

    
14502
		while ($position < $string_length)
14503
		{
14504
			$len = strcspn($string, $space_characters, $position);
14505
			$tokens[] = substr($string, $position, $len);
14506
			$position += $len;
14507
			$position += strspn($string, $space_characters, $position);
14508
		}
14509

    
14510
		return $tokens;
14511
	}
14512

    
14513
	/**
14514
	 * Converts a unicode codepoint to a UTF-8 character
14515
	 *
14516
	 * @static
14517
	 * @param int $codepoint Unicode codepoint
14518
	 * @return string UTF-8 character
14519
	 */
14520
	public static function codepoint_to_utf8($codepoint)
14521
	{
14522
		$codepoint = (int) $codepoint;
14523
		if ($codepoint < 0)
14524
		{
14525
			return false;
14526
		}
14527
		else if ($codepoint <= 0x7f)
14528
		{
14529
			return chr($codepoint);
14530
		}
14531
		else if ($codepoint <= 0x7ff)
14532
		{
14533
			return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
14534
		}
14535
		else if ($codepoint <= 0xffff)
14536
		{
14537
			return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14538
		}
14539
		else if ($codepoint <= 0x10ffff)
14540
		{
14541
			return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14542
		}
14543
		else
14544
		{
14545
			// U+FFFD REPLACEMENT CHARACTER
14546
			return "\xEF\xBF\xBD";
14547
		}
14548
	}
14549

    
14550
	/**
14551
	 * Similar to parse_str()
14552
	 *
14553
	 * Returns an associative array of name/value pairs, where the value is an
14554
	 * array of values that have used the same name
14555
	 *
14556
	 * @static
14557
	 * @param string $str The input string.
14558
	 * @return array
14559
	 */
14560
	public static function parse_str($str)
14561
	{
14562
		$return = array();
14563
		$str = explode('&', $str);
14564

    
14565
		foreach ($str as $section)
14566
		{
14567
			if (strpos($section, '=') !== false)
14568
			{
14569
				list($name, $value) = explode('=', $section, 2);
14570
				$return[urldecode($name)][] = urldecode($value);
14571
			}
14572
			else
14573
			{
14574
				$return[urldecode($section)][] = null;
14575
			}
14576
		}
14577

    
14578
		return $return;
14579
	}
14580

    
14581
	/**
14582
	 * Detect XML encoding, as per XML 1.0 Appendix F.1
14583
	 *
14584
	 * @todo Add support for EBCDIC
14585
	 * @param string $data XML data
14586
	 * @param SimplePie_Registry $registry Class registry
14587
	 * @return array Possible encodings
14588
	 */
14589
	public static function xml_encoding($data, $registry)
14590
	{
14591
		// UTF-32 Big Endian BOM
14592
		if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
14593
		{
14594
			$encoding[] = 'UTF-32BE';
14595
		}
14596
		// UTF-32 Little Endian BOM
14597
		elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
14598
		{
14599
			$encoding[] = 'UTF-32LE';
14600
		}
14601
		// UTF-16 Big Endian BOM
14602
		elseif (substr($data, 0, 2) === "\xFE\xFF")
14603
		{
14604
			$encoding[] = 'UTF-16BE';
14605
		}
14606
		// UTF-16 Little Endian BOM
14607
		elseif (substr($data, 0, 2) === "\xFF\xFE")
14608
		{
14609
			$encoding[] = 'UTF-16LE';
14610
		}
14611
		// UTF-8 BOM
14612
		elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
14613
		{
14614
			$encoding[] = 'UTF-8';
14615
		}
14616
		// UTF-32 Big Endian Without BOM
14617
		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")
14618
		{
14619
			if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
14620
			{
14621
				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')));
14622
				if ($parser->parse())
14623
				{
14624
					$encoding[] = $parser->encoding;
14625
				}
14626
			}
14627
			$encoding[] = 'UTF-32BE';
14628
		}
14629
		// UTF-32 Little Endian Without BOM
14630
		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")
14631
		{
14632
			if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
14633
			{
14634
				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')));
14635
				if ($parser->parse())
14636
				{
14637
					$encoding[] = $parser->encoding;
14638
				}
14639
			}
14640
			$encoding[] = 'UTF-32LE';
14641
		}
14642
		// UTF-16 Big Endian Without BOM
14643
		elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
14644
		{
14645
			if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
14646
			{
14647
				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')));
14648
				if ($parser->parse())
14649
				{
14650
					$encoding[] = $parser->encoding;
14651
				}
14652
			}
14653
			$encoding[] = 'UTF-16BE';
14654
		}
14655
		// UTF-16 Little Endian Without BOM
14656
		elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
14657
		{
14658
			if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
14659
			{
14660
				$parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')));
14661
				if ($parser->parse())
14662
				{
14663
					$encoding[] = $parser->encoding;
14664
				}
14665
			}
14666
			$encoding[] = 'UTF-16LE';
14667
		}
14668
		// US-ASCII (or superset)
14669
		elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
14670
		{
14671
			if ($pos = strpos($data, "\x3F\x3E"))
14672
			{
14673
				$parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
14674
				if ($parser->parse())
14675
				{
14676
					$encoding[] = $parser->encoding;
14677
				}
14678
			}
14679
			$encoding[] = 'UTF-8';
14680
		}
14681
		// Fallback to UTF-8
14682
		else
14683
		{
14684
			$encoding[] = 'UTF-8';
14685
		}
14686
		return $encoding;
14687
	}
14688

    
14689
	public static function output_javascript()
14690
	{
14691
		if (function_exists('ob_gzhandler'))
14692
		{
14693
			ob_start('ob_gzhandler');
14694
		}
14695
		header('Content-type: text/javascript; charset: UTF-8');
14696
		header('Cache-Control: must-revalidate');
14697
		header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
14698
		?>
14699
function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
14700
	if (placeholder != '') {
14701
		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>');
14702
	}
14703
	else {
14704
		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>');
14705
	}
14706
}
14707

    
14708
function embed_flash(bgcolor, width, height, link, loop, type) {
14709
	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>');
14710
}
14711

    
14712
function embed_flv(width, height, link, placeholder, loop, player) {
14713
	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>');
14714
}
14715

    
14716
function embed_wmedia(width, height, link) {
14717
	document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
14718
}
14719
		<?php
14720
	}
14721

    
14722
	/**
14723
	 * Get the SimplePie build timestamp
14724
	 *
14725
	 * Uses the git index if it exists, otherwise uses the modification time
14726
	 * of the newest file.
14727
	 */
14728
	public static function get_build()
14729
	{
14730
		$root = dirname(dirname(__FILE__));
14731
		if (file_exists($root . '/.git/index'))
14732
		{
14733
			return filemtime($root . '/.git/index');
14734
		}
14735
		elseif (file_exists($root . '/SimplePie'))
14736
		{
14737
			$time = 0;
14738
			foreach (glob($root . '/SimplePie/*.php') as $file)
14739
			{
14740
				if (($mtime = filemtime($file)) > $time)
14741
				{
14742
					$time = $mtime;
14743
				}
14744
			}
14745
			return $time;
14746
		}
14747
		elseif (file_exists(dirname(__FILE__) . '/Core.php'))
14748
		{
14749
			return filemtime(dirname(__FILE__) . '/Core.php');
14750
		}
14751
		else
14752
		{
14753
			return filemtime(__FILE__);
14754
		}
14755
	}
14756

    
14757
	/**
14758
	 * Format debugging information
14759
	 */
14760
	public static function debug(&$sp)
14761
	{
14762
		$info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n";
14763
		$info .= 'PHP ' . PHP_VERSION . "\n";
14764
		if ($sp->error() !== null)
14765
		{
14766
			$info .= 'Error occurred: ' . $sp->error() . "\n";
14767
		}
14768
		else
14769
		{
14770
			$info .= "No error found.\n";
14771
		}
14772
		$info .= "Extensions:\n";
14773
		$extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml');
14774
		foreach ($extensions as $ext)
14775
		{
14776
			if (extension_loaded($ext))
14777
			{
14778
				$info .= "    $ext loaded\n";
14779
				switch ($ext)
14780
				{
14781
					case 'pcre':
14782
						$info .= '      Version ' . PCRE_VERSION . "\n";
14783
						break;
14784
					case 'curl':
14785
						$version = curl_version();
14786
						$info .= '      Version ' . $version['version'] . "\n";
14787
						break;
14788
					case 'mbstring':
14789
						$info .= '      Overloading: ' . mb_get_info('func_overload') . "\n";
14790
						break;
14791
					case 'iconv':
14792
						$info .= '      Version ' . ICONV_VERSION . "\n";
14793
						break;
14794
					case 'xml':
14795
						$info .= '      Version ' . LIBXML_DOTTED_VERSION . "\n";
14796
						break;
14797
				}
14798
			}
14799
			else
14800
			{
14801
				$info .= "    $ext not loaded\n";
14802
			}
14803
		}
14804
		return $info;
14805
	}
14806

    
14807
	public static function silence_errors($num, $str)
14808
	{
14809
		// No-op
14810
	}
14811
}
14812

    
14813
/**
14814
 * Class to validate and to work with IPv6 addresses.
14815
 *
14816
 * @package SimplePie
14817
 * @subpackage HTTP
14818
 * @copyright 2003-2005 The PHP Group
14819
 * @license http://www.opensource.org/licenses/bsd-license.php
14820
 * @link http://pear.php.net/package/Net_IPv6
14821
 * @author Alexander Merz <alexander.merz@web.de>
14822
 * @author elfrink at introweb dot nl
14823
 * @author Josh Peck <jmp at joshpeck dot org>
14824
 * @author Geoffrey Sneddon <geoffers@gmail.com>
14825
 */
14826
class SimplePie_Net_IPv6
14827
{
14828
	/**
14829
	 * Uncompresses an IPv6 address
14830
	 *
14831
	 * RFC 4291 allows you to compress concecutive zero pieces in an address to
14832
	 * '::'. This method expects a valid IPv6 address and expands the '::' to
14833
	 * the required number of zero pieces.
14834
	 *
14835
	 * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
14836
	 *           ::1         ->  0:0:0:0:0:0:0:1
14837
	 *
14838
	 * @author Alexander Merz <alexander.merz@web.de>
14839
	 * @author elfrink at introweb dot nl
14840
	 * @author Josh Peck <jmp at joshpeck dot org>
14841
	 * @copyright 2003-2005 The PHP Group
14842
	 * @license http://www.opensource.org/licenses/bsd-license.php
14843
	 * @param string $ip An IPv6 address
14844
	 * @return string The uncompressed IPv6 address
14845
	 */
14846
	public static function uncompress($ip)
14847
	{
14848
		$c1 = -1;
14849
		$c2 = -1;
14850
		if (substr_count($ip, '::') === 1)
14851
		{
14852
			list($ip1, $ip2) = explode('::', $ip);
14853
			if ($ip1 === '')
14854
			{
14855
				$c1 = -1;
14856
			}
14857
			else
14858
			{
14859
				$c1 = substr_count($ip1, ':');
14860
			}
14861
			if ($ip2 === '')
14862
			{
14863
				$c2 = -1;
14864
			}
14865
			else
14866
			{
14867
				$c2 = substr_count($ip2, ':');
14868
			}
14869
			if (strpos($ip2, '.') !== false)
14870
			{
14871
				$c2++;
14872
			}
14873
			// ::
14874
			if ($c1 === -1 && $c2 === -1)
14875
			{
14876
				$ip = '0:0:0:0:0:0:0:0';
14877
			}
14878
			// ::xxx
14879
			else if ($c1 === -1)
14880
			{
14881
				$fill = str_repeat('0:', 7 - $c2);
14882
				$ip = str_replace('::', $fill, $ip);
14883
			}
14884
			// xxx::
14885
			else if ($c2 === -1)
14886
			{
14887
				$fill = str_repeat(':0', 7 - $c1);
14888
				$ip = str_replace('::', $fill, $ip);
14889
			}
14890
			// xxx::xxx
14891
			else
14892
			{
14893
				$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
14894
				$ip = str_replace('::', $fill, $ip);
14895
			}
14896
		}
14897
		return $ip;
14898
	}
14899

    
14900
	/**
14901
	 * Compresses an IPv6 address
14902
	 *
14903
	 * RFC 4291 allows you to compress concecutive zero pieces in an address to
14904
	 * '::'. This method expects a valid IPv6 address and compresses consecutive
14905
	 * zero pieces to '::'.
14906
	 *
14907
	 * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
14908
	 *           0:0:0:0:0:0:0:1        ->  ::1
14909
	 *
14910
	 * @see uncompress()
14911
	 * @param string $ip An IPv6 address
14912
	 * @return string The compressed IPv6 address
14913
	 */
14914
	public static function compress($ip)
14915
	{
14916
		// Prepare the IP to be compressed
14917
		$ip = self::uncompress($ip);
14918
		$ip_parts = self::split_v6_v4($ip);
14919

    
14920
		// Replace all leading zeros
14921
		$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
14922

    
14923
		// Find bunches of zeros
14924
		if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE))
14925
		{
14926
			$max = 0;
14927
			$pos = null;
14928
			foreach ($matches[0] as $match)
14929
			{
14930
				if (strlen($match[0]) > $max)
14931
				{
14932
					$max = strlen($match[0]);
14933
					$pos = $match[1];
14934
				}
14935
			}
14936

    
14937
			$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
14938
		}
14939

    
14940
		if ($ip_parts[1] !== '')
14941
		{
14942
			return implode(':', $ip_parts);
14943
		}
14944
		else
14945
		{
14946
			return $ip_parts[0];
14947
		}
14948
	}
14949

    
14950
	/**
14951
	 * Splits an IPv6 address into the IPv6 and IPv4 representation parts
14952
	 *
14953
	 * RFC 4291 allows you to represent the last two parts of an IPv6 address
14954
	 * using the standard IPv4 representation
14955
	 *
14956
	 * Example:  0:0:0:0:0:0:13.1.68.3
14957
	 *           0:0:0:0:0:FFFF:129.144.52.38
14958
	 *
14959
	 * @param string $ip An IPv6 address
14960
	 * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
14961
	 */
14962
	private static function split_v6_v4($ip)
14963
	{
14964
		if (strpos($ip, '.') !== false)
14965
		{
14966
			$pos = strrpos($ip, ':');
14967
			$ipv6_part = substr($ip, 0, $pos);
14968
			$ipv4_part = substr($ip, $pos + 1);
14969
			return array($ipv6_part, $ipv4_part);
14970
		}
14971
		else
14972
		{
14973
			return array($ip, '');
14974
		}
14975
	}
14976

    
14977
	/**
14978
	 * Checks an IPv6 address
14979
	 *
14980
	 * Checks if the given IP is a valid IPv6 address
14981
	 *
14982
	 * @param string $ip An IPv6 address
14983
	 * @return bool true if $ip is a valid IPv6 address
14984
	 */
14985
	public static function check_ipv6($ip)
14986
	{
14987
		$ip = self::uncompress($ip);
14988
		list($ipv6, $ipv4) = self::split_v6_v4($ip);
14989
		$ipv6 = explode(':', $ipv6);
14990
		$ipv4 = explode('.', $ipv4);
14991
		if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4)
14992
		{
14993
			foreach ($ipv6 as $ipv6_part)
14994
			{
14995
				// The section can't be empty
14996
				if ($ipv6_part === '')
14997
					return false;
14998

    
14999
				// Nor can it be over four characters
15000
				if (strlen($ipv6_part) > 4)
15001
					return false;
15002

    
15003
				// Remove leading zeros (this is safe because of the above)
15004
				$ipv6_part = ltrim($ipv6_part, '0');
15005
				if ($ipv6_part === '')
15006
					$ipv6_part = '0';
15007

    
15008
				// Check the value is valid
15009
				$value = hexdec($ipv6_part);
15010
				if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF)
15011
					return false;
15012
			}
15013
			if (count($ipv4) === 4)
15014
			{
15015
				foreach ($ipv4 as $ipv4_part)
15016
				{
15017
					$value = (int) $ipv4_part;
15018
					if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF)
15019
						return false;
15020
				}
15021
			}
15022
			return true;
15023
		}
15024
		else
15025
		{
15026
			return false;
15027
		}
15028
	}
15029

    
15030
	/**
15031
	 * Checks if the given IP is a valid IPv6 address
15032
	 *
15033
	 * @codeCoverageIgnore
15034
	 * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead
15035
	 * @see check_ipv6
15036
	 * @param string $ip An IPv6 address
15037
	 * @return bool true if $ip is a valid IPv6 address
15038
	 */
15039
	public static function checkIPv6($ip)
15040
	{
15041
		return self::check_ipv6($ip);
15042
	}
15043
}
15044

    
15045
/**
15046
 * Date Parser
15047
 *
15048
 * @package SimplePie
15049
 * @subpackage Parsing
15050
 */
15051
class SimplePie_Parse_Date
15052
{
15053
	/**
15054
	 * Input data
15055
	 *
15056
	 * @access protected
15057
	 * @var string
15058
	 */
15059
	var $date;
15060

    
15061
	/**
15062
	 * List of days, calendar day name => ordinal day number in the week
15063
	 *
15064
	 * @access protected
15065
	 * @var array
15066
	 */
15067
	var $day = array(
15068
		// English
15069
		'mon' => 1,
15070
		'monday' => 1,
15071
		'tue' => 2,
15072
		'tuesday' => 2,
15073
		'wed' => 3,
15074
		'wednesday' => 3,
15075
		'thu' => 4,
15076
		'thursday' => 4,
15077
		'fri' => 5,
15078
		'friday' => 5,
15079
		'sat' => 6,
15080
		'saturday' => 6,
15081
		'sun' => 7,
15082
		'sunday' => 7,
15083
		// Dutch
15084
		'maandag' => 1,
15085
		'dinsdag' => 2,
15086
		'woensdag' => 3,
15087
		'donderdag' => 4,
15088
		'vrijdag' => 5,
15089
		'zaterdag' => 6,
15090
		'zondag' => 7,
15091
		// French
15092
		'lundi' => 1,
15093
		'mardi' => 2,
15094
		'mercredi' => 3,
15095
		'jeudi' => 4,
15096
		'vendredi' => 5,
15097
		'samedi' => 6,
15098
		'dimanche' => 7,
15099
		// German
15100
		'montag' => 1,
15101
		'dienstag' => 2,
15102
		'mittwoch' => 3,
15103
		'donnerstag' => 4,
15104
		'freitag' => 5,
15105
		'samstag' => 6,
15106
		'sonnabend' => 6,
15107
		'sonntag' => 7,
15108
		// Italian
15109
		'lunedì' => 1,
15110
		'martedì' => 2,
15111
		'mercoledì' => 3,
15112
		'giovedì' => 4,
15113
		'venerdì' => 5,
15114
		'sabato' => 6,
15115
		'domenica' => 7,
15116
		// Spanish
15117
		'lunes' => 1,
15118
		'martes' => 2,
15119
		'miércoles' => 3,
15120
		'jueves' => 4,
15121
		'viernes' => 5,
15122
		'sábado' => 6,
15123
		'domingo' => 7,
15124
		// Finnish
15125
		'maanantai' => 1,
15126
		'tiistai' => 2,
15127
		'keskiviikko' => 3,
15128
		'torstai' => 4,
15129
		'perjantai' => 5,
15130
		'lauantai' => 6,
15131
		'sunnuntai' => 7,
15132
		// Hungarian
15133
		'hétfő' => 1,
15134
		'kedd' => 2,
15135
		'szerda' => 3,
15136
		'csütörtok' => 4,
15137
		'péntek' => 5,
15138
		'szombat' => 6,
15139
		'vasárnap' => 7,
15140
		// Greek
15141
		'Δευ' => 1,
15142
		'Τρι' => 2,
15143
		'Τετ' => 3,
15144
		'Πεμ' => 4,
15145
		'Παρ' => 5,
15146
		'Σαβ' => 6,
15147
		'Κυρ' => 7,
15148
	);
15149

    
15150
	/**
15151
	 * List of months, calendar month name => calendar month number
15152
	 *
15153
	 * @access protected
15154
	 * @var array
15155
	 */
15156
	var $month = array(
15157
		// English
15158
		'jan' => 1,
15159
		'january' => 1,
15160
		'feb' => 2,
15161
		'february' => 2,
15162
		'mar' => 3,
15163
		'march' => 3,
15164
		'apr' => 4,
15165
		'april' => 4,
15166
		'may' => 5,
15167
		// No long form of May
15168
		'jun' => 6,
15169
		'june' => 6,
15170
		'jul' => 7,
15171
		'july' => 7,
15172
		'aug' => 8,
15173
		'august' => 8,
15174
		'sep' => 9,
15175
		'september' => 9,
15176
		'oct' => 10,
15177
		'october' => 10,
15178
		'nov' => 11,
15179
		'november' => 11,
15180
		'dec' => 12,
15181
		'december' => 12,
15182
		// Dutch
15183
		'januari' => 1,
15184
		'februari' => 2,
15185
		'maart' => 3,
15186
		'april' => 4,
15187
		'mei' => 5,
15188
		'juni' => 6,
15189
		'juli' => 7,
15190
		'augustus' => 8,
15191
		'september' => 9,
15192
		'oktober' => 10,
15193
		'november' => 11,
15194
		'december' => 12,
15195
		// French
15196
		'janvier' => 1,
15197
		'février' => 2,
15198
		'mars' => 3,
15199
		'avril' => 4,
15200
		'mai' => 5,
15201
		'juin' => 6,
15202
		'juillet' => 7,
15203
		'août' => 8,
15204
		'septembre' => 9,
15205
		'octobre' => 10,
15206
		'novembre' => 11,
15207
		'décembre' => 12,
15208
		// German
15209
		'januar' => 1,
15210
		'februar' => 2,
15211
		'märz' => 3,
15212
		'april' => 4,
15213
		'mai' => 5,
15214
		'juni' => 6,
15215
		'juli' => 7,
15216
		'august' => 8,
15217
		'september' => 9,
15218
		'oktober' => 10,
15219
		'november' => 11,
15220
		'dezember' => 12,
15221
		// Italian
15222
		'gennaio' => 1,
15223
		'febbraio' => 2,
15224
		'marzo' => 3,
15225
		'aprile' => 4,
15226
		'maggio' => 5,
15227
		'giugno' => 6,
15228
		'luglio' => 7,
15229
		'agosto' => 8,
15230
		'settembre' => 9,
15231
		'ottobre' => 10,
15232
		'novembre' => 11,
15233
		'dicembre' => 12,
15234
		// Spanish
15235
		'enero' => 1,
15236
		'febrero' => 2,
15237
		'marzo' => 3,
15238
		'abril' => 4,
15239
		'mayo' => 5,
15240
		'junio' => 6,
15241
		'julio' => 7,
15242
		'agosto' => 8,
15243
		'septiembre' => 9,
15244
		'setiembre' => 9,
15245
		'octubre' => 10,
15246
		'noviembre' => 11,
15247
		'diciembre' => 12,
15248
		// Finnish
15249
		'tammikuu' => 1,
15250
		'helmikuu' => 2,
15251
		'maaliskuu' => 3,
15252
		'huhtikuu' => 4,
15253
		'toukokuu' => 5,
15254
		'kesäkuu' => 6,
15255
		'heinäkuu' => 7,
15256
		'elokuu' => 8,
15257
		'suuskuu' => 9,
15258
		'lokakuu' => 10,
15259
		'marras' => 11,
15260
		'joulukuu' => 12,
15261
		// Hungarian
15262
		'január' => 1,
15263
		'február' => 2,
15264
		'március' => 3,
15265
		'április' => 4,
15266
		'május' => 5,
15267
		'június' => 6,
15268
		'július' => 7,
15269
		'augusztus' => 8,
15270
		'szeptember' => 9,
15271
		'október' => 10,
15272
		'november' => 11,
15273
		'december' => 12,
15274
		// Greek
15275
		'Ιαν' => 1,
15276
		'Φεβ' => 2,
15277
		'Μάώ' => 3,
15278
		'Μαώ' => 3,
15279
		'Απρ' => 4,
15280
		'Μάι' => 5,
15281
		'Μαϊ' => 5,
15282
		'Μαι' => 5,
15283
		'Ιούν' => 6,
15284
		'Ιον' => 6,
15285
		'Ιούλ' => 7,
15286
		'Ιολ' => 7,
15287
		'Αύγ' => 8,
15288
		'Αυγ' => 8,
15289
		'Σεπ' => 9,
15290
		'Οκτ' => 10,
15291
		'Νοέ' => 11,
15292
		'Δεκ' => 12,
15293
	);
15294

    
15295
	/**
15296
	 * List of timezones, abbreviation => offset from UTC
15297
	 *
15298
	 * @access protected
15299
	 * @var array
15300
	 */
15301
	var $timezone = array(
15302
		'ACDT' => 37800,
15303
		'ACIT' => 28800,
15304
		'ACST' => 34200,
15305
		'ACT' => -18000,
15306
		'ACWDT' => 35100,
15307
		'ACWST' => 31500,
15308
		'AEDT' => 39600,
15309
		'AEST' => 36000,
15310
		'AFT' => 16200,
15311
		'AKDT' => -28800,
15312
		'AKST' => -32400,
15313
		'AMDT' => 18000,
15314
		'AMT' => -14400,
15315
		'ANAST' => 46800,
15316
		'ANAT' => 43200,
15317
		'ART' => -10800,
15318
		'AZOST' => -3600,
15319
		'AZST' => 18000,
15320
		'AZT' => 14400,
15321
		'BIOT' => 21600,
15322
		'BIT' => -43200,
15323
		'BOT' => -14400,
15324
		'BRST' => -7200,
15325
		'BRT' => -10800,
15326
		'BST' => 3600,
15327
		'BTT' => 21600,
15328
		'CAST' => 18000,
15329
		'CAT' => 7200,
15330
		'CCT' => 23400,
15331
		'CDT' => -18000,
15332
		'CEDT' => 7200,
15333
		'CEST' => 7200,
15334
		'CET' => 3600,
15335
		'CGST' => -7200,
15336
		'CGT' => -10800,
15337
		'CHADT' => 49500,
15338
		'CHAST' => 45900,
15339
		'CIST' => -28800,
15340
		'CKT' => -36000,
15341
		'CLDT' => -10800,
15342
		'CLST' => -14400,
15343
		'COT' => -18000,
15344
		'CST' => -21600,
15345
		'CVT' => -3600,
15346
		'CXT' => 25200,
15347
		'DAVT' => 25200,
15348
		'DTAT' => 36000,
15349
		'EADT' => -18000,
15350
		'EAST' => -21600,
15351
		'EAT' => 10800,
15352
		'ECT' => -18000,
15353
		'EDT' => -14400,
15354
		'EEST' => 10800,
15355
		'EET' => 7200,
15356
		'EGT' => -3600,
15357
		'EKST' => 21600,
15358
		'EST' => -18000,
15359
		'FJT' => 43200,
15360
		'FKDT' => -10800,
15361
		'FKST' => -14400,
15362
		'FNT' => -7200,
15363
		'GALT' => -21600,
15364
		'GEDT' => 14400,
15365
		'GEST' => 10800,
15366
		'GFT' => -10800,
15367
		'GILT' => 43200,
15368
		'GIT' => -32400,
15369
		'GST' => 14400,
15370
		'GST' => -7200,
15371
		'GYT' => -14400,
15372
		'HAA' => -10800,
15373
		'HAC' => -18000,
15374
		'HADT' => -32400,
15375
		'HAE' => -14400,
15376
		'HAP' => -25200,
15377
		'HAR' => -21600,
15378
		'HAST' => -36000,
15379
		'HAT' => -9000,
15380
		'HAY' => -28800,
15381
		'HKST' => 28800,
15382
		'HMT' => 18000,
15383
		'HNA' => -14400,
15384
		'HNC' => -21600,
15385
		'HNE' => -18000,
15386
		'HNP' => -28800,
15387
		'HNR' => -25200,
15388
		'HNT' => -12600,
15389
		'HNY' => -32400,
15390
		'IRDT' => 16200,
15391
		'IRKST' => 32400,
15392
		'IRKT' => 28800,
15393
		'IRST' => 12600,
15394
		'JFDT' => -10800,
15395
		'JFST' => -14400,
15396
		'JST' => 32400,
15397
		'KGST' => 21600,
15398
		'KGT' => 18000,
15399
		'KOST' => 39600,
15400
		'KOVST' => 28800,
15401
		'KOVT' => 25200,
15402
		'KRAST' => 28800,
15403
		'KRAT' => 25200,
15404
		'KST' => 32400,
15405
		'LHDT' => 39600,
15406
		'LHST' => 37800,
15407
		'LINT' => 50400,
15408
		'LKT' => 21600,
15409
		'MAGST' => 43200,
15410
		'MAGT' => 39600,
15411
		'MAWT' => 21600,
15412
		'MDT' => -21600,
15413
		'MESZ' => 7200,
15414
		'MEZ' => 3600,
15415
		'MHT' => 43200,
15416
		'MIT' => -34200,
15417
		'MNST' => 32400,
15418
		'MSDT' => 14400,
15419
		'MSST' => 10800,
15420
		'MST' => -25200,
15421
		'MUT' => 14400,
15422
		'MVT' => 18000,
15423
		'MYT' => 28800,
15424
		'NCT' => 39600,
15425
		'NDT' => -9000,
15426
		'NFT' => 41400,
15427
		'NMIT' => 36000,
15428
		'NOVST' => 25200,
15429
		'NOVT' => 21600,
15430
		'NPT' => 20700,
15431
		'NRT' => 43200,
15432
		'NST' => -12600,
15433
		'NUT' => -39600,
15434
		'NZDT' => 46800,
15435
		'NZST' => 43200,
15436
		'OMSST' => 25200,
15437
		'OMST' => 21600,
15438
		'PDT' => -25200,
15439
		'PET' => -18000,
15440
		'PETST' => 46800,
15441
		'PETT' => 43200,
15442
		'PGT' => 36000,
15443
		'PHOT' => 46800,
15444
		'PHT' => 28800,
15445
		'PKT' => 18000,
15446
		'PMDT' => -7200,
15447
		'PMST' => -10800,
15448
		'PONT' => 39600,
15449
		'PST' => -28800,
15450
		'PWT' => 32400,
15451
		'PYST' => -10800,
15452
		'PYT' => -14400,
15453
		'RET' => 14400,
15454
		'ROTT' => -10800,
15455
		'SAMST' => 18000,
15456
		'SAMT' => 14400,
15457
		'SAST' => 7200,
15458
		'SBT' => 39600,
15459
		'SCDT' => 46800,
15460
		'SCST' => 43200,
15461
		'SCT' => 14400,
15462
		'SEST' => 3600,
15463
		'SGT' => 28800,
15464
		'SIT' => 28800,
15465
		'SRT' => -10800,
15466
		'SST' => -39600,
15467
		'SYST' => 10800,
15468
		'SYT' => 7200,
15469
		'TFT' => 18000,
15470
		'THAT' => -36000,
15471
		'TJT' => 18000,
15472
		'TKT' => -36000,
15473
		'TMT' => 18000,
15474
		'TOT' => 46800,
15475
		'TPT' => 32400,
15476
		'TRUT' => 36000,
15477
		'TVT' => 43200,
15478
		'TWT' => 28800,
15479
		'UYST' => -7200,
15480
		'UYT' => -10800,
15481
		'UZT' => 18000,
15482
		'VET' => -14400,
15483
		'VLAST' => 39600,
15484
		'VLAT' => 36000,
15485
		'VOST' => 21600,
15486
		'VUT' => 39600,
15487
		'WAST' => 7200,
15488
		'WAT' => 3600,
15489
		'WDT' => 32400,
15490
		'WEST' => 3600,
15491
		'WFT' => 43200,
15492
		'WIB' => 25200,
15493
		'WIT' => 32400,
15494
		'WITA' => 28800,
15495
		'WKST' => 18000,
15496
		'WST' => 28800,
15497
		'YAKST' => 36000,
15498
		'YAKT' => 32400,
15499
		'YAPT' => 36000,
15500
		'YEKST' => 21600,
15501
		'YEKT' => 18000,
15502
	);
15503

    
15504
	/**
15505
	 * Cached PCRE for SimplePie_Parse_Date::$day
15506
	 *
15507
	 * @access protected
15508
	 * @var string
15509
	 */
15510
	var $day_pcre;
15511

    
15512
	/**
15513
	 * Cached PCRE for SimplePie_Parse_Date::$month
15514
	 *
15515
	 * @access protected
15516
	 * @var string
15517
	 */
15518
	var $month_pcre;
15519

    
15520
	/**
15521
	 * Array of user-added callback methods
15522
	 *
15523
	 * @access private
15524
	 * @var array
15525
	 */
15526
	var $built_in = array();
15527

    
15528
	/**
15529
	 * Array of user-added callback methods
15530
	 *
15531
	 * @access private
15532
	 * @var array
15533
	 */
15534
	var $user = array();
15535

    
15536
	/**
15537
	 * Create new SimplePie_Parse_Date object, and set self::day_pcre,
15538
	 * self::month_pcre, and self::built_in
15539
	 *
15540
	 * @access private
15541
	 */
15542
	public function __construct()
15543
	{
15544
		$this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
15545
		$this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
15546

    
15547
		static $cache;
15548
		if (!isset($cache[get_class($this)]))
15549
		{
15550
			$all_methods = get_class_methods($this);
15551

    
15552
			foreach ($all_methods as $method)
15553
			{
15554
				if (strtolower(substr($method, 0, 5)) === 'date_')
15555
				{
15556
					$cache[get_class($this)][] = $method;
15557
				}
15558
			}
15559
		}
15560

    
15561
		foreach ($cache[get_class($this)] as $method)
15562
		{
15563
			$this->built_in[] = $method;
15564
		}
15565
	}
15566

    
15567
	/**
15568
	 * Get the object
15569
	 *
15570
	 * @access public
15571
	 */
15572
	public static function get()
15573
	{
15574
		static $object;
15575
		if (!$object)
15576
		{
15577
			$object = new SimplePie_Parse_Date;
15578
		}
15579
		return $object;
15580
	}
15581

    
15582
	/**
15583
	 * Parse a date
15584
	 *
15585
	 * @final
15586
	 * @access public
15587
	 * @param string $date Date to parse
15588
	 * @return int Timestamp corresponding to date string, or false on failure
15589
	 */
15590
	public function parse($date)
15591
	{
15592
		foreach ($this->user as $method)
15593
		{
15594
			if (($returned = call_user_func($method, $date)) !== false)
15595
			{
15596
				return $returned;
15597
			}
15598
		}
15599

    
15600
		foreach ($this->built_in as $method)
15601
		{
15602
			if (($returned = call_user_func(array($this, $method), $date)) !== false)
15603
			{
15604
				return $returned;
15605
			}
15606
		}
15607

    
15608
		return false;
15609
	}
15610

    
15611
	/**
15612
	 * Add a callback method to parse a date
15613
	 *
15614
	 * @final
15615
	 * @access public
15616
	 * @param callback $callback
15617
	 */
15618
	public function add_callback($callback)
15619
	{
15620
		if (is_callable($callback))
15621
		{
15622
			$this->user[] = $callback;
15623
		}
15624
		else
15625
		{
15626
			trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
15627
		}
15628
	}
15629

    
15630
	/**
15631
	 * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
15632
	 * well as allowing any of upper or lower case "T", horizontal tabs, or
15633
	 * spaces to be used as the time separator (including more than one))
15634
	 *
15635
	 * @access protected
15636
	 * @return int Timestamp
15637
	 */
15638
	public function date_w3cdtf($date)
15639
	{
15640
		static $pcre;
15641
		if (!$pcre)
15642
		{
15643
			$year = '([0-9]{4})';
15644
			$month = $day = $hour = $minute = $second = '([0-9]{2})';
15645
			$decimal = '([0-9]*)';
15646
			$zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
15647
			$pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
15648
		}
15649
		if (preg_match($pcre, $date, $match))
15650
		{
15651
			/*
15652
			Capturing subpatterns:
15653
			1: Year
15654
			2: Month
15655
			3: Day
15656
			4: Hour
15657
			5: Minute
15658
			6: Second
15659
			7: Decimal fraction of a second
15660
			8: Zulu
15661
			9: Timezone ±
15662
			10: Timezone hours
15663
			11: Timezone minutes
15664
			*/
15665

    
15666
			// Fill in empty matches
15667
			for ($i = count($match); $i <= 3; $i++)
15668
			{
15669
				$match[$i] = '1';
15670
			}
15671

    
15672
			for ($i = count($match); $i <= 7; $i++)
15673
			{
15674
				$match[$i] = '0';
15675
			}
15676

    
15677
			// Numeric timezone
15678
			if (isset($match[9]) && $match[9] !== '')
15679
			{
15680
				$timezone = $match[10] * 3600;
15681
				$timezone += $match[11] * 60;
15682
				if ($match[9] === '-')
15683
				{
15684
					$timezone = 0 - $timezone;
15685
				}
15686
			}
15687
			else
15688
			{
15689
				$timezone = 0;
15690
			}
15691

    
15692
			// Convert the number of seconds to an integer, taking decimals into account
15693
			$second = round((int)$match[6] + (int)$match[7] / pow(10, strlen($match[7])));
15694

    
15695
			return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
15696
		}
15697
		else
15698
		{
15699
			return false;
15700
		}
15701
	}
15702

    
15703
	/**
15704
	 * Remove RFC822 comments
15705
	 *
15706
	 * @access protected
15707
	 * @param string $data Data to strip comments from
15708
	 * @return string Comment stripped string
15709
	 */
15710
	public function remove_rfc2822_comments($string)
15711
	{
15712
		$string = (string) $string;
15713
		$position = 0;
15714
		$length = strlen($string);
15715
		$depth = 0;
15716

    
15717
		$output = '';
15718

    
15719
		while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
15720
		{
15721
			$output .= substr($string, $position, $pos - $position);
15722
			$position = $pos + 1;
15723
			if ($pos === 0 || $string[$pos - 1] !== '\\')
15724
			{
15725
				$depth++;
15726
				while ($depth && $position < $length)
15727
				{
15728
					$position += strcspn($string, '()', $position);
15729
					if ($string[$position - 1] === '\\')
15730
					{
15731
						$position++;
15732
						continue;
15733
					}
15734
					elseif (isset($string[$position]))
15735
					{
15736
						switch ($string[$position])
15737
						{
15738
							case '(':
15739
								$depth++;
15740
								break;
15741

    
15742
							case ')':
15743
								$depth--;
15744
								break;
15745
						}
15746
						$position++;
15747
					}
15748
					else
15749
					{
15750
						break;
15751
					}
15752
				}
15753
			}
15754
			else
15755
			{
15756
				$output .= '(';
15757
			}
15758
		}
15759
		$output .= substr($string, $position);
15760

    
15761
		return $output;
15762
	}
15763

    
15764
	/**
15765
	 * Parse RFC2822's date format
15766
	 *
15767
	 * @access protected
15768
	 * @return int Timestamp
15769
	 */
15770
	public function date_rfc2822($date)
15771
	{
15772
		static $pcre;
15773
		if (!$pcre)
15774
		{
15775
			$wsp = '[\x09\x20]';
15776
			$fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
15777
			$optional_fws = $fws . '?';
15778
			$day_name = $this->day_pcre;
15779
			$month = $this->month_pcre;
15780
			$day = '([0-9]{1,2})';
15781
			$hour = $minute = $second = '([0-9]{2})';
15782
			$year = '([0-9]{2,4})';
15783
			$num_zone = '([+\-])([0-9]{2})([0-9]{2})';
15784
			$character_zone = '([A-Z]{1,5})';
15785
			$zone = '(?:' . $num_zone . '|' . $character_zone . ')';
15786
			$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';
15787
		}
15788
		if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
15789
		{
15790
			/*
15791
			Capturing subpatterns:
15792
			1: Day name
15793
			2: Day
15794
			3: Month
15795
			4: Year
15796
			5: Hour
15797
			6: Minute
15798
			7: Second
15799
			8: Timezone ±
15800
			9: Timezone hours
15801
			10: Timezone minutes
15802
			11: Alphabetic timezone
15803
			*/
15804

    
15805
			// Find the month number
15806
			$month = $this->month[strtolower($match[3])];
15807

    
15808
			// Numeric timezone
15809
			if ($match[8] !== '')
15810
			{
15811
				$timezone = $match[9] * 3600;
15812
				$timezone += $match[10] * 60;
15813
				if ($match[8] === '-')
15814
				{
15815
					$timezone = 0 - $timezone;
15816
				}
15817
			}
15818
			// Character timezone
15819
			elseif (isset($this->timezone[strtoupper($match[11])]))
15820
			{
15821
				$timezone = $this->timezone[strtoupper($match[11])];
15822
			}
15823
			// Assume everything else to be -0000
15824
			else
15825
			{
15826
				$timezone = 0;
15827
			}
15828

    
15829
			// Deal with 2/3 digit years
15830
			if ($match[4] < 50)
15831
			{
15832
				$match[4] += 2000;
15833
			}
15834
			elseif ($match[4] < 1000)
15835
			{
15836
				$match[4] += 1900;
15837
			}
15838

    
15839
			// Second is optional, if it is empty set it to zero
15840
			if ($match[7] !== '')
15841
			{
15842
				$second = $match[7];
15843
			}
15844
			else
15845
			{
15846
				$second = 0;
15847
			}
15848

    
15849
			return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
15850
		}
15851
		else
15852
		{
15853
			return false;
15854
		}
15855
	}
15856

    
15857
	/**
15858
	 * Parse RFC850's date format
15859
	 *
15860
	 * @access protected
15861
	 * @return int Timestamp
15862
	 */
15863
	public function date_rfc850($date)
15864
	{
15865
		static $pcre;
15866
		if (!$pcre)
15867
		{
15868
			$space = '[\x09\x20]+';
15869
			$day_name = $this->day_pcre;
15870
			$month = $this->month_pcre;
15871
			$day = '([0-9]{1,2})';
15872
			$year = $hour = $minute = $second = '([0-9]{2})';
15873
			$zone = '([A-Z]{1,5})';
15874
			$pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
15875
		}
15876
		if (preg_match($pcre, $date, $match))
15877
		{
15878
			/*
15879
			Capturing subpatterns:
15880
			1: Day name
15881
			2: Day
15882
			3: Month
15883
			4: Year
15884
			5: Hour
15885
			6: Minute
15886
			7: Second
15887
			8: Timezone
15888
			*/
15889

    
15890
			// Month
15891
			$month = $this->month[strtolower($match[3])];
15892

    
15893
			// Character timezone
15894
			if (isset($this->timezone[strtoupper($match[8])]))
15895
			{
15896
				$timezone = $this->timezone[strtoupper($match[8])];
15897
			}
15898
			// Assume everything else to be -0000
15899
			else
15900
			{
15901
				$timezone = 0;
15902
			}
15903

    
15904
			// Deal with 2 digit year
15905
			if ($match[4] < 50)
15906
			{
15907
				$match[4] += 2000;
15908
			}
15909
			else
15910
			{
15911
				$match[4] += 1900;
15912
			}
15913

    
15914
			return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
15915
		}
15916
		else
15917
		{
15918
			return false;
15919
		}
15920
	}
15921

    
15922
	/**
15923
	 * Parse C99's asctime()'s date format
15924
	 *
15925
	 * @access protected
15926
	 * @return int Timestamp
15927
	 */
15928
	public function date_asctime($date)
15929
	{
15930
		static $pcre;
15931
		if (!$pcre)
15932
		{
15933
			$space = '[\x09\x20]+';
15934
			$wday_name = $this->day_pcre;
15935
			$mon_name = $this->month_pcre;
15936
			$day = '([0-9]{1,2})';
15937
			$hour = $sec = $min = '([0-9]{2})';
15938
			$year = '([0-9]{4})';
15939
			$terminator = '\x0A?\x00?';
15940
			$pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
15941
		}
15942
		if (preg_match($pcre, $date, $match))
15943
		{
15944
			/*
15945
			Capturing subpatterns:
15946
			1: Day name
15947
			2: Month
15948
			3: Day
15949
			4: Hour
15950
			5: Minute
15951
			6: Second
15952
			7: Year
15953
			*/
15954

    
15955
			$month = $this->month[strtolower($match[2])];
15956
			return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
15957
		}
15958
		else
15959
		{
15960
			return false;
15961
		}
15962
	}
15963

    
15964
	/**
15965
	 * Parse dates using strtotime()
15966
	 *
15967
	 * @access protected
15968
	 * @return int Timestamp
15969
	 */
15970
	public function date_strtotime($date)
15971
	{
15972
		$strtotime = strtotime($date);
15973
		if ($strtotime === -1 || $strtotime === false)
15974
		{
15975
			return false;
15976
		}
15977
		else
15978
		{
15979
			return $strtotime;
15980
		}
15981
	}
15982
}
15983

    
15984
/**
15985
 * Parses XML into something sane
15986
 *
15987
 *
15988
 * This class can be overloaded with {@see SimplePie::set_parser_class()}
15989
 *
15990
 * @package SimplePie
15991
 * @subpackage Parsing
15992
 */
15993
class SimplePie_Parser
15994
{
15995
	var $error_code;
15996
	var $error_string;
15997
	var $current_line;
15998
	var $current_column;
15999
	var $current_byte;
16000
	var $separator = ' ';
16001
	var $namespace = array('');
16002
	var $element = array('');
16003
	var $xml_base = array('');
16004
	var $xml_base_explicit = array(false);
16005
	var $xml_lang = array('');
16006
	var $data = array();
16007
	var $datas = array(array());
16008
	var $current_xhtml_construct = -1;
16009
	var $encoding;
16010
	protected $registry;
16011

    
16012
	public function set_registry(SimplePie_Registry $registry)
16013
	{
16014
		$this->registry = $registry;
16015
	}
16016

    
16017
	public function parse(&$data, $encoding, $url = '')
16018
	{
16019
		if (function_exists('Mf2\parse')) {
16020
			// Check for both h-feed and h-entry, as both a feed with no entries
16021
			// and a list of entries without an h-feed wrapper are both valid.
16022
			$position = 0;
16023
			while ($position = strpos($data, 'h-feed', $position)) {
16024
				$start = $position < 200 ? 0 : $position - 200;
16025
				$check = substr($data, $start, 400);
16026
				if (preg_match('/class="[^"]*h-feed/', $check)) {
16027
					return $this->parse_microformats($data, $url);
16028
				}
16029
				$position += 7;
16030
			}
16031
			$position = 0;
16032
			while ($position = strpos($data, 'h-entry', $position)) {
16033
				$start = $position < 200 ? 0 : $position - 200;
16034
				$check = substr($data, $start, 400);
16035
				if (preg_match('/class="[^"]*h-entry/', $check)) {
16036
					return $this->parse_microformats($data, $url);
16037
				}
16038
				$position += 7;
16039
			}
16040
		}
16041

    
16042
		// Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
16043
		if (strtoupper($encoding) === 'US-ASCII')
16044
		{
16045
			$this->encoding = 'UTF-8';
16046
		}
16047
		else
16048
		{
16049
			$this->encoding = $encoding;
16050
		}
16051

    
16052
		// Strip BOM:
16053
		// UTF-32 Big Endian BOM
16054
		if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
16055
		{
16056
			$data = substr($data, 4);
16057
		}
16058
		// UTF-32 Little Endian BOM
16059
		elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
16060
		{
16061
			$data = substr($data, 4);
16062
		}
16063
		// UTF-16 Big Endian BOM
16064
		elseif (substr($data, 0, 2) === "\xFE\xFF")
16065
		{
16066
			$data = substr($data, 2);
16067
		}
16068
		// UTF-16 Little Endian BOM
16069
		elseif (substr($data, 0, 2) === "\xFF\xFE")
16070
		{
16071
			$data = substr($data, 2);
16072
		}
16073
		// UTF-8 BOM
16074
		elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
16075
		{
16076
			$data = substr($data, 3);
16077
		}
16078

    
16079
		if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
16080
		{
16081
			$declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
16082
			if ($declaration->parse())
16083
			{
16084
				$data = substr($data, $pos + 2);
16085
				$data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' ."\n". $this->declare_html_entities() . $data;
16086
			}
16087
			else
16088
			{
16089
				$this->error_string = 'SimplePie bug! Please report this!';
16090
				return false;
16091
			}
16092
		}
16093

    
16094
		$return = true;
16095

    
16096
		static $xml_is_sane = null;
16097
		if ($xml_is_sane === null)
16098
		{
16099
			$parser_check = xml_parser_create();
16100
			xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
16101
			xml_parser_free($parser_check);
16102
			$xml_is_sane = isset($values[0]['value']);
16103
		}
16104

    
16105
		// Create the parser
16106
		if ($xml_is_sane)
16107
		{
16108
			$xml = xml_parser_create_ns($this->encoding, $this->separator);
16109
			xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
16110
			xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
16111
			xml_set_object($xml, $this);
16112
			xml_set_character_data_handler($xml, 'cdata');
16113
			xml_set_element_handler($xml, 'tag_open', 'tag_close');
16114

    
16115
			// Parse!
16116
			if (!xml_parse($xml, $data, true))
16117
			{
16118
				$this->error_code = xml_get_error_code($xml);
16119
				$this->error_string = xml_error_string($this->error_code);
16120
				$return = false;
16121
			}
16122
			$this->current_line = xml_get_current_line_number($xml);
16123
			$this->current_column = xml_get_current_column_number($xml);
16124
			$this->current_byte = xml_get_current_byte_index($xml);
16125
			xml_parser_free($xml);
16126
			return $return;
16127
		}
16128
		else
16129
		{
16130
			libxml_clear_errors();
16131
			$xml = new XMLReader();
16132
			$xml->xml($data);
16133
			while (@$xml->read())
16134
			{
16135
				switch ($xml->nodeType)
16136
				{
16137

    
16138
					case constant('XMLReader::END_ELEMENT'):
16139
						if ($xml->namespaceURI !== '')
16140
						{
16141
							$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
16142
						}
16143
						else
16144
						{
16145
							$tagName = $xml->localName;
16146
						}
16147
						$this->tag_close(null, $tagName);
16148
						break;
16149
					case constant('XMLReader::ELEMENT'):
16150
						$empty = $xml->isEmptyElement;
16151
						if ($xml->namespaceURI !== '')
16152
						{
16153
							$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
16154
						}
16155
						else
16156
						{
16157
							$tagName = $xml->localName;
16158
						}
16159
						$attributes = array();
16160
						while ($xml->moveToNextAttribute())
16161
						{
16162
							if ($xml->namespaceURI !== '')
16163
							{
16164
								$attrName = $xml->namespaceURI . $this->separator . $xml->localName;
16165
							}
16166
							else
16167
							{
16168
								$attrName = $xml->localName;
16169
							}
16170
							$attributes[$attrName] = $xml->value;
16171
						}
16172
						$this->tag_open(null, $tagName, $attributes);
16173
						if ($empty)
16174
						{
16175
							$this->tag_close(null, $tagName);
16176
						}
16177
						break;
16178
					case constant('XMLReader::TEXT'):
16179

    
16180
					case constant('XMLReader::CDATA'):
16181
						$this->cdata(null, $xml->value);
16182
						break;
16183
				}
16184
			}
16185
			if ($error = libxml_get_last_error())
16186
			{
16187
				$this->error_code = $error->code;
16188
				$this->error_string = $error->message;
16189
				$this->current_line = $error->line;
16190
				$this->current_column = $error->column;
16191
				return false;
16192
			}
16193
			else
16194
			{
16195
				return true;
16196
			}
16197
		}
16198
	}
16199

    
16200
	public function get_error_code()
16201
	{
16202
		return $this->error_code;
16203
	}
16204

    
16205
	public function get_error_string()
16206
	{
16207
		return $this->error_string;
16208
	}
16209

    
16210
	public function get_current_line()
16211
	{
16212
		return $this->current_line;
16213
	}
16214

    
16215
	public function get_current_column()
16216
	{
16217
		return $this->current_column;
16218
	}
16219

    
16220
	public function get_current_byte()
16221
	{
16222
		return $this->current_byte;
16223
	}
16224

    
16225
	public function get_data()
16226
	{
16227
		return $this->data;
16228
	}
16229

    
16230
	public function tag_open($parser, $tag, $attributes)
16231
	{
16232
		list($this->namespace[], $this->element[]) = $this->split_ns($tag);
16233

    
16234
		$attribs = array();
16235
		foreach ($attributes as $name => $value)
16236
		{
16237
			list($attrib_namespace, $attribute) = $this->split_ns($name);
16238
			$attribs[$attrib_namespace][$attribute] = $value;
16239
		}
16240

    
16241
		if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
16242
		{
16243
			$base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)));
16244
			if ($base !== false)
16245
			{
16246
				$this->xml_base[] = $base;
16247
				$this->xml_base_explicit[] = true;
16248
			}
16249
		}
16250
		else
16251
		{
16252
			$this->xml_base[] = end($this->xml_base);
16253
			$this->xml_base_explicit[] = end($this->xml_base_explicit);
16254
		}
16255

    
16256
		if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
16257
		{
16258
			$this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
16259
		}
16260
		else
16261
		{
16262
			$this->xml_lang[] = end($this->xml_lang);
16263
		}
16264

    
16265
		if ($this->current_xhtml_construct >= 0)
16266
		{
16267
			$this->current_xhtml_construct++;
16268
			if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
16269
			{
16270
				$this->data['data'] .= '<' . end($this->element);
16271
				if (isset($attribs['']))
16272
				{
16273
					foreach ($attribs[''] as $name => $value)
16274
					{
16275
						$this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
16276
					}
16277
				}
16278
				$this->data['data'] .= '>';
16279
			}
16280
		}
16281
		else
16282
		{
16283
			$this->datas[] =& $this->data;
16284
			$this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
16285
			$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));
16286
			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')
16287
			|| (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')
16288
			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title')))
16289
			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title')))
16290
			|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title'))))
16291
			{
16292
				$this->current_xhtml_construct = 0;
16293
			}
16294
		}
16295
	}
16296

    
16297
	public function cdata($parser, $cdata)
16298
	{
16299
		if ($this->current_xhtml_construct >= 0)
16300
		{
16301
			$this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
16302
		}
16303
		else
16304
		{
16305
			$this->data['data'] .= $cdata;
16306
		}
16307
	}
16308

    
16309
	public function tag_close($parser, $tag)
16310
	{
16311
		if ($this->current_xhtml_construct >= 0)
16312
		{
16313
			$this->current_xhtml_construct--;
16314
			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')))
16315
			{
16316
				$this->data['data'] .= '</' . end($this->element) . '>';
16317
			}
16318
		}
16319
		if ($this->current_xhtml_construct === -1)
16320
		{
16321
			$this->data =& $this->datas[count($this->datas) - 1];
16322
			array_pop($this->datas);
16323
		}
16324

    
16325
		array_pop($this->element);
16326
		array_pop($this->namespace);
16327
		array_pop($this->xml_base);
16328
		array_pop($this->xml_base_explicit);
16329
		array_pop($this->xml_lang);
16330
	}
16331

    
16332
	public function split_ns($string)
16333
	{
16334
		static $cache = array();
16335
		if (!isset($cache[$string]))
16336
		{
16337
			if ($pos = strpos($string, $this->separator))
16338
			{
16339
				static $separator_length;
16340
				if (!$separator_length)
16341
				{
16342
					$separator_length = strlen($this->separator);
16343
				}
16344
				$namespace = substr($string, 0, $pos);
16345
				$local_name = substr($string, $pos + $separator_length);
16346
				if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
16347
				{
16348
					$namespace = SIMPLEPIE_NAMESPACE_ITUNES;
16349
				}
16350

    
16351
				// Normalize the Media RSS namespaces
16352
				if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
16353
					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
16354
					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
16355
					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
16356
					$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
16357
				{
16358
					$namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
16359
				}
16360
				$cache[$string] = array($namespace, $local_name);
16361
			}
16362
			else
16363
			{
16364
				$cache[$string] = array('', $string);
16365
			}
16366
		}
16367
		return $cache[$string];
16368
	}
16369

    
16370
	private function parse_hcard($data, $category = false) {
16371
		$name = '';
16372
		$link = '';
16373
		// Check if h-card is set and pass that information on in the link.
16374
		if (isset($data['type']) && in_array('h-card', $data['type'])) {
16375
			if (isset($data['properties']['name'][0])) {
16376
				$name = $data['properties']['name'][0];
16377
			}
16378
			if (isset($data['properties']['url'][0])) {
16379
				$link = $data['properties']['url'][0];
16380
				if ($name === '') {
16381
					$name = $link;
16382
				}
16383
				else {
16384
					// can't have commas in categories.
16385
					$name = str_replace(',', '', $name);
16386
				}
16387
				$person_tag = $category ? '<span class="person-tag"></span>' : '';
16388
				return '<a class="h-card" href="'.$link.'">'.$person_tag.$name.'</a>';
16389
			}
16390
		}
16391
		return isset($data['value']) ? $data['value'] : '';
16392
	}
16393

    
16394
	private function parse_microformats(&$data, $url) {
16395
		$feed_title = '';
16396
		$feed_author = NULL;
16397
		$author_cache = array();
16398
		$items = array();
16399
		$entries = array();
16400
		$mf = Mf2\parse($data, $url);
16401
		// First look for an h-feed.
16402
		$h_feed = array();
16403
		foreach ($mf['items'] as $mf_item) {
16404
			if (in_array('h-feed', $mf_item['type'])) {
16405
				$h_feed = $mf_item;
16406
				break;
16407
			}
16408
			// Also look for an h-feed in the children of each top level item.
16409
			if (!isset($mf_item['children'][0]['type'])) continue;
16410
			if (in_array('h-feed', $mf_item['children'][0]['type'])) {
16411
				$h_feed = $mf_item['children'][0];
16412
				// In this case the parent of the h-feed may be an h-card, so use it as
16413
				// the feed_author.
16414
				if (in_array('h-card', $mf_item['type'])) $feed_author = $mf_item;
16415
				break;
16416
			}
16417
		}
16418
		if (isset($h_feed['children'])) {
16419
			$entries = $h_feed['children'];
16420
			// Also set the feed title and store author from the h-feed if available.
16421
			if (isset($mf['items'][0]['properties']['name'][0])) {
16422
				$feed_title = $mf['items'][0]['properties']['name'][0];
16423
			}
16424
			if (isset($mf['items'][0]['properties']['author'][0])) {
16425
				$feed_author = $mf['items'][0]['properties']['author'][0];
16426
			}
16427
		}
16428
		else {
16429
			$entries = $mf['items'];
16430
		}
16431
		for ($i = 0; $i < count($entries); $i++) {
16432
			$entry = $entries[$i];
16433
			if (in_array('h-entry', $entry['type'])) {
16434
				$item = array();
16435
				$title = '';
16436
				$description = '';
16437
				if (isset($entry['properties']['url'][0])) {
16438
					$link = $entry['properties']['url'][0];
16439
					if (isset($link['value'])) $link = $link['value'];
16440
					$item['link'] = array(array('data' => $link));
16441
				}
16442
				if (isset($entry['properties']['uid'][0])) {
16443
					$guid = $entry['properties']['uid'][0];
16444
					if (isset($guid['value'])) $guid = $guid['value'];
16445
					$item['guid'] = array(array('data' => $guid));
16446
				}
16447
				if (isset($entry['properties']['name'][0])) {
16448
					$title = $entry['properties']['name'][0];
16449
					if (isset($title['value'])) $title = $title['value'];
16450
					$item['title'] = array(array('data' => $title));
16451
				}
16452
				if (isset($entry['properties']['author'][0]) || isset($feed_author)) {
16453
					// author is a special case, it can be plain text or an h-card array.
16454
					// If it's plain text it can also be a url that should be followed to
16455
					// get the actual h-card.
16456
					$author = isset($entry['properties']['author'][0]) ?
16457
						$entry['properties']['author'][0] : $feed_author;
16458
					if (!is_string($author)) {
16459
						$author = $this->parse_hcard($author);
16460
					}
16461
					else if (strpos($author, 'http') === 0) {
16462
						if (isset($author_cache[$author])) {
16463
							$author = $author_cache[$author];
16464
						}
16465
						else {
16466
							$mf = Mf2\fetch($author);
16467
							foreach ($mf['items'] as $hcard) {
16468
								// Only interested in an h-card by itself in this case.
16469
								if (!in_array('h-card', $hcard['type'])) {
16470
									continue;
16471
								}
16472
								// It must have a url property matching what we fetched.
16473
								if (!isset($hcard['properties']['url']) ||
16474
										!(in_array($author, $hcard['properties']['url']))) {
16475
									continue;
16476
								}
16477
								// Save parse_hcard the trouble of finding the correct url.
16478
								$hcard['properties']['url'][0] = $author;
16479
								// Cache this h-card for the next h-entry to check.
16480
								$author_cache[$author] = $this->parse_hcard($hcard);
16481
								$author = $author_cache[$author];
16482
								break;
16483
							}
16484
						}
16485
					}
16486
					$item['author'] = array(array('data' => $author));
16487
				}
16488
				if (isset($entry['properties']['photo'][0])) {
16489
					// If a photo is also in content, don't need to add it again here.
16490
					$content = '';
16491
					if (isset($entry['properties']['content'][0]['html'])) {
16492
						$content = $entry['properties']['content'][0]['html'];
16493
					}
16494
					$photo_list = array();
16495
					for ($j = 0; $j < count($entry['properties']['photo']); $j++) {
16496
						$photo = $entry['properties']['photo'][$j];
16497
						if (strpos($content, $photo) === false) {
16498
							$photo_list[] = $photo;
16499
						}
16500
					}
16501
					// When there's more than one photo show the first and use a lightbox.
16502
					$count = count($photo_list);
16503
					if ($count > 1) {
16504
						$description = '<p>';
16505
						for ($j = 0; $j < $count; $j++) {
16506
							$hidden = $j === 0 ? '' : 'class="hidden" ';
16507
							$description .= '<a href="'.$photo_list[$j].'" '.$hidden.
16508
								'data-lightbox="image-set-'.$i.'">'.
16509
								'<img src="'.$photo_list[$j].'"></a>';
16510
						}
16511
						$description .= '<br><b>'.$count.' photos</b></p>';
16512
					}
16513
					else if ($count == 1) {
16514
						$description = '<p><img src="'.$photo_list[0].'"></p>';
16515
					}
16516
				}
16517
				if (isset($entry['properties']['content'][0]['html'])) {
16518
					// e-content['value'] is the same as p-name when they are on the same
16519
					// element. Use this to replace title with a strip_tags version so
16520
					// that alt text from images is not included in the title.
16521
					if ($entry['properties']['content'][0]['value'] === $title) {
16522
						$title = strip_tags($entry['properties']['content'][0]['html']);
16523
						$item['title'] = array(array('data' => $title));
16524
					}
16525
					$description .= $entry['properties']['content'][0]['html'];
16526
					if (isset($entry['properties']['in-reply-to'][0]['value'])) {
16527
						$in_reply_to = $entry['properties']['in-reply-to'][0]['value'];
16528
						$description .= '<p><span class="in-reply-to"></span> '.
16529
							'<a href="'.$in_reply_to.'">'.$in_reply_to.'</a><p>';
16530
					}
16531
					$item['description'] = array(array('data' => $description));
16532
				}
16533
				if (isset($entry['properties']['category'])) {
16534
					$category_csv = '';
16535
					// Categories can also contain h-cards.
16536
					foreach ($entry['properties']['category'] as $category) {
16537
						if ($category_csv !== '') $category_csv .= ', ';
16538
						if (is_string($category)) {
16539
							// Can't have commas in categories.
16540
							$category_csv .= str_replace(',', '', $category);
16541
						}
16542
						else {
16543
							$category_csv .= $this->parse_hcard($category, true);
16544
						}
16545
					}
16546
					$item['category'] = array(array('data' => $category_csv));
16547
				}
16548
				if (isset($entry['properties']['published'][0])) {
16549
					$timestamp = strtotime($entry['properties']['published'][0]);
16550
					$pub_date = date('F j Y g:ia', $timestamp).' GMT';
16551
					$item['pubDate'] = array(array('data' => $pub_date));
16552
				}
16553
				// The title and description are set to the empty string to represent
16554
				// a deleted item (which also makes it an invalid rss item).
16555
				if (isset($entry['properties']['deleted'][0])) {
16556
					$item['title'] = array(array('data' => ''));
16557
					$item['description'] = array(array('data' => ''));
16558
				}
16559
				$items[] = array('child' => array('' => $item));
16560
			}
16561
		}
16562
		// Mimic RSS data format when storing microformats.
16563
		$link = array(array('data' => $url));
16564
		$image = '';
16565
		if (!is_string($feed_author) &&
16566
				isset($feed_author['properties']['photo'][0])) {
16567
			$image = array(array('child' => array('' => array('url' =>
16568
				array(array('data' => $feed_author['properties']['photo'][0]))))));
16569
		}
16570
		// Use the a name given for the h-feed, or get the title from the html.
16571
		if ($feed_title !== '') {
16572
			$feed_title = array(array('data' => htmlspecialchars($feed_title)));
16573
		}
16574
		else if ($position = strpos($data, '<title>')) {
16575
			$start = $position < 200 ? 0 : $position - 200;
16576
			$check = substr($data, $start, 400);
16577
			$matches = array();
16578
			if (preg_match('/<title>(.+)<\/title>/', $check, $matches)) {
16579
				$feed_title = array(array('data' => htmlspecialchars($matches[1])));
16580
			}
16581
		}
16582
		$channel = array('channel' => array(array('child' => array('' =>
16583
			array('link' => $link, 'image' => $image, 'title' => $feed_title,
16584
			      'item' => $items)))));
16585
		$rss = array(array('attribs' => array('' => array('version' => '2.0')),
16586
		                   'child' => array('' => $channel)));
16587
		$this->data = array('child' => array('' => array('rss' => $rss)));
16588
		return true;
16589
	}
16590

    
16591
	private function declare_html_entities() {
16592
		// This is required because the RSS specification says that entity-encoded
16593
		// html is allowed, but the xml specification says they must be declared.
16594
		return '<!DOCTYPE html [ <!ENTITY nbsp "&#x00A0;"> <!ENTITY iexcl "&#x00A1;"> <!ENTITY cent "&#x00A2;"> <!ENTITY pound "&#x00A3;"> <!ENTITY curren "&#x00A4;"> <!ENTITY yen "&#x00A5;"> <!ENTITY brvbar "&#x00A6;"> <!ENTITY sect "&#x00A7;"> <!ENTITY uml "&#x00A8;"> <!ENTITY copy "&#x00A9;"> <!ENTITY ordf "&#x00AA;"> <!ENTITY laquo "&#x00AB;"> <!ENTITY not "&#x00AC;"> <!ENTITY shy "&#x00AD;"> <!ENTITY reg "&#x00AE;"> <!ENTITY macr "&#x00AF;"> <!ENTITY deg "&#x00B0;"> <!ENTITY plusmn "&#x00B1;"> <!ENTITY sup2 "&#x00B2;"> <!ENTITY sup3 "&#x00B3;"> <!ENTITY acute "&#x00B4;"> <!ENTITY micro "&#x00B5;"> <!ENTITY para "&#x00B6;"> <!ENTITY middot "&#x00B7;"> <!ENTITY cedil "&#x00B8;"> <!ENTITY sup1 "&#x00B9;"> <!ENTITY ordm "&#x00BA;"> <!ENTITY raquo "&#x00BB;"> <!ENTITY frac14 "&#x00BC;"> <!ENTITY frac12 "&#x00BD;"> <!ENTITY frac34 "&#x00BE;"> <!ENTITY iquest "&#x00BF;"> <!ENTITY Agrave "&#x00C0;"> <!ENTITY Aacute "&#x00C1;"> <!ENTITY Acirc "&#x00C2;"> <!ENTITY Atilde "&#x00C3;"> <!ENTITY Auml "&#x00C4;"> <!ENTITY Aring "&#x00C5;"> <!ENTITY AElig "&#x00C6;"> <!ENTITY Ccedil "&#x00C7;"> <!ENTITY Egrave "&#x00C8;"> <!ENTITY Eacute "&#x00C9;"> <!ENTITY Ecirc "&#x00CA;"> <!ENTITY Euml "&#x00CB;"> <!ENTITY Igrave "&#x00CC;"> <!ENTITY Iacute "&#x00CD;"> <!ENTITY Icirc "&#x00CE;"> <!ENTITY Iuml "&#x00CF;"> <!ENTITY ETH "&#x00D0;"> <!ENTITY Ntilde "&#x00D1;"> <!ENTITY Ograve "&#x00D2;"> <!ENTITY Oacute "&#x00D3;"> <!ENTITY Ocirc "&#x00D4;"> <!ENTITY Otilde "&#x00D5;"> <!ENTITY Ouml "&#x00D6;"> <!ENTITY times "&#x00D7;"> <!ENTITY Oslash "&#x00D8;"> <!ENTITY Ugrave "&#x00D9;"> <!ENTITY Uacute "&#x00DA;"> <!ENTITY Ucirc "&#x00DB;"> <!ENTITY Uuml "&#x00DC;"> <!ENTITY Yacute "&#x00DD;"> <!ENTITY THORN "&#x00DE;"> <!ENTITY szlig "&#x00DF;"> <!ENTITY agrave "&#x00E0;"> <!ENTITY aacute "&#x00E1;"> <!ENTITY acirc "&#x00E2;"> <!ENTITY atilde "&#x00E3;"> <!ENTITY auml "&#x00E4;"> <!ENTITY aring "&#x00E5;"> <!ENTITY aelig "&#x00E6;"> <!ENTITY ccedil "&#x00E7;"> <!ENTITY egrave "&#x00E8;"> <!ENTITY eacute "&#x00E9;"> <!ENTITY ecirc "&#x00EA;"> <!ENTITY euml "&#x00EB;"> <!ENTITY igrave "&#x00EC;"> <!ENTITY iacute "&#x00ED;"> <!ENTITY icirc "&#x00EE;"> <!ENTITY iuml "&#x00EF;"> <!ENTITY eth "&#x00F0;"> <!ENTITY ntilde "&#x00F1;"> <!ENTITY ograve "&#x00F2;"> <!ENTITY oacute "&#x00F3;"> <!ENTITY ocirc "&#x00F4;"> <!ENTITY otilde "&#x00F5;"> <!ENTITY ouml "&#x00F6;"> <!ENTITY divide "&#x00F7;"> <!ENTITY oslash "&#x00F8;"> <!ENTITY ugrave "&#x00F9;"> <!ENTITY uacute "&#x00FA;"> <!ENTITY ucirc "&#x00FB;"> <!ENTITY uuml "&#x00FC;"> <!ENTITY yacute "&#x00FD;"> <!ENTITY thorn "&#x00FE;"> <!ENTITY yuml "&#x00FF;"> <!ENTITY OElig "&#x0152;"> <!ENTITY oelig "&#x0153;"> <!ENTITY Scaron "&#x0160;"> <!ENTITY scaron "&#x0161;"> <!ENTITY Yuml "&#x0178;"> <!ENTITY fnof "&#x0192;"> <!ENTITY circ "&#x02C6;"> <!ENTITY tilde "&#x02DC;"> <!ENTITY Alpha "&#x0391;"> <!ENTITY Beta "&#x0392;"> <!ENTITY Gamma "&#x0393;"> <!ENTITY Epsilon "&#x0395;"> <!ENTITY Zeta "&#x0396;"> <!ENTITY Eta "&#x0397;"> <!ENTITY Theta "&#x0398;"> <!ENTITY Iota "&#x0399;"> <!ENTITY Kappa "&#x039A;"> <!ENTITY Lambda "&#x039B;"> <!ENTITY Mu "&#x039C;"> <!ENTITY Nu "&#x039D;"> <!ENTITY Xi "&#x039E;"> <!ENTITY Omicron "&#x039F;"> <!ENTITY Pi "&#x03A0;"> <!ENTITY Rho "&#x03A1;"> <!ENTITY Sigma "&#x03A3;"> <!ENTITY Tau "&#x03A4;"> <!ENTITY Upsilon "&#x03A5;"> <!ENTITY Phi "&#x03A6;"> <!ENTITY Chi "&#x03A7;"> <!ENTITY Psi "&#x03A8;"> <!ENTITY Omega "&#x03A9;"> <!ENTITY alpha "&#x03B1;"> <!ENTITY beta "&#x03B2;"> <!ENTITY gamma "&#x03B3;"> <!ENTITY delta "&#x03B4;"> <!ENTITY epsilon "&#x03B5;"> <!ENTITY zeta "&#x03B6;"> <!ENTITY eta "&#x03B7;"> <!ENTITY theta "&#x03B8;"> <!ENTITY iota "&#x03B9;"> <!ENTITY kappa "&#x03BA;"> <!ENTITY lambda "&#x03BB;"> <!ENTITY mu "&#x03BC;"> <!ENTITY nu "&#x03BD;"> <!ENTITY xi "&#x03BE;"> <!ENTITY omicron "&#x03BF;"> <!ENTITY pi "&#x03C0;"> <!ENTITY rho "&#x03C1;"> <!ENTITY sigmaf "&#x03C2;"> <!ENTITY sigma "&#x03C3;"> <!ENTITY tau "&#x03C4;"> <!ENTITY upsilon "&#x03C5;"> <!ENTITY phi "&#x03C6;"> <!ENTITY chi "&#x03C7;"> <!ENTITY psi "&#x03C8;"> <!ENTITY omega "&#x03C9;"> <!ENTITY thetasym "&#x03D1;"> <!ENTITY upsih "&#x03D2;"> <!ENTITY piv "&#x03D6;"> <!ENTITY ensp "&#x2002;"> <!ENTITY emsp "&#x2003;"> <!ENTITY thinsp "&#x2009;"> <!ENTITY zwnj "&#x200C;"> <!ENTITY zwj "&#x200D;"> <!ENTITY lrm "&#x200E;"> <!ENTITY rlm "&#x200F;"> <!ENTITY ndash "&#x2013;"> <!ENTITY mdash "&#x2014;"> <!ENTITY lsquo "&#x2018;"> <!ENTITY rsquo "&#x2019;"> <!ENTITY sbquo "&#x201A;"> <!ENTITY ldquo "&#x201C;"> <!ENTITY rdquo "&#x201D;"> <!ENTITY bdquo "&#x201E;"> <!ENTITY dagger "&#x2020;"> <!ENTITY Dagger "&#x2021;"> <!ENTITY bull "&#x2022;"> <!ENTITY hellip "&#x2026;"> <!ENTITY permil "&#x2030;"> <!ENTITY prime "&#x2032;"> <!ENTITY Prime "&#x2033;"> <!ENTITY lsaquo "&#x2039;"> <!ENTITY rsaquo "&#x203A;"> <!ENTITY oline "&#x203E;"> <!ENTITY frasl "&#x2044;"> <!ENTITY euro "&#x20AC;"> <!ENTITY image "&#x2111;"> <!ENTITY weierp "&#x2118;"> <!ENTITY real "&#x211C;"> <!ENTITY trade "&#x2122;"> <!ENTITY alefsym "&#x2135;"> <!ENTITY larr "&#x2190;"> <!ENTITY uarr "&#x2191;"> <!ENTITY rarr "&#x2192;"> <!ENTITY darr "&#x2193;"> <!ENTITY harr "&#x2194;"> <!ENTITY crarr "&#x21B5;"> <!ENTITY lArr "&#x21D0;"> <!ENTITY uArr "&#x21D1;"> <!ENTITY rArr "&#x21D2;"> <!ENTITY dArr "&#x21D3;"> <!ENTITY hArr "&#x21D4;"> <!ENTITY forall "&#x2200;"> <!ENTITY part "&#x2202;"> <!ENTITY exist "&#x2203;"> <!ENTITY empty "&#x2205;"> <!ENTITY nabla "&#x2207;"> <!ENTITY isin "&#x2208;"> <!ENTITY notin "&#x2209;"> <!ENTITY ni "&#x220B;"> <!ENTITY prod "&#x220F;"> <!ENTITY sum "&#x2211;"> <!ENTITY minus "&#x2212;"> <!ENTITY lowast "&#x2217;"> <!ENTITY radic "&#x221A;"> <!ENTITY prop "&#x221D;"> <!ENTITY infin "&#x221E;"> <!ENTITY ang "&#x2220;"> <!ENTITY and "&#x2227;"> <!ENTITY or "&#x2228;"> <!ENTITY cap "&#x2229;"> <!ENTITY cup "&#x222A;"> <!ENTITY int "&#x222B;"> <!ENTITY there4 "&#x2234;"> <!ENTITY sim "&#x223C;"> <!ENTITY cong "&#x2245;"> <!ENTITY asymp "&#x2248;"> <!ENTITY ne "&#x2260;"> <!ENTITY equiv "&#x2261;"> <!ENTITY le "&#x2264;"> <!ENTITY ge "&#x2265;"> <!ENTITY sub "&#x2282;"> <!ENTITY sup "&#x2283;"> <!ENTITY nsub "&#x2284;"> <!ENTITY sube "&#x2286;"> <!ENTITY supe "&#x2287;"> <!ENTITY oplus "&#x2295;"> <!ENTITY otimes "&#x2297;"> <!ENTITY perp "&#x22A5;"> <!ENTITY sdot "&#x22C5;"> <!ENTITY lceil "&#x2308;"> <!ENTITY rceil "&#x2309;"> <!ENTITY lfloor "&#x230A;"> <!ENTITY rfloor "&#x230B;"> <!ENTITY lang "&#x2329;"> <!ENTITY rang "&#x232A;"> <!ENTITY loz "&#x25CA;"> <!ENTITY spades "&#x2660;"> <!ENTITY clubs "&#x2663;"> <!ENTITY hearts "&#x2665;"> <!ENTITY diams "&#x2666;"> ]>';
16595
	}
16596
}
16597

    
16598
/**
16599
 * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
16600
 *
16601
 * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()}
16602
 *
16603
 * This class can be overloaded with {@see SimplePie::set_rating_class()}
16604
 *
16605
 * @package SimplePie
16606
 * @subpackage API
16607
 */
16608
class SimplePie_Rating
16609
{
16610
	/**
16611
	 * Rating scheme
16612
	 *
16613
	 * @var string
16614
	 * @see get_scheme()
16615
	 */
16616
	var $scheme;
16617

    
16618
	/**
16619
	 * Rating value
16620
	 *
16621
	 * @var string
16622
	 * @see get_value()
16623
	 */
16624
	var $value;
16625

    
16626
	/**
16627
	 * Constructor, used to input the data
16628
	 *
16629
	 * For documentation on all the parameters, see the corresponding
16630
	 * properties and their accessors
16631
	 */
16632
	public function __construct($scheme = null, $value = null)
16633
	{
16634
		$this->scheme = $scheme;
16635
		$this->value = $value;
16636
	}
16637

    
16638
	/**
16639
	 * String-ified version
16640
	 *
16641
	 * @return string
16642
	 */
16643
	public function __toString()
16644
	{
16645
		// There is no $this->data here
16646
		return md5(serialize($this));
16647
	}
16648

    
16649
	/**
16650
	 * Get the organizational scheme for the rating
16651
	 *
16652
	 * @return string|null
16653
	 */
16654
	public function get_scheme()
16655
	{
16656
		if ($this->scheme !== null)
16657
		{
16658
			return $this->scheme;
16659
		}
16660
		else
16661
		{
16662
			return null;
16663
		}
16664
	}
16665

    
16666
	/**
16667
	 * Get the value of the rating
16668
	 *
16669
	 * @return string|null
16670
	 */
16671
	public function get_value()
16672
	{
16673
		if ($this->value !== null)
16674
		{
16675
			return $this->value;
16676
		}
16677
		else
16678
		{
16679
			return null;
16680
		}
16681
	}
16682
}
16683

    
16684
/**
16685
 * Handles creating objects and calling methods
16686
 *
16687
 * Access this via {@see SimplePie::get_registry()}
16688
 *
16689
 * @package SimplePie
16690
 */
16691
class SimplePie_Registry
16692
{
16693
	/**
16694
	 * Default class mapping
16695
	 *
16696
	 * Overriding classes *must* subclass these.
16697
	 *
16698
	 * @var array
16699
	 */
16700
	protected $default = array(
16701
		'Cache' => 'SimplePie_Cache',
16702
		'Locator' => 'SimplePie_Locator',
16703
		'Parser' => 'SimplePie_Parser',
16704
		'File' => 'SimplePie_File',
16705
		'Sanitize' => 'SimplePie_Sanitize',
16706
		'Item' => 'SimplePie_Item',
16707
		'Author' => 'SimplePie_Author',
16708
		'Category' => 'SimplePie_Category',
16709
		'Enclosure' => 'SimplePie_Enclosure',
16710
		'Caption' => 'SimplePie_Caption',
16711
		'Copyright' => 'SimplePie_Copyright',
16712
		'Credit' => 'SimplePie_Credit',
16713
		'Rating' => 'SimplePie_Rating',
16714
		'Restriction' => 'SimplePie_Restriction',
16715
		'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer',
16716
		'Source' => 'SimplePie_Source',
16717
		'Misc' => 'SimplePie_Misc',
16718
		'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser',
16719
		'Parse_Date' => 'SimplePie_Parse_Date',
16720
	);
16721

    
16722
	/**
16723
	 * Class mapping
16724
	 *
16725
	 * @see register()
16726
	 * @var array
16727
	 */
16728
	protected $classes = array();
16729

    
16730
	/**
16731
	 * Legacy classes
16732
	 *
16733
	 * @see register()
16734
	 * @var array
16735
	 */
16736
	protected $legacy = array();
16737

    
16738
	/**
16739
	 * Constructor
16740
	 *
16741
	 * No-op
16742
	 */
16743
	public function __construct() { }
16744

    
16745
	/**
16746
	 * Register a class
16747
	 *
16748
	 * @param string $type See {@see $default} for names
16749
	 * @param string $class Class name, must subclass the corresponding default
16750
	 * @param bool $legacy Whether to enable legacy support for this class
16751
	 * @return bool Successfulness
16752
	 */
16753
	public function register($type, $class, $legacy = false)
16754
	{
16755
		if (!@is_subclass_of($class, $this->default[$type]))
16756
		{
16757
			return false;
16758
		}
16759

    
16760
		$this->classes[$type] = $class;
16761

    
16762
		if ($legacy)
16763
		{
16764
			$this->legacy[] = $class;
16765
		}
16766

    
16767
		return true;
16768
	}
16769

    
16770
	/**
16771
	 * Get the class registered for a type
16772
	 *
16773
	 * Where possible, use {@see create()} or {@see call()} instead
16774
	 *
16775
	 * @param string $type
16776
	 * @return string|null
16777
	 */
16778
	public function get_class($type)
16779
	{
16780
		if (!empty($this->classes[$type]))
16781
		{
16782
			return $this->classes[$type];
16783
		}
16784
		if (!empty($this->default[$type]))
16785
		{
16786
			return $this->default[$type];
16787
		}
16788

    
16789
		return null;
16790
	}
16791

    
16792
	/**
16793
	 * Create a new instance of a given type
16794
	 *
16795
	 * @param string $type
16796
	 * @param array $parameters Parameters to pass to the constructor
16797
	 * @return object Instance of class
16798
	 */
16799
	public function &create($type, $parameters = array())
16800
	{
16801
		$class = $this->get_class($type);
16802

    
16803
		if (in_array($class, $this->legacy))
16804
		{
16805
			switch ($type)
16806
			{
16807
				case 'locator':
16808
					// Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class
16809
					// Specified: file, timeout, useragent, max_checked_feeds
16810
					$replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer'));
16811
					array_splice($parameters, 3, 1, $replacement);
16812
					break;
16813
			}
16814
		}
16815

    
16816
		if (!method_exists($class, '__construct'))
16817
		{
16818
			$instance = new $class;
16819
		}
16820
		else
16821
		{
16822
			$reflector = new ReflectionClass($class);
16823
			$instance = $reflector->newInstanceArgs($parameters);
16824
		}
16825

    
16826
		if (method_exists($instance, 'set_registry'))
16827
		{
16828
			$instance->set_registry($this);
16829
		}
16830
		return $instance;
16831
	}
16832

    
16833
	/**
16834
	 * Call a static method for a type
16835
	 *
16836
	 * @param string $type
16837
	 * @param string $method
16838
	 * @param array $parameters
16839
	 * @return mixed
16840
	 */
16841
	public function &call($type, $method, $parameters = array())
16842
	{
16843
		$class = $this->get_class($type);
16844

    
16845
		if (in_array($class, $this->legacy))
16846
		{
16847
			switch ($type)
16848
			{
16849
				case 'Cache':
16850
					// For backwards compatibility with old non-static
16851
					// Cache::create() methods
16852
					if ($method === 'get_handler')
16853
					{
16854
						$result = @call_user_func_array(array($class, 'create'), $parameters);
16855
						return $result;
16856
					}
16857
					break;
16858
			}
16859
		}
16860

    
16861
		$result = call_user_func_array(array($class, $method), $parameters);
16862
		return $result;
16863
	}
16864
}
16865

    
16866
/**
16867
 * Handles `<media:restriction>` as defined in Media RSS
16868
 *
16869
 * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()}
16870
 *
16871
 * This class can be overloaded with {@see SimplePie::set_restriction_class()}
16872
 *
16873
 * @package SimplePie
16874
 * @subpackage API
16875
 */
16876
class SimplePie_Restriction
16877
{
16878
	/**
16879
	 * Relationship ('allow'/'deny')
16880
	 *
16881
	 * @var string
16882
	 * @see get_relationship()
16883
	 */
16884
	var $relationship;
16885

    
16886
	/**
16887
	 * Type of restriction
16888
	 *
16889
	 * @var string
16890
	 * @see get_type()
16891
	 */
16892
	var $type;
16893

    
16894
	/**
16895
	 * Restricted values
16896
	 *
16897
	 * @var string
16898
	 * @see get_value()
16899
	 */
16900
	var $value;
16901

    
16902
	/**
16903
	 * Constructor, used to input the data
16904
	 *
16905
	 * For documentation on all the parameters, see the corresponding
16906
	 * properties and their accessors
16907
	 */
16908
	public function __construct($relationship = null, $type = null, $value = null)
16909
	{
16910
		$this->relationship = $relationship;
16911
		$this->type = $type;
16912
		$this->value = $value;
16913
	}
16914

    
16915
	/**
16916
	 * String-ified version
16917
	 *
16918
	 * @return string
16919
	 */
16920
	public function __toString()
16921
	{
16922
		// There is no $this->data here
16923
		return md5(serialize($this));
16924
	}
16925

    
16926
	/**
16927
	 * Get the relationship
16928
	 *
16929
	 * @return string|null Either 'allow' or 'deny'
16930
	 */
16931
	public function get_relationship()
16932
	{
16933
		if ($this->relationship !== null)
16934
		{
16935
			return $this->relationship;
16936
		}
16937
		else
16938
		{
16939
			return null;
16940
		}
16941
	}
16942

    
16943
	/**
16944
	 * Get the type
16945
	 *
16946
	 * @return string|null
16947
	 */
16948
	public function get_type()
16949
	{
16950
		if ($this->type !== null)
16951
		{
16952
			return $this->type;
16953
		}
16954
		else
16955
		{
16956
			return null;
16957
		}
16958
	}
16959

    
16960
	/**
16961
	 * Get the list of restricted things
16962
	 *
16963
	 * @return string|null
16964
	 */
16965
	public function get_value()
16966
	{
16967
		if ($this->value !== null)
16968
		{
16969
			return $this->value;
16970
		}
16971
		else
16972
		{
16973
			return null;
16974
		}
16975
	}
16976
}
16977

    
16978
/**
16979
 * Used for data cleanup and post-processing
16980
 *
16981
 *
16982
 * This class can be overloaded with {@see SimplePie::set_sanitize_class()}
16983
 *
16984
 * @package SimplePie
16985
 * @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
16986
 */
16987
class SimplePie_Sanitize
16988
{
16989
	// Private vars
16990
	var $base;
16991

    
16992
	// Options
16993
	var $remove_div = true;
16994
	var $image_handler = '';
16995
	var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
16996
	var $encode_instead_of_strip = false;
16997
	var $strip_attributes = array('bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
16998
	var $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none'));
16999
	var $strip_comments = false;
17000
	var $output_encoding = 'UTF-8';
17001
	var $enable_cache = true;
17002
	var $cache_location = './cache';
17003
	var $cache_name_function = 'md5';
17004
	var $timeout = 10;
17005
	var $useragent = '';
17006
	var $force_fsockopen = false;
17007
	var $replace_url_attributes = null;
17008

    
17009
	public function __construct()
17010
	{
17011
		// Set defaults
17012
		$this->set_url_replacements(null);
17013
	}
17014

    
17015
	public function remove_div($enable = true)
17016
	{
17017
		$this->remove_div = (bool) $enable;
17018
	}
17019

    
17020
	public function set_image_handler($page = false)
17021
	{
17022
		if ($page)
17023
		{
17024
			$this->image_handler = (string) $page;
17025
		}
17026
		else
17027
		{
17028
			$this->image_handler = false;
17029
		}
17030
	}
17031

    
17032
	public function set_registry(SimplePie_Registry $registry)
17033
	{
17034
		$this->registry = $registry;
17035
	}
17036

    
17037
	public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
17038
	{
17039
		if (isset($enable_cache))
17040
		{
17041
			$this->enable_cache = (bool) $enable_cache;
17042
		}
17043

    
17044
		if ($cache_location)
17045
		{
17046
			$this->cache_location = (string) $cache_location;
17047
		}
17048

    
17049
		if ($cache_name_function)
17050
		{
17051
			$this->cache_name_function = (string) $cache_name_function;
17052
		}
17053
	}
17054

    
17055
	public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
17056
	{
17057
		if ($timeout)
17058
		{
17059
			$this->timeout = (string) $timeout;
17060
		}
17061

    
17062
		if ($useragent)
17063
		{
17064
			$this->useragent = (string) $useragent;
17065
		}
17066

    
17067
		if ($force_fsockopen)
17068
		{
17069
			$this->force_fsockopen = (string) $force_fsockopen;
17070
		}
17071
	}
17072

    
17073
	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'))
17074
	{
17075
		if ($tags)
17076
		{
17077
			if (is_array($tags))
17078
			{
17079
				$this->strip_htmltags = $tags;
17080
			}
17081
			else
17082
			{
17083
				$this->strip_htmltags = explode(',', $tags);
17084
			}
17085
		}
17086
		else
17087
		{
17088
			$this->strip_htmltags = false;
17089
		}
17090
	}
17091

    
17092
	public function encode_instead_of_strip($encode = false)
17093
	{
17094
		$this->encode_instead_of_strip = (bool) $encode;
17095
	}
17096

    
17097
	public function strip_attributes($attribs = array('bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
17098
	{
17099
		if ($attribs)
17100
		{
17101
			if (is_array($attribs))
17102
			{
17103
				$this->strip_attributes = $attribs;
17104
			}
17105
			else
17106
			{
17107
				$this->strip_attributes = explode(',', $attribs);
17108
			}
17109
		}
17110
		else
17111
		{
17112
			$this->strip_attributes = false;
17113
		}
17114
	}
17115

    
17116
	public function add_attributes($attribs = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')))
17117
	{
17118
		if ($attribs)
17119
		{
17120
			if (is_array($attribs))
17121
			{
17122
				$this->add_attributes = $attribs;
17123
			}
17124
			else
17125
			{
17126
				$this->add_attributes = explode(',', $attribs);
17127
			}
17128
		}
17129
		else
17130
		{
17131
			$this->add_attributes = false;
17132
		}
17133
	}
17134

    
17135
	public function strip_comments($strip = false)
17136
	{
17137
		$this->strip_comments = (bool) $strip;
17138
	}
17139

    
17140
	public function set_output_encoding($encoding = 'UTF-8')
17141
	{
17142
		$this->output_encoding = (string) $encoding;
17143
	}
17144

    
17145
	/**
17146
	 * Set element/attribute key/value pairs of HTML attributes
17147
	 * containing URLs that need to be resolved relative to the feed
17148
	 *
17149
	 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
17150
	 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
17151
	 * |q|@cite
17152
	 *
17153
	 * @since 1.0
17154
	 * @param array|null $element_attribute Element/attribute key/value pairs, null for default
17155
	 */
17156
	public function set_url_replacements($element_attribute = null)
17157
	{
17158
		if ($element_attribute === null)
17159
		{
17160
			$element_attribute = array(
17161
				'a' => 'href',
17162
				'area' => 'href',
17163
				'blockquote' => 'cite',
17164
				'del' => 'cite',
17165
				'form' => 'action',
17166
				'img' => array(
17167
					'longdesc',
17168
					'src'
17169
				),
17170
				'input' => 'src',
17171
				'ins' => 'cite',
17172
				'q' => 'cite'
17173
			);
17174
		}
17175
		$this->replace_url_attributes = (array) $element_attribute;
17176
	}
17177

    
17178
	public function sanitize($data, $type, $base = '')
17179
	{
17180
		$data = trim($data);
17181
		if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
17182
		{
17183
			if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
17184
			{
17185
				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))
17186
				{
17187
					$type |= SIMPLEPIE_CONSTRUCT_HTML;
17188
				}
17189
				else
17190
				{
17191
					$type |= SIMPLEPIE_CONSTRUCT_TEXT;
17192
				}
17193
			}
17194

    
17195
			if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
17196
			{
17197
				$data = base64_decode($data);
17198
			}
17199

    
17200
			if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
17201
			{
17202

    
17203
				if (!class_exists('DOMDocument'))
17204
				{
17205
					throw new SimplePie_Exception('DOMDocument not found, unable to use sanitizer');
17206
				}
17207
				$document = new DOMDocument();
17208
				$document->encoding = 'UTF-8';
17209

    
17210
				$data = $this->preprocess($data, $type);
17211

    
17212
				set_error_handler(array('SimplePie_Misc', 'silence_errors'));
17213
				$document->loadHTML($data);
17214
				restore_error_handler();
17215

    
17216
				$xpath = new DOMXPath($document);
17217

    
17218
				// Strip comments
17219
				if ($this->strip_comments)
17220
				{
17221
					$comments = $xpath->query('//comment()');
17222

    
17223
					foreach ($comments as $comment)
17224
					{
17225
						$comment->parentNode->removeChild($comment);
17226
					}
17227
				}
17228

    
17229
				// Strip out HTML tags and attributes that might cause various security problems.
17230
				// Based on recommendations by Mark Pilgrim at:
17231
				// http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
17232
				if ($this->strip_htmltags)
17233
				{
17234
					foreach ($this->strip_htmltags as $tag)
17235
					{
17236
						$this->strip_tag($tag, $document, $xpath, $type);
17237
					}
17238
				}
17239

    
17240
				if ($this->strip_attributes)
17241
				{
17242
					foreach ($this->strip_attributes as $attrib)
17243
					{
17244
						$this->strip_attr($attrib, $xpath);
17245
					}
17246
				}
17247

    
17248
				if ($this->add_attributes)
17249
				{
17250
					foreach ($this->add_attributes as $tag => $valuePairs)
17251
					{
17252
						$this->add_attr($tag, $valuePairs, $document);
17253
					}
17254
				}
17255

    
17256
				// Replace relative URLs
17257
				$this->base = $base;
17258
				foreach ($this->replace_url_attributes as $element => $attributes)
17259
				{
17260
					$this->replace_urls($document, $element, $attributes);
17261
				}
17262

    
17263
				// If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
17264
				if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
17265
				{
17266
					$images = $document->getElementsByTagName('img');
17267
					foreach ($images as $img)
17268
					{
17269
						if ($img->hasAttribute('src'))
17270
						{
17271
							$image_url = call_user_func($this->cache_name_function, $img->getAttribute('src'));
17272
							$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi'));
17273

    
17274
							if ($cache->load())
17275
							{
17276
								$img->setAttribute('src', $this->image_handler . $image_url);
17277
							}
17278
							else
17279
							{
17280
								$file = $this->registry->create('File', array($img->getAttribute('src'), $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen));
17281
								$headers = $file->headers;
17282

    
17283
								if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
17284
								{
17285
									if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
17286
									{
17287
										$img->setAttribute('src', $this->image_handler . $image_url);
17288
									}
17289
									else
17290
									{
17291
										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);
17292
									}
17293
								}
17294
							}
17295
						}
17296
					}
17297
				}
17298

    
17299
				// Get content node
17300
				$div = $document->getElementsByTagName('body')->item(0)->firstChild;
17301
				// Finally, convert to a HTML string
17302
				if (version_compare(PHP_VERSION, '5.3.6', '>='))
17303
				{
17304
					$data = trim($document->saveHTML($div));
17305
				}
17306
				else
17307
				{
17308
					$data = trim($document->saveXML($div));
17309
				}
17310

    
17311
				if ($this->remove_div)
17312
				{
17313
					$data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
17314
					$data = preg_replace('/<\/div>$/', '', $data);
17315
				}
17316
				else
17317
				{
17318
					$data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
17319
				}
17320
			}
17321

    
17322
			if ($type & SIMPLEPIE_CONSTRUCT_IRI)
17323
			{
17324
				$absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
17325
				if ($absolute !== false)
17326
				{
17327
					$data = $absolute;
17328
				}
17329
			}
17330

    
17331
			if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
17332
			{
17333
				$data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
17334
			}
17335

    
17336
			if ($this->output_encoding !== 'UTF-8')
17337
			{
17338
				$data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding));
17339
			}
17340
		}
17341
		return $data;
17342
	}
17343

    
17344
	protected function preprocess($html, $type)
17345
	{
17346
		$ret = '';
17347
		$html = preg_replace('%</?(?:html|body)[^>]*?'.'>%is', '', $html);
17348
		if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML)
17349
		{
17350
			// Atom XHTML constructs are wrapped with a div by default
17351
			// Note: No protection if $html contains a stray </div>!
17352
			$html = '<div>' . $html . '</div>';
17353
			$ret .= '<!DOCTYPE html>';
17354
			$content_type = 'text/html';
17355
		}
17356
		else
17357
		{
17358
			$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
17359
			$content_type = 'application/xhtml+xml';
17360
		}
17361

    
17362
		$ret .= '<html><head>';
17363
		$ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
17364
		$ret .= '</head><body>' . $html . '</body></html>';
17365
		return $ret;
17366
	}
17367

    
17368
	public function replace_urls($document, $tag, $attributes)
17369
	{
17370
		if (!is_array($attributes))
17371
		{
17372
			$attributes = array($attributes);
17373
		}
17374

    
17375
		if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
17376
		{
17377
			$elements = $document->getElementsByTagName($tag);
17378
			foreach ($elements as $element)
17379
			{
17380
				foreach ($attributes as $attribute)
17381
				{
17382
					if ($element->hasAttribute($attribute))
17383
					{
17384
						$value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base));
17385
						if ($value !== false)
17386
						{
17387
							$element->setAttribute($attribute, $value);
17388
						}
17389
					}
17390
				}
17391
			}
17392
		}
17393
	}
17394

    
17395
	public function do_strip_htmltags($match)
17396
	{
17397
		if ($this->encode_instead_of_strip)
17398
		{
17399
			if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
17400
			{
17401
				$match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
17402
				$match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
17403
				return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
17404
			}
17405
			else
17406
			{
17407
				return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
17408
			}
17409
		}
17410
		elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
17411
		{
17412
			return $match[4];
17413
		}
17414
		else
17415
		{
17416
			return '';
17417
		}
17418
	}
17419

    
17420
	protected function strip_tag($tag, $document, $xpath, $type)
17421
	{
17422
		$elements = $xpath->query('body//' . $tag);
17423
		if ($this->encode_instead_of_strip)
17424
		{
17425
			foreach ($elements as $element)
17426
			{
17427
				$fragment = $document->createDocumentFragment();
17428

    
17429
				// For elements which aren't script or style, include the tag itself
17430
				if (!in_array($tag, array('script', 'style')))
17431
				{
17432
					$text = '<' . $tag;
17433
					if ($element->hasAttributes())
17434
					{
17435
						$attrs = array();
17436
						foreach ($element->attributes as $name => $attr)
17437
						{
17438
							$value = $attr->value;
17439

    
17440
							// In XHTML, empty values should never exist, so we repeat the value
17441
							if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML))
17442
							{
17443
								$value = $name;
17444
							}
17445
							// For HTML, empty is fine
17446
							elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML))
17447
							{
17448
								$attrs[] = $name;
17449
								continue;
17450
							}
17451

    
17452
							// Standard attribute text
17453
							$attrs[] = $name . '="' . $attr->value . '"';
17454
						}
17455
						$text .= ' ' . implode(' ', $attrs);
17456
					}
17457
					$text .= '>';
17458
					$fragment->appendChild(new DOMText($text));
17459
				}
17460

    
17461
				$number = $element->childNodes->length;
17462
				for ($i = $number; $i > 0; $i--)
17463
				{
17464
					$child = $element->childNodes->item(0);
17465
					$fragment->appendChild($child);
17466
				}
17467

    
17468
				if (!in_array($tag, array('script', 'style')))
17469
				{
17470
					$fragment->appendChild(new DOMText('</' . $tag . '>'));
17471
				}
17472

    
17473
				$element->parentNode->replaceChild($fragment, $element);
17474
			}
17475

    
17476
			return;
17477
		}
17478
		elseif (in_array($tag, array('script', 'style')))
17479
		{
17480
			foreach ($elements as $element)
17481
			{
17482
				$element->parentNode->removeChild($element);
17483
			}
17484

    
17485
			return;
17486
		}
17487
		else
17488
		{
17489
			foreach ($elements as $element)
17490
			{
17491
				$fragment = $document->createDocumentFragment();
17492
				$number = $element->childNodes->length;
17493
				for ($i = $number; $i > 0; $i--)
17494
				{
17495
					$child = $element->childNodes->item(0);
17496
					$fragment->appendChild($child);
17497
				}
17498

    
17499
				$element->parentNode->replaceChild($fragment, $element);
17500
			}
17501
		}
17502
	}
17503

    
17504
	protected function strip_attr($attrib, $xpath)
17505
	{
17506
		$elements = $xpath->query('//*[@' . $attrib . ']');
17507

    
17508
		foreach ($elements as $element)
17509
		{
17510
			$element->removeAttribute($attrib);
17511
		}
17512
	}
17513

    
17514
	protected function add_attr($tag, $valuePairs, $document)
17515
	{
17516
		$elements = $document->getElementsByTagName($tag);
17517
		foreach ($elements as $element)
17518
		{
17519
			foreach ($valuePairs as $attrib => $value)
17520
			{
17521
				$element->setAttribute($attrib, $value);
17522
			}
17523
		}
17524
	}
17525
}
17526

    
17527
/**
17528
 * Handles `<atom:source>`
17529
 *
17530
 * Used by {@see SimplePie_Item::get_source()}
17531
 *
17532
 * This class can be overloaded with {@see SimplePie::set_source_class()}
17533
 *
17534
 * @package SimplePie
17535
 * @subpackage API
17536
 */
17537
class SimplePie_Source
17538
{
17539
	var $item;
17540
	var $data = array();
17541
	protected $registry;
17542

    
17543
	public function __construct($item, $data)
17544
	{
17545
		$this->item = $item;
17546
		$this->data = $data;
17547
	}
17548

    
17549
	public function set_registry(SimplePie_Registry $registry)
17550
	{
17551
		$this->registry = $registry;
17552
	}
17553

    
17554
	public function __toString()
17555
	{
17556
		return md5(serialize($this->data));
17557
	}
17558

    
17559
	public function get_source_tags($namespace, $tag)
17560
	{
17561
		if (isset($this->data['child'][$namespace][$tag]))
17562
		{
17563
			return $this->data['child'][$namespace][$tag];
17564
		}
17565
		else
17566
		{
17567
			return null;
17568
		}
17569
	}
17570

    
17571
	public function get_base($element = array())
17572
	{
17573
		return $this->item->get_base($element);
17574
	}
17575

    
17576
	public function sanitize($data, $type, $base = '')
17577
	{
17578
		return $this->item->sanitize($data, $type, $base);
17579
	}
17580

    
17581
	public function get_item()
17582
	{
17583
		return $this->item;
17584
	}
17585

    
17586
	public function get_title()
17587
	{
17588
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
17589
		{
17590
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17591
		}
17592
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
17593
		{
17594
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17595
		}
17596
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
17597
		{
17598
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17599
		}
17600
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
17601
		{
17602
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17603
		}
17604
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
17605
		{
17606
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17607
		}
17608
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
17609
		{
17610
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17611
		}
17612
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
17613
		{
17614
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17615
		}
17616
		else
17617
		{
17618
			return null;
17619
		}
17620
	}
17621

    
17622
	public function get_category($key = 0)
17623
	{
17624
		$categories = $this->get_categories();
17625
		if (isset($categories[$key]))
17626
		{
17627
			return $categories[$key];
17628
		}
17629
		else
17630
		{
17631
			return null;
17632
		}
17633
	}
17634

    
17635
	public function get_categories()
17636
	{
17637
		$categories = array();
17638

    
17639
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
17640
		{
17641
			$term = null;
17642
			$scheme = null;
17643
			$label = null;
17644
			if (isset($category['attribs']['']['term']))
17645
			{
17646
				$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
17647
			}
17648
			if (isset($category['attribs']['']['scheme']))
17649
			{
17650
				$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
17651
			}
17652
			if (isset($category['attribs']['']['label']))
17653
			{
17654
				$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
17655
			}
17656
			$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
17657
		}
17658
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
17659
		{
17660
			// This is really the label, but keep this as the term also for BC.
17661
			// Label will also work on retrieving because that falls back to term.
17662
			$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17663
			if (isset($category['attribs']['']['domain']))
17664
			{
17665
				$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
17666
			}
17667
			else
17668
			{
17669
				$scheme = null;
17670
			}
17671
			$categories[] = $this->registry->create('Category', array($term, $scheme, null));
17672
		}
17673
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
17674
		{
17675
			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17676
		}
17677
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
17678
		{
17679
			$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17680
		}
17681

    
17682
		if (!empty($categories))
17683
		{
17684
			return array_unique($categories);
17685
		}
17686
		else
17687
		{
17688
			return null;
17689
		}
17690
	}
17691

    
17692
	public function get_author($key = 0)
17693
	{
17694
		$authors = $this->get_authors();
17695
		if (isset($authors[$key]))
17696
		{
17697
			return $authors[$key];
17698
		}
17699
		else
17700
		{
17701
			return null;
17702
		}
17703
	}
17704

    
17705
	public function get_authors()
17706
	{
17707
		$authors = array();
17708
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
17709
		{
17710
			$name = null;
17711
			$uri = null;
17712
			$email = null;
17713
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17714
			{
17715
				$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17716
			}
17717
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17718
			{
17719
				$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]));
17720
			}
17721
			if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17722
			{
17723
				$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17724
			}
17725
			if ($name !== null || $email !== null || $uri !== null)
17726
			{
17727
				$authors[] = $this->registry->create('Author', array($name, $uri, $email));
17728
			}
17729
		}
17730
		if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
17731
		{
17732
			$name = null;
17733
			$url = null;
17734
			$email = null;
17735
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17736
			{
17737
				$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17738
			}
17739
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17740
			{
17741
				$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]));
17742
			}
17743
			if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17744
			{
17745
				$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17746
			}
17747
			if ($name !== null || $email !== null || $url !== null)
17748
			{
17749
				$authors[] = $this->registry->create('Author', array($name, $url, $email));
17750
			}
17751
		}
17752
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
17753
		{
17754
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17755
		}
17756
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
17757
		{
17758
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17759
		}
17760
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
17761
		{
17762
			$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17763
		}
17764

    
17765
		if (!empty($authors))
17766
		{
17767
			return array_unique($authors);
17768
		}
17769
		else
17770
		{
17771
			return null;
17772
		}
17773
	}
17774

    
17775
	public function get_contributor($key = 0)
17776
	{
17777
		$contributors = $this->get_contributors();
17778
		if (isset($contributors[$key]))
17779
		{
17780
			return $contributors[$key];
17781
		}
17782
		else
17783
		{
17784
			return null;
17785
		}
17786
	}
17787

    
17788
	public function get_contributors()
17789
	{
17790
		$contributors = array();
17791
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
17792
		{
17793
			$name = null;
17794
			$uri = null;
17795
			$email = null;
17796
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17797
			{
17798
				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17799
			}
17800
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17801
			{
17802
				$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]));
17803
			}
17804
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17805
			{
17806
				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17807
			}
17808
			if ($name !== null || $email !== null || $uri !== null)
17809
			{
17810
				$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
17811
			}
17812
		}
17813
		foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
17814
		{
17815
			$name = null;
17816
			$url = null;
17817
			$email = null;
17818
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17819
			{
17820
				$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17821
			}
17822
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17823
			{
17824
				$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]));
17825
			}
17826
			if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17827
			{
17828
				$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17829
			}
17830
			if ($name !== null || $email !== null || $url !== null)
17831
			{
17832
				$contributors[] = $this->registry->create('Author', array($name, $url, $email));
17833
			}
17834
		}
17835

    
17836
		if (!empty($contributors))
17837
		{
17838
			return array_unique($contributors);
17839
		}
17840
		else
17841
		{
17842
			return null;
17843
		}
17844
	}
17845

    
17846
	public function get_link($key = 0, $rel = 'alternate')
17847
	{
17848
		$links = $this->get_links($rel);
17849
		if (isset($links[$key]))
17850
		{
17851
			return $links[$key];
17852
		}
17853
		else
17854
		{
17855
			return null;
17856
		}
17857
	}
17858

    
17859
	/**
17860
	 * Added for parity between the parent-level and the item/entry-level.
17861
	 */
17862
	public function get_permalink()
17863
	{
17864
		return $this->get_link(0);
17865
	}
17866

    
17867
	public function get_links($rel = 'alternate')
17868
	{
17869
		if (!isset($this->data['links']))
17870
		{
17871
			$this->data['links'] = array();
17872
			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
17873
			{
17874
				foreach ($links as $link)
17875
				{
17876
					if (isset($link['attribs']['']['href']))
17877
					{
17878
						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17879
						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17880
					}
17881
				}
17882
			}
17883
			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
17884
			{
17885
				foreach ($links as $link)
17886
				{
17887
					if (isset($link['attribs']['']['href']))
17888
					{
17889
						$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17890
						$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17891

    
17892
					}
17893
				}
17894
			}
17895
			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
17896
			{
17897
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17898
			}
17899
			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
17900
			{
17901
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17902
			}
17903
			if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
17904
			{
17905
				$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17906
			}
17907

    
17908
			$keys = array_keys($this->data['links']);
17909
			foreach ($keys as $key)
17910
			{
17911
				if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
17912
				{
17913
					if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
17914
					{
17915
						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
17916
						$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
17917
					}
17918
					else
17919
					{
17920
						$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
17921
					}
17922
				}
17923
				elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
17924
				{
17925
					$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
17926
				}
17927
				$this->data['links'][$key] = array_unique($this->data['links'][$key]);
17928
			}
17929
		}
17930

    
17931
		if (isset($this->data['links'][$rel]))
17932
		{
17933
			return $this->data['links'][$rel];
17934
		}
17935
		else
17936
		{
17937
			return null;
17938
		}
17939
	}
17940

    
17941
	public function get_description()
17942
	{
17943
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
17944
		{
17945
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17946
		}
17947
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
17948
		{
17949
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17950
		}
17951
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
17952
		{
17953
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17954
		}
17955
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
17956
		{
17957
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17958
		}
17959
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
17960
		{
17961
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17962
		}
17963
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
17964
		{
17965
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17966
		}
17967
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
17968
		{
17969
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17970
		}
17971
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
17972
		{
17973
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17974
		}
17975
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
17976
		{
17977
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17978
		}
17979
		else
17980
		{
17981
			return null;
17982
		}
17983
	}
17984

    
17985
	public function get_copyright()
17986
	{
17987
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
17988
		{
17989
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17990
		}
17991
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
17992
		{
17993
			return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17994
		}
17995
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
17996
		{
17997
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17998
		}
17999
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
18000
		{
18001
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
18002
		}
18003
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
18004
		{
18005
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
18006
		}
18007
		else
18008
		{
18009
			return null;
18010
		}
18011
	}
18012

    
18013
	public function get_language()
18014
	{
18015
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
18016
		{
18017
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
18018
		}
18019
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
18020
		{
18021
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
18022
		}
18023
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
18024
		{
18025
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
18026
		}
18027
		elseif (isset($this->data['xml_lang']))
18028
		{
18029
			return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
18030
		}
18031
		else
18032
		{
18033
			return null;
18034
		}
18035
	}
18036

    
18037
	public function get_latitude()
18038
	{
18039
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
18040
		{
18041
			return (float) $return[0]['data'];
18042
		}
18043
		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))
18044
		{
18045
			return (float) $match[1];
18046
		}
18047
		else
18048
		{
18049
			return null;
18050
		}
18051
	}
18052

    
18053
	public function get_longitude()
18054
	{
18055
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
18056
		{
18057
			return (float) $return[0]['data'];
18058
		}
18059
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
18060
		{
18061
			return (float) $return[0]['data'];
18062
		}
18063
		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))
18064
		{
18065
			return (float) $match[2];
18066
		}
18067
		else
18068
		{
18069
			return null;
18070
		}
18071
	}
18072

    
18073
	public function get_image_url()
18074
	{
18075
		if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
18076
		{
18077
			return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
18078
		}
18079
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
18080
		{
18081
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
18082
		}
18083
		elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
18084
		{
18085
			return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
18086
		}
18087
		else
18088
		{
18089
			return null;
18090
		}
18091
	}
18092
}
18093

    
18094
/**
18095
 * Parses the XML Declaration
18096
 *
18097
 * @package SimplePie
18098
 * @subpackage Parsing
18099
 */
18100
class SimplePie_XML_Declaration_Parser
18101
{
18102
	/**
18103
	 * XML Version
18104
	 *
18105
	 * @access public
18106
	 * @var string
18107
	 */
18108
	var $version = '1.0';
18109

    
18110
	/**
18111
	 * Encoding
18112
	 *
18113
	 * @access public
18114
	 * @var string
18115
	 */
18116
	var $encoding = 'UTF-8';
18117

    
18118
	/**
18119
	 * Standalone
18120
	 *
18121
	 * @access public
18122
	 * @var bool
18123
	 */
18124
	var $standalone = false;
18125

    
18126
	/**
18127
	 * Current state of the state machine
18128
	 *
18129
	 * @access private
18130
	 * @var string
18131
	 */
18132
	var $state = 'before_version_name';
18133

    
18134
	/**
18135
	 * Input data
18136
	 *
18137
	 * @access private
18138
	 * @var string
18139
	 */
18140
	var $data = '';
18141

    
18142
	/**
18143
	 * Input data length (to avoid calling strlen() everytime this is needed)
18144
	 *
18145
	 * @access private
18146
	 * @var int
18147
	 */
18148
	var $data_length = 0;
18149

    
18150
	/**
18151
	 * Current position of the pointer
18152
	 *
18153
	 * @var int
18154
	 * @access private
18155
	 */
18156
	var $position = 0;
18157

    
18158
	/**
18159
	 * Create an instance of the class with the input data
18160
	 *
18161
	 * @access public
18162
	 * @param string $data Input data
18163
	 */
18164
	public function __construct($data)
18165
	{
18166
		$this->data = $data;
18167
		$this->data_length = strlen($this->data);
18168
	}
18169

    
18170
	/**
18171
	 * Parse the input data
18172
	 *
18173
	 * @access public
18174
	 * @return bool true on success, false on failure
18175
	 */
18176
	public function parse()
18177
	{
18178
		while ($this->state && $this->state !== 'emit' && $this->has_data())
18179
		{
18180
			$state = $this->state;
18181
			$this->$state();
18182
		}
18183
		$this->data = '';
18184
		if ($this->state === 'emit')
18185
		{
18186
			return true;
18187
		}
18188
		else
18189
		{
18190
			$this->version = '';
18191
			$this->encoding = '';
18192
			$this->standalone = '';
18193
			return false;
18194
		}
18195
	}
18196

    
18197
	/**
18198
	 * Check whether there is data beyond the pointer
18199
	 *
18200
	 * @access private
18201
	 * @return bool true if there is further data, false if not
18202
	 */
18203
	public function has_data()
18204
	{
18205
		return (bool) ($this->position < $this->data_length);
18206
	}
18207

    
18208
	/**
18209
	 * Advance past any whitespace
18210
	 *
18211
	 * @return int Number of whitespace characters passed
18212
	 */
18213
	public function skip_whitespace()
18214
	{
18215
		$whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
18216
		$this->position += $whitespace;
18217
		return $whitespace;
18218
	}
18219

    
18220
	/**
18221
	 * Read value
18222
	 */
18223
	public function get_value()
18224
	{
18225
		$quote = substr($this->data, $this->position, 1);
18226
		if ($quote === '"' || $quote === "'")
18227
		{
18228
			$this->position++;
18229
			$len = strcspn($this->data, $quote, $this->position);
18230
			if ($this->has_data())
18231
			{
18232
				$value = substr($this->data, $this->position, $len);
18233
				$this->position += $len + 1;
18234
				return $value;
18235
			}
18236
		}
18237
		return false;
18238
	}
18239

    
18240
	public function before_version_name()
18241
	{
18242
		if ($this->skip_whitespace())
18243
		{
18244
			$this->state = 'version_name';
18245
		}
18246
		else
18247
		{
18248
			$this->state = false;
18249
		}
18250
	}
18251

    
18252
	public function version_name()
18253
	{
18254
		if (substr($this->data, $this->position, 7) === 'version')
18255
		{
18256
			$this->position += 7;
18257
			$this->skip_whitespace();
18258
			$this->state = 'version_equals';
18259
		}
18260
		else
18261
		{
18262
			$this->state = false;
18263
		}
18264
	}
18265

    
18266
	public function version_equals()
18267
	{
18268
		if (substr($this->data, $this->position, 1) === '=')
18269
		{
18270
			$this->position++;
18271
			$this->skip_whitespace();
18272
			$this->state = 'version_value';
18273
		}
18274
		else
18275
		{
18276
			$this->state = false;
18277
		}
18278
	}
18279

    
18280
	public function version_value()
18281
	{
18282
		if ($this->version = $this->get_value())
18283
		{
18284
			$this->skip_whitespace();
18285
			if ($this->has_data())
18286
			{
18287
				$this->state = 'encoding_name';
18288
			}
18289
			else
18290
			{
18291
				$this->state = 'emit';
18292
			}
18293
		}
18294
		else
18295
		{
18296
			$this->state = false;
18297
		}
18298
	}
18299

    
18300
	public function encoding_name()
18301
	{
18302
		if (substr($this->data, $this->position, 8) === 'encoding')
18303
		{
18304
			$this->position += 8;
18305
			$this->skip_whitespace();
18306
			$this->state = 'encoding_equals';
18307
		}
18308
		else
18309
		{
18310
			$this->state = 'standalone_name';
18311
		}
18312
	}
18313

    
18314
	public function encoding_equals()
18315
	{
18316
		if (substr($this->data, $this->position, 1) === '=')
18317
		{
18318
			$this->position++;
18319
			$this->skip_whitespace();
18320
			$this->state = 'encoding_value';
18321
		}
18322
		else
18323
		{
18324
			$this->state = false;
18325
		}
18326
	}
18327

    
18328
	public function encoding_value()
18329
	{
18330
		if ($this->encoding = $this->get_value())
18331
		{
18332
			$this->skip_whitespace();
18333
			if ($this->has_data())
18334
			{
18335
				$this->state = 'standalone_name';
18336
			}
18337
			else
18338
			{
18339
				$this->state = 'emit';
18340
			}
18341
		}
18342
		else
18343
		{
18344
			$this->state = false;
18345
		}
18346
	}
18347

    
18348
	public function standalone_name()
18349
	{
18350
		if (substr($this->data, $this->position, 10) === 'standalone')
18351
		{
18352
			$this->position += 10;
18353
			$this->skip_whitespace();
18354
			$this->state = 'standalone_equals';
18355
		}
18356
		else
18357
		{
18358
			$this->state = false;
18359
		}
18360
	}
18361

    
18362
	public function standalone_equals()
18363
	{
18364
		if (substr($this->data, $this->position, 1) === '=')
18365
		{
18366
			$this->position++;
18367
			$this->skip_whitespace();
18368
			$this->state = 'standalone_value';
18369
		}
18370
		else
18371
		{
18372
			$this->state = false;
18373
		}
18374
	}
18375

    
18376
	public function standalone_value()
18377
	{
18378
		if ($standalone = $this->get_value())
18379
		{
18380
			switch ($standalone)
18381
			{
18382
				case 'yes':
18383
					$this->standalone = true;
18384
					break;
18385

    
18386
				case 'no':
18387
					$this->standalone = false;
18388
					break;
18389

    
18390
				default:
18391
					$this->state = false;
18392
					return;
18393
			}
18394

    
18395
			$this->skip_whitespace();
18396
			if ($this->has_data())
18397
			{
18398
				$this->state = false;
18399
			}
18400
			else
18401
			{
18402
				$this->state = 'emit';
18403
			}
18404
		}
18405
		else
18406
		{
18407
			$this->state = false;
18408
		}
18409
	}
18410
}
18411

    
18412
/**
18413
 * Decode 'gzip' encoded HTTP data
18414
 *
18415
 * @package SimplePie
18416
 * @subpackage HTTP
18417
 * @link http://www.gzip.org/format.txt
18418
 */
18419
class SimplePie_gzdecode
18420
{
18421
	/**
18422
	 * Compressed data
18423
	 *
18424
	 * @access private
18425
	 * @var string
18426
	 * @see gzdecode::$data
18427
	 */
18428
	var $compressed_data;
18429

    
18430
	/**
18431
	 * Size of compressed data
18432
	 *
18433
	 * @access private
18434
	 * @var int
18435
	 */
18436
	var $compressed_size;
18437

    
18438
	/**
18439
	 * Minimum size of a valid gzip string
18440
	 *
18441
	 * @access private
18442
	 * @var int
18443
	 */
18444
	var $min_compressed_size = 18;
18445

    
18446
	/**
18447
	 * Current position of pointer
18448
	 *
18449
	 * @access private
18450
	 * @var int
18451
	 */
18452
	var $position = 0;
18453

    
18454
	/**
18455
	 * Flags (FLG)
18456
	 *
18457
	 * @access private
18458
	 * @var int
18459
	 */
18460
	var $flags;
18461

    
18462
	/**
18463
	 * Uncompressed data
18464
	 *
18465
	 * @access public
18466
	 * @see gzdecode::$compressed_data
18467
	 * @var string
18468
	 */
18469
	var $data;
18470

    
18471
	/**
18472
	 * Modified time
18473
	 *
18474
	 * @access public
18475
	 * @var int
18476
	 */
18477
	var $MTIME;
18478

    
18479
	/**
18480
	 * Extra Flags
18481
	 *
18482
	 * @access public
18483
	 * @var int
18484
	 */
18485
	var $XFL;
18486

    
18487
	/**
18488
	 * Operating System
18489
	 *
18490
	 * @access public
18491
	 * @var int
18492
	 */
18493
	var $OS;
18494

    
18495
	/**
18496
	 * Subfield ID 1
18497
	 *
18498
	 * @access public
18499
	 * @see gzdecode::$extra_field
18500
	 * @see gzdecode::$SI2
18501
	 * @var string
18502
	 */
18503
	var $SI1;
18504

    
18505
	/**
18506
	 * Subfield ID 2
18507
	 *
18508
	 * @access public
18509
	 * @see gzdecode::$extra_field
18510
	 * @see gzdecode::$SI1
18511
	 * @var string
18512
	 */
18513
	var $SI2;
18514

    
18515
	/**
18516
	 * Extra field content
18517
	 *
18518
	 * @access public
18519
	 * @see gzdecode::$SI1
18520
	 * @see gzdecode::$SI2
18521
	 * @var string
18522
	 */
18523
	var $extra_field;
18524

    
18525
	/**
18526
	 * Original filename
18527
	 *
18528
	 * @access public
18529
	 * @var string
18530
	 */
18531
	var $filename;
18532

    
18533
	/**
18534
	 * Human readable comment
18535
	 *
18536
	 * @access public
18537
	 * @var string
18538
	 */
18539
	var $comment;
18540

    
18541
	/**
18542
	 * Don't allow anything to be set
18543
	 *
18544
	 * @param string $name
18545
	 * @param mixed $value
18546
	 */
18547
	public function __set($name, $value)
18548
	{
18549
		trigger_error("Cannot write property $name", E_USER_ERROR);
18550
	}
18551

    
18552
	/**
18553
	 * Set the compressed string and related properties
18554
	 *
18555
	 * @param string $data
18556
	 */
18557
	public function __construct($data)
18558
	{
18559
		$this->compressed_data = $data;
18560
		$this->compressed_size = strlen($data);
18561
	}
18562

    
18563
	/**
18564
	 * Decode the GZIP stream
18565
	 *
18566
	 * @return bool Successfulness
18567
	 */
18568
	public function parse()
18569
	{
18570
		if ($this->compressed_size >= $this->min_compressed_size)
18571
		{
18572
			// Check ID1, ID2, and CM
18573
			if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
18574
			{
18575
				return false;
18576
			}
18577

    
18578
			// Get the FLG (FLaGs)
18579
			$this->flags = ord($this->compressed_data[3]);
18580

    
18581
			// FLG bits above (1 << 4) are reserved
18582
			if ($this->flags > 0x1F)
18583
			{
18584
				return false;
18585
			}
18586

    
18587
			// Advance the pointer after the above
18588
			$this->position += 4;
18589

    
18590
			// MTIME
18591
			$mtime = substr($this->compressed_data, $this->position, 4);
18592
			// Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
18593
			if (current(unpack('S', "\x00\x01")) === 1)
18594
			{
18595
				$mtime = strrev($mtime);
18596
			}
18597
			$this->MTIME = current(unpack('l', $mtime));
18598
			$this->position += 4;
18599

    
18600
			// Get the XFL (eXtra FLags)
18601
			$this->XFL = ord($this->compressed_data[$this->position++]);
18602

    
18603
			// Get the OS (Operating System)
18604
			$this->OS = ord($this->compressed_data[$this->position++]);
18605

    
18606
			// Parse the FEXTRA
18607
			if ($this->flags & 4)
18608
			{
18609
				// Read subfield IDs
18610
				$this->SI1 = $this->compressed_data[$this->position++];
18611
				$this->SI2 = $this->compressed_data[$this->position++];
18612

    
18613
				// SI2 set to zero is reserved for future use
18614
				if ($this->SI2 === "\x00")
18615
				{
18616
					return false;
18617
				}
18618

    
18619
				// Get the length of the extra field
18620
				$len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
18621
				$this->position += 2;
18622

    
18623
				// Check the length of the string is still valid
18624
				$this->min_compressed_size += $len + 4;
18625
				if ($this->compressed_size >= $this->min_compressed_size)
18626
				{
18627
					// Set the extra field to the given data
18628
					$this->extra_field = substr($this->compressed_data, $this->position, $len);
18629
					$this->position += $len;
18630
				}
18631
				else
18632
				{
18633
					return false;
18634
				}
18635
			}
18636

    
18637
			// Parse the FNAME
18638
			if ($this->flags & 8)
18639
			{
18640
				// Get the length of the filename
18641
				$len = strcspn($this->compressed_data, "\x00", $this->position);
18642

    
18643
				// Check the length of the string is still valid
18644
				$this->min_compressed_size += $len + 1;
18645
				if ($this->compressed_size >= $this->min_compressed_size)
18646
				{
18647
					// Set the original filename to the given string
18648
					$this->filename = substr($this->compressed_data, $this->position, $len);
18649
					$this->position += $len + 1;
18650
				}
18651
				else
18652
				{
18653
					return false;
18654
				}
18655
			}
18656

    
18657
			// Parse the FCOMMENT
18658
			if ($this->flags & 16)
18659
			{
18660
				// Get the length of the comment
18661
				$len = strcspn($this->compressed_data, "\x00", $this->position);
18662

    
18663
				// Check the length of the string is still valid
18664
				$this->min_compressed_size += $len + 1;
18665
				if ($this->compressed_size >= $this->min_compressed_size)
18666
				{
18667
					// Set the original comment to the given string
18668
					$this->comment = substr($this->compressed_data, $this->position, $len);
18669
					$this->position += $len + 1;
18670
				}
18671
				else
18672
				{
18673
					return false;
18674
				}
18675
			}
18676

    
18677
			// Parse the FHCRC
18678
			if ($this->flags & 2)
18679
			{
18680
				// Check the length of the string is still valid
18681
				$this->min_compressed_size += $len + 2;
18682
				if ($this->compressed_size >= $this->min_compressed_size)
18683
				{
18684
					// Read the CRC
18685
					$crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
18686

    
18687
					// Check the CRC matches
18688
					if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
18689
					{
18690
						$this->position += 2;
18691
					}
18692
					else
18693
					{
18694
						return false;
18695
					}
18696
				}
18697
				else
18698
				{
18699
					return false;
18700
				}
18701
			}
18702

    
18703
			// Decompress the actual data
18704
			if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
18705
			{
18706
				return false;
18707
			}
18708
			else
18709
			{
18710
				$this->position = $this->compressed_size - 8;
18711
			}
18712

    
18713
			// Check CRC of data
18714
			$crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
18715
			$this->position += 4;
18716
			/*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
18717
			{
18718
				return false;
18719
			}*/
18720

    
18721
			// Check ISIZE of data
18722
			$isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
18723
			$this->position += 4;
18724
			if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
18725
			{
18726
				return false;
18727
			}
18728

    
18729
			// Wow, against all odds, we've actually got a valid gzip string
18730
			return true;
18731
		}
18732
		else
18733
		{
18734
			return false;
18735
		}
18736
	}
18737
}
18738

    
(2-2/2)