| 11 | | function fetch_rss($url) { |
|---|
| 12 | | $url = apply_filters('fetch_rss_url', $url); |
|---|
| 13 | | |
|---|
| 14 | | $feeder = new WP_Feeder(); |
|---|
| 15 | | |
|---|
| 16 | | $feed = $feeder->get($url); |
|---|
| 17 | | |
|---|
| 18 | | $magpie = $feed->to_magpie(); |
|---|
| 19 | | |
|---|
| 20 | | return $magpie; |
|---|
| 21 | | } |
|---|
| 22 | | |
|---|
| 23 | | class WP_Feeder { |
|---|
| 24 | | var $url, $http_client, $last_fetch, $wp_object_cache, $cache; |
|---|
| 25 | | var $redirects = 0; |
|---|
| 26 | | var $max_redirects = 3; |
|---|
| 27 | | var $cache_redirects = true; |
|---|
| 28 | | |
|---|
| 29 | | function WP_Feeder () { |
|---|
| 30 | | global $wp_object_cache; |
|---|
| 31 | | |
|---|
| 32 | | if ( $wp_object_cache->cache_enabled ) { |
|---|
| 33 | | $this->wp_object_cache = true; |
|---|
| | 10 | define('RSS', 'RSS'); |
|---|
| | 11 | define('ATOM', 'Atom'); |
|---|
| | 12 | define('MAGPIE_USER_AGENT', 'WordPressMU/' . $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 | trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" ); |
|---|
| | 44 | |
|---|
| | 45 | $parser = @xml_parser_create(); |
|---|
| | 46 | |
|---|
| | 47 | if ( !is_resource($parser) ) |
|---|
| | 48 | trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php"); |
|---|
| | 49 | |
|---|
| | 50 | |
|---|
| | 51 | $this->parser = $parser; |
|---|
| | 52 | |
|---|
| | 53 | # pass in parser, and a reference to this object |
|---|
| | 54 | # setup handlers |
|---|
| | 55 | # |
|---|
| | 56 | xml_set_object( $this->parser, $this ); |
|---|
| | 57 | xml_set_element_handler($this->parser, |
|---|
| | 58 | 'feed_start_element', 'feed_end_element' ); |
|---|
| | 59 | |
|---|
| | 60 | xml_set_character_data_handler( $this->parser, 'feed_cdata' ); |
|---|
| | 61 | |
|---|
| | 62 | $status = xml_parse( $this->parser, $source ); |
|---|
| | 63 | |
|---|
| | 64 | if (! $status ) { |
|---|
| | 65 | $errorcode = xml_get_error_code( $this->parser ); |
|---|
| | 66 | if ( $errorcode != XML_ERROR_NONE ) { |
|---|
| | 67 | $xml_error = xml_error_string( $errorcode ); |
|---|
| | 68 | $error_line = xml_get_current_line_number($this->parser); |
|---|
| | 69 | $error_col = xml_get_current_column_number($this->parser); |
|---|
| | 70 | $errormsg = "$xml_error at line $error_line, column $error_col"; |
|---|
| | 71 | |
|---|
| | 72 | $this->error( $errormsg ); |
|---|
| | 73 | } |
|---|
| | 74 | } |
|---|
| | 75 | |
|---|
| | 76 | xml_parser_free( $this->parser ); |
|---|
| | 77 | |
|---|
| | 78 | $this->normalize(); |
|---|
| | 79 | } |
|---|
| | 80 | |
|---|
| | 81 | function feed_start_element($p, $element, &$attrs) { |
|---|
| | 82 | $el = $element = strtolower($element); |
|---|
| | 83 | $attrs = array_change_key_case($attrs, CASE_LOWER); |
|---|
| | 84 | |
|---|
| | 85 | // check for a namespace, and split if found |
|---|
| | 86 | $ns = false; |
|---|
| | 87 | if ( strpos( $element, ':' ) ) { |
|---|
| | 88 | list($ns, $el) = split( ':', $element, 2); |
|---|
| | 89 | } |
|---|
| | 90 | if ( $ns and $ns != 'rdf' ) { |
|---|
| | 91 | $this->current_namespace = $ns; |
|---|
| | 92 | } |
|---|
| | 93 | |
|---|
| | 94 | # if feed type isn't set, then this is first element of feed |
|---|
| | 95 | # identify feed from root element |
|---|
| | 96 | # |
|---|
| | 97 | if (!isset($this->feed_type) ) { |
|---|
| | 98 | if ( $el == 'rdf' ) { |
|---|
| | 99 | $this->feed_type = RSS; |
|---|
| | 100 | $this->feed_version = '1.0'; |
|---|
| | 101 | } |
|---|
| | 102 | elseif ( $el == 'rss' ) { |
|---|
| | 103 | $this->feed_type = RSS; |
|---|
| | 104 | $this->feed_version = $attrs['version']; |
|---|
| | 105 | } |
|---|
| | 106 | elseif ( $el == 'feed' ) { |
|---|
| | 107 | $this->feed_type = ATOM; |
|---|
| | 108 | $this->feed_version = $attrs['version']; |
|---|
| | 109 | $this->inchannel = true; |
|---|
| | 110 | } |
|---|
| | 111 | return; |
|---|
| | 112 | } |
|---|
| | 113 | |
|---|
| | 114 | if ( $el == 'channel' ) |
|---|
| | 115 | { |
|---|
| | 116 | $this->inchannel = true; |
|---|
| | 117 | } |
|---|
| | 118 | elseif ($el == 'item' or $el == 'entry' ) |
|---|
| | 119 | { |
|---|
| | 120 | $this->initem = true; |
|---|
| | 121 | if ( isset($attrs['rdf:about']) ) { |
|---|
| | 122 | $this->current_item['about'] = $attrs['rdf:about']; |
|---|
| | 123 | } |
|---|
| | 124 | } |
|---|
| | 125 | |
|---|
| | 126 | // if we're in the default namespace of an RSS feed, |
|---|
| | 127 | // record textinput or image fields |
|---|
| | 128 | elseif ( |
|---|
| | 129 | $this->feed_type == RSS and |
|---|
| | 130 | $this->current_namespace == '' and |
|---|
| | 131 | $el == 'textinput' ) |
|---|
| | 132 | { |
|---|
| | 133 | $this->intextinput = true; |
|---|
| | 134 | } |
|---|
| | 135 | |
|---|
| | 136 | elseif ( |
|---|
| | 137 | $this->feed_type == RSS and |
|---|
| | 138 | $this->current_namespace == '' and |
|---|
| | 139 | $el == 'image' ) |
|---|
| | 140 | { |
|---|
| | 141 | $this->inimage = true; |
|---|
| | 142 | } |
|---|
| | 143 | |
|---|
| | 144 | # handle atom content constructs |
|---|
| | 145 | elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) ) |
|---|
| | 146 | { |
|---|
| | 147 | // avoid clashing w/ RSS mod_content |
|---|
| | 148 | if ($el == 'content' ) { |
|---|
| | 149 | $el = 'atom_content'; |
|---|
| | 150 | } |
|---|
| | 151 | |
|---|
| | 152 | $this->incontent = $el; |
|---|
| | 153 | |
|---|
| | 154 | |
|---|
| | 155 | } |
|---|
| | 156 | |
|---|
| | 157 | // if inside an Atom content construct (e.g. content or summary) field treat tags as text |
|---|
| | 158 | elseif ($this->feed_type == ATOM and $this->incontent ) |
|---|
| | 159 | { |
|---|
| | 160 | // if tags are inlined, then flatten |
|---|
| | 161 | $attrs_str = join(' ', |
|---|
| | 162 | array_map('map_attrs', |
|---|
| | 163 | array_keys($attrs), |
|---|
| | 164 | array_values($attrs) ) ); |
|---|
| | 165 | |
|---|
| | 166 | $this->append_content( "<$element $attrs_str>" ); |
|---|
| | 167 | |
|---|
| | 168 | array_unshift( $this->stack, $el ); |
|---|
| | 169 | } |
|---|
| | 170 | |
|---|
| | 171 | // Atom support many links per containging element. |
|---|
| | 172 | // Magpie treats link elements of type rel='alternate' |
|---|
| | 173 | // as being equivalent to RSS's simple link element. |
|---|
| | 174 | // |
|---|
| | 175 | elseif ($this->feed_type == ATOM and $el == 'link' ) |
|---|
| | 176 | { |
|---|
| | 177 | if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' ) |
|---|
| | 178 | { |
|---|
| | 179 | $link_el = 'link'; |
|---|
| | 180 | } |
|---|
| | 181 | else { |
|---|
| | 182 | $link_el = 'link_' . $attrs['rel']; |
|---|
| | 183 | } |
|---|
| | 184 | |
|---|
| | 185 | $this->append($link_el, $attrs['href']); |
|---|
| | 186 | } |
|---|
| | 187 | // set stack[0] to current element |
|---|
| | 188 | else { |
|---|
| | 189 | array_unshift($this->stack, $el); |
|---|
| | 190 | } |
|---|
| | 191 | } |
|---|
| | 192 | |
|---|
| | 193 | |
|---|
| | 194 | |
|---|
| | 195 | function feed_cdata ($p, $text) { |
|---|
| | 196 | |
|---|
| | 197 | if ($this->feed_type == ATOM and $this->incontent) |
|---|
| | 198 | { |
|---|
| | 199 | $this->append_content( $text ); |
|---|
| | 200 | } |
|---|
| | 201 | else { |
|---|
| | 202 | $current_el = join('_', array_reverse($this->stack)); |
|---|
| | 203 | $this->append($current_el, $text); |
|---|
| | 204 | } |
|---|
| | 205 | } |
|---|
| | 206 | |
|---|
| | 207 | function feed_end_element ($p, $el) { |
|---|
| | 208 | $el = strtolower($el); |
|---|
| | 209 | |
|---|
| | 210 | if ( $el == 'item' or $el == 'entry' ) |
|---|
| | 211 | { |
|---|
| | 212 | $this->items[] = $this->current_item; |
|---|
| | 213 | $this->current_item = array(); |
|---|
| | 214 | $this->initem = false; |
|---|
| | 215 | } |
|---|
| | 216 | elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' ) |
|---|
| | 217 | { |
|---|
| | 218 | $this->intextinput = false; |
|---|
| | 219 | } |
|---|
| | 220 | elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' ) |
|---|
| | 221 | { |
|---|
| | 222 | $this->inimage = false; |
|---|
| | 223 | } |
|---|
| | 224 | elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) ) |
|---|
| | 225 | { |
|---|
| | 226 | $this->incontent = false; |
|---|
| | 227 | } |
|---|
| | 228 | elseif ($el == 'channel' or $el == 'feed' ) |
|---|
| | 229 | { |
|---|
| | 230 | $this->inchannel = false; |
|---|
| | 231 | } |
|---|
| | 232 | elseif ($this->feed_type == ATOM and $this->incontent ) { |
|---|
| | 233 | // balance tags properly |
|---|
| | 234 | // note: i don't think this is actually neccessary |
|---|
| | 235 | if ( $this->stack[0] == $el ) |
|---|
| | 236 | { |
|---|
| | 237 | $this->append_content("</$el>"); |
|---|
| | 238 | } |
|---|
| | 239 | else { |
|---|
| | 240 | $this->append_content("<$el />"); |
|---|
| | 241 | } |
|---|
| | 242 | |
|---|
| | 243 | array_shift( $this->stack ); |
|---|
| | 244 | } |
|---|
| | 245 | else { |
|---|
| | 246 | array_shift( $this->stack ); |
|---|
| | 247 | } |
|---|
| | 248 | |
|---|
| | 249 | $this->current_namespace = false; |
|---|
| | 250 | } |
|---|
| | 251 | |
|---|
| | 252 | function concat (&$str1, $str2="") { |
|---|
| | 253 | if (!isset($str1) ) { |
|---|
| | 254 | $str1=""; |
|---|
| | 255 | } |
|---|
| | 256 | $str1 .= $str2; |
|---|
| | 257 | } |
|---|
| | 258 | |
|---|
| | 259 | function append_content($text) { |
|---|
| | 260 | if ( $this->initem ) { |
|---|
| | 261 | $this->concat( $this->current_item[ $this->incontent ], $text ); |
|---|
| | 262 | } |
|---|
| | 263 | elseif ( $this->inchannel ) { |
|---|
| | 264 | $this->concat( $this->channel[ $this->incontent ], $text ); |
|---|
| | 265 | } |
|---|
| | 266 | } |
|---|
| | 267 | |
|---|
| | 268 | // smart append - field and namespace aware |
|---|
| | 269 | function append($el, $text) { |
|---|
| | 270 | if (!$el) { |
|---|
| | 271 | return; |
|---|
| | 272 | } |
|---|
| | 273 | if ( $this->current_namespace ) |
|---|
| | 274 | { |
|---|
| | 275 | if ( $this->initem ) { |
|---|
| | 276 | $this->concat( |
|---|
| | 277 | $this->current_item[ $this->current_namespace ][ $el ], $text); |
|---|
| | 278 | } |
|---|
| | 279 | elseif ($this->inchannel) { |
|---|
| | 280 | $this->concat( |
|---|
| | 281 | $this->channel[ $this->current_namespace][ $el ], $text ); |
|---|
| | 282 | } |
|---|
| | 283 | elseif ($this->intextinput) { |
|---|
| | 284 | $this->concat( |
|---|
| | 285 | $this->textinput[ $this->current_namespace][ $el ], $text ); |
|---|
| | 286 | } |
|---|
| | 287 | elseif ($this->inimage) { |
|---|
| | 288 | $this->concat( |
|---|
| | 289 | $this->image[ $this->current_namespace ][ $el ], $text ); |
|---|
| | 290 | } |
|---|
| | 291 | } |
|---|
| | 292 | else { |
|---|
| | 293 | if ( $this->initem ) { |
|---|
| | 294 | $this->concat( |
|---|
| | 295 | $this->current_item[ $el ], $text); |
|---|
| | 296 | } |
|---|
| | 297 | elseif ($this->intextinput) { |
|---|
| | 298 | $this->concat( |
|---|
| | 299 | $this->textinput[ $el ], $text ); |
|---|
| | 300 | } |
|---|
| | 301 | elseif ($this->inimage) { |
|---|
| | 302 | $this->concat( |
|---|
| | 303 | $this->image[ $el ], $text ); |
|---|
| | 304 | } |
|---|
| | 305 | elseif ($this->inchannel) { |
|---|
| | 306 | $this->concat( |
|---|
| | 307 | $this->channel[ $el ], $text ); |
|---|
| | 308 | } |
|---|
| | 309 | |
|---|
| | 310 | } |
|---|
| | 311 | } |
|---|
| | 312 | |
|---|
| | 313 | function normalize () { |
|---|
| | 314 | // if atom populate rss fields |
|---|
| | 315 | if ( $this->is_atom() ) { |
|---|
| | 316 | $this->channel['descripton'] = $this->channel['tagline']; |
|---|
| | 317 | for ( $i = 0; $i < count($this->items); $i++) { |
|---|
| | 318 | $item = $this->items[$i]; |
|---|
| | 319 | if ( isset($item['summary']) ) |
|---|
| | 320 | $item['description'] = $item['summary']; |
|---|
| | 321 | if ( isset($item['atom_content'])) |
|---|
| | 322 | $item['content']['encoded'] = $item['atom_content']; |
|---|
| | 323 | |
|---|
| | 324 | $this->items[$i] = $item; |
|---|
| | 325 | } |
|---|
| | 326 | } |
|---|
| | 327 | elseif ( $this->is_rss() ) { |
|---|
| | 328 | $this->channel['tagline'] = $this->channel['description']; |
|---|
| | 329 | for ( $i = 0; $i < count($this->items); $i++) { |
|---|
| | 330 | $item = $this->items[$i]; |
|---|
| | 331 | if ( isset($item['description'])) |
|---|
| | 332 | $item['summary'] = $item['description']; |
|---|
| | 333 | if ( isset($item['content']['encoded'] ) ) |
|---|
| | 334 | $item['atom_content'] = $item['content']['encoded']; |
|---|
| | 335 | |
|---|
| | 336 | $this->items[$i] = $item; |
|---|
| | 337 | } |
|---|
| | 338 | } |
|---|
| | 339 | } |
|---|
| | 340 | |
|---|
| | 341 | function is_rss () { |
|---|
| | 342 | if ( $this->feed_type == RSS ) { |
|---|
| | 343 | return $this->feed_version; |
|---|
| | 344 | } |
|---|
| | 345 | else { |
|---|
| | 346 | return false; |
|---|
| | 347 | } |
|---|
| | 348 | } |
|---|
| | 349 | |
|---|
| | 350 | function is_atom() { |
|---|
| | 351 | if ( $this->feed_type == ATOM ) { |
|---|
| | 352 | return $this->feed_version; |
|---|
| | 353 | } |
|---|
| | 354 | else { |
|---|
| | 355 | return false; |
|---|
| | 356 | } |
|---|
| | 357 | } |
|---|
| | 358 | |
|---|
| | 359 | function map_attrs($k, $v) { |
|---|
| | 360 | return "$k=\"$v\""; |
|---|
| | 361 | } |
|---|
| | 362 | |
|---|
| | 363 | function error( $errormsg, $lvl = E_USER_WARNING ) { |
|---|
| | 364 | // append PHP's error message if track_errors enabled |
|---|
| | 365 | if ( isset($php_errormsg) ) { |
|---|
| | 366 | $errormsg .= " ($php_errormsg)"; |
|---|
| | 367 | } |
|---|
| | 368 | if ( MAGPIE_DEBUG ) { |
|---|
| | 369 | trigger_error( $errormsg, $lvl); |
|---|
| 35 | | $this->wp_object_cache = false; |
|---|
| 36 | | $this->cache = new RSSCache(); |
|---|
| 37 | | } |
|---|
| 38 | | } |
|---|
| 39 | | |
|---|
| 40 | | function get ($url) { |
|---|
| 41 | | $cached = false; |
|---|
| 42 | | |
|---|
| 43 | | $feed = $this->cache_get($url); |
|---|
| 44 | | |
|---|
| 45 | | if ( is_object($feed) ) { |
|---|
| 46 | | $cached = true; |
|---|
| 47 | | } else { |
|---|
| 48 | | unset($feed); |
|---|
| 49 | | |
|---|
| 50 | | $this->fetch($url); |
|---|
| 51 | | |
|---|
| 52 | | $feed = new WP_Feed($this->http_client); |
|---|
| 53 | | } |
|---|
| 54 | | |
|---|
| 55 | | // Handle redirects |
|---|
| 56 | | if ( $feed->status >= 300 && $feed->status < 400 && $this->redirects < $this->max_redirects ) { |
|---|
| 57 | | ++$this->redirects; |
|---|
| 58 | | |
|---|
| 59 | | if ( $this->cache_redirects && !$cached ) |
|---|
| 60 | | $this->cache_set($url, $feed); |
|---|
| 61 | | |
|---|
| 62 | | return $this->get($feed->redirect_location); |
|---|
| 63 | | } |
|---|
| 64 | | |
|---|
| 65 | | if ( !$cached ) |
|---|
| 66 | | $this->cache_set($url, $feed); |
|---|
| 67 | | |
|---|
| 68 | | return $feed; |
|---|
| 69 | | } |
|---|
| 70 | | |
|---|
| 71 | | function fetch ($url) { |
|---|
| 72 | | $this->last_fetch = $url; |
|---|
| 73 | | $parts = parse_url($url); |
|---|
| 74 | | $url = ($parts['path'] ? $parts['path'] : '/') . ($parts['query'] ? '?'.$parts['query'] : ''); |
|---|
| 75 | | $this->http_client = new HttpClient('', 80); |
|---|
| 76 | | $this->http_client->handle_redirects = false; |
|---|
| 77 | | $this->http_client->host = $parts['host']; |
|---|
| 78 | | $this->http_client->port = $parts['port'] ? $parts['port'] : 80; |
|---|
| 79 | | $this->http_client->user_agent = 'WordPress ' . $GLOBALS['wp_version'] . ' Feed Client'; |
|---|
| 80 | | $this->http_client->get($url); |
|---|
| 81 | | } |
|---|
| 82 | | |
|---|
| 83 | | function cache_get ($url) { |
|---|
| 84 | | if ( $this->wp_object_cache ) |
|---|
| 85 | | return unserialize(wp_cache_get($url, 'rss')); |
|---|
| 86 | | |
|---|
| 87 | | return $this->cache->get($url); |
|---|
| 88 | | } |
|---|
| 89 | | |
|---|
| 90 | | function cache_set ($url, $object) { |
|---|
| 91 | | if ( $this->wp_object_cache ) |
|---|
| 92 | | return wp_cache_set($url, serialize($object), 'rss', 3600); |
|---|
| 93 | | |
|---|
| 94 | | return $this->cache->set($url, $object); |
|---|
| 95 | | } |
|---|
| | 371 | error_log( $errormsg, 0); |
|---|
| | 372 | } |
|---|
| | 373 | } |
|---|
| | 374 | |
|---|
| | 375 | } |
|---|
| | 376 | require_once( dirname(__FILE__) . '/class-snoopy.php'); |
|---|
| | 377 | |
|---|
| | 378 | function fetch_rss ($url) { |
|---|
| | 379 | // initialize constants |
|---|
| | 380 | init(); |
|---|
| | 381 | |
|---|
| | 382 | if ( !isset($url) ) { |
|---|
| | 383 | // error("fetch_rss called without a url"); |
|---|
| | 384 | return false; |
|---|
| | 385 | } |
|---|
| | 386 | |
|---|
| | 387 | // if cache is disabled |
|---|
| | 388 | if ( !MAGPIE_CACHE_ON ) { |
|---|
| | 389 | // fetch file, and parse it |
|---|
| | 390 | $resp = _fetch_remote_file( $url ); |
|---|
| | 391 | if ( is_success( $resp->status ) ) { |
|---|
| | 392 | return _response_to_rss( $resp ); |
|---|
| | 393 | } |
|---|
| | 394 | else { |
|---|
| | 395 | // error("Failed to fetch $url and cache is off"); |
|---|
| | 396 | return false; |
|---|
| | 397 | } |
|---|
| | 398 | } |
|---|
| | 399 | // else cache is ON |
|---|
| | 400 | else { |
|---|
| | 401 | // Flow |
|---|
| | 402 | // 1. check cache |
|---|
| | 403 | // 2. if there is a hit, make sure its fresh |
|---|
| | 404 | // 3. if cached obj fails freshness check, fetch remote |
|---|
| | 405 | // 4. if remote fails, return stale object, or error |
|---|
| | 406 | |
|---|
| | 407 | $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE ); |
|---|
| | 408 | |
|---|
| | 409 | if (MAGPIE_DEBUG and $cache->ERROR) { |
|---|
| | 410 | debug($cache->ERROR, E_USER_WARNING); |
|---|
| | 411 | } |
|---|
| | 412 | |
|---|
| | 413 | |
|---|
| | 414 | $cache_status = 0; // response of check_cache |
|---|
| | 415 | $request_headers = array(); // HTTP headers to send with fetch |
|---|
| | 416 | $rss = 0; // parsed RSS object |
|---|
| | 417 | $errormsg = 0; // errors, if any |
|---|
| | 418 | |
|---|
| | 419 | if (!$cache->ERROR) { |
|---|
| | 420 | // return cache HIT, MISS, or STALE |
|---|
| | 421 | $cache_status = $cache->check_cache( $url ); |
|---|
| | 422 | } |
|---|
| | 423 | |
|---|
| | 424 | // if object cached, and cache is fresh, return cached obj |
|---|
| | 425 | if ( $cache_status == 'HIT' ) { |
|---|
| | 426 | $rss = $cache->get( $url ); |
|---|
| | 427 | if ( isset($rss) and $rss ) { |
|---|
| | 428 | $rss->from_cache = 1; |
|---|
| | 429 | if ( MAGPIE_DEBUG > 1) { |
|---|
| | 430 | debug("MagpieRSS: Cache HIT", E_USER_NOTICE); |
|---|
| | 431 | } |
|---|
| | 432 | return $rss; |
|---|
| | 433 | } |
|---|
| | 434 | } |
|---|
| | 435 | |
|---|
| | 436 | // else attempt a conditional get |
|---|
| | 437 | |
|---|
| | 438 | // setup headers |
|---|
| | 439 | if ( $cache_status == 'STALE' ) { |
|---|
| | 440 | $rss = $cache->get( $url ); |
|---|
| | 441 | if ( $rss->etag and $rss->last_modified ) { |
|---|
| | 442 | $request_headers['If-None-Match'] = $rss->etag; |
|---|
| | 443 | $request_headers['If-Last-Modified'] = $rss->last_modified; |
|---|
| | 444 | } |
|---|
| | 445 | } |
|---|
| | 446 | |
|---|
| | 447 | $resp = _fetch_remote_file( $url, $request_headers ); |
|---|
| | 448 | |
|---|
| | 449 | if (isset($resp) and $resp) { |
|---|
| | 450 | if ($resp->status == '304' ) { |
|---|
| | 451 | // we have the most current copy |
|---|
| | 452 | if ( MAGPIE_DEBUG > 1) { |
|---|
| | 453 | debug("Got 304 for $url"); |
|---|
| | 454 | } |
|---|
| | 455 | // reset cache on 304 (at minutillo insistent prodding) |
|---|
| | 456 | $cache->set($url, $rss); |
|---|
| | 457 | return $rss; |
|---|
| | 458 | } |
|---|
| | 459 | elseif ( is_success( $resp->status ) ) { |
|---|
| | 460 | $rss = _response_to_rss( $resp ); |
|---|
| | 461 | if ( $rss ) { |
|---|
| | 462 | if (MAGPIE_DEBUG > 1) { |
|---|
| | 463 | debug("Fetch successful"); |
|---|
| | 464 | } |
|---|
| | 465 | // add object to cache |
|---|
| | 466 | $cache->set( $url, $rss ); |
|---|
| | 467 | return $rss; |
|---|
| | 468 | } |
|---|
| | 469 | } |
|---|
| | 470 | else { |
|---|
| | 471 | $errormsg = "Failed to fetch $url. "; |
|---|
| | 472 | if ( $resp->error ) { |
|---|
| | 473 | # compensate for Snoopy's annoying habbit to tacking |
|---|
| | 474 | # on '\n' |
|---|
| | 475 | $http_error = substr($resp->error, 0, -2); |
|---|
| | 476 | $errormsg .= "(HTTP Error: $http_error)"; |
|---|
| | 477 | } |
|---|
| | 478 | else { |
|---|
| | 479 | $errormsg .= "(HTTP Response: " . $resp->response_code .')'; |
|---|
| | 480 | } |
|---|
| | 481 | } |
|---|
| | 482 | } |
|---|
| | 483 | else { |
|---|
| | 484 | $errormsg = "Unable to retrieve RSS file for unknown reasons."; |
|---|
| | 485 | } |
|---|
| | 486 | |
|---|
| | 487 | // else fetch failed |
|---|
| | 488 | |
|---|
| | 489 | // attempt to return cached object |
|---|
| | 490 | if ($rss) { |
|---|
| | 491 | if ( MAGPIE_DEBUG ) { |
|---|
| | 492 | debug("Returning STALE object for $url"); |
|---|
| | 493 | } |
|---|
| | 494 | return $rss; |
|---|
| | 495 | } |
|---|
| | 496 | |
|---|
| | 497 | // else we totally failed |
|---|
| | 498 | // error( $errormsg ); |
|---|
| | 499 | |
|---|
| | 500 | return false; |
|---|
| | 501 | |
|---|
| | 502 | } // end if ( !MAGPIE_CACHE_ON ) { |
|---|
| | 503 | } // end fetch_rss() |
|---|
| | 504 | |
|---|
| | 505 | function _fetch_remote_file ($url, $headers = "" ) { |
|---|
| | 506 | // Snoopy is an HTTP client in PHP |
|---|
| | 507 | $client = new Snoopy(); |
|---|
| | 508 | $client->agent = MAGPIE_USER_AGENT; |
|---|
| | 509 | $client->read_timeout = MAGPIE_FETCH_TIME_OUT; |
|---|
| | 510 | $client->use_gzip = MAGPIE_USE_GZIP; |
|---|
| | 511 | if (is_array($headers) ) { |
|---|
| | 512 | $client->rawheaders = $headers; |
|---|
| | 513 | } |
|---|
| | 514 | |
|---|
| | 515 | @$client->fetch($url); |
|---|
| | 516 | return $client; |
|---|
| | 517 | |
|---|
| | 518 | } |
|---|
| | 519 | |
|---|
| | 520 | function _response_to_rss ($resp) { |
|---|
| | 521 | $rss = new MagpieRSS( $resp->results ); |
|---|
| | 522 | |
|---|
| | 523 | // if RSS parsed successfully |
|---|
| | 524 | if ( $rss and !$rss->ERROR) { |
|---|
| | 525 | |
|---|
| | 526 | // find Etag, and Last-Modified |
|---|
| | 527 | foreach($resp->headers as $h) { |
|---|
| | 528 | // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1" |
|---|
| | 529 | if (strpos($h, ": ")) { |
|---|
| | 530 | list($field, $val) = explode(": ", $h, 2); |
|---|
| | 531 | } |
|---|
| | 532 | else { |
|---|
| | 533 | $field = $h; |
|---|
| | 534 | $val = ""; |
|---|
| | 535 | } |
|---|
| | 536 | |
|---|
| | 537 | if ( $field == 'ETag' ) { |
|---|
| | 538 | $rss->etag = $val; |
|---|
| | 539 | } |
|---|
| | 540 | |
|---|
| | 541 | if ( $field == 'Last-Modified' ) { |
|---|
| | 542 | $rss->last_modified = $val; |
|---|
| | 543 | } |
|---|
| | 544 | } |
|---|
| | 545 | |
|---|
| | 546 | return $rss; |
|---|
| | 547 | } // else construct error message |
|---|
| | 548 | else { |
|---|
| | 549 | $errormsg = "Failed to parse RSS file."; |
|---|
| | 550 | |
|---|
| | 551 | if ($rss) { |
|---|
| | 552 | $errormsg .= " (" . $rss->ERROR . ")"; |
|---|
| | 553 | } |
|---|
| | 554 | // error($errormsg); |
|---|
| | 555 | |
|---|
| | 556 | return false; |
|---|
| | 557 | } // end if ($rss and !$rss->error) |
|---|
| | 558 | } |
|---|
| | 559 | |
|---|
| | 560 | /*=======================================================================*\ |
|---|
| | 561 | Function: init |
|---|
| | 562 | Purpose: setup constants with default values |
|---|
| | 563 | check for user overrides |
|---|
| | 564 | \*=======================================================================*/ |
|---|
| | 565 | function init () { |
|---|
| | 566 | if ( defined('MAGPIE_INITALIZED') ) { |
|---|
| | 567 | return; |
|---|
| | 568 | } |
|---|
| | 569 | else { |
|---|
| | 570 | define('MAGPIE_INITALIZED', 1); |
|---|
| | 571 | } |
|---|
| | 572 | |
|---|
| | 573 | if ( !defined('MAGPIE_CACHE_ON') ) { |
|---|
| | 574 | define('MAGPIE_CACHE_ON', 1); |
|---|
| | 575 | } |
|---|
| | 576 | |
|---|
| | 577 | if ( !defined('MAGPIE_CACHE_DIR') ) { |
|---|
| | 578 | define('MAGPIE_CACHE_DIR', './cache'); |
|---|
| | 579 | } |
|---|
| | 580 | |
|---|
| | 581 | if ( !defined('MAGPIE_CACHE_AGE') ) { |
|---|
| | 582 | define('MAGPIE_CACHE_AGE', 60*60); // one hour |
|---|
| | 583 | } |
|---|
| | 584 | |
|---|
| | 585 | if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) { |
|---|
| | 586 | define('MAGPIE_CACHE_FRESH_ONLY', 0); |
|---|
| | 587 | } |
|---|
| | 588 | |
|---|
| | 589 | if ( !defined('MAGPIE_DEBUG') ) { |
|---|
| | 590 | define('MAGPIE_DEBUG', 0); |
|---|
| | 591 | } |
|---|
| | 592 | |
|---|
| | 593 | if ( !defined('MAGPIE_USER_AGENT') ) { |
|---|
| | 594 | $ua = 'WordPress/' . $wp_version; |
|---|
| | 595 | |
|---|
| | 596 | if ( MAGPIE_CACHE_ON ) { |
|---|
| | 597 | $ua = $ua . ')'; |
|---|
| | 598 | } |
|---|
| | 599 | else { |
|---|
| | 600 | $ua = $ua . '; No cache)'; |
|---|
| | 601 | } |
|---|
| | 602 | |
|---|
| | 603 | define('MAGPIE_USER_AGENT', $ua); |
|---|
| | 604 | } |
|---|
| | 605 | |
|---|
| | 606 | if ( !defined('MAGPIE_FETCH_TIME_OUT') ) { |
|---|
| | 607 | define('MAGPIE_FETCH_TIME_OUT', 2); // 2 second timeout |
|---|
| | 608 | } |
|---|
| | 609 | |
|---|
| | 610 | // use gzip encoding to fetch rss files if supported? |
|---|
| | 611 | if ( !defined('MAGPIE_USE_GZIP') ) { |
|---|
| | 612 | define('MAGPIE_USE_GZIP', true); |
|---|
| | 613 | } |
|---|
| | 614 | } |
|---|
| | 615 | |
|---|
| | 616 | function is_info ($sc) { |
|---|
| | 617 | return $sc >= 100 && $sc < 200; |
|---|
| | 618 | } |
|---|
| | 619 | |
|---|
| | 620 | function is_success ($sc) { |
|---|
| | 621 | return $sc >= 200 && $sc < 300; |
|---|
| | 622 | } |
|---|
| | 623 | |
|---|
| | 624 | function is_redirect ($sc) { |
|---|
| | 625 | return $sc >= 300 && $sc < 400; |
|---|
| | 626 | } |
|---|
| | 627 | |
|---|
| | 628 | function is_error ($sc) { |
|---|
| | 629 | return $sc >= 400 && $sc < 600; |
|---|
| | 630 | } |
|---|
| | 631 | |
|---|
| | 632 | function is_client_error ($sc) { |
|---|
| | 633 | return $sc >= 400 && $sc < 500; |
|---|
| | 634 | } |
|---|
| | 635 | |
|---|
| | 636 | function is_server_error ($sc) { |
|---|
| | 637 | return $sc >= 500 && $sc < 600; |
|---|