home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress2 / wp-includes / rss.php < prev    next >
Encoding:
PHP Script  |  2016-10-30  |  22.7 KB  |  955 lines

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