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