PageRenderTime 37ms CodeModel.GetById 18ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 1ms

/wp-includes/rss.php

https://bitbucket.org/acipriani/madeinapulia.com
PHP | 936 lines | 655 code | 118 blank | 163 comment | 163 complexity | 565968c6a4b133fae4b81cd3cc750994 MD5 | raw file
  1<?php
  2/**
  3 * MagpieRSS: a simple RSS integration tool
  4 *
  5 * A compiled file for RSS syndication
  6 *
  7 * @author Kellan Elliott-McCrea <kellan@protest.net>
  8 * @version 0.51
  9 * @license GPL
 10 *
 11 * @package External
 12 * @subpackage MagpieRSS
 13 * @deprecated 3.0.0 Use SimplePie instead.
 14 */
 15
 16/**
 17 * Deprecated. Use SimplePie (class-simplepie.php) instead.
 18 */
 19_deprecated_file( basename( __FILE__ ), '3.0', WPINC . '/class-simplepie.php' );
 20
 21/**
 22 * Fires before MagpieRSS is loaded, to optionally replace it.
 23 *
 24 * @since 2.3.0
 25 * @deprecated 3.0.0
 26 */
 27do_action( 'load_feed_engine' );
 28
 29/** RSS feed constant. */
 30define('RSS', 'RSS');
 31define('ATOM', 'Atom');
 32define('MAGPIE_USER_AGENT', 'WordPress/' . $GLOBALS['wp_version']);
 33
 34class MagpieRSS {
 35	var $parser;
 36	var $current_item	= array();	// item currently being parsed
 37	var $items			= array();	// collection of parsed items
 38	var $channel		= array();	// hash of channel fields
 39	var $textinput		= array();
 40	var $image			= array();
 41	var $feed_type;
 42	var $feed_version;
 43
 44	// parser variables
 45	var $stack				= array(); // parser stack
 46	var $inchannel			= false;
 47	var $initem 			= false;
 48	var $incontent			= false; // if in Atom <content mode="xml"> field
 49	var $intextinput		= false;
 50	var $inimage 			= false;
 51	var $current_field		= '';
 52	var $current_namespace	= false;
 53
 54	//var $ERROR = "";
 55
 56	var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
 57
 58	function MagpieRSS ($source) {
 59
 60		# if PHP xml isn't compiled in, die
 61		#
 62		if ( !function_exists('xml_parser_create') )
 63			trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" );
 64
 65		$parser = @xml_parser_create();
 66
 67		if ( !is_resource($parser) )
 68			trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php");
 69
 70		$this->parser = $parser;
 71
 72		# pass in parser, and a reference to this object
 73		# set up handlers
 74		#
 75		xml_set_object( $this->parser, $this );
 76		xml_set_element_handler($this->parser,
 77				'feed_start_element', 'feed_end_element' );
 78
 79		xml_set_character_data_handler( $this->parser, 'feed_cdata' );
 80
 81		$status = xml_parse( $this->parser, $source );
 82
 83		if (! $status ) {
 84			$errorcode = xml_get_error_code( $this->parser );
 85			if ( $errorcode != XML_ERROR_NONE ) {
 86				$xml_error = xml_error_string( $errorcode );
 87				$error_line = xml_get_current_line_number($this->parser);
 88				$error_col = xml_get_current_column_number($this->parser);
 89				$errormsg = "$xml_error at line $error_line, column $error_col";
 90
 91				$this->error( $errormsg );
 92			}
 93		}
 94
 95		xml_parser_free( $this->parser );
 96
 97		$this->normalize();
 98	}
 99
100	function feed_start_element($p, $element, &$attrs) {
101		$el = $element = strtolower($element);
102		$attrs = array_change_key_case($attrs, CASE_LOWER);
103
104		// check for a namespace, and split if found
105		$ns	= false;
106		if ( strpos( $element, ':' ) ) {
107			list($ns, $el) = split( ':', $element, 2);
108		}
109		if ( $ns and $ns != 'rdf' ) {
110			$this->current_namespace = $ns;
111		}
112
113		# if feed type isn't set, then this is first element of feed
114		# identify feed from root element
115		#
116		if (!isset($this->feed_type) ) {
117			if ( $el == 'rdf' ) {
118				$this->feed_type = RSS;
119				$this->feed_version = '1.0';
120			}
121			elseif ( $el == 'rss' ) {
122				$this->feed_type = RSS;
123				$this->feed_version = $attrs['version'];
124			}
125			elseif ( $el == 'feed' ) {
126				$this->feed_type = ATOM;
127				$this->feed_version = $attrs['version'];
128				$this->inchannel = true;
129			}
130			return;
131		}
132
133		if ( $el == 'channel' )
134		{
135			$this->inchannel = true;
136		}
137		elseif ($el == 'item' or $el == 'entry' )
138		{
139			$this->initem = true;
140			if ( isset($attrs['rdf:about']) ) {
141				$this->current_item['about'] = $attrs['rdf:about'];
142			}
143		}
144
145		// if we're in the default namespace of an RSS feed,
146		//  record textinput or image fields
147		elseif (
148			$this->feed_type == RSS and
149			$this->current_namespace == '' and
150			$el == 'textinput' )
151		{
152			$this->intextinput = true;
153		}
154
155		elseif (
156			$this->feed_type == RSS and
157			$this->current_namespace == '' and
158			$el == 'image' )
159		{
160			$this->inimage = true;
161		}
162
163		# handle atom content constructs
164		elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
165		{
166			// avoid clashing w/ RSS mod_content
167			if ($el == 'content' ) {
168				$el = 'atom_content';
169			}
170
171			$this->incontent = $el;
172
173		}
174
175		// if inside an Atom content construct (e.g. content or summary) field treat tags as text
176		elseif ($this->feed_type == ATOM and $this->incontent )
177		{
178			// if tags are inlined, then flatten
179			$attrs_str = join(' ',
180					array_map(array('MagpieRSS', 'map_attrs'),
181					array_keys($attrs),
182					array_values($attrs) ) );
183
184			$this->append_content( "<$element $attrs_str>"  );
185
186			array_unshift( $this->stack, $el );
187		}
188
189		// Atom support many links per containging element.
190		// Magpie treats link elements of type rel='alternate'
191		// as being equivalent to RSS's simple link element.
192		//
193		elseif ($this->feed_type == ATOM and $el == 'link' )
194		{
195			if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
196			{
197				$link_el = 'link';
198			}
199			else {
200				$link_el = 'link_' . $attrs['rel'];
201			}
202
203			$this->append($link_el, $attrs['href']);
204		}
205		// set stack[0] to current element
206		else {
207			array_unshift($this->stack, $el);
208		}
209	}
210
211	function feed_cdata ($p, $text) {
212
213		if ($this->feed_type == ATOM and $this->incontent)
214		{
215			$this->append_content( $text );
216		}
217		else {
218			$current_el = join('_', array_reverse($this->stack));
219			$this->append($current_el, $text);
220		}
221	}
222
223	function feed_end_element ($p, $el) {
224		$el = strtolower($el);
225
226		if ( $el == 'item' or $el == 'entry' )
227		{
228			$this->items[] = $this->current_item;
229			$this->current_item = array();
230			$this->initem = false;
231		}
232		elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
233		{
234			$this->intextinput = false;
235		}
236		elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
237		{
238			$this->inimage = false;
239		}
240		elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
241		{
242			$this->incontent = false;
243		}
244		elseif ($el == 'channel' or $el == 'feed' )
245		{
246			$this->inchannel = false;
247		}
248		elseif ($this->feed_type == ATOM and $this->incontent  ) {
249			// balance tags properly
250			// note: This may not actually be necessary
251			if ( $this->stack[0] == $el )
252			{
253				$this->append_content("</$el>");
254			}
255			else {
256				$this->append_content("<$el />");
257			}
258
259			array_shift( $this->stack );
260		}
261		else {
262			array_shift( $this->stack );
263		}
264
265		$this->current_namespace = false;
266	}
267
268	function concat (&$str1, $str2="") {
269		if (!isset($str1) ) {
270			$str1="";
271		}
272		$str1 .= $str2;
273	}
274
275	function append_content($text) {
276		if ( $this->initem ) {
277			$this->concat( $this->current_item[ $this->incontent ], $text );
278		}
279		elseif ( $this->inchannel ) {
280			$this->concat( $this->channel[ $this->incontent ], $text );
281		}
282	}
283
284	// smart append - field and namespace aware
285	function append($el, $text) {
286		if (!$el) {
287			return;
288		}
289		if ( $this->current_namespace )
290		{
291			if ( $this->initem ) {
292				$this->concat(
293					$this->current_item[ $this->current_namespace ][ $el ], $text);
294			}
295			elseif ($this->inchannel) {
296				$this->concat(
297					$this->channel[ $this->current_namespace][ $el ], $text );
298			}
299			elseif ($this->intextinput) {
300				$this->concat(
301					$this->textinput[ $this->current_namespace][ $el ], $text );
302			}
303			elseif ($this->inimage) {
304				$this->concat(
305					$this->image[ $this->current_namespace ][ $el ], $text );
306			}
307		}
308		else {
309			if ( $this->initem ) {
310				$this->concat(
311					$this->current_item[ $el ], $text);
312			}
313			elseif ($this->intextinput) {
314				$this->concat(
315					$this->textinput[ $el ], $text );
316			}
317			elseif ($this->inimage) {
318				$this->concat(
319					$this->image[ $el ], $text );
320			}
321			elseif ($this->inchannel) {
322				$this->concat(
323					$this->channel[ $el ], $text );
324			}
325
326		}
327	}
328
329	function normalize () {
330		// if atom populate rss fields
331		if ( $this->is_atom() ) {
332			$this->channel['descripton'] = $this->channel['tagline'];
333			for ( $i = 0; $i < count($this->items); $i++) {
334				$item = $this->items[$i];
335				if ( isset($item['summary']) )
336					$item['description'] = $item['summary'];
337				if ( isset($item['atom_content']))
338					$item['content']['encoded'] = $item['atom_content'];
339
340				$this->items[$i] = $item;
341			}
342		}
343		elseif ( $this->is_rss() ) {
344			$this->channel['tagline'] = $this->channel['description'];
345			for ( $i = 0; $i < count($this->items); $i++) {
346				$item = $this->items[$i];
347				if ( isset($item['description']))
348					$item['summary'] = $item['description'];
349				if ( isset($item['content']['encoded'] ) )
350					$item['atom_content'] = $item['content']['encoded'];
351
352				$this->items[$i] = $item;
353			}
354		}
355	}
356
357	function is_rss () {
358		if ( $this->feed_type == RSS ) {
359			return $this->feed_version;
360		}
361		else {
362			return false;
363		}
364	}
365
366	function is_atom() {
367		if ( $this->feed_type == ATOM ) {
368			return $this->feed_version;
369		}
370		else {
371			return false;
372		}
373	}
374
375	function map_attrs($k, $v) {
376		return "$k=\"$v\"";
377	}
378
379	function error( $errormsg, $lvl = E_USER_WARNING ) {
380		// append PHP's error message if track_errors enabled
381		if ( isset($php_errormsg) ) {
382			$errormsg .= " ($php_errormsg)";
383		}
384		if ( MAGPIE_DEBUG ) {
385			trigger_error( $errormsg, $lvl);
386		} else {
387			error_log( $errormsg, 0);
388		}
389	}
390
391}
392
393if ( !function_exists('fetch_rss') ) :
394/**
395 * Build Magpie object based on RSS from URL.
396 *
397 * @since 1.5.0
398 * @package External
399 * @subpackage MagpieRSS
400 *
401 * @param string $url URL to retrieve feed
402 * @return bool|MagpieRSS false on failure or MagpieRSS object on success.
403 */
404function fetch_rss ($url) {
405	// initialize constants
406	init();
407
408	if ( !isset($url) ) {
409		// error("fetch_rss called without a url");
410		return false;
411	}
412
413	// if cache is disabled
414	if ( !MAGPIE_CACHE_ON ) {
415		// fetch file, and parse it
416		$resp = _fetch_remote_file( $url );
417		if ( is_success( $resp->status ) ) {
418			return _response_to_rss( $resp );
419		}
420		else {
421			// error("Failed to fetch $url and cache is off");
422			return false;
423		}
424	}
425	// else cache is ON
426	else {
427		// Flow
428		// 1. check cache
429		// 2. if there is a hit, make sure it's fresh
430		// 3. if cached obj fails freshness check, fetch remote
431		// 4. if remote fails, return stale object, or error
432
433		$cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
434
435		if (MAGPIE_DEBUG and $cache->ERROR) {
436			debug($cache->ERROR, E_USER_WARNING);
437		}
438
439		$cache_status 	 = 0;		// response of check_cache
440		$request_headers = array(); // HTTP headers to send with fetch
441		$rss 			 = 0;		// parsed RSS object
442		$errormsg		 = 0;		// errors, if any
443
444		if (!$cache->ERROR) {
445			// return cache HIT, MISS, or STALE
446			$cache_status = $cache->check_cache( $url );
447		}
448
449		// if object cached, and cache is fresh, return cached obj
450		if ( $cache_status == 'HIT' ) {
451			$rss = $cache->get( $url );
452			if ( isset($rss) and $rss ) {
453				$rss->from_cache = 1;
454				if ( MAGPIE_DEBUG > 1) {
455				debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
456			}
457				return $rss;
458			}
459		}
460
461		// else attempt a conditional get
462
463		// set up headers
464		if ( $cache_status == 'STALE' ) {
465			$rss = $cache->get( $url );
466			if ( isset($rss->etag) and $rss->last_modified ) {
467				$request_headers['If-None-Match'] = $rss->etag;
468				$request_headers['If-Last-Modified'] = $rss->last_modified;
469			}
470		}
471
472		$resp = _fetch_remote_file( $url, $request_headers );
473
474		if (isset($resp) and $resp) {
475			if ($resp->status == '304' ) {
476				// we have the most current copy
477				if ( MAGPIE_DEBUG > 1) {
478					debug("Got 304 for $url");
479				}
480				// reset cache on 304 (at minutillo insistent prodding)
481				$cache->set($url, $rss);
482				return $rss;
483			}
484			elseif ( is_success( $resp->status ) ) {
485				$rss = _response_to_rss( $resp );
486				if ( $rss ) {
487					if (MAGPIE_DEBUG > 1) {
488						debug("Fetch successful");
489					}
490					// add object to cache
491					$cache->set( $url, $rss );
492					return $rss;
493				}
494			}
495			else {
496				$errormsg = "Failed to fetch $url. ";
497				if ( $resp->error ) {
498					# compensate for Snoopy's annoying habbit to tacking
499					# on '\n'
500					$http_error = substr($resp->error, 0, -2);
501					$errormsg .= "(HTTP Error: $http_error)";
502				}
503				else {
504					$errormsg .=  "(HTTP Response: " . $resp->response_code .')';
505				}
506			}
507		}
508		else {
509			$errormsg = "Unable to retrieve RSS file for unknown reasons.";
510		}
511
512		// else fetch failed
513
514		// attempt to return cached object
515		if ($rss) {
516			if ( MAGPIE_DEBUG ) {
517				debug("Returning STALE object for $url");
518			}
519			return $rss;
520		}
521
522		// else we totally failed
523		// error( $errormsg );
524
525		return false;
526
527	} // end if ( !MAGPIE_CACHE_ON ) {
528} // end fetch_rss()
529endif;
530
531/**
532 * Retrieve URL headers and content using WP HTTP Request API.
533 *
534 * @since 1.5.0
535 * @package External
536 * @subpackage MagpieRSS
537 *
538 * @param string $url URL to retrieve
539 * @param array $headers Optional. Headers to send to the URL.
540 * @return Snoopy style response
541 */
542function _fetch_remote_file($url, $headers = "" ) {
543	$resp = wp_safe_remote_request( $url, array( 'headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT ) );
544	if ( is_wp_error($resp) ) {
545		$error = array_shift($resp->errors);
546
547		$resp = new stdClass;
548		$resp->status = 500;
549		$resp->response_code = 500;
550		$resp->error = $error[0] . "\n"; //\n = Snoopy compatibility
551		return $resp;
552	}
553
554	// Snoopy returns headers unprocessed.
555	// Also note, WP_HTTP lowercases all keys, Snoopy did not.
556	$return_headers = array();
557	foreach ( wp_remote_retrieve_headers( $resp ) as $key => $value ) {
558		if ( !is_array($value) ) {
559			$return_headers[] = "$key: $value";
560		} else {
561			foreach ( $value as $v )
562				$return_headers[] = "$key: $v";
563		}
564	}
565
566	$response = new stdClass;
567	$response->status = wp_remote_retrieve_response_code( $resp );
568	$response->response_code = wp_remote_retrieve_response_code( $resp );
569	$response->headers = $return_headers;
570	$response->results = wp_remote_retrieve_body( $resp );
571
572	return $response;
573}
574
575/**
576 * Retrieve
577 *
578 * @since 1.5.0
579 * @package External
580 * @subpackage MagpieRSS
581 *
582 * @param array $resp
583 * @return MagpieRSS|bool
584 */
585function _response_to_rss ($resp) {
586	$rss = new MagpieRSS( $resp->results );
587
588	// if RSS parsed successfully
589	if ( $rss && (!isset($rss->ERROR) || !$rss->ERROR) ) {
590
591		// find Etag, and Last-Modified
592		foreach( (array) $resp->headers as $h) {
593			// 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
594			if (strpos($h, ": ")) {
595				list($field, $val) = explode(": ", $h, 2);
596			}
597			else {
598				$field = $h;
599				$val = "";
600			}
601
602			if ( $field == 'etag' ) {
603				$rss->etag = $val;
604			}
605
606			if ( $field == 'last-modified' ) {
607				$rss->last_modified = $val;
608			}
609		}
610
611		return $rss;
612	} // else construct error message
613	else {
614		$errormsg = "Failed to parse RSS file.";
615
616		if ($rss) {
617			$errormsg .= " (" . $rss->ERROR . ")";
618		}
619		// error($errormsg);
620
621		return false;
622	} // end if ($rss and !$rss->error)
623}
624
625/**
626 * Set up constants with default values, unless user overrides.
627 *
628 * @since 1.5.0
629 * @package External
630 * @subpackage MagpieRSS
631 */
632function init () {
633	if ( defined('MAGPIE_INITALIZED') ) {
634		return;
635	}
636	else {
637		define('MAGPIE_INITALIZED', 1);
638	}
639
640	if ( !defined('MAGPIE_CACHE_ON') ) {
641		define('MAGPIE_CACHE_ON', 1);
642	}
643
644	if ( !defined('MAGPIE_CACHE_DIR') ) {
645		define('MAGPIE_CACHE_DIR', './cache');
646	}
647
648	if ( !defined('MAGPIE_CACHE_AGE') ) {
649		define('MAGPIE_CACHE_AGE', 60*60); // one hour
650	}
651
652	if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
653		define('MAGPIE_CACHE_FRESH_ONLY', 0);
654	}
655
656		if ( !defined('MAGPIE_DEBUG') ) {
657		define('MAGPIE_DEBUG', 0);
658	}
659
660	if ( !defined('MAGPIE_USER_AGENT') ) {
661		$ua = 'WordPress/' . $GLOBALS['wp_version'];
662
663		if ( MAGPIE_CACHE_ON ) {
664			$ua = $ua . ')';
665		}
666		else {
667			$ua = $ua . '; No cache)';
668		}
669
670		define('MAGPIE_USER_AGENT', $ua);
671	}
672
673	if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
674		define('MAGPIE_FETCH_TIME_OUT', 2);	// 2 second timeout
675	}
676
677	// use gzip encoding to fetch rss files if supported?
678	if ( !defined('MAGPIE_USE_GZIP') ) {
679		define('MAGPIE_USE_GZIP', true);
680	}
681}
682
683function is_info ($sc) {
684	return $sc >= 100 && $sc < 200;
685}
686
687function is_success ($sc) {
688	return $sc >= 200 && $sc < 300;
689}
690
691function is_redirect ($sc) {
692	return $sc >= 300 && $sc < 400;
693}
694
695function is_error ($sc) {
696	return $sc >= 400 && $sc < 600;
697}
698
699function is_client_error ($sc) {
700	return $sc >= 400 && $sc < 500;
701}
702
703function is_server_error ($sc) {
704	return $sc >= 500 && $sc < 600;
705}
706
707class RSSCache {
708	var $BASE_CACHE;	// where the cache files are stored
709	var $MAX_AGE	= 43200;  		// when are files stale, default twelve hours
710	var $ERROR 		= '';			// accumulate error messages
711
712	function RSSCache ($base='', $age='') {
713		$this->BASE_CACHE = WP_CONTENT_DIR . '/cache';
714		if ( $base ) {
715			$this->BASE_CACHE = $base;
716		}
717		if ( $age ) {
718			$this->MAX_AGE = $age;
719		}
720
721	}
722
723/*=======================================================================*\
724	Function:	set
725	Purpose:	add an item to the cache, keyed on url
726	Input:		url from wich the rss file was fetched
727	Output:		true on success
728\*=======================================================================*/
729	function set ($url, $rss) {
730		$cache_option = 'rss_' . $this->file_name( $url );
731
732		set_transient($cache_option, $rss, $this->MAX_AGE);
733
734		return $cache_option;
735	}
736
737/*=======================================================================*\
738	Function:	get
739	Purpose:	fetch an item from the cache
740	Input:		url from wich the rss file was fetched
741	Output:		cached object on HIT, false on MISS
742\*=======================================================================*/
743	function get ($url) {
744		$this->ERROR = "";
745		$cache_option = 'rss_' . $this->file_name( $url );
746
747		if ( ! $rss = get_transient( $cache_option ) ) {
748			$this->debug(
749				"Cache doesn't contain: $url (cache option: $cache_option)"
750			);
751			return 0;
752		}
753
754		return $rss;
755	}
756
757/*=======================================================================*\
758	Function:	check_cache
759	Purpose:	check a url for membership in the cache
760				and whether the object is older then MAX_AGE (ie. STALE)
761	Input:		url from wich the rss file was fetched
762	Output:		cached object on HIT, false on MISS
763\*=======================================================================*/
764	function check_cache ( $url ) {
765		$this->ERROR = "";
766		$cache_option = 'rss_' . $this->file_name( $url );
767
768		if ( get_transient($cache_option) ) {
769			// object exists and is current
770				return 'HIT';
771		} else {
772			// object does not exist
773			return 'MISS';
774		}
775	}
776
777/*=======================================================================*\
778	Function:	serialize
779\*=======================================================================*/
780	function serialize ( $rss ) {
781		return serialize( $rss );
782	}
783
784/*=======================================================================*\
785	Function:	unserialize
786\*=======================================================================*/
787	function unserialize ( $data ) {
788		return unserialize( $data );
789	}
790
791/*=======================================================================*\
792	Function:	file_name
793	Purpose:	map url to location in cache
794	Input:		url from wich the rss file was fetched
795	Output:		a file name
796\*=======================================================================*/
797	function file_name ($url) {
798		return md5( $url );
799	}
800
801/*=======================================================================*\
802	Function:	error
803	Purpose:	register error
804\*=======================================================================*/
805	function error ($errormsg, $lvl=E_USER_WARNING) {
806		// append PHP's error message if track_errors enabled
807		if ( isset($php_errormsg) ) {
808			$errormsg .= " ($php_errormsg)";
809		}
810		$this->ERROR = $errormsg;
811		if ( MAGPIE_DEBUG ) {
812			trigger_error( $errormsg, $lvl);
813		}
814		else {
815			error_log( $errormsg, 0);
816		}
817	}
818			function debug ($debugmsg, $lvl=E_USER_NOTICE) {
819		if ( MAGPIE_DEBUG ) {
820			$this->error("MagpieRSS [debug] $debugmsg", $lvl);
821		}
822	}
823}
824
825if ( !function_exists('parse_w3cdtf') ) :
826function parse_w3cdtf ( $date_str ) {
827
828	# regex to match wc3dtf
829	$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
830
831	if ( preg_match( $pat, $date_str, $match ) ) {
832		list( $year, $month, $day, $hours, $minutes, $seconds) =
833			array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
834
835		# calc epoch for current date assuming GMT
836		$epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
837
838		$offset = 0;
839		if ( $match[11] == 'Z' ) {
840			# zulu time, aka GMT
841		}
842		else {
843			list( $tz_mod, $tz_hour, $tz_min ) =
844				array( $match[8], $match[9], $match[10]);
845
846			# zero out the variables
847			if ( ! $tz_hour ) { $tz_hour = 0; }
848			if ( ! $tz_min ) { $tz_min = 0; }
849
850			$offset_secs = (($tz_hour*60)+$tz_min)*60;
851
852			# is timezone ahead of GMT?  then subtract offset
853			#
854			if ( $tz_mod == '+' ) {
855				$offset_secs = $offset_secs * -1;
856			}
857
858			$offset = $offset_secs;
859		}
860		$epoch = $epoch + $offset;
861		return $epoch;
862	}
863	else {
864		return -1;
865	}
866}
867endif;
868
869if ( !function_exists('wp_rss') ) :
870/**
871 * Display all RSS items in a HTML ordered list.
872 *
873 * @since 1.5.0
874 * @package External
875 * @subpackage MagpieRSS
876 *
877 * @param string $url URL of feed to display. Will not auto sense feed URL.
878 * @param int $num_items Optional. Number of items to display, default is all.
879 */
880function wp_rss( $url, $num_items = -1 ) {
881	if ( $rss = fetch_rss( $url ) ) {
882		echo '<ul>';
883
884		if ( $num_items !== -1 ) {
885			$rss->items = array_slice( $rss->items, 0, $num_items );
886		}
887
888		foreach ( (array) $rss->items as $item ) {
889			printf(
890				'<li><a href="%1$s" title="%2$s">%3$s</a></li>',
891				esc_url( $item['link'] ),
892				esc_attr( strip_tags( $item['description'] ) ),
893				esc_html( $item['title'] )
894			);
895		}
896
897		echo '</ul>';
898	} else {
899		_e( 'An error has occurred, which probably means the feed is down. Try again later.' );
900	}
901}
902endif;
903
904if ( !function_exists('get_rss') ) :
905/**
906 * Display RSS items in HTML list items.
907 *
908 * You have to specify which HTML list you want, either ordered or unordered
909 * before using the function. You also have to specify how many items you wish
910 * to display. You can't display all of them like you can with wp_rss()
911 * function.
912 *
913 * @since 1.5.0
914 * @package External
915 * @subpackage MagpieRSS
916 *
917 * @param string $url URL of feed to display. Will not auto sense feed URL.
918 * @param int $num_items Optional. Number of items to display, default is all.
919 * @return bool False on failure.
920 */
921function get_rss ($url, $num_items = 5) { // Like get posts, but for RSS
922	$rss = fetch_rss($url);
923	if ( $rss ) {
924		$rss->items = array_slice($rss->items, 0, $num_items);
925		foreach ( (array) $rss->items as $item ) {
926			echo "<li>\n";
927			echo "<a href='$item[link]' title='$item[description]'>";
928			echo esc_html($item['title']);
929			echo "</a><br />\n";
930			echo "</li>\n";
931		}
932	} else {
933		return false;
934	}
935}
936endif;