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