PluginProbe
FeedWordPress / 0.9
FeedWordPress v0.9
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
← All changes | OPTIONAL/wp-includes/rss-functions.php +1206 -3 0.990.9 View file →
@@ -1,4 +1,1207 @@
1 1 <?php
2 -// Deprecated. Use rss.php instead.
3 -require_once (ABSPATH . WPINC . '/rss.php');
4 -?>
2 +/* Project: MagpieRSS: a simple RSS integration tool
3 + * File: A compiled file for RSS syndication
4 + * Author: Kellan Elliot-McCrea <kellan@protest.net>
5 + * WordPress development team <http://www.wordpress.org/>
6 + * Charles Johnson <technophilia@radgeek.com>
7 + * Version: 0.7wp
8 + * License: GPL
9 + *
10 + * Provenance:
11 + *
12 + * This is a drop-in replacement for the `rss-functions.php` provided with the
13 + * WordPress 1.5 distribution, which upgrades the version of MagpieRSS from 0.51
14 + * to a modification of 0.7. In addition to improved handling of character
15 + * encoding and other updates, this branch of MagpieRSS 0.7 also supports
16 + * multiple categorization of posts (using <dc:subject> or <category>). The
17 + * file is, therefore, derived from four sources: (1) Kellan's MagpieRSS 0.51,
18 + * (2) the WordPress development team's modifications to MagpieRSS 0.51,
19 + * (3) Kellan's MagpieRSS 0.7, and (4) Charles Johnson's modifications to
20 + * MagpieRSS 0.7. All possible because of the GPL. Yay for free software!
21 + *
22 + * Differences from the main branch of MagpieRSS:
23 + *
24 + * 1. Everything in rss_parse.inc, rss_fetch.inc, rss_cache.inc, and
25 + * rss_utils.inc is included in one file.
26 + *
27 + * 2. MagpieRSS returns the WordPress version as the user agent, rather than
28 + * Magpie
29 + *
30 + * 3. class RSSCache is a modified version by WordPress developers, which
31 + * caches feeds in the WordPress database (in the options table), rather
32 + * than writing external files directly.
33 + *
34 + * 4. There are two WordPress-specific functions, get_rss() and wp_rss()
35 + *
36 + * 5. New cases added to MagpieRSS::feed_start_element(),
37 + * MagpieRSS::feed_end_element(), and MagpieRSS::normalize() to handle
38 + * multiple categories correctly.
39 + */
40 +
41 +define('RSS', 'RSS');
42 +define('ATOM', 'Atom');
43 +define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version);
44 +
45 +# UPDATED: rss_parse.inc: class MagpieRSS, function map_attrs
46 +# --- cut here ---
47 +/**
48 +* Hybrid parser, and object, takes RSS as a string and returns a simple object.
49 +*
50 +* see: rss_fetch.inc for a simpler interface with integrated caching support
51 +*
52 +*/
53 +class MagpieRSS {
54 + var $parser;
55 +
56 + var $current_item = array(); // item currently being parsed
57 + var $items = array(); // collection of parsed items
58 + var $channel = array(); // hash of channel fields
59 + var $textinput = array();
60 + var $image = array();
61 + var $feed_type;
62 + var $feed_version;
63 + var $encoding = ''; // output encoding of parsed rss
64 +
65 + var $_source_encoding = ''; // only set if we have to parse xml prolog
66 +
67 + var $ERROR = "";
68 + var $WARNING = "";
69 +
70 + // define some constants
71 +
72 + var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
73 + var $_KNOWN_ENCODINGS = array('UTF-8', 'US-ASCII', 'ISO-8859-1');
74 +
75 + // parser variables, useless if you're not a parser, treat as private
76 + var $stack = array(); // parser stack
77 + var $inchannel = false;
78 + var $initem = false;
79 + var $incontent = false; // if in Atom <content mode="xml"> field
80 + var $intextinput = false;
81 + var $inimage = false;
82 + var $current_namespace = false;
83 +
84 + var $incategory = false;
85 + var $current_category = 0;
86 +
87 + /**
88 + * Set up XML parser, parse source, and return populated RSS object..
89 + *
90 + * @param string $source string containing the RSS to be parsed
91 + *
92 + * NOTE: Probably a good idea to leave the encoding options alone unless
93 + * you know what you're doing as PHP's character set support is
94 + * a little weird.
95 + *
96 + * NOTE: A lot of this is unnecessary but harmless with PHP5
97 + *
98 + *
99 + * @param string $output_encoding output the parsed RSS in this character
100 + * set defaults to ISO-8859-1 as this is PHP's
101 + * default.
102 + *
103 + * NOTE: might be changed to UTF-8 in future
104 + * versions.
105 + *
106 + * @param string $input_encoding the character set of the incoming RSS source.
107 + * Leave blank and Magpie will try to figure it
108 + * out.
109 + *
110 + *
111 + * @param bool $detect_encoding if false Magpie won't attempt to detect
112 + * source encoding. (caveat emptor)
113 + *
114 + */
115 + function MagpieRSS ($source, $output_encoding='ISO-8859-1',
116 + $input_encoding=null, $detect_encoding=true)
117 + {
118 + # if PHP xml isn't compiled in, die
119 + #
120 + if (!function_exists('xml_parser_create')) {
121 + $this->error( "Failed to load PHP's XML Extension. " .
122 + "http://www.php.net/manual/en/ref.xml.php",
123 + E_USER_ERROR );
124 + }
125 +
126 + list($parser, $source) = $this->create_parser($source,
127 + $output_encoding, $input_encoding, $detect_encoding);
128 +
129 +
130 + if (!is_resource($parser)) {
131 + $this->error( "Failed to create an instance of PHP's XML parser. " .
132 + "http://www.php.net/manual/en/ref.xml.php",
133 + E_USER_ERROR );
134 + }
135 +
136 +
137 + $this->parser = $parser;
138 +
139 + # pass in parser, and a reference to this object
140 + # setup handlers
141 + #
142 + xml_set_object( $this->parser, $this );
143 + xml_set_element_handler($this->parser,
144 + 'feed_start_element', 'feed_end_element' );
145 +
146 + xml_set_character_data_handler( $this->parser, 'feed_cdata' );
147 +
148 + $status = xml_parse( $this->parser, $source );
149 +
150 + if (! $status ) {
151 + $errorcode = xml_get_error_code( $this->parser );
152 + if ( $errorcode != XML_ERROR_NONE ) {
153 + $xml_error = xml_error_string( $errorcode );
154 + $error_line = xml_get_current_line_number($this->parser);
155 + $error_col = xml_get_current_column_number($this->parser);
156 + $errormsg = "$xml_error at line $error_line, column $error_col";
157 +
158 + $this->error( $errormsg );
159 + }
160 + }
161 +
162 + xml_parser_free( $this->parser );
163 +
164 + $this->normalize();
165 + }
166 +
167 + function feed_start_element($p, $element, &$attrs) {
168 + $el = $element = strtolower($element);
169 + $attrs = array_change_key_case($attrs, CASE_LOWER);
170 +
171 + // check for a namespace, and split if found
172 + $ns = false;
173 + if ( strpos( $element, ':' ) ) {
174 + list($ns, $el) = split( ':', $element, 2);
175 + }
176 + if ( $ns and $ns != 'rdf' ) {
177 + $this->current_namespace = $ns;
178 + }
179 +
180 + # if feed type isn't set, then this is first element of feed
181 + # identify feed from root element
182 + #
183 + if (!isset($this->feed_type) ) {
184 + if ( $el == 'rdf' ) {
185 + $this->feed_type = RSS;
186 + $this->feed_version = '1.0';
187 + }
188 + elseif ( $el == 'rss' ) {
189 + $this->feed_type = RSS;
190 + $this->feed_version = $attrs['version'];
191 + }
192 + elseif ( $el == 'feed' ) {
193 + $this->feed_type = ATOM;
194 + $this->feed_version = $attrs['version'];
195 + $this->inchannel = true;
196 + }
197 + return;
198 + }
199 +
200 + if ( $el == 'channel' )
201 + {
202 + $this->inchannel = true;
203 + }
204 + elseif ($el == 'item' or $el == 'entry' )
205 + {
206 + $this->initem = true;
207 + if ( isset($attrs['rdf:about']) ) {
208 + $this->current_item['about'] = $attrs['rdf:about'];
209 + }
210 + }
211 +
212 + elseif ($this->initem and ($el == 'category' or ($this->current_namespace == 'dc' and $el == 'subject'))) {
213 + $this->incategory = true;
214 + array_unshift( $this->stack, $el );
215 + }
216 +
217 + // if we're in the default namespace of an RSS feed,
218 + // record textinput or image fields
219 + elseif (
220 + $this->feed_type == RSS and
221 + $this->current_namespace == '' and
222 + $el == 'textinput' )
223 + {
224 + $this->intextinput = true;
225 + }
226 +
227 + elseif (
228 + $this->feed_type == RSS and
229 + $this->current_namespace == '' and
230 + $el == 'image' )
231 + {
232 + $this->inimage = true;
233 + }
234 +
235 + # handle atom content constructs
236 + elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
237 + {
238 + // avoid clashing w/ RSS mod_content
239 + if ($el == 'content' ) {
240 + $el = 'atom_content';
241 + }
242 +
243 + $this->incontent = $el;
244 +
245 +
246 + }
247 +
248 + // if inside an Atom content construct (e.g. content or summary) field treat tags as text
249 + elseif ($this->feed_type == ATOM and $this->incontent )
250 + {
251 + // if tags are inlined, then flatten
252 + $attrs_str = join(' ',
253 + array_map('map_attrs',
254 + array_keys($attrs),
255 + array_values($attrs) ) );
256 +
257 + $this->append_content( "<$element $attrs_str>" );
258 +
259 + array_unshift( $this->stack, $el );
260 + }
261 +
262 + // Atom support many links per containging element.
263 + // Magpie treats link elements of type rel='alternate'
264 + // as being equivalent to RSS's simple link element.
265 + //
266 + elseif ($this->feed_type == ATOM and $el == 'link' )
267 + {
268 + if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
269 + {
270 + $link_el = 'link';
271 + }
272 + else {
273 + $link_el = 'link_' . $attrs['rel'];
274 + }
275 +
276 + $this->append($link_el, $attrs['href']);
277 + }
278 +
279 + // set stack[0] to current element
280 + else {
281 + array_unshift($this->stack, $el);
282 + }
283 + }
284 +
285 +
286 +
287 + function feed_cdata ($p, $text) {
288 + if ($this->feed_type == ATOM and $this->incontent)
289 + {
290 + $this->append_content( $text );
291 + }
292 + else {
293 + $current_el = join('_', array_reverse($this->stack));
294 + $this->append($current_el, $text);
295 + }
296 + }
297 +
298 + function feed_end_element ($p, $el) {
299 + $el = strtolower($el);
300 +
301 + if ( $el == 'item' or $el == 'entry' )
302 + {
303 + $this->items[] = $this->current_item;
304 + $this->current_item = array();
305 + $this->initem = false;
306 +
307 + $this->current_category = 0;
308 + }
309 + elseif ($this->initem and ($el == 'category' or $el == 'dc:subject')) {
310 + $this->incategory = false;
311 + $this->current_category = $this->current_category + 1;
312 + array_shift( $this->stack );
313 + }
314 + elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
315 + {
316 + $this->intextinput = false;
317 + }
318 + elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
319 + {
320 + $this->inimage = false;
321 + }
322 + elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
323 + {
324 + $this->incontent = false;
325 + }
326 + elseif ($el == 'channel' or $el == 'feed' )
327 + {
328 + $this->inchannel = false;
329 + }
330 + elseif ($this->feed_type == ATOM and $this->incontent ) {
331 + // balance tags properly
332 + // note: i don't think this is actually neccessary
333 + if ( $this->stack[0] == $el )
334 + {
335 + $this->append_content("</$el>");
336 + }
337 + else {
338 + $this->append_content("<$el />");
339 + }
340 +
341 + array_shift( $this->stack );
342 + }
343 + else {
344 + array_shift( $this->stack );
345 + }
346 +
347 + $this->current_namespace = false;
348 + }
349 +
350 + function concat (&$str1, $str2="") {
351 + if (!isset($str1) ) {
352 + $str1="";
353 + }
354 + $str1 .= $str2;
355 + }
356 +
357 +
358 +
359 + function append_content($text) {
360 + if ( $this->initem ) {
361 + $this->concat( $this->current_item[ $this->incontent ], $text );
362 + }
363 + elseif ( $this->inchannel ) {
364 + $this->concat( $this->channel[ $this->incontent ], $text );
365 + }
366 + }
367 +
368 + // smart append - field and namespace aware
369 + function append($el, $text) {
370 + if (!$el) {
371 + return;
372 + }
373 + if ( $this->current_namespace )
374 + {
375 + if ( $this->incategory ) {
376 + $this->concat( $this->current_item['categories'][$this->current_category], $text );
377 + }
378 + elseif ( $this->initem ) {
379 + $this->concat(
380 + $this->current_item[ $this->current_namespace ][ $el ], $text );
381 + }
382 + elseif ($this->inchannel) {
383 + $this->concat(
384 + $this->channel[ $this->current_namespace][ $el ], $text );
385 + }
386 + elseif ($this->intextinput) {
387 + $this->concat(
388 + $this->textinput[ $this->current_namespace][ $el ], $text );
389 + }
390 + elseif ($this->inimage) {
391 + $this->concat(
392 + $this->image[ $this->current_namespace ][ $el ], $text );
393 + }
394 + }
395 + else {
396 + if ( $this->incategory ) {
397 + $this->concat( $this->current_item['categories'][$this->current_category], $text );
398 + }
399 + elseif ( $this->initem ) {
400 + $this->concat(
401 + $this->current_item[ $el ], $text);
402 + }
403 + elseif ($this->intextinput) {
404 + $this->concat(
405 + $this->textinput[ $el ], $text );
406 + }
407 + elseif ($this->inimage) {
408 + $this->concat(
409 + $this->image[ $el ], $text );
410 + }
411 + elseif ($this->inchannel) {
412 + $this->concat(
413 + $this->channel[ $el ], $text );
414 + }
415 +
416 + }
417 + }
418 +
419 + function normalize () {
420 + // if atom populate rss fields
421 + if ( $this->is_atom() ) {
422 + $this->channel['description'] = $this->channel['tagline'];
423 + for ( $i = 0; $i < count($this->items); $i++) {
424 + $item = $this->items[$i];
425 + if ( isset($item['summary']) )
426 + $item['description'] = $item['summary'];
427 + if ( isset($item['atom_content']))
428 + $item['content']['encoded'] = $item['atom_content'];
429 +
430 + $atom_date = (isset($item['issued']) ) ? $item['issued'] : $item['modified'];
431 + if ( $atom_date ) {
432 + $epoch = @parse_w3cdtf($item['modified']);
433 + if ($epoch and $epoch > 0) {
434 + $item['date_timestamp'] = $epoch;
435 + }
436 + }
437 +
438 + if ( is_array($item['categories']) ) {
439 + $item['category'] = $item['categories'][0];
440 + $item['dc']['subjects'] = $item['categories'];
441 + $item['dc']['subject'] = $item['category'];
442 + }
443 +
444 + $this->items[$i] = $item;
445 + }
446 + }
447 + elseif ( $this->is_rss() ) {
448 + $this->channel['tagline'] = $this->channel['description'];
449 + for ( $i = 0; $i < count($this->items); $i++) {
450 + $item = $this->items[$i];
451 + if ( isset($item['description']))
452 + $item['summary'] = $item['description'];
453 + if ( isset($item['content']['encoded'] ) )
454 + $item['atom_content'] = $item['content']['encoded'];
455 +
456 + if ( $this->is_rss() == '1.0' and isset($item['dc']['date']) ) {
457 + $epoch = @parse_w3cdtf($item['dc']['date']);
458 + if ($epoch and $epoch > 0) {
459 + $item['date_timestamp'] = $epoch;
460 + }
461 + }
462 + elseif ( isset($item['pubdate']) ) {
463 + $epoch = @strtotime($item['pubdate']);
464 + if ($epoch > 0) {
465 + $item['date_timestamp'] = $epoch;
466 + }
467 + }
468 +
469 + if ( is_array($item['categories']) ) {
470 + $item['category'] = $item['categories'][0];
471 + $item['dc']['subjects'] = $item['categories'];
472 + $item['dc']['subject'] = $item['category'];
473 + }
474 +
475 + $this->items[$i] = $item;
476 + }
477 + }
478 + }
479 +
480 +
481 + function is_rss () {
482 + if ( $this->feed_type == RSS ) {
483 + return $this->feed_version;
484 + }
485 + else {
486 + return false;
487 + }
488 + }
489 +
490 + function is_atom() {
491 + if ( $this->feed_type == ATOM ) {
492 + return $this->feed_version;
493 + }
494 + else {
495 + return false;
496 + }
497 + }
498 +
499 + /**
500 + * return XML parser, and possibly re-encoded source
501 + *
502 + */
503 + function create_parser($source, $out_enc, $in_enc, $detect) {
504 + if ( substr(phpversion(),0,1) == 5) {
505 + $parser = $this->php5_create_parser($in_enc, $detect);
506 + }
507 + else {
508 + list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect);
509 + }
510 + if ($out_enc) {
511 + $this->encoding = $out_enc;
512 + xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc);
513 + }
514 +
515 + return array($parser, $source);
516 + }
517 +
518 + /**
519 + * Instantiate an XML parser under PHP5
520 + *
521 + * PHP5 will do a fine job of detecting input encoding
522 + * if passed an empty string as the encoding.
523 + *
524 + * All hail libxml2!
525 + *
526 + */
527 + function php5_create_parser($in_enc, $detect) {
528 + // by default php5 does a fine job of detecting input encodings
529 + if(!$detect && $in_enc) {
530 + return xml_parser_create($in_enc);
531 + }
532 + else {
533 + return xml_parser_create('');
534 + }
535 + }
536 +
537 + /**
538 + * Instaniate an XML parser under PHP4
539 + *
540 + * Unfortunately PHP4's support for character encodings
541 + * and especially XML and character encodings sucks. As
542 + * long as the documents you parse only contain characters
543 + * from the ISO-8859-1 character set (a superset of ASCII,
544 + * and a subset of UTF-8) you're fine. However once you
545 + * step out of that comfy little world things get mad, bad,
546 + * and dangerous to know.
547 + *
548 + * The following code is based on SJM's work with FoF
549 + * @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
550 + *
551 + */
552 + function php4_create_parser($source, $in_enc, $detect) {
553 + if ( !$detect ) {
554 + return array(xml_parser_create($in_enc), $source);
555 + }
556 +
557 + if (!$in_enc) {
558 + if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $source, $m)) {
559 + $in_enc = strtoupper($m[1]);
560 + $this->source_encoding = $in_enc;
561 + }
562 + else {
563 + $in_enc = 'UTF-8';
564 + }
565 + }
566 +
567 + if ($this->known_encoding($in_enc)) {
568 + return array(xml_parser_create($in_enc), $source);
569 + }
570 +
571 + // the dectected encoding is not one of the simple encodings PHP knows
572 +
573 + // attempt to use the iconv extension to
574 + // cast the XML to a known encoding
575 + // @see http://php.net/iconv
576 +
577 + if (function_exists('iconv')) {
578 + $encoded_source = iconv($in_enc,'UTF-8', $source);
579 + if ($encoded_source) {
580 + return array(xml_parser_create('UTF-8'), $encoded_source);
581 + }
582 + }
583 +
584 + // iconv didn't work, try mb_convert_encoding
585 + // @see http://php.net/mbstring
586 + if(function_exists('mb_convert_encoding')) {
587 + $encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc );
588 + if ($encoded_source) {
589 + return array(xml_parser_create('UTF-8'), $encoded_source);
590 + }
591 + }
592 +
593 + // else
594 + $this->error("Feed is in an unsupported character encoding. ($in_enc) " .
595 + "You may see strange artifacts, and mangled characters.",
596 + E_USER_NOTICE);
597 +
598 + return array(xml_parser_create(), $source);
599 + }
600 +
601 + function known_encoding($enc) {
602 + $enc = strtoupper($enc);
603 + if ( in_array($enc, $this->_KNOWN_ENCODINGS) ) {
604 + return $enc;
605 + }
606 + else {
607 + return false;
608 + }
609 + }
610 +
611 + function error ($errormsg, $lvl=E_USER_WARNING) {
612 + // append PHP's error message if track_errors enabled
613 + if ( $php_errormsg ) {
614 + $errormsg .= " ($php_errormsg)";
615 + }
616 + if ( MAGPIE_DEBUG ) {
617 + trigger_error( $errormsg, $lvl);
618 + }
619 + else {
620 + error_log( $errormsg, 0);
621 + }
622 +
623 + $notices = E_USER_NOTICE|E_NOTICE;
624 + if ( $lvl&$notices ) {
625 + $this->WARNING = $errormsg;
626 + } else {
627 + $this->ERROR = $errormsg;
628 + }
629 + }
630 +} // end class RSS
631 +
632 +function map_attrs($k, $v) {
633 + return "$k=\"$v\"";
634 +}
635 +# ---- cut here ----
636 +
637 +require_once( dirname(__FILE__) . '/class-snoopy.php');
638 +
639 +# -- UPDATED from rss_fetch.inc: fetch_rss, error, debug, magpie_error
640 +# --- cut here ---
641 +function fetch_rss ($url) {
642 + // initialize constants
643 + init();
644 +
645 + if ( !isset($url) ) {
646 + error("fetch_rss called without a url");
647 + return false;
648 + }
649 +
650 + // if cache is disabled
651 + if ( !MAGPIE_CACHE_ON ) {
652 + // fetch file, and parse it
653 + $resp = _fetch_remote_file( $url );
654 + if ( is_success( $resp->status ) ) {
655 + return _response_to_rss( $resp );
656 + }
657 + else {
658 + error("Failed to fetch $url and cache is off");
659 + return false;
660 + }
661 + }
662 + // else cache is ON
663 + else {
664 + // Flow
665 + // 1. check cache
666 + // 2. if there is a hit, make sure its fresh
667 + // 3. if cached obj fails freshness check, fetch remote
668 + // 4. if remote fails, return stale object, or error
669 +
670 + $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
671 +
672 + if (MAGPIE_DEBUG and $cache->ERROR) {
673 + debug($cache->ERROR, E_USER_WARNING);
674 + }
675 +
676 +
677 + $cache_status = 0; // response of check_cache
678 + $request_headers = array(); // HTTP headers to send with fetch
679 + $rss = 0; // parsed RSS object
680 + $errormsg = 0; // errors, if any
681 +
682 + // store parsed XML by desired output encoding
683 + // as character munging happens at parse time
684 + $cache_key = $url . MAGPIE_OUTPUT_ENCODING;
685 +
686 + if (!$cache->ERROR) {
687 + // return cache HIT, MISS, or STALE
688 + $cache_status = $cache->check_cache( $cache_key);
689 + }
690 +
691 + // if object cached, and cache is fresh, return cached obj
692 + if ( $cache_status == 'HIT' ) {
693 + $rss = $cache->get( $cache_key );
694 + if ( isset($rss) and $rss ) {
695 + // should be cache age
696 + $rss->from_cache = 1;
697 + if ( MAGPIE_DEBUG > 1) {
698 + debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
699 + }
700 + return $rss;
701 + }
702 + }
703 +
704 + // else attempt a conditional get
705 +
706 + // setup headers
707 + if ( $cache_status == 'STALE' ) {
708 + $rss = $cache->get( $cache_key );
709 + if ( $rss and $rss->etag and $rss->last_modified ) {
710 + $request_headers['If-None-Match'] = $rss->etag;
711 + $request_headers['If-Last-Modified'] = $rss->last_modified;
712 + }
713 + }
714 +
715 + $resp = _fetch_remote_file( $url, $request_headers );
716 +
717 + if (isset($resp) and $resp) {
718 + if ($resp->status == '304' ) {
719 + // we have the most current copy
720 + if ( MAGPIE_DEBUG > 1) {
721 + debug("Got 304 for $url");
722 + }
723 + // reset cache on 304 (at minutillo insistent prodding)
724 + $cache->set($cache_key, $rss);
725 + return $rss;
726 + }
727 + elseif ( is_success( $resp->status ) ) {
728 + $rss = _response_to_rss( $resp );
729 + if ( $rss ) {
730 + if (MAGPIE_DEBUG > 1) {
731 + debug("Fetch successful");
732 + }
733 + // add object to cache
734 + $cache->set( $cache_key, $rss );
735 + return $rss;
736 + }
737 + }
738 + else {
739 + $errormsg = "Failed to fetch $url ";
740 + if ( $resp->status == '-100' ) {
741 + $errormsg .= "(Request timed out after " . MAGPIE_FETCH_TIME_OUT . " seconds)";
742 + }
743 + elseif ( $resp->error ) {
744 + # compensate for Snoopy's annoying habbit to tacking
745 + # on '\n'
746 + $http_error = substr($resp->error, 0, -2);
747 + $errormsg .= "(HTTP Error: $http_error)";
748 + }
749 + else {
750 + $errormsg .= "(HTTP Response: " . $resp->response_code .')';
751 + }
752 + }
753 + }
754 + else {
755 + $errormsg = "Unable to retrieve RSS file for unknown reasons.";
756 + }
757 +
758 + // else fetch failed
759 +
760 + // attempt to return cached object
761 + if ($rss) {
762 + if ( MAGPIE_DEBUG ) {
763 + debug("Returning STALE object for $url");
764 + }
765 + return $rss;
766 + }
767 +
768 + // else we totally failed
769 + error( $errormsg );
770 +
771 + return false;
772 +
773 + } // end if ( !MAGPIE_CACHE_ON ) {
774 +} // end fetch_rss()
775 +
776 +/*=======================================================================*\
777 + Function: error
778 + Purpose: set MAGPIE_ERROR, and trigger error
779 +\*=======================================================================*/
780 +
781 +function error ($errormsg, $lvl=E_USER_WARNING) {
782 + global $MAGPIE_ERROR;
783 +
784 + // append PHP's error message if track_errors enabled
785 + if ( isset($php_errormsg) ) {
786 + $errormsg .= " ($php_errormsg)";
787 + }
788 + if ( $errormsg ) {
789 + $errormsg = "MagpieRSS: $errormsg";
790 + $MAGPIE_ERROR = $errormsg;
791 + trigger_error( $errormsg, $lvl);
792 + }
793 +}
794 +
795 +function debug ($debugmsg, $lvl=E_USER_NOTICE) {
796 + trigger_error("MagpieRSS [debug] $debugmsg", $lvl);
797 +}
798 +
799 +/*=======================================================================*\
800 + Function: magpie_error
801 + Purpose: accessor for the magpie error variable
802 +\*=======================================================================*/
803 +function magpie_error ($errormsg="") {
804 + global $MAGPIE_ERROR;
805 +
806 + if ( isset($errormsg) and $errormsg ) {
807 + $MAGPIE_ERROR = $errormsg;
808 + }
809 +
810 + return $MAGPIE_ERROR;
811 +}
812 +# --- cut here ---
813 +
814 +# UPDATED FROM: rss_fetch.inc: _fetch_remote_file, _response_to_rss, init
815 +# --- cut here ---
816 +/*=======================================================================*\
817 + Function: _fetch_remote_file
818 + Purpose: retrieve an arbitrary remote file
819 + Input: url of the remote file
820 + headers to send along with the request (optional)
821 + Output: an HTTP response object (see Snoopy.class.inc)
822 +\*=======================================================================*/
823 +function _fetch_remote_file ($url, $headers = "" ) {
824 + // Snoopy is an HTTP client in PHP
825 + $client = new Snoopy();
826 + $client->agent = MAGPIE_USER_AGENT;
827 + $client->read_timeout = MAGPIE_FETCH_TIME_OUT;
828 + $client->use_gzip = MAGPIE_USE_GZIP;
829 + if (is_array($headers) ) {
830 + $client->rawheaders = $headers;
831 + }
832 +
833 + @$client->fetch($url);
834 + return $client;
835 +
836 +}
837 +
838 +/*=======================================================================*\
839 + Function: _response_to_rss
840 + Purpose: parse an HTTP response object into an RSS object
841 + Input: an HTTP response object (see Snoopy)
842 + Output: parsed RSS object (see rss_parse)
843 +\*=======================================================================*/
844 +function _response_to_rss ($resp) {
845 + $rss = new MagpieRSS( $resp->results, MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
846 +
847 + // if RSS parsed successfully
848 + if ( $rss and !$rss->ERROR) {
849 +
850 + // find Etag, and Last-Modified
851 + foreach($resp->headers as $h) {
852 + // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
853 + if (strpos($h, ": ")) {
854 + list($field, $val) = explode(": ", $h, 2);
855 + }
856 + else {
857 + $field = $h;
858 + $val = "";
859 + }
860 +
861 + if ( $field == 'ETag' ) {
862 + $rss->etag = $val;
863 + }
864 +
865 + if ( $field == 'Last-Modified' ) {
866 + $rss->last_modified = $val;
867 + }
868 + }
869 +
870 + return $rss;
871 + } // else construct error message
872 + else {
873 + $errormsg = "Failed to parse RSS file.";
874 +
875 + if ($rss) {
876 + $errormsg .= " (" . $rss->ERROR . ")";
877 + }
878 + error($errormsg);
879 +
880 + return false;
881 + } // end if ($rss and !$rss->error)
882 +}
883 +
884 +/*=======================================================================*\
885 + Function: init
886 + Purpose: setup constants with default values
887 + check for user overrides
888 +\*=======================================================================*/
889 +function init () {
890 + if ( defined('MAGPIE_INITALIZED') ) {
891 + return;
892 + }
893 + else {
894 + define('MAGPIE_INITALIZED', true);
895 + }
896 +
897 + if ( !defined('MAGPIE_CACHE_ON') ) {
898 + define('MAGPIE_CACHE_ON', true);
899 + }
900 +
901 + if ( !defined('MAGPIE_CACHE_DIR') ) {
902 + define('MAGPIE_CACHE_DIR', './cache');
903 + }
904 +
905 + if ( !defined('MAGPIE_CACHE_AGE') ) {
906 + define('MAGPIE_CACHE_AGE', 60*60); // one hour
907 + }
908 +
909 + if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
910 + define('MAGPIE_CACHE_FRESH_ONLY', false);
911 + }
912 +
913 + if ( !defined('MAGPIE_OUTPUT_ENCODING') ) {
914 + # WORDPRESS MODIFICATION: use whatever charset the blog is using
915 + # --- cut here ---
916 + $wp_encoding = get_settings('blog_charset');
917 + define('MAGPIE_OUTPUT_ENCODING', ($wp_encoding?$wp_encoding:'ISO-8859-1'));
918 + # --- cut here ---
919 + }
920 +
921 + if ( !defined('MAGPIE_INPUT_ENCODING') ) {
922 + define('MAGPIE_INPUT_ENCODING', null);
923 + }
924 +
925 + if ( !defined('MAGPIE_DETECT_ENCODING') ) {
926 + define('MAGPIE_DETECT_ENCODING', true);
927 + }
928 +
929 + if ( !defined('MAGPIE_DEBUG') ) {
930 + define('MAGPIE_DEBUG', 0);
931 + }
932 +
933 + if ( !defined('MAGPIE_USER_AGENT') ) {
934 + # WORDPRESS MODIFICATION: send WordPress as user-agent
935 + # --- cut here ---
936 + $ua = 'WordPress/'. $wp_version . ' (+http://www.wordpress.org';
937 + # --- cut here ---
938 +
939 + if ( MAGPIE_CACHE_ON ) {
940 + $ua = $ua . ')';
941 + }
942 + else {
943 + $ua = $ua . '; No cache)';
944 + }
945 +
946 + define('MAGPIE_USER_AGENT', $ua);
947 + }
948 +
949 + if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
950 + define('MAGPIE_FETCH_TIME_OUT', 5); // 5 second timeout
951 + }
952 +
953 + // use gzip encoding to fetch rss files if supported?
954 + if ( !defined('MAGPIE_USE_GZIP') ) {
955 + define('MAGPIE_USE_GZIP', true);
956 + }
957 +}
958 +# --- cut here ---
959 +
960 +function is_info ($sc) {
961 + return $sc >= 100 && $sc < 200;
962 +}
963 +
964 +function is_success ($sc) {
965 + return $sc >= 200 && $sc < 300;
966 +}
967 +
968 +function is_redirect ($sc) {
969 + return $sc >= 300 && $sc < 400;
970 +}
971 +
972 +function is_error ($sc) {
973 + return $sc >= 400 && $sc < 600;
974 +}
975 +
976 +function is_client_error ($sc) {
977 + return $sc >= 400 && $sc < 500;
978 +}
979 +
980 +function is_server_error ($sc) {
981 + return $sc >= 500 && $sc < 600;
982 +}
983 +
984 +# WORDPRESS-SPECIFIC: class RSSCache (modified to use WP database)
985 +# --- cut here ---
986 +class RSSCache {
987 + var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
988 + var $MAX_AGE = 43200; // when are files stale, default twelve hours
989 + var $ERROR = ''; // accumulate error messages
990 +
991 + function RSSCache ($base='', $age='') {
992 + if ( $base ) {
993 + $this->BASE_CACHE = $base;
994 + }
995 + if ( $age ) {
996 + $this->MAX_AGE = $age;
997 + }
998 +
999 + }
1000 +
1001 +/*=======================================================================*\
1002 + Function: set
1003 + Purpose: add an item to the cache, keyed on url
1004 + Input: url from wich the rss file was fetched
1005 + Output: true on sucess
1006 +\*=======================================================================*/
1007 + function set ($url, $rss) {
1008 + global $wpdb;
1009 + $cache_option = 'rss_' . $this->file_name( $url );
1010 + $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
1011 +
1012 + if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") )
1013 + add_option($cache_option, '', '', 'no');
1014 + if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") )
1015 + add_option($cache_timestamp, '', '', 'no');
1016 +
1017 + update_option($cache_option, $rss);
1018 + update_option($cache_timestamp, time() );
1019 +
1020 + return $cache_option;
1021 + }
1022 +
1023 +/*=======================================================================*\
1024 + Function: get
1025 + Purpose: fetch an item from the cache
1026 + Input: url from wich the rss file was fetched
1027 + Output: cached object on HIT, false on MISS
1028 +\*=======================================================================*/
1029 + function get ($url) {
1030 + $this->ERROR = "";
1031 + $cache_option = 'rss_' . $this->file_name( $url );
1032 +
1033 + if ( ! get_option( $cache_option ) ) {
1034 + $this->debug(
1035 + "Cache doesn't contain: $url (cache option: $cache_option)"
1036 + );
1037 + return 0;
1038 + }
1039 +
1040 + $rss = get_option( $cache_option );
1041 +
1042 + return $rss;
1043 + }
1044 +
1045 +/*=======================================================================*\
1046 + Function: check_cache
1047 + Purpose: check a url for membership in the cache
1048 + and whether the object is older then MAX_AGE (ie. STALE)
1049 + Input: url from wich the rss file was fetched
1050 + Output: cached object on HIT, false on MISS
1051 +\*=======================================================================*/
1052 + function check_cache ( $url ) {
1053 + $this->ERROR = "";
1054 + $cache_option = $this->file_name( $url );
1055 + $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
1056 +
1057 + if ( $mtime = get_option($cache_timestamp) ) {
1058 + // find how long ago the file was added to the cache
1059 + // and whether that is longer then MAX_AGE
1060 + $age = time() - $mtime;
1061 + if ( $this->MAX_AGE > $age ) {
1062 + // object exists and is current
1063 + return 'HIT';
1064 + }
1065 + else {
1066 + // object exists but is old
1067 + return 'STALE';
1068 + }
1069 + }
1070 + else {
1071 + // object does not exist
1072 + return 'MISS';
1073 + }
1074 + }
1075 +
1076 +/*=======================================================================*\
1077 + Function: serialize
1078 +\*=======================================================================*/
1079 + function serialize ( $rss ) {
1080 + return serialize( $rss );
1081 + }
1082 +
1083 +/*=======================================================================*\
1084 + Function: unserialize
1085 +\*=======================================================================*/
1086 + function unserialize ( $data ) {
1087 + return unserialize( $data );
1088 + }
1089 +
1090 +/*=======================================================================*\
1091 + Function: file_name
1092 + Purpose: map url to location in cache
1093 + Input: url from wich the rss file was fetched
1094 + Output: a file name
1095 +\*=======================================================================*/
1096 + function file_name ($url) {
1097 + return md5( $url );
1098 + }
1099 +
1100 +/*=======================================================================*\
1101 + Function: error
1102 + Purpose: register error
1103 +\*=======================================================================*/
1104 + function error ($errormsg, $lvl=E_USER_WARNING) {
1105 + // append PHP's error message if track_errors enabled
1106 + if ( isset($php_errormsg) ) {
1107 + $errormsg .= " ($php_errormsg)";
1108 + }
1109 + $this->ERROR = $errormsg;
1110 + if ( MAGPIE_DEBUG ) {
1111 + trigger_error( $errormsg, $lvl);
1112 + }
1113 + else {
1114 + error_log( $errormsg, 0);
1115 + }
1116 + }
1117 + function debug ($debugmsg, $lvl=E_USER_NOTICE) {
1118 + if ( MAGPIE_DEBUG ) {
1119 + $this->error("MagpieRSS [debug] $debugmsg", $lvl);
1120 + }
1121 + }
1122 +}
1123 +# --- cut here ---
1124 +
1125 +function parse_w3cdtf ( $date_str ) {
1126 +
1127 + # regex to match wc3dtf
1128 + $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
1129 +
1130 + if ( preg_match( $pat, $date_str, $match ) ) {
1131 + list( $year, $month, $day, $hours, $minutes, $seconds) =
1132 + array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
1133 +
1134 + # calc epoch for current date assuming GMT
1135 + $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
1136 +
1137 + $offset = 0;
1138 + if ( $match[10] == 'Z' ) {
1139 + # zulu time, aka GMT
1140 + }
1141 + else {
1142 + list( $tz_mod, $tz_hour, $tz_min ) =
1143 + array( $match[8], $match[9], $match[10]);
1144 +
1145 + # zero out the variables
1146 + if ( ! $tz_hour ) { $tz_hour = 0; }
1147 + if ( ! $tz_min ) { $tz_min = 0; }
1148 +
1149 + $offset_secs = (($tz_hour*60)+$tz_min)*60;
1150 +
1151 + # is timezone ahead of GMT? then subtract offset
1152 + #
1153 + if ( $tz_mod == '+' ) {
1154 + $offset_secs = $offset_secs * -1;
1155 + }
1156 +
1157 + $offset = $offset_secs;
1158 + }
1159 + $epoch = $epoch + $offset;
1160 + return $epoch;
1161 + }
1162 + else {
1163 + return -1;
1164 + }
1165 +}
1166 +
1167 +# WORDPRESS-SPECIFIC: wp_rss (), get_rss ()
1168 +# --- cut here ---
1169 +function wp_rss ($url, $num) {
1170 + //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned.
1171 + $num_items = $num;
1172 + $rss = fetch_rss($url);
1173 + if ( $rss ) {
1174 + echo "<ul>";
1175 + $rss->items = array_slice($rss->items, 0, $num_items);
1176 + foreach ($rss->items as $item ) {
1177 + echo "<li>\n";
1178 + echo "<a href='$item[link]' title='$item[description]'>";
1179 + echo htmlentities($item['title']);
1180 + echo "</a><br />\n";
1181 + echo "</li>\n";
1182 + }
1183 + echo "</ul>";
1184 + }
1185 + else {
1186 + echo "an error has occured the feed is probably down, try again later.";
1187 + }
1188 +}
1189 +
1190 +function get_rss ($uri, $num = 5) { // Like get posts, but for RSS
1191 + $rss = fetch_rss($url);
1192 + if ( $rss ) {
1193 + $rss->items = array_slice($rss->items, 0, $num_items);
1194 + foreach ($rss->items as $item ) {
1195 + echo "<li>\n";
1196 + echo "<a href='$item[link]' title='$item[description]'>";
1197 + echo htmlentities($item['title']);
1198 + echo "</a><br />\n";
1199 + echo "</li>\n";
1200 + }
1201 + return $posts;
1202 + } else {
1203 + return false;
1204 + }
1205 +}
1206 +# --- cut here ---
1207 +?>