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