summaryrefslogtreecommitdiffstats
path: root/wp-includes/rss.php
diff options
context:
space:
mode:
authordonncha <donncha@7be80a69-a1ef-0310-a953-fb0f7c49ff36>2006-06-27 11:03:10 +0000
committerdonncha <donncha@7be80a69-a1ef-0310-a953-fb0f7c49ff36>2006-06-27 11:03:10 +0000
commit8db1e81228b0e295199f6e3ee8a99d223c48f8c2 (patch)
tree131d8b23b9e939a9c23f24ec650b343efa918c5c /wp-includes/rss.php
parent13aff523358403d08dc7fcb0d844a2e6c12df41a (diff)
downloadwordpress-mu-8db1e81228b0e295199f6e3ee8a99d223c48f8c2.tar.gz
wordpress-mu-8db1e81228b0e295199f6e3ee8a99d223c48f8c2.tar.xz
wordpress-mu-8db1e81228b0e295199f6e3ee8a99d223c48f8c2.zip
WP Merge
git-svn-id: http://svn.automattic.com/wordpress-mu/trunk@599 7be80a69-a1ef-0310-a953-fb0f7c49ff36
Diffstat (limited to 'wp-includes/rss.php')
-rw-r--r--wp-includes/rss.php1619
1 files changed, 975 insertions, 644 deletions
diff --git a/wp-includes/rss.php b/wp-includes/rss.php
index 2f99d3f..e0c1d84 100644
--- a/wp-includes/rss.php
+++ b/wp-includes/rss.php
@@ -1,793 +1,1159 @@
<?php
-/*
- * Project: MagpieRSS: a simple RSS integration tool
- * File: A compiled file for RSS syndication
- * Author: Kellan Elliott-McCrea <kellan@protest.net>
- * Version: 0.51
- * License: GPL
+
+/* Much of the code in this file was taken from MagpieRSS
+ * by Kellan Elliott-McCrea <kellan@protest.net> which is
+ * released under the GPL license.
+ *
+ * The lastest version of MagpieRSS can be obtained from:
+ * http://magpierss.sourceforge.net
*/
-define('RSS', 'RSS');
-define('ATOM', 'Atom');
-define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version);
+function fetch_rss($url) {
+ $url = apply_filters('fetch_rss_url', $url);
-class MagpieRSS {
- var $parser;
- var $current_item = array(); // item currently being parsed
- var $items = array(); // collection of parsed items
- var $channel = array(); // hash of channel fields
- var $textinput = array();
- var $image = array();
- var $feed_type;
- var $feed_version;
+ $feeder = new WP_Feeder();
- // parser variables
- var $stack = array(); // parser stack
- var $inchannel = false;
- var $initem = false;
- var $incontent = false; // if in Atom <content mode="xml"> field
- var $intextinput = false;
- var $inimage = false;
- var $current_field = '';
- var $current_namespace = false;
+ $feed = $feeder->get($url);
- //var $ERROR = "";
+ $magpie = $feed->to_magpie();
- var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
+ return $magpie;
+}
- function MagpieRSS ($source) {
+class WP_Feeder {
+ var $url, $http_client, $last_fetch, $wp_object_cache, $cache;
+ var $redirects = 0;
+ var $max_redirects = 3;
+ var $cache_redirects = true;
+
+ function WP_Feeder () {
+ global $wp_object_cache;
+
+ if ( $wp_object_cache->cache_enabled ) {
+ $this->wp_object_cache = true;
+ } else {
+ $this->wp_object_cache = false;
+ $this->cache = new RSSCache();
+ }
+ }
- # if PHP xml isn't compiled in, die
- #
- if ( !function_exists('xml_parser_create') )
- trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" );
+ function get ($url) {
+ $cached = false;
- $parser = @xml_parser_create();
+ $feed = $this->cache_get($url);
- if ( !is_resource($parser) )
- trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php");
+ if ( is_object($feed) ) {
+ $cached = true;
+ } else {
+ unset($feed);
+ $this->fetch($url);
- $this->parser = $parser;
-
- # pass in parser, and a reference to this object
- # setup handlers
- #
- xml_set_object( $this->parser, $this );
- xml_set_element_handler($this->parser,
- 'feed_start_element', 'feed_end_element' );
+ $feed = new WP_Feed($this->http_client);
+ }
- xml_set_character_data_handler( $this->parser, 'feed_cdata' );
+ // Handle redirects
+ if ( $feed->status >= 300 && $feed->status < 400 && $this->redirects < $this->max_redirects ) {
+ ++$this->redirects;
- $status = xml_parse( $this->parser, $source );
+ if ( $this->cache_redirects && !$cached )
+ $this->cache_set($url, $feed);
- if (! $status ) {
- $errorcode = xml_get_error_code( $this->parser );
- if ( $errorcode != XML_ERROR_NONE ) {
- $xml_error = xml_error_string( $errorcode );
- $error_line = xml_get_current_line_number($this->parser);
- $error_col = xml_get_current_column_number($this->parser);
- $errormsg = "$xml_error at line $error_line, column $error_col";
-
- $this->error( $errormsg );
- }
+ return $this->get($feed->redirect_location);
}
- xml_parser_free( $this->parser );
+ if ( !$cached )
+ $this->cache_set($url, $feed);
- $this->normalize();
+ return $feed;
}
- function feed_start_element($p, $element, &$attrs) {
- $el = $element = strtolower($element);
- $attrs = array_change_key_case($attrs, CASE_LOWER);
+ function fetch ($url) {
+ $this->last_fetch = $url;
+ $parts = parse_url($url);
+ $url = ($parts['path'] ? $parts['path'] : '/') . ($parts['query'] ? '?'.$parts['query'] : '');
+ $this->http_client = new HttpClient('', 80);
+ $this->http_client->handle_redirects = false;
+ $this->http_client->host = $parts['host'];
+ $this->http_client->port = $parts['port'] ? $parts['port'] : 80;
+ $this->http_client->user_agent = 'WordPress ' . $GLOBALS['wp_version'] . ' Feed Client';
+ $this->http_client->get($url);
+ }
+
+ function cache_get ($url) {
+ if ( $this->wp_object_cache )
+ return unserialize(wp_cache_get($url, 'rss'));
- // check for a namespace, and split if found
- $ns = false;
- if ( strpos( $element, ':' ) ) {
- list($ns, $el) = split( ':', $element, 2);
- }
- if ( $ns and $ns != 'rdf' ) {
- $this->current_namespace = $ns;
- }
+ return $this->cache->get($url);
+ }
+
+ function cache_set ($url, $object) {
+ if ( $this->wp_object_cache )
+ return wp_cache_set($url, serialize($object), 'rss', 3600);
+
+ return $this->cache->set($url, $object);
+ }
+}
- # if feed type isn't set, then this is first element of feed
- # identify feed from root element
- #
- if (!isset($this->feed_type) ) {
- if ( $el == 'rdf' ) {
- $this->feed_type = RSS;
- $this->feed_version = '1.0';
- }
- elseif ( $el == 'rss' ) {
- $this->feed_type = RSS;
- $this->feed_version = $attrs['version'];
- }
- elseif ( $el == 'feed' ) {
- $this->feed_type = ATOM;
- $this->feed_version = $attrs['version'];
- $this->inchannel = true;
- }
- return;
- }
+class RSSCache {
+ var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
+ var $MAX_AGE = 43200; // when are files stale, default twelve hours
+ var $ERROR = ''; // accumulate error messages
- if ( $el == 'channel' )
- {
- $this->inchannel = true;
+ function RSSCache ($base='', $age='') {
+ if ( $base ) {
+ $this->BASE_CACHE = $base;
}
- elseif ($el == 'item' or $el == 'entry' )
- {
- $this->initem = true;
- if ( isset($attrs['rdf:about']) ) {
- $this->current_item['about'] = $attrs['rdf:about'];
- }
+ if ( $age ) {
+ $this->MAX_AGE = $age;
}
- // if we're in the default namespace of an RSS feed,
- // record textinput or image fields
- elseif (
- $this->feed_type == RSS and
- $this->current_namespace == '' and
- $el == 'textinput' )
- {
- $this->intextinput = true;
- }
+ }
- elseif (
- $this->feed_type == RSS and
- $this->current_namespace == '' and
- $el == 'image' )
- {
- $this->inimage = true;
- }
+ function set ($url, $rss) {
+ global $wpdb;
+ $cache_option = 'rss_' . $this->file_name( $url );
+ $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
- # handle atom content constructs
- elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
- {
- // avoid clashing w/ RSS mod_content
- if ($el == 'content' ) {
- $el = 'atom_content';
- }
+ if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") )
+ add_option($cache_option, '', '', 'no');
+ if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") )
+ add_option($cache_timestamp, '', '', 'no');
+
+ update_option($cache_option, $rss);
+ update_option($cache_timestamp, time() );
- $this->incontent = $el;
+ return $cache_option;
+ }
+ function get ($url) {
+ $this->ERROR = "";
+ $cache_option = 'rss_' . $this->file_name( $url );
+ if ( ! get_option( $cache_option ) ) {
+ $this->debug(
+ "Cache doesn't contain: $url (cache option: $cache_option)"
+ );
+ return 0;
}
- // if inside an Atom content construct (e.g. content or summary) field treat tags as text
- elseif ($this->feed_type == ATOM and $this->incontent )
- {
- // if tags are inlined, then flatten
- $attrs_str = join(' ',
- array_map('map_attrs',
- array_keys($attrs),
- array_values($attrs) ) );
+ $rss = get_option( $cache_option );
- $this->append_content( "<$element $attrs_str>" );
+ return $rss;
+ }
- array_unshift( $this->stack, $el );
- }
+ function check_cache ( $url ) {
+ $this->ERROR = "";
+ $cache_option = $this->file_name( $url );
+ $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
- // Atom support many links per containging element.
- // Magpie treats link elements of type rel='alternate'
- // as being equivalent to RSS's simple link element.
- //
- elseif ($this->feed_type == ATOM and $el == 'link' )
- {
- if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
- {
- $link_el = 'link';
+ if ( $mtime = get_option($cache_timestamp) ) {
+ // find how long ago the file was added to the cache
+ // and whether that is longer then MAX_AGE
+ $age = time() - $mtime;
+ if ( $this->MAX_AGE > $age ) {
+ // object exists and is current
+ return 'HIT';
}
else {
- $link_el = 'link_' . $attrs['rel'];
+ // object exists but is old
+ return 'STALE';
}
-
- $this->append($link_el, $attrs['href']);
}
- // set stack[0] to current element
else {
- array_unshift($this->stack, $el);
+ // object does not exist
+ return 'MISS';
}
}
+ function serialize ( $rss ) {
+ return serialize( $rss );
+ }
+ function unserialize ( $data ) {
+ return unserialize( $data );
+ }
- function feed_cdata ($p, $text) {
+ function file_name ($url) {
+ return md5( $url );
+ }
- if ($this->feed_type == ATOM and $this->incontent)
- {
- $this->append_content( $text );
+ function error ($errormsg, $lvl=E_USER_WARNING) {
+ // append PHP's error message if track_errors enabled
+ if ( isset($php_errormsg) ) {
+ $errormsg .= " ($php_errormsg)";
+ }
+ $this->ERROR = $errormsg;
+ if ( MAGPIE_DEBUG ) {
+ trigger_error( $errormsg, $lvl);
}
else {
- $current_el = join('_', array_reverse($this->stack));
- $this->append($current_el, $text);
+ error_log( $errormsg, 0);
+ }
+ }
+ function debug ($debugmsg, $lvl=E_USER_NOTICE) {
+ if ( MAGPIE_DEBUG ) {
+ $this->error("MagpieRSS [debug] $debugmsg", $lvl);
}
}
+}
- function feed_end_element ($p, $el) {
- $el = strtolower($el);
+class WP_Feed {
+ var $status;
+ var $raw_xml;
+ var $last_updated;
+ var $tree;
+ var $items;
+ var $children;
- if ( $el == 'item' or $el == 'entry' )
- {
- $this->items[] = $this->current_item;
- $this->current_item = array();
- $this->initem = false;
- }
- elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
- {
- $this->intextinput = false;
- }
- elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
- {
- $this->inimage = false;
+ var $parser;
+ var $feed_type;
+ var $feed_version;
+ var $stack = array();
+
+ function WP_Feed ($source)
+ {
+ # if PHP xml isn't compiled in, die
+ #
+ if (!function_exists('xml_parser_create')) {
+ $this->error( "Failed to load PHP's XML Extension. " .
+ "http://www.php.net/manual/en/ref.xml.php",
+ E_USER_ERROR );
}
- elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
- {
- $this->incontent = false;
+
+ // Handle overloaded arg (string or HttpClient object)
+ if ( is_object($source) ) {
+ if ( $source->status >= 200 && $source->status < 300) {
+ $this->etag = $source->headers['etag'];
+ $this->last_modified = $source->headers['last-modified'];
+ $source = $source->content;
+ } else {
+ $this->scour();
+ $this->status = $source->status;
+ $this->redirect_location = $source->headers->location;
+ $this->bathe();
+ return;
+ }
}
- elseif ($el == 'channel' or $el == 'feed' )
- {
- $this->inchannel = false;
+
+ list($parser, $source) = $this->create_parser($source, 'UTF-8', null, true);
+
+ if (!is_resource($parser)) {
+ $this->error( "Failed to create an instance of PHP's XML parser. " .
+ "http://www.php.net/manual/en/ref.xml.php",
+ E_USER_ERROR );
}
- elseif ($this->feed_type == ATOM and $this->incontent ) {
- // balance tags properly
- // note: i don't think this is actually neccessary
- if ( $this->stack[0] == $el )
- {
- $this->append_content("</$el>");
- }
- else {
- $this->append_content("<$el />");
+
+ $this->parser = $parser;
+
+ # pass in parser, and a reference to this object
+ # setup handlers
+ #
+ xml_set_object($this->parser, $this);
+ xml_set_element_handler($this->parser, 'start_element', 'end_element');
+ xml_set_character_data_handler( $this->parser, 'cdata');
+
+ $status = xml_parse( $this->parser, $source );
+
+ if (! $status ) {
+ $errorcode = xml_get_error_code( $this->parser );
+ if ( $errorcode != XML_ERROR_NONE ) {
+ $xml_error = xml_error_string( $errorcode );
+ $error_line = xml_get_current_line_number($this->parser);
+ $error_col = xml_get_current_column_number($this->parser);
+ $errormsg = "$xml_error at line $error_line, column $error_col";
+
+ $this->error( $errormsg );
}
+ }
- array_shift( $this->stack );
+ // SUPER SLOPPY FEED DISCOVERY!! TO-DO: AXE THIS CRAP!!
+ if ( !is_object($this->feed) || !method_exists($this->feed, 'to_xml') ) {
+ if ( preg_match_all('/<link [^>]*href=([^ >]+)[^>]+>/i', $source, $matches) ) {
+ $types = array('rss', 'atom');
+ foreach ( $types as $type )
+ foreach ( $matches[0] as $key => $link )
+ if ( preg_match('/rel=.alternate./', $link) && preg_match("/type=[^ >]*{$type}[^ >]*/", $link) )
+ break 2;
+ $this->scour();
+ $this->redirect_location = 'http://xml.wordpress.com/get/' . trim($matches[1][$key], '\'"');
+ $this->status = 301;
+ return;
+ } else {
+ $this->scour();
+ $this->status = 404;
+ return;
+ }
+ } else {
+ $this->status = 200;
}
- else {
- array_shift( $this->stack );
+
+ xml_parser_free( $this->parser );
+ unset($this->parser);
+
+ $this->bathe();
+ }
+
+ function to_xml() {
+ if ( is_object($this->feed) && method_exists($this->feed, 'to_xml') )
+ return $this->feed->to_xml();
+
+ return false;
+ }
+
+ // Called internally by xml_parse(). We create an object and call its start_element method.
+ function start_element($p, $element, &$attrs) {
+ $el = $element;// = strtolower($element);
+ // $attrs = array_change_key_case($attrs, CASE_LOWER);
+
+ // If there is an extended class for this element, use it.
+ $class = 'element';
+
+ $maybe_class = $test_class = strtolower(str_replace(':', '_', $el));
+ if ( class_exists($maybe_class) ) {
+ for ($classes[] = $test_class; $test_class = get_parent_class ($test_class); $classes[] = $test_class);
+ if ( in_array($class, $classes) )
+ $class = $maybe_class;
}
- $this->current_namespace = false;
+ // Instantiate an object for this element.
+ $object = new $class();
+
+ // Tell the element to start itself.
+ $object->start_element($p, $element, $attrs, $this);
+ }
+
+ function cdata ($p, $data) {
+ $this->stack[0]->cdata($p, $data, $this);
+ }
+
+ function end_element ($p, $el) {
+ $this->stack[0]->end_element($p, $el, $this);
}
- function concat (&$str1, $str2="") {
- if (!isset($str1) ) {
- $str1="";
+ function create_parser($source, $out_enc, $in_enc, $detect) {
+ if ( substr(phpversion(),0,1) == 5) {
+ $parser = $this->php5_create_parser($in_enc, $detect);
+ }
+ else {
+ list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect);
}
- $str1 .= $str2;
+ if ($out_enc) {
+ $this->encoding = $out_enc;
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc);
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
+ }
+
+ return array($parser, $source);
}
- function append_content($text) {
- if ( $this->initem ) {
- $this->concat( $this->current_item[ $this->incontent ], $text );
+ function php5_create_parser($in_enc, $detect) {
+ // by default php5 does a fine job of detecting input encodings
+ if(!$detect && $in_enc) {
+ return xml_parser_create($in_enc);
}
- elseif ( $this->inchannel ) {
- $this->concat( $this->channel[ $this->incontent ], $text );
+ else {
+ return xml_parser_create('');
}
}
- // smart append - field and namespace aware
- function append($el, $text) {
- if (!$el) {
- return;
+ /**
+ * Instaniate an XML parser under PHP4
+ *
+ * Unfortunately PHP4's support for character encodings
+ * and especially XML and character encodings sucks. As
+ * long as the documents you parse only contain characters
+ * from the ISO-8859-1 character set (a superset of ASCII,
+ * and a subset of UTF-8) you're fine. However once you
+ * step out of that comfy little world things get mad, bad,
+ * and dangerous to know.
+ *
+ * The following code is based on SJM's work with FoF
+ * @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
+ *
+ */
+ function php4_create_parser($source, $in_enc, $detect) {
+ if ( !$detect ) {
+ return array(xml_parser_create($in_enc), $source);
}
- if ( $this->current_namespace )
- {
- if ( $this->initem ) {
- $this->concat(
- $this->current_item[ $this->current_namespace ][ $el ], $text);
- }
- elseif ($this->inchannel) {
- $this->concat(
- $this->channel[ $this->current_namespace][ $el ], $text );
- }
- elseif ($this->intextinput) {
- $this->concat(
- $this->textinput[ $this->current_namespace][ $el ], $text );
+
+ if (!$in_enc) {
+ if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $source, $m)) {
+ $in_enc = strtoupper($m[1]);
+ $this->source_encoding = $in_enc;
}
- elseif ($this->inimage) {
- $this->concat(
- $this->image[ $this->current_namespace ][ $el ], $text );
+ else {
+ $in_enc = 'UTF-8';
}
}
- else {
- if ( $this->initem ) {
- $this->concat(
- $this->current_item[ $el ], $text);
- }
- elseif ($this->intextinput) {
- $this->concat(
- $this->textinput[ $el ], $text );
- }
- elseif ($this->inimage) {
- $this->concat(
- $this->image[ $el ], $text );
- }
- elseif ($this->inchannel) {
- $this->concat(
- $this->channel[ $el ], $text );
- }
+ if ($this->known_encoding($in_enc)) {
+ return array(xml_parser_create($in_enc), $source);
}
- }
- function normalize () {
- // if atom populate rss fields
- if ( $this->is_atom() ) {
- $this->channel['descripton'] = $this->channel['tagline'];
- for ( $i = 0; $i < count($this->items); $i++) {
- $item = $this->items[$i];
- if ( isset($item['summary']) )
- $item['description'] = $item['summary'];
- if ( isset($item['atom_content']))
- $item['content']['encoded'] = $item['atom_content'];
+ // the dectected encoding is not one of the simple encodings PHP knows
+
+ // attempt to use the iconv extension to
+ // cast the XML to a known encoding
+ // @see http://php.net/iconv
- $this->items[$i] = $item;
+ if (function_exists('iconv')) {
+ $encoded_source = iconv($in_enc,'UTF-8', $source);
+ if ($encoded_source) {
+ return array(xml_parser_create('UTF-8'), $encoded_source);
}
}
- elseif ( $this->is_rss() ) {
- $this->channel['tagline'] = $this->channel['description'];
- for ( $i = 0; $i < count($this->items); $i++) {
- $item = $this->items[$i];
- if ( isset($item['description']))
- $item['summary'] = $item['description'];
- if ( isset($item['content']['encoded'] ) )
- $item['atom_content'] = $item['content']['encoded'];
- $this->items[$i] = $item;
+ // iconv didn't work, try mb_convert_encoding
+ // @see http://php.net/mbstring
+ if(function_exists('mb_convert_encoding')) {
+ $encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc );
+ if ($encoded_source) {
+ return array(xml_parser_create('UTF-8'), $encoded_source);
}
}
- }
- function is_rss () {
- if ( $this->feed_type == RSS ) {
- return $this->feed_version;
- }
- else {
- return false;
- }
+ // else
+ $this->error("Feed is in an unsupported character encoding. ($in_enc) " .
+ "You may see strange artifacts, and mangled characters.",
+ E_USER_NOTICE);
+
+ return array(xml_parser_create(), $source);
}
- function is_atom() {
- if ( $this->feed_type == ATOM ) {
- return $this->feed_version;
+ function known_encoding($enc) {
+ $enc = strtoupper($enc);
+ if ( in_array($enc, array('UTF-8', 'US-ASCII', 'ISO-8859-1')) ) {
+ return $enc;
}
else {
return false;
}
}
- function map_attrs($k, $v) {
- return "$k=\"$v\"";
- }
-
- function error( $errormsg, $lvl = E_USER_WARNING ) {
+ function error ($errormsg, $lvl=E_USER_WARNING) {
// append PHP's error message if track_errors enabled
if ( isset($php_errormsg) ) {
$errormsg .= " ($php_errormsg)";
}
if ( MAGPIE_DEBUG ) {
- trigger_error( $errormsg, $lvl);
- } else {
- error_log( $errormsg, 0);
- }
- }
-
-}
-
-function map_attrs($k, $v) {
- return "$k=\"$v\"";
-}
-
-require_once( dirname(__FILE__) . '/class-snoopy.php');
-
-function fetch_rss ($url) {
- // initialize constants
- init();
-
- if ( !isset($url) ) {
- // error("fetch_rss called without a url");
- return false;
- }
-
- // if cache is disabled
- if ( !MAGPIE_CACHE_ON ) {
- // fetch file, and parse it
- $resp = _fetch_remote_file( $url );
- if ( is_success( $resp->status ) ) {
- return _response_to_rss( $resp );
+ // trigger_error( $errormsg, $lvl);
}
else {
- // error("Failed to fetch $url and cache is off");
- return false;
+ error_log( $errormsg, 0);
}
- }
- // else cache is ON
- else {
- // Flow
- // 1. check cache
- // 2. if there is a hit, make sure its fresh
- // 3. if cached obj fails freshness check, fetch remote
- // 4. if remote fails, return stale object, or error
-
- $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
- if (MAGPIE_DEBUG and $cache->ERROR) {
- debug($cache->ERROR, E_USER_WARNING);
+ $notices = E_USER_NOTICE|E_NOTICE;
+ if ( $lvl&$notices ) {
+ $this->WARNING = $errormsg;
+ } else {
+ $this->ERROR = $errormsg;
}
+ }
+ // Remove empty and |^_.*| object vars
+ function bathe() {
+ foreach ( get_object_vars($this) as $key => $data )
+ if ( empty($this->$key) || substr($key, 0, 1) == '_' )
+ unset($this->$key);
+ }
- $cache_status = 0; // response of check_cache
- $request_headers = array(); // HTTP headers to send with fetch
- $rss = 0; // parsed RSS object
- $errormsg = 0; // errors, if any
-
- if (!$cache->ERROR) {
- // return cache HIT, MISS, or STALE
- $cache_status = $cache->check_cache( $url );
- }
-
- // if object cached, and cache is fresh, return cached obj
- if ( $cache_status == 'HIT' ) {
- $rss = $cache->get( $url );
- if ( isset($rss) and $rss ) {
- $rss->from_cache = 1;
- if ( MAGPIE_DEBUG > 1) {
- debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
- }
- return $rss;
+ // Remove ALL object vars
+ function scour() {
+ foreach ( get_object_vars($this) as $key => $data )
+ unset($this->$key);
+ }
+
+ function to_magpie() {
+ $magpie = new stdClass();
+
+ foreach ( $this as $var => $value ) {
+ if ( $var == 'feed' ) {
+ continue;
+ } else {
+ $magpie->$var = $this->$var;
}
}
- // else attempt a conditional get
-
- // setup headers
- if ( $cache_status == 'STALE' ) {
- $rss = $cache->get( $url );
- if ( $rss->etag and $rss->last_modified ) {
- $request_headers['If-None-Match'] = $rss->etag;
- $request_headers['If-Last-Modified'] = $rss->last_modified;
- }
- }
+ $magpie->items = array();
- $resp = _fetch_remote_file( $url, $request_headers );
+ if ( is_object($this->feed) && method_exists($this->feed, 'to_magpie') ) {
+ $feed = $this->feed->to_magpie();
- if (isset($resp) and $resp) {
- if ($resp->status == '304' ) {
- // we have the most current copy
- if ( MAGPIE_DEBUG > 1) {
- debug("Got 304 for $url");
- }
- // reset cache on 304 (at minutillo insistent prodding)
- $cache->set($url, $rss);
- return $rss;
- }
- elseif ( is_success( $resp->status ) ) {
- $rss = _response_to_rss( $resp );
- if ( $rss ) {
- if (MAGPIE_DEBUG > 1) {
- debug("Fetch successful");
- }
- // add object to cache
- $cache->set( $url, $rss );
- return $rss;
- }
- }
- else {
- $errormsg = "Failed to fetch $url. ";
- if ( $resp->error ) {
- # compensate for Snoopy's annoying habbit to tacking
- # on '\n'
- $http_error = substr($resp->error, 0, -2);
- $errormsg .= "(HTTP Error: $http_error)";
- }
- else {
- $errormsg .= "(HTTP Response: " . $resp->response_code .')';
+ if ( is_array($feed) ) {
+ foreach ( $this->feed->to_magpie() as $var => $val ) {
+ if ( $var == 'items' )
+ $magpie->items = $val;
+ else
+ $magpie->channel["$var"] = $val;
}
}
}
- else {
- $errormsg = "Unable to retrieve RSS file for unknown reasons.";
- }
- // else fetch failed
-
- // attempt to return cached object
- if ($rss) {
- if ( MAGPIE_DEBUG ) {
- debug("Returning STALE object for $url");
- }
- return $rss;
- }
+ return $magpie;
+ }
+}
- // else we totally failed
- // error( $errormsg );
- return false;
+class element {
+ function element() {
+ }
- } // end if ( !MAGPIE_CACHE_ON ) {
-} // end fetch_rss()
+ function start_element($p, $el, $attr, &$mag) {
+ $this->name = $el;
+ $this->attributes = $attr;
-function _fetch_remote_file ($url, $headers = "" ) {
- // Snoopy is an HTTP client in PHP
- $client = new Snoopy();
- $client->agent = MAGPIE_USER_AGENT;
- $client->read_timeout = MAGPIE_FETCH_TIME_OUT;
- $client->use_gzip = MAGPIE_USE_GZIP;
- if (is_array($headers) ) {
- $client->rawheaders = $headers;
+ array_unshift($mag->stack, $this);
+ }
+ function cdata($p, $data, &$mag) {
+ if ( empty($this->children) )
+ $this->appendText($data);
}
- @$client->fetch($url);
- return $client;
+ function end_element($p, $el, &$mag) {
+ array_shift($mag->stack);
-}
+ $this->bathe();
-function _response_to_rss ($resp) {
- $rss = new MagpieRSS( $resp->results );
-
- // if RSS parsed successfully
- if ( $rss and !$rss->ERROR) {
+ if ( is_object($mag->stack[0]) )
+ $mag->stack[0]->appendChild($this);
+ }
- // find Etag, and Last-Modified
- foreach($resp->headers as $h) {
- // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
- if (strpos($h, ": ")) {
- list($field, $val) = explode(": ", $h, 2);
- }
- else {
- $field = $h;
- $val = "";
- }
+ function wrap_cdata() {
+ if ( strpos($this->innerText, '<') ) {
+ $spacer = (substr($this->innerText, -1) == ']') ? ' ' : '';
+ $this->innerText = str_replace(']]>', ']]&gt;', $this->innerText);
+ $this->innerText = '<![CDATA[' . $this->innerText . $spacer . ']]>';
+ }
+ }
- if ( $field == 'ETag' ) {
- $rss->etag = $val;
- }
+ function appendText($text) {
+ $this->innerText .= $text;
+ }
- if ( $field == 'Last-Modified' ) {
- $rss->last_modified = $val;
- }
- }
+ function appendChild($object) {
+ $this->children[] = & $object;
+ }
- return $rss;
- } // else construct error message
- else {
- $errormsg = "Failed to parse RSS file.";
+ // Add self to array
+ function to_array(&$array) {
+ $self = get_object_vars($this);
- if ($rss) {
- $errormsg .= " (" . $rss->ERROR . ")";
- }
- // error($errormsg);
+ unset($self['children']);
- return false;
- } // end if ($rss and !$rss->error)
-}
+ foreach ( $this->children as $child )
+ $child->to_array($self);
-/*=======================================================================*\
- Function: init
- Purpose: setup constants with default values
- check for user overrides
-\*=======================================================================*/
-function init () {
- if ( defined('MAGPIE_INITALIZED') ) {
- return;
- }
- else {
- define('MAGPIE_INITALIZED', 1);
+ $array[$this->name] = $self;
}
- if ( !defined('MAGPIE_CACHE_ON') ) {
- define('MAGPIE_CACHE_ON', 1);
+ // Return self as XML
+ function to_xml($indent = "\t", $generation = 0) {
+ $self = "<$this->name";
+ $self .= $this->attributes ? ' '.string_attributes($this->attributes) : '';
+ if ( empty($this->innerText) && empty($this->children) )
+ $self .= " />\n";
+ else {
+ $self .= ">";
+ if ( $this->children ) {
+ $self .= "\n";
+ foreach ( $this->children as $child )
+ $self .= $child->to_xml($indent, $generation + 1);
+ $self .= $indent ? $this->indent('', $indent, $generation) : '';
+ } else {
+ $self .= $this->innerText;
+ }
+ $self .= "</$this->name>\n";
+ }
+ return $indent ? $this->indent($self, $indent, $generation) : $self;
}
- if ( !defined('MAGPIE_CACHE_DIR') ) {
- define('MAGPIE_CACHE_DIR', './cache');
+ function indent($string, $indent, $generation) {
+ for ( $i = ''; strlen($i) < $generation; $i .= $indent ) ;
+ return $i . $string;
}
- if ( !defined('MAGPIE_CACHE_AGE') ) {
- define('MAGPIE_CACHE_AGE', 60*60); // one hour
+ /**
+ * Gets the innerText of the first matching element in the vars
+ * @param string Provide any number of strings to try
+ * @return mixed
+ */
+ function getChildText() {
+ foreach ( func_get_args() as $name )
+ foreach ( $this->children as $element )
+ if ( $element->name == $name )
+ return $element->innerText;
+ return false;
}
- if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
- define('MAGPIE_CACHE_FRESH_ONLY', 0);
+ /**
+ * Gets a ref to the first matching element
+ * @param string Provide any number of strings to try
+ * @return object
+ */
+ function &getChildElement() {
+ foreach ( func_get_args() as $name )
+ foreach ( $this->children as $key => $element )
+ if ( $element->name == $name )
+ return $this->children[$key];
+ return false;
}
- if ( !defined('MAGPIE_DEBUG') ) {
- define('MAGPIE_DEBUG', 0);
+ function getAttribute($name) {
+ if ( is_array($this->attributes) )
+ foreach ( $this->attributes as $attr => $value )
+ if ( $attr == $name )
+ return $value;
+ return null;
}
- if ( !defined('MAGPIE_USER_AGENT') ) {
- $ua = 'WordPress/' . $wp_version;
+ function bathe() {
+ foreach ( get_object_vars($this) as $var => $value )
+ if ( empty($this->$var) )
+ unset($this->$var);
+ }
- if ( MAGPIE_CACHE_ON ) {
- $ua = $ua . ')';
- }
- else {
- $ua = $ua . '; No cache)';
- }
+ function to_magpie() {
+ if ( strlen(trim($this->innerText)) > 0 )
+ return $this->innerText;
- define('MAGPIE_USER_AGENT', $ua);
- }
+ if ( is_array($this->attributes) )
+ $e = $this->attributes;
- if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
- define('MAGPIE_FETCH_TIME_OUT', 2); // 2 second timeout
- }
+ if ( is_array($this->children) && count($this->children) > 0 )
+ foreach ( $this->children as $k => $c )
+ $e[$k] = $this->children[$k]->to_magpie();
- // use gzip encoding to fetch rss files if supported?
- if ( !defined('MAGPIE_USE_GZIP') ) {
- define('MAGPIE_USE_GZIP', true);
+ return $e;
}
}
-function is_info ($sc) {
- return $sc >= 100 && $sc < 200;
-}
-
-function is_success ($sc) {
- return $sc >= 200 && $sc < 300;
-}
+// Base Atom class
+class feed extends element {
+ function end_element($p, $el, &$mag) {
+ $mag->feed = $this;
+ $mag->is_feed = true;
+ $mag->last_modified = $this->last_modified();
+ $mag->stack = array();
+ }
+ function last_modified() {
+ $time = parse_w3cdtf($this->getChildText('modified'));
+ return gmdate('D, d M Y H:i:s T', $time);
+ }
+ function to_magpie() {
+ foreach ( $this->children as $k => $c ) {
+ if ( $this->children[$k]->name == 'entry' )
+ $magpie['items'][] = $this->children[$k]->to_magpie();
+ else
+ $magpie[$this->children[$k]->name] = $this->children[$k]->innerText;
+ }
-function is_redirect ($sc) {
- return $sc >= 300 && $sc < 400;
+ return $magpie;
+ }
}
-function is_error ($sc) {
- return $sc >= 400 && $sc < 600;
+// RSS base class
+class rss extends feed {
+ function last_modified() {
+ $channel =& $this->getChildElement('channel');
+ $date = $channel->getChildText('pubDate', 'lastBuildDate');
+ return gmdate('D, d M Y H:i:s T', strtotime($date));
+ }
+ function to_magpie() {
+ return $this->children[0]->to_magpie();
+ }
}
+class rdf_rdf extends feed {
+ function to_magpie() {
+ $magpie = array();
+ foreach ( $this->children as $k => $child ) {
+ if ( $this->children[$k]->name == 'item' )
+ $magpie['items'][] = $this->children[$k]->to_magpie();
+ elseif ( method_exists($this->children[$k], 'to_magpie') )
+ $magpie[$this->children[$k]->name] = $this->children[$k]->to_magpie();
+ else
+ $magpie[$this->children[$k]->name] = $this->children[$k]->innerText;
+ }
+ if ( is_array($magpie['channel']) )
+ $magpie = array_merge($magpie['channel'], $magpie);
-function is_client_error ($sc) {
- return $sc >= 400 && $sc < 500;
+ return $magpie;
+ }
}
+class channel extends element {
+ function to_magpie() {
+ foreach ( $this->children as $k => $c ) {
+ if ( $this->children[$k]->name == 'item' )
+ $magpie['items'][] = $this->children[$k]->to_magpie();
+ else
+ $magpie[$this->children[$k]->name] = $this->children[$k]->innerText;
+ }
-function is_server_error ($sc) {
- return $sc >= 500 && $sc < 600;
+ return $magpie;
+ }
}
-class RSSCache {
- var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
- var $MAX_AGE = 43200; // when are files stale, default twelve hours
- var $ERROR = ''; // accumulate error messages
-
- function RSSCache ($base='', $age='') {
- if ( $base ) {
- $this->BASE_CACHE = $base;
- }
- if ( $age ) {
- $this->MAX_AGE = $age;
+// Atom article class
+class entry extends element {
+ function to_magpie() {
+ foreach ( $this->children as $k => $v ) {
+ if ( is_object($this->children[$k]) )
+ $value = $this->children[$k]->to_magpie();
+ else
+ $value = $this->children[$k]->innerText;
+
+ $name = $this->children[$k]->name;
+
+ $norms = array(
+ 'dc:subject' => 'categories',
+ 'summary' => 'description',
+ );
+ $name = str_replace(array_keys($norms), array_values($norms), $name);
+
+ switch ( $name ) {
+ // The ones that needs to be dereferenced
+ case 'author':
+ $magpie[$name] = $value[0];
+ break;
+
+ // The ones that can be multiple
+ case 'categories':
+ $magpie[$name][] = $value;
+ break;
+
+ default:
+ $magpie[$name] = $value;
+ }
}
-
+ return $magpie;
}
-/*=======================================================================*\
- Function: set
- Purpose: add an item to the cache, keyed on url
- Input: url from wich the rss file was fetched
- Output: true on sucess
-\*=======================================================================*/
- function set ($url, $rss) {
- global $wpdb;
- $cache_option = 'rss_' . $this->file_name( $url );
- $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
+ function to_array(&$array) {
+ $self = get_object_vars($this);
- if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") )
- add_option($cache_option, '', '', 'no');
- if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") )
- add_option($cache_timestamp, '', '', 'no');
+ unset($self['children']);
- update_option($cache_option, $rss);
- update_option($cache_timestamp, time() );
+ foreach ( $this->children as $child )
+ $child->to_array($self);
- return $cache_option;
+ $array['items'][] = $self;
}
+}
-/*=======================================================================*\
- Function: get
- Purpose: fetch an item from the cache
- Input: url from wich the rss file was fetched
- Output: cached object on HIT, false on MISS
-\*=======================================================================*/
- function get ($url) {
- $this->ERROR = "";
- $cache_option = 'rss_' . $this->file_name( $url );
-
- if ( ! get_option( $cache_option ) ) {
- $this->debug(
- "Cache doesn't contain: $url (cache option: $cache_option)"
- );
- return 0;
+// RSS article class
+class item extends entry {
+ function to_magpie() {
+ foreach ( $this->children as $k => $v ) {
+ if ( is_object($this->children[$k]) )
+ $value = $this->children[$k]->to_magpie();
+ else
+ $value = $this->children[$k]->innerText;
+
+ $name = $this->children[$k]->name;
+
+ $norms = array(
+ 'category' => 'categories',
+ 'content:encoded' => 'content',
+ 'dc:creator' => 'author',
+ 'wfw:commentRss' => 'comments',
+ 'wfw:commentRSS' => 'comments',
+ 'pubDate' => 'pubdate',
+ );
+ $name = str_replace(array_keys($norms), array_values($norms), $name);
+
+ switch ( $name ) {
+ // The ones that needs to be dereferenced
+ case 'taxo:topics':
+ $magpie[$name] = $value[0];
+ break;
+
+ // The ones that can be multiple
+ case 'categories':
+ $magpie[$name][] = $value;
+ break;
+
+ default:
+ $magpie[$name] = $value;
+ }
}
+ return $magpie;
+ }
+}
- $rss = get_option( $cache_option );
+class category extends element {
+ function to_array(&$array) {
+ $self = get_object_vars($this);
- return $rss;
- }
+ unset($self['children']);
-/*=======================================================================*\
- Function: check_cache
- Purpose: check a url for membership in the cache
- and whether the object is older then MAX_AGE (ie. STALE)
- Input: url from wich the rss file was fetched
- Output: cached object on HIT, false on MISS
-\*=======================================================================*/
- function check_cache ( $url ) {
- $this->ERROR = "";
- $cache_option = $this->file_name( $url );
- $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
+ foreach ( $this->children as $child )
+ $child->to_array($self);
- if ( $mtime = get_option($cache_timestamp) ) {
- // find how long ago the file was added to the cache
- // and whether that is longer then MAX_AGE
- $age = time() - $mtime;
- if ( $this->MAX_AGE > $age ) {
- // object exists and is current
- return 'HIT';
- }
- else {
- // object exists but is old
- return 'STALE';
- }
- }
- else {
- // object does not exist
- return 'MISS';
- }
+ $array['categories'][] = $self;
}
+}
-/*=======================================================================*\
- Function: serialize
-\*=======================================================================*/
- function serialize ( $rss ) {
- return serialize( $rss );
- }
+class link extends element {
+ function end_element($p, $el, &$mag) {
+ if ( $href = $this->getAttribute('href') )
+ $this->innerText = $href;
-/*=======================================================================*\
- Function: unserialize
-\*=======================================================================*/
- function unserialize ( $data ) {
- return unserialize( $data );
+ parent::end_element($p, $el, $mag);
}
+}
-/*=======================================================================*\
- Function: file_name
- Purpose: map url to location in cache
- Input: url from wich the rss file was fetched
- Output: a file name
-\*=======================================================================*/
- function file_name ($url) {
- return md5( $url );
- }
-/*=======================================================================*\
- Function: error
- Purpose: register error
-\*=======================================================================*/
- function error ($errormsg, $lvl=E_USER_WARNING) {
- // append PHP's error message if track_errors enabled
- if ( isset($php_errormsg) ) {
- $errormsg .= " ($php_errormsg)";
- }
- $this->ERROR = $errormsg;
- if ( MAGPIE_DEBUG ) {
- trigger_error( $errormsg, $lvl);
- }
- else {
- error_log( $errormsg, 0);
- }
- }
- function debug ($debugmsg, $lvl=E_USER_NOTICE) {
- if ( MAGPIE_DEBUG ) {
- $this->error("MagpieRSS [debug] $debugmsg", $lvl);
- }
- }
+/* Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ )
+ Manual: http://scripts.incutio.com/httpclient/
+*/
+
+if ( !class_exists('HttpClient') ) :
+class HttpClient {
+ // Request vars
+ var $host;
+ var $port;
+ var $path;
+ var $method;
+ var $postdata = '';
+ var $cookies = array();
+ var $referer;
+ var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
+ var $accept_encoding = 'gzip';
+ var $accept_language = 'en-us';
+ var $user_agent = 'Incutio HttpClient v0.9';
+ // Options
+ var $timeout = 20;
+ var $use_gzip = true;
+ var $no_cache = false;
+ var $persist_cookies = true; // If true, received cookies are placed in the $this->cookies array ready for the next request
+ // Note: This currently ignores the cookie path (and time) completely. Time is not important,
+ // but path could possibly lead to security problems.
+ var $persist_referers = true; // For each request, sends path of last request as referer
+ var $debug = false;
+ var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found
+ var $max_redirects = 5;
+ var $headers_only = false; // If true, stops receiving once headers have been read.
+ // Basic authorization variables
+ var $username;
+ var $password;
+ // Response vars
+ var $status;
+ var $headers = array();
+ var $content = '';
+ var $errormsg;
+ // Tracker variables
+ var $redirect_count = 0;
+ var $cookie_host = '';
+ function HttpClient($host, $port=80) {
+ $this->host = $host;
+ $this->port = $port;
+ }
+ function get($path, $data = false) {
+ $this->path = $path;
+ $this->method = 'GET';
+ if ($data) {
+ $this->path .= '?'.$this->buildQueryString($data);
+ }
+ return $this->doRequest();
+ }
+ function post($path, $data) {
+ $this->path = $path;
+ $this->method = 'POST';
+ $this->postdata = $this->buildQueryString($data);
+ return $this->doRequest();
+ }
+ function buildQueryString($data) {
+ $querystring = '';
+ if (is_array($data)) {
+ // Change data in to postable data
+ foreach ($data as $key => $val) {
+ if (is_array($val)) {
+ foreach ($val as $val2) {
+ $querystring .= urlencode($key).'='.urlencode($val2).'&';
+ }
+ } else {
+ $querystring .= urlencode($key).'='.urlencode($val).'&';
+ }
+ }
+ $querystring = substr($querystring, 0, -1); // Eliminate unnecessary &
+ } else {
+ $querystring = $data;
+ }
+ return $querystring;
+ }
+ function doRequest() {
+ // Performs the actual HTTP request, returning true or false depending on outcome
+ if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
+ // Set error message
+ switch($errno) {
+ case -3:
+ $this->errormsg = 'Socket creation failed (-3)';
+ case -4:
+ $this->errormsg = 'DNS lookup failure (-4)';
+ case -5:
+ $this->errormsg = 'Connection refused or timed out (-5)';
+ default:
+ $this->errormsg = 'Connection failed ('.$errno.')';
+ $this->errormsg .= ' '.$errstr;
+ $this->debug($this->errormsg);
+ }
+ return false;
+ }
+ socket_set_timeout($fp, $this->timeout);
+ $request = $this->buildRequest();
+ $this->debug('Request', $request);
+ fwrite($fp, $request);
+ // Reset all the variables that should not persist between requests
+ $this->headers = array();
+ $this->content = '';
+ $this->errormsg = '';
+ // Set a couple of flags
+ $inHeaders = true;
+ // Now start reading back the response
+ $line = fgets($fp, 4096);
+ // Deal with first line of returned data
+ if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
+ $this->errormsg = "Status code line invalid: ".htmlentities($line);
+ $this->debug($this->errormsg);
+ return false;
+ }
+ $http_version = $m[1]; // not used
+ $this->status = $m[2];
+ $status_string = $m[3]; // not used
+ $this->debug(trim($line));
+ while (!feof($fp)) {
+ $line = fgets($fp, 4096);
+ if ($inHeaders) {
+ if (trim($line) == '') {
+ $inHeaders = false;
+ $this->debug('Received Headers', $this->headers);
+ if ($this->headers_only) {
+ break; // Skip the rest of the input
+ }
+ continue;
+ }
+ if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
+ // Skip to the next header
+ continue;
+ }
+ $key = strtolower(trim($m[1]));
+ $val = trim($m[2]);
+ // Deal with the possibility of multiple headers of same name
+ if (isset($this->headers[$key])) {
+ if (is_array($this->headers[$key])) {
+ $this->headers[$key][] = $val;
+ } else {
+ $this->headers[$key] = array($this->headers[$key], $val);
+ }
+ } else {
+ $this->headers[$key] = $val;
+ }
+ continue;
+ }
+ // We're not in the headers, so append the line to the contents
+ $this->content .= $line;
+ }
+ fclose($fp);
+ // If data is compressed, uncompress it
+ if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') {
+ $this->debug('Content is gzip encoded, unzipping it');
+ $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php
+ $this->content = gzinflate($this->content);
+ }
+ // If $persist_cookies, deal with any cookies
+ if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) {
+ $cookies = $this->headers['set-cookie'];
+ if (!is_array($cookies)) {
+ $cookies = array($cookies);
+ }
+ foreach ($cookies as $cookie) {
+ if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
+ $this->cookies[$m[1]] = $m[2];
+ }
+ }
+ // Record domain of cookies for security reasons
+ $this->cookie_host = $this->host;
+ }
+ // If $persist_referers, set the referer ready for the next request
+ if ($this->persist_referers) {
+ $this->debug('Persisting referer: '.$this->getRequestURL());
+ $this->referer = $this->getRequestURL();
+ }
+ // Finally, if handle_redirects and a redirect is sent, do that
+ if ($this->handle_redirects) {
+ if (++$this->redirect_count >= $this->max_redirects) {
+ $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')';
+ $this->debug($this->errormsg);
+ $this->redirect_count = 0;
+ return false;
+ }
+ $location = isset($this->headers['location']) ? $this->headers['location'] : '';
+ $uri = isset($this->headers['uri']) ? $this->headers['uri'] : '';
+ if ($location || $uri) {
+ $url = parse_url($location.$uri);
+ // This will FAIL if redirect is to a different site
+ return $this->get($url['path']);
+ }
+ }
+ return true;
+ }
+ function buildRequest() {
+ $headers = array();
+ $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding
+ $headers[] = "Host: {$this->host}";
+ $headers[] = "User-Agent: {$this->user_agent}";
+ $headers[] = "Accept: {$this->accept}";
+ if ($this->use_gzip) {
+ $headers[] = "Accept-encoding: {$this->accept_encoding}";
+ }
+ $headers[] = "Accept-language: {$this->accept_language}";
+ if ($this->referer) {
+ $headers[] = "Referer: {$this->referer}";
+ }
+ if ($this->no_cache) {
+ $headers[] = "Pragma: no-cache";
+ $headers[] = "Cache-control: no-cache";
+ }
+ // Cookies
+ if ($this->cookies) {
+ $cookie = 'Cookie: ';
+ foreach ($this->cookies as $key => $value) {
+ $cookie .= "$key=$value; ";
+ }
+ $headers[] = $cookie;
+ }
+ // Basic authentication
+ if ($this->username && $this->password) {
+ $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password);
+ }
+ // If this is a POST, set the content type and length
+ if ($this->postdata) {
+ $headers[] = 'Content-Type: application/x-www-form-urlencoded';
+ $headers[] = 'Content-Length: '.strlen($this->postdata);
+ }
+ $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata;
+ return $request;
+ }
+ function getStatus() {
+ return $this->status;
+ }
+ function getContent() {
+ return $this->content;
+ }
+ function getHeaders() {
+ return $this->headers;
+ }
+ function getHeader($header) {
+ $header = strtolower($header);
+ if (isset($this->headers[$header])) {
+ return $this->headers[$header];
+ } else {
+ return false;
+ }
+ }
+ function getError() {
+ return $this->errormsg;
+ }
+ function getCookies() {
+ return $this->cookies;
+ }
+ function getRequestURL() {
+ $url = 'http://'.$this->host;
+ if ($this->port != 80) {
+ $url .= ':'.$this->port;
+ }
+ $url .= $this->path;
+ return $url;
+ }
+ // Setter methods
+ function setUserAgent($string) {
+ $this->user_agent = $string;
+ }
+ function setAuthorization($username, $password) {
+ $this->username = $username;
+ $this->password = $password;
+ }
+ function setCookies($array) {
+ $this->cookies = $array;
+ }
+ // Option setting methods
+ function useGzip($boolean) {
+ $this->use_gzip = $boolean;
+ }
+ function setPersistCookies($boolean) {
+ $this->persist_cookies = $boolean;
+ }
+ function setPersistReferers($boolean) {
+ $this->persist_referers = $boolean;
+ }
+ function setHandleRedirects($boolean) {
+ $this->handle_redirects = $boolean;
+ }
+ function setMaxRedirects($num) {
+ $this->max_redirects = $num;
+ }
+ function setHeadersOnly($boolean) {
+ $this->headers_only = $boolean;
+ }
+ function setDebug($boolean) {
+ $this->debug = $boolean;
+ }
+ // "Quick" static methods
+ function quickGet($url) {
+ $bits = parse_url($url);
+ $host = $bits['host'];
+ $port = isset($bits['port']) ? $bits['port'] : 80;
+ $path = isset($bits['path']) ? $bits['path'] : '/';
+ if (isset($bits['query'])) {
+ $path .= '?'.$bits['query'];
+ }
+ $client = new HttpClient($host, $port);
+ if (!$client->get($path)) {
+ return false;
+ } else {
+ return $client->getContent();
+ }
+ }
+ function quickPost($url, $data) {
+ $bits = parse_url($url);
+ $host = $bits['host'];
+ $port = isset($bits['port']) ? $bits['port'] : 80;
+ $path = isset($bits['path']) ? $bits['path'] : '/';
+ $client = new HttpClient($host, $port);
+ if (!$client->post($path, $data)) {
+ return false;
+ } else {
+ return $client->getContent();
+ }
+ }
+ function debug($msg, $object = false) {
+ if ($this->debug) {
+ print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
+ if ($object) {
+ ob_start();
+ print_r($object);
+ $content = htmlentities(ob_get_contents());
+ ob_end_clean();
+ print '<pre>'.$content.'</pre>';
+ }
+ print '</div>';
+ }
+ }
}
+endif;
-function parse_w3cdtf ( $date_str ) {
+function string_attributes($attrs) {
+ return join(' ', array_map(create_function('$k,$v', 'return "$k=\"".htmlspecialchars($v)."\"";'), array_keys($attrs), array_values($attrs) ) );
+}
+if ( !function_exists('parse_w3cdtf') ) :
+function parse_w3cdtf ( $date_str ) {
# regex to match wc3dtf
$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
if ( preg_match( $pat, $date_str, $match ) ) {
- list( $year, $month, $day, $hours, $minutes, $seconds) =
- array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
+ list( $year, $month, $day, $hours, $minutes, $seconds) =
+ array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
# calc epoch for current date assuming GMT
$epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
@@ -798,7 +1164,7 @@ function parse_w3cdtf ( $date_str ) {
}
else {
list( $tz_mod, $tz_hour, $tz_min ) =
- array( $match[8], $match[9], $match[10]);
+ array( $match[8], $match[9], $match[10]);
# zero out the variables
if ( ! $tz_hour ) { $tz_hour = 0; }
@@ -820,42 +1186,7 @@ function parse_w3cdtf ( $date_str ) {
else {
return -1;
}
- }
-function wp_rss ($url, $num) {
- //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned.
- $num_items = $num;
- $rss = fetch_rss($url);
- if ( $rss ) {
- echo "<ul>";
- $rss->items = array_slice($rss->items, 0, $num_items);
- foreach ($rss->items as $item ) {
- echo "<li>\n";
- echo "<a href='$item[link]' title='$item[description]'>";
- echo htmlentities($item['title']);
- echo "</a><br />\n";
- echo "</li>\n";
- }
- echo "</ul>";
- }
- else {
- echo "an error has occured the feed is probably down, try again later.";
- }
}
+endif;
-function get_rss ($uri, $num = 5) { // Like get posts, but for RSS
- $rss = fetch_rss($url);
- if ( $rss ) {
- $rss->items = array_slice($rss->items, 0, $num_items);
- foreach ($rss->items as $item ) {
- echo "<li>\n";
- echo "<a href='$item[link]' title='$item[description]'>";
- echo htmlentities($item['title']);
- echo "</a><br />\n";
- echo "</li>\n";
- }
- return $posts;
- } else {
- return false;
- }
-}
?>