Wordpress 3.2
[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  */
14
15 /**
16  * Deprecated. Use SimplePie (class-simplepie.php) instead.
17  */
18 _deprecated_file( basename( __FILE__ ), '3.0', WPINC . '/class-simplepie.php' );
19
20 /*
21  * Hook to use another RSS object instead of MagpieRSS
22  */
23 do_action('load_feed_engine');
24
25 /** RSS feed constant. */
26 define('RSS', 'RSS');
27 define('ATOM', 'Atom');
28 define('MAGPIE_USER_AGENT', 'WordPress/' . $GLOBALS['wp_version']);
29
30 class MagpieRSS {
31         var $parser;
32         var $current_item       = array();      // item currently being parsed
33         var $items                      = array();      // collection of parsed items
34         var $channel            = array();      // hash of channel fields
35         var $textinput          = array();
36         var $image                      = array();
37         var $feed_type;
38         var $feed_version;
39
40         // parser variables
41         var $stack                              = array(); // parser stack
42         var $inchannel                  = false;
43         var $initem                     = false;
44         var $incontent                  = false; // if in Atom <content mode="xml"> field
45         var $intextinput                = false;
46         var $inimage                    = false;
47         var $current_field              = '';
48         var $current_namespace  = false;
49
50         //var $ERROR = "";
51
52         var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
53
54         function MagpieRSS ($source) {
55
56                 # if PHP xml isn't compiled in, die
57                 #
58                 if ( !function_exists('xml_parser_create') )
59                         trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" );
60
61                 $parser = @xml_parser_create();
62
63                 if ( !is_resource($parser) )
64                         trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php");
65
66
67                 $this->parser = $parser;
68
69                 # pass in parser, and a reference to this object
70                 # set up handlers
71                 #
72                 xml_set_object( $this->parser, $this );
73                 xml_set_element_handler($this->parser,
74                                 'feed_start_element', 'feed_end_element' );
75
76                 xml_set_character_data_handler( $this->parser, 'feed_cdata' );
77
78                 $status = xml_parse( $this->parser, $source );
79
80                 if (! $status ) {
81                         $errorcode = xml_get_error_code( $this->parser );
82                         if ( $errorcode != XML_ERROR_NONE ) {
83                                 $xml_error = xml_error_string( $errorcode );
84                                 $error_line = xml_get_current_line_number($this->parser);
85                                 $error_col = xml_get_current_column_number($this->parser);
86                                 $errormsg = "$xml_error at line $error_line, column $error_col";
87
88                                 $this->error( $errormsg );
89                         }
90                 }
91
92                 xml_parser_free( $this->parser );
93
94                 $this->normalize();
95         }
96
97         function feed_start_element($p, $element, &$attrs) {
98                 $el = $element = strtolower($element);
99                 $attrs = array_change_key_case($attrs, CASE_LOWER);
100
101                 // check for a namespace, and split if found
102                 $ns     = false;
103                 if ( strpos( $element, ':' ) ) {
104                         list($ns, $el) = split( ':', $element, 2);
105                 }
106                 if ( $ns and $ns != 'rdf' ) {
107                         $this->current_namespace = $ns;
108                 }
109
110                 # if feed type isn't set, then this is first element of feed
111                 # identify feed from root element
112                 #
113                 if (!isset($this->feed_type) ) {
114                         if ( $el == 'rdf' ) {
115                                 $this->feed_type = RSS;
116                                 $this->feed_version = '1.0';
117                         }
118                         elseif ( $el == 'rss' ) {
119                                 $this->feed_type = RSS;
120                                 $this->feed_version = $attrs['version'];
121                         }
122                         elseif ( $el == 'feed' ) {
123                                 $this->feed_type = ATOM;
124                                 $this->feed_version = $attrs['version'];
125                                 $this->inchannel = true;
126                         }
127                         return;
128                 }
129
130                 if ( $el == 'channel' )
131                 {
132                         $this->inchannel = true;
133                 }
134                 elseif ($el == 'item' or $el == 'entry' )
135                 {
136                         $this->initem = true;
137                         if ( isset($attrs['rdf:about']) ) {
138                                 $this->current_item['about'] = $attrs['rdf:about'];
139                         }
140                 }
141
142                 // if we're in the default namespace of an RSS feed,
143                 //  record textinput or image fields
144                 elseif (
145                         $this->feed_type == RSS and
146                         $this->current_namespace == '' and
147                         $el == 'textinput' )
148                 {
149                         $this->intextinput = true;
150                 }
151
152                 elseif (
153                         $this->feed_type == RSS and
154                         $this->current_namespace == '' and
155                         $el == 'image' )
156                 {
157                         $this->inimage = true;
158                 }
159
160                 # handle atom content constructs
161                 elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
162                 {
163                         // avoid clashing w/ RSS mod_content
164                         if ($el == 'content' ) {
165                                 $el = 'atom_content';
166                         }
167
168                         $this->incontent = $el;
169
170
171                 }
172
173                 // if inside an Atom content construct (e.g. content or summary) field treat tags as text
174                 elseif ($this->feed_type == ATOM and $this->incontent )
175                 {
176                         // if tags are inlined, then flatten
177                         $attrs_str = join(' ',
178                                         array_map(array('MagpieRSS', 'map_attrs'),
179                                         array_keys($attrs),
180                                         array_values($attrs) ) );
181
182                         $this->append_content( "<$element $attrs_str>"  );
183
184                         array_unshift( $this->stack, $el );
185                 }
186
187                 // Atom support many links per containging element.
188                 // Magpie treats link elements of type rel='alternate'
189                 // as being equivalent to RSS's simple link element.
190                 //
191                 elseif ($this->feed_type == ATOM and $el == 'link' )
192                 {
193                         if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
194                         {
195                                 $link_el = 'link';
196                         }
197                         else {
198                                 $link_el = 'link_' . $attrs['rel'];
199                         }
200
201                         $this->append($link_el, $attrs['href']);
202                 }
203                 // set stack[0] to current element
204                 else {
205                         array_unshift($this->stack, $el);
206                 }
207         }
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:  i don't think this is actually neccessary
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
393 if ( !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  */
404 function 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 its 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
440                 $cache_status    = 0;           // response of check_cache
441                 $request_headers = array(); // HTTP headers to send with fetch
442                 $rss                     = 0;           // parsed RSS object
443                 $errormsg                = 0;           // errors, if any
444
445                 if (!$cache->ERROR) {
446                         // return cache HIT, MISS, or STALE
447                         $cache_status = $cache->check_cache( $url );
448                 }
449
450                 // if object cached, and cache is fresh, return cached obj
451                 if ( $cache_status == 'HIT' ) {
452                         $rss = $cache->get( $url );
453                         if ( isset($rss) and $rss ) {
454                                 $rss->from_cache = 1;
455                                 if ( MAGPIE_DEBUG > 1) {
456                                 debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
457                         }
458                                 return $rss;
459                         }
460                 }
461
462                 // else attempt a conditional get
463
464                 // set up headers
465                 if ( $cache_status == 'STALE' ) {
466                         $rss = $cache->get( $url );
467                         if ( isset($rss->etag) and $rss->last_modified ) {
468                                 $request_headers['If-None-Match'] = $rss->etag;
469                                 $request_headers['If-Last-Modified'] = $rss->last_modified;
470                         }
471                 }
472
473                 $resp = _fetch_remote_file( $url, $request_headers );
474
475                 if (isset($resp) and $resp) {
476                         if ($resp->status == '304' ) {
477                                 // we have the most current copy
478                                 if ( MAGPIE_DEBUG > 1) {
479                                         debug("Got 304 for $url");
480                                 }
481                                 // reset cache on 304 (at minutillo insistent prodding)
482                                 $cache->set($url, $rss);
483                                 return $rss;
484                         }
485                         elseif ( is_success( $resp->status ) ) {
486                                 $rss = _response_to_rss( $resp );
487                                 if ( $rss ) {
488                                         if (MAGPIE_DEBUG > 1) {
489                                                 debug("Fetch successful");
490                                         }
491                                         // add object to cache
492                                         $cache->set( $url, $rss );
493                                         return $rss;
494                                 }
495                         }
496                         else {
497                                 $errormsg = "Failed to fetch $url. ";
498                                 if ( $resp->error ) {
499                                         # compensate for Snoopy's annoying habbit to tacking
500                                         # on '\n'
501                                         $http_error = substr($resp->error, 0, -2);
502                                         $errormsg .= "(HTTP Error: $http_error)";
503                                 }
504                                 else {
505                                         $errormsg .=  "(HTTP Response: " . $resp->response_code .')';
506                                 }
507                         }
508                 }
509                 else {
510                         $errormsg = "Unable to retrieve RSS file for unknown reasons.";
511                 }
512
513                 // else fetch failed
514
515                 // attempt to return cached object
516                 if ($rss) {
517                         if ( MAGPIE_DEBUG ) {
518                                 debug("Returning STALE object for $url");
519                         }
520                         return $rss;
521                 }
522
523                 // else we totally failed
524                 // error( $errormsg );
525
526                 return false;
527
528         } // end if ( !MAGPIE_CACHE_ON ) {
529 } // end fetch_rss()
530 endif;
531
532 /**
533  * Retrieve URL headers and content using WP HTTP Request API.
534  *
535  * @since 1.5.0
536  * @package External
537  * @subpackage MagpieRSS
538  *
539  * @param string $url URL to retrieve
540  * @param array $headers Optional. Headers to send to the URL.
541  * @return Snoopy style response
542  */
543 function _fetch_remote_file($url, $headers = "" ) {
544         $resp = wp_remote_request($url, array('headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT));
545         if ( is_wp_error($resp) ) {
546                 $error = array_shift($resp->errors);
547
548                 $resp = new stdClass;
549                 $resp->status = 500;
550                 $resp->response_code = 500;
551                 $resp->error = $error[0] . "\n"; //\n = Snoopy compatibility
552                 return $resp;
553         }
554
555         // Snoopy returns headers unprocessed.
556         // Also note, WP_HTTP lowercases all keys, Snoopy did not.
557         $return_headers = array();
558         foreach ( wp_remote_retrieve_headers( $resp ) as $key => $value ) {
559                 if ( !is_array($value) ) {
560                         $return_headers[] = "$key: $value";
561                 } else {
562                         foreach ( $value as $v )
563                                 $return_headers[] = "$key: $v";
564                 }
565         }
566
567         $response = new stdClass;
568         $response->status = wp_remote_retrieve_response_code( $resp );
569         $response->response_code = wp_remote_retrieve_response_code( $resp );
570         $response->headers = $return_headers;
571         $response->results = wp_remote_retrieve_body( $resp );
572
573         return $response;
574 }
575
576 /**
577  * Retrieve
578  *
579  * @since 1.5.0
580  * @package External
581  * @subpackage MagpieRSS
582  *
583  * @param unknown_type $resp
584  * @return unknown
585  */
586 function _response_to_rss ($resp) {
587         $rss = new MagpieRSS( $resp->results );
588
589         // if RSS parsed successfully
590         if ( $rss && (!isset($rss->ERROR) || !$rss->ERROR) ) {
591
592                 // find Etag, and Last-Modified
593                 foreach( (array) $resp->headers as $h) {
594                         // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
595                         if (strpos($h, ": ")) {
596                                 list($field, $val) = explode(": ", $h, 2);
597                         }
598                         else {
599                                 $field = $h;
600                                 $val = "";
601                         }
602
603                         if ( $field == 'etag' ) {
604                                 $rss->etag = $val;
605                         }
606
607                         if ( $field == 'last-modified' ) {
608                                 $rss->last_modified = $val;
609                         }
610                 }
611
612                 return $rss;
613         } // else construct error message
614         else {
615                 $errormsg = "Failed to parse RSS file.";
616
617                 if ($rss) {
618                         $errormsg .= " (" . $rss->ERROR . ")";
619                 }
620                 // error($errormsg);
621
622                 return false;
623         } // end if ($rss and !$rss->error)
624 }
625
626 /**
627  * Set up constants with default values, unless user overrides.
628  *
629  * @since 1.5.0
630  * @package External
631  * @subpackage MagpieRSS
632  */
633 function init () {
634         if ( defined('MAGPIE_INITALIZED') ) {
635                 return;
636         }
637         else {
638                 define('MAGPIE_INITALIZED', 1);
639         }
640
641         if ( !defined('MAGPIE_CACHE_ON') ) {
642                 define('MAGPIE_CACHE_ON', 1);
643         }
644
645         if ( !defined('MAGPIE_CACHE_DIR') ) {
646                 define('MAGPIE_CACHE_DIR', './cache');
647         }
648
649         if ( !defined('MAGPIE_CACHE_AGE') ) {
650                 define('MAGPIE_CACHE_AGE', 60*60); // one hour
651         }
652
653         if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
654                 define('MAGPIE_CACHE_FRESH_ONLY', 0);
655         }
656
657                 if ( !defined('MAGPIE_DEBUG') ) {
658                 define('MAGPIE_DEBUG', 0);
659         }
660
661         if ( !defined('MAGPIE_USER_AGENT') ) {
662                 $ua = 'WordPress/' . $GLOBALS['wp_version'];
663
664                 if ( MAGPIE_CACHE_ON ) {
665                         $ua = $ua . ')';
666                 }
667                 else {
668                         $ua = $ua . '; No cache)';
669                 }
670
671                 define('MAGPIE_USER_AGENT', $ua);
672         }
673
674         if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
675                 define('MAGPIE_FETCH_TIME_OUT', 2);     // 2 second timeout
676         }
677
678         // use gzip encoding to fetch rss files if supported?
679         if ( !defined('MAGPIE_USE_GZIP') ) {
680                 define('MAGPIE_USE_GZIP', true);
681         }
682 }
683
684 function is_info ($sc) {
685         return $sc >= 100 && $sc < 200;
686 }
687
688 function is_success ($sc) {
689         return $sc >= 200 && $sc < 300;
690 }
691
692 function is_redirect ($sc) {
693         return $sc >= 300 && $sc < 400;
694 }
695
696 function is_error ($sc) {
697         return $sc >= 400 && $sc < 600;
698 }
699
700 function is_client_error ($sc) {
701         return $sc >= 400 && $sc < 500;
702 }
703
704 function is_server_error ($sc) {
705         return $sc >= 500 && $sc < 600;
706 }
707
708 class RSSCache {
709         var $BASE_CACHE;        // where the cache files are stored
710         var $MAX_AGE    = 43200;                // when are files stale, default twelve hours
711         var $ERROR              = '';                   // accumulate error messages
712
713         function RSSCache ($base='', $age='') {
714                 $this->BASE_CACHE = WP_CONTENT_DIR . '/cache';
715                 if ( $base ) {
716                         $this->BASE_CACHE = $base;
717                 }
718                 if ( $age ) {
719                         $this->MAX_AGE = $age;
720                 }
721
722         }
723
724 /*=======================================================================*\
725         Function:       set
726         Purpose:        add an item to the cache, keyed on url
727         Input:          url from wich the rss file was fetched
728         Output:         true on sucess
729 \*=======================================================================*/
730         function set ($url, $rss) {
731                 $cache_option = 'rss_' . $this->file_name( $url );
732
733                 set_transient($cache_option, $rss, $this->MAX_AGE);
734
735                 return $cache_option;
736         }
737
738 /*=======================================================================*\
739         Function:       get
740         Purpose:        fetch an item from the cache
741         Input:          url from wich the rss file was fetched
742         Output:         cached object on HIT, false on MISS
743 \*=======================================================================*/
744         function get ($url) {
745                 $this->ERROR = "";
746                 $cache_option = 'rss_' . $this->file_name( $url );
747
748                 if ( ! $rss = get_transient( $cache_option ) ) {
749                         $this->debug(
750                                 "Cache doesn't contain: $url (cache option: $cache_option)"
751                         );
752                         return 0;
753                 }
754
755                 return $rss;
756         }
757
758 /*=======================================================================*\
759         Function:       check_cache
760         Purpose:        check a url for membership in the cache
761                                 and whether the object is older then MAX_AGE (ie. STALE)
762         Input:          url from wich the rss file was fetched
763         Output:         cached object on HIT, false on MISS
764 \*=======================================================================*/
765         function check_cache ( $url ) {
766                 $this->ERROR = "";
767                 $cache_option = 'rss_' . $this->file_name( $url );
768
769                 if ( get_transient($cache_option) ) {
770                         // object exists and is current
771                                 return 'HIT';
772                 } else {
773                         // object does not exist
774                         return 'MISS';
775                 }
776         }
777
778 /*=======================================================================*\
779         Function:       serialize
780 \*=======================================================================*/
781         function serialize ( $rss ) {
782                 return serialize( $rss );
783         }
784
785 /*=======================================================================*\
786         Function:       unserialize
787 \*=======================================================================*/
788         function unserialize ( $data ) {
789                 return unserialize( $data );
790         }
791
792 /*=======================================================================*\
793         Function:       file_name
794         Purpose:        map url to location in cache
795         Input:          url from wich the rss file was fetched
796         Output:         a file name
797 \*=======================================================================*/
798         function file_name ($url) {
799                 return md5( $url );
800         }
801
802 /*=======================================================================*\
803         Function:       error
804         Purpose:        register error
805 \*=======================================================================*/
806         function error ($errormsg, $lvl=E_USER_WARNING) {
807                 // append PHP's error message if track_errors enabled
808                 if ( isset($php_errormsg) ) {
809                         $errormsg .= " ($php_errormsg)";
810                 }
811                 $this->ERROR = $errormsg;
812                 if ( MAGPIE_DEBUG ) {
813                         trigger_error( $errormsg, $lvl);
814                 }
815                 else {
816                         error_log( $errormsg, 0);
817                 }
818         }
819                         function debug ($debugmsg, $lvl=E_USER_NOTICE) {
820                 if ( MAGPIE_DEBUG ) {
821                         $this->error("MagpieRSS [debug] $debugmsg", $lvl);
822                 }
823         }
824 }
825
826 if ( !function_exists('parse_w3cdtf') ) :
827 function parse_w3cdtf ( $date_str ) {
828
829         # regex to match wc3dtf
830         $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
831
832         if ( preg_match( $pat, $date_str, $match ) ) {
833                 list( $year, $month, $day, $hours, $minutes, $seconds) =
834                         array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
835
836                 # calc epoch for current date assuming GMT
837                 $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
838
839                 $offset = 0;
840                 if ( $match[11] == 'Z' ) {
841                         # zulu time, aka GMT
842                 }
843                 else {
844                         list( $tz_mod, $tz_hour, $tz_min ) =
845                                 array( $match[8], $match[9], $match[10]);
846
847                         # zero out the variables
848                         if ( ! $tz_hour ) { $tz_hour = 0; }
849                         if ( ! $tz_min ) { $tz_min = 0; }
850
851                         $offset_secs = (($tz_hour*60)+$tz_min)*60;
852
853                         # is timezone ahead of GMT?  then subtract offset
854                         #
855                         if ( $tz_mod == '+' ) {
856                                 $offset_secs = $offset_secs * -1;
857                         }
858
859                         $offset = $offset_secs;
860                 }
861                 $epoch = $epoch + $offset;
862                 return $epoch;
863         }
864         else {
865                 return -1;
866         }
867 }
868 endif;
869
870 if ( !function_exists('wp_rss') ) :
871 /**
872  * Display all RSS items in a HTML ordered list.
873  *
874  * @since 1.5.0
875  * @package External
876  * @subpackage MagpieRSS
877  *
878  * @param string $url URL of feed to display. Will not auto sense feed URL.
879  * @param int $num_items Optional. Number of items to display, default is all.
880  */
881 function wp_rss( $url, $num_items = -1 ) {
882         if ( $rss = fetch_rss( $url ) ) {
883                 echo '<ul>';
884
885                 if ( $num_items !== -1 ) {
886                         $rss->items = array_slice( $rss->items, 0, $num_items );
887                 }
888
889                 foreach ( (array) $rss->items as $item ) {
890                         printf(
891                                 '<li><a href="%1$s" title="%2$s">%3$s</a></li>',
892                                 esc_url( $item['link'] ),
893                                 esc_attr( strip_tags( $item['description'] ) ),
894                                 esc_html( $item['title'] )
895                         );
896                 }
897
898                 echo '</ul>';
899         } else {
900                 _e( 'An error has occurred, which probably means the feed is down. Try again later.' );
901         }
902 }
903 endif;
904
905 if ( !function_exists('get_rss') ) :
906 /**
907  * Display RSS items in HTML list items.
908  *
909  * You have to specify which HTML list you want, either ordered or unordered
910  * before using the function. You also have to specify how many items you wish
911  * to display. You can't display all of them like you can with wp_rss()
912  * function.
913  *
914  * @since 1.5.0
915  * @package External
916  * @subpackage MagpieRSS
917  *
918  * @param string $url URL of feed to display. Will not auto sense feed URL.
919  * @param int $num_items Optional. Number of items to display, default is all.
920  * @return bool False on failure.
921  */
922 function get_rss ($url, $num_items = 5) { // Like get posts, but for RSS
923         $rss = fetch_rss($url);
924         if ( $rss ) {
925                 $rss->items = array_slice($rss->items, 0, $num_items);
926                 foreach ( (array) $rss->items as $item ) {
927                         echo "<li>\n";
928                         echo "<a href='$item[link]' title='$item[description]'>";
929                         echo esc_html($item['title']);
930                         echo "</a><br />\n";
931                         echo "</li>\n";
932                 }
933         } else {
934                 return false;
935         }
936 }
937 endif;
938
939 ?>