home *** CD-ROM | disk | FTP | other *** search
/ MacAddict 108 / MacAddict108.iso / Software / Internet & Communication / WordPress 1.5.1.dmg / wordpress / wp-includes / rss-functions.php < prev    next >
Encoding:
PHP Script  |  2005-02-01  |  20.7 KB  |  850 lines

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