| | 1 | <?php |
| | 2 | /**************************************************** |
| | 3 | SIMPLEPIE |
| | 4 | A PHP-Based RSS and Atom Feed Framework |
| | 5 | Takes the hard work out of managing a complete RSS/Atom solution. |
| | 6 | |
| | 7 | Version: "Razzleberry" |
| | 8 | Updated: 9 June 2006 |
| | 9 | Copyright: 2004-2006 Ryan Parman, Geoffrey Sneddon |
| | 10 | http://simplepie.org |
| | 11 | |
| | 12 | ***************************************************** |
| | 13 | LICENSE: |
| | 14 | |
| | 15 | GNU Lesser General Public License 2.1 (LGPL) |
| | 16 | http://creativecommons.org/licenses/LGPL/2.1/ |
| | 17 | |
| | 18 | ***************************************************** |
| | 19 | Please submit all bug reports and feature requests to the SimplePie forums. |
| | 20 | http://simplepie.org/support/ |
| | 21 | |
| | 22 | ****************************************************/ |
| | 23 | |
| | 24 | class SimplePie { |
| | 25 | |
| | 26 | // SimplePie Information |
| | 27 | var $name = 'SimplePie'; |
| | 28 | var $version = 'Razzleberry'; |
| | 29 | var $build = '20060609'; |
| | 30 | var $url = 'http://simplepie.org/'; |
| | 31 | var $useragent; |
| | 32 | var $linkback; |
| | 33 | |
| | 34 | // Run-time Variables |
| | 35 | var $rss_url; |
| | 36 | var $encoding; |
| | 37 | var $xml_dump = false; |
| | 38 | var $caching = true; |
| | 39 | var $max_minutes = 60; |
| | 40 | var $cache_location = './cache'; |
| | 41 | var $bypass_image_hotlink = 'i'; |
| | 42 | var $bypass_image_hotlink_page = false; |
| | 43 | var $replace_headers = false; |
| | 44 | var $remove_div = true; |
| | 45 | var $order_by_date = true; |
| | 46 | var $strip_ads = false; |
| | 47 | var $strip_htmltags = 'blink,body,doctype,embed,font,form,frame,frameset,html,iframe,input,marquee,meta,noscript,object,param,script,style'; |
| | 48 | var $strip_attributes = 'class,id,style,onclick,onmouseover,onmouseout,onfocus,onblur'; |
| | 49 | var $encode_instead_of_strip = false; |
| | 50 | var $http_proxy = false; |
| | 51 | |
| | 52 | // RSS Auto-Discovery Variables |
| | 53 | var $parsed_url; |
| | 54 | var $local = array(); |
| | 55 | var $elsewhere = array(); |
| | 56 | |
| | 57 | // XML Parsing Variables |
| | 58 | var $xml; |
| | 59 | var $tagName; |
| | 60 | var $insideItem; |
| | 61 | var $insideChannel; |
| | 62 | var $insideImage; |
| | 63 | var $insideAuthor; |
| | 64 | var $itemNumber = 0; |
| | 65 | var $authorNumber = 0; |
| | 66 | var $categoryNumber = 0; |
| | 67 | var $enclosureNumber = 0; |
| | 68 | var $linkNumber = 0; |
| | 69 | var $itemLinkNumber = 0; |
| | 70 | var $data = false; |
| | 71 | var $attribs; |
| | 72 | var $xmldata; |
| | 73 | var $feed_xmlbase; |
| | 74 | var $item_xmlbase; |
| | 75 | var $xhtml_prefix; |
| | 76 | |
| | 77 | |
| | 78 | |
| | 79 | |
| | 80 | /**************************************************** |
| | 81 | CONSTRUCTOR |
| | 82 | Initiates a couple of variables. Accepts feed_url, cache_location, |
| | 83 | and cache_max_minutes. |
| | 84 | ****************************************************/ |
| | 85 | function SimplePie($feed_url = null, $cache_location = null, $cache_max_minutes = null) { |
| | 86 | $this->useragent = $this->name . '/' . $this->version . ' (Feed Parser; ' . $this->url . '; Allow like Gecko) Build/' . $this->build; |
| | 87 | $this->linkback = '<a href="' . $this->url . '" title="' . $this->name . ' ' . $this->version . '">' . $this->name . '</a>'; |
| | 88 | |
| | 89 | if (!is_null($feed_url)) { |
| | 90 | $this->feed_url($feed_url); |
| | 91 | } |
| | 92 | |
| | 93 | if (!is_null($cache_location)) { |
| | 94 | $this->cache_location($cache_location); |
| | 95 | } |
| | 96 | |
| | 97 | if (!is_null($cache_max_minutes)) { |
| | 98 | $this->cache_max_minutes($cache_max_minutes); |
| | 99 | } |
| | 100 | |
| | 101 | if (!is_null($feed_url)) { |
| | 102 | return $this->init(); |
| | 103 | } |
| | 104 | |
| | 105 | // If we've passed an xmldump variable in the URL, snap into XMLdump mode |
| | 106 | if (isset($_GET['xmldump'])) { |
| | 107 | $this->enable_xmldump($_GET['xmldump']); |
| | 108 | } |
| | 109 | } |
| | 110 | |
| | 111 | |
| | 112 | |
| | 113 | |
| | 114 | /**************************************************** |
| | 115 | CONFIGURE OPTIONS |
| | 116 | Set various options (feed URL, XML dump, caching, etc.) |
| | 117 | ****************************************************/ |
| | 118 | // Feed URL |
| | 119 | function feed_url($url) { |
| | 120 | $url = $this->fix_protocol($url, 1); |
| | 121 | $this->rss_url = $url; |
| | 122 | return true; |
| | 123 | } |
| | 124 | |
| | 125 | // XML Dump |
| | 126 | function enable_xmldump($enable) { |
| | 127 | $this->xml_dump = (bool) $enable; |
| | 128 | return true; |
| | 129 | } |
| | 130 | |
| | 131 | // Bypass Image Hotlink |
| | 132 | function bypass_image_hotlink($getvar='i') { |
| | 133 | $this->bypass_image_hotlink = (string) $getvar; |
| | 134 | return true; |
| | 135 | } |
| | 136 | |
| | 137 | // Bypass Image Hotlink Page |
| | 138 | function bypass_image_hotlink_page($page = false) { |
| | 139 | $this->bypass_image_hotlink_page = (string) $page; |
| | 140 | return true; |
| | 141 | } |
| | 142 | |
| | 143 | // Caching |
| | 144 | function enable_caching($enable) { |
| | 145 | $this->caching = (bool) $enable; |
| | 146 | return true; |
| | 147 | } |
| | 148 | |
| | 149 | // Cache Timeout |
| | 150 | function cache_max_minutes($minutes) { |
| | 151 | $this->max_minutes = (int) $minutes; |
| | 152 | return true; |
| | 153 | } |
| | 154 | |
| | 155 | // Cache Location |
| | 156 | function cache_location($location) { |
| | 157 | $this->cache_location = (string) $location; |
| | 158 | return true; |
| | 159 | } |
| | 160 | |
| | 161 | // Replace H1, H2, and H3 tags with the less important H4 tags. |
| | 162 | function replace_headers($enable) { |
| | 163 | $this->replace_headers = (bool) $enable; |
| | 164 | return true; |
| | 165 | } |
| | 166 | |
| | 167 | // Remove outer div in XHTML content within Atom |
| | 168 | function remove_div($enable) { |
| | 169 | $this->remove_div = (bool) $enable; |
| | 170 | return true; |
| | 171 | } |
| | 172 | |
| | 173 | // Order the items by date |
| | 174 | function order_by_date($enable) { |
| | 175 | $this->order_by_date = (bool) $enable; |
| | 176 | return true; |
| | 177 | } |
| | 178 | |
| | 179 | // Strip out certain well-known ads |
| | 180 | function strip_ads($enable) { |
| | 181 | $this->strip_ads = (bool) $enable; |
| | 182 | return true; |
| | 183 | } |
| | 184 | |
| | 185 | // Strip out potentially dangerous tags |
| | 186 | function strip_htmltags($tags, $encode=false) { |
| | 187 | $this->strip_htmltags = (string) $tags; |
| | 188 | $this->encode_instead_of_strip = (bool) $encode; |
| | 189 | return true; |
| | 190 | } |
| | 191 | |
| | 192 | // Encode dangerous tags instead of stripping them |
| | 193 | function encode_instead_of_strip($encode=true) { |
| | 194 | $this->encode_instead_of_strip = (bool) $encode; |
| | 195 | return true; |
| | 196 | } |
| | 197 | |
| | 198 | // Strip out potentially dangerous attributes |
| | 199 | function strip_attributes($attrib) { |
| | 200 | $this->strip_attributes = (string) $attrib; |
| | 201 | return true; |
| | 202 | } |
| | 203 | |
| | 204 | // Set HTTP proxy for curl |
| | 205 | function proxy($host_port) { |
| | 206 | $this->http_proxy = (string) $host_port; |
| | 207 | return true; |
| | 208 | } |
| | 209 | |
| | 210 | // Set HTTP User-agent prefix |
| | 211 | function user_agent($prefix) { |
| | 212 | $this->useragent = (string) $prefix . " " . $this->useragent; |
| | 213 | return true; |
| | 214 | } |
| | 215 | |
| | 216 | /**************************************************** |
| | 217 | MAIN INITIALIZATION FUNCTION |
| | 218 | Rewrites the feed so that it actually resembles XML, processes the XML, |
| | 219 | and builds an array from the feed. |
| | 220 | ****************************************************/ |
| | 221 | function init() { |
| | 222 | // If Bypass Image Hotlink is enabled, send image to the page and quit. |
| | 223 | if ($this->bypass_image_hotlink) { |
| | 224 | if (isset($_GET[$this->bypass_image_hotlink]) && !empty($_GET[$this->bypass_image_hotlink])) { |
| | 225 | $this->display_image($_GET[$this->bypass_image_hotlink]); |
| | 226 | exit; |
| | 227 | } |
| | 228 | } |
| | 229 | |
| | 230 | // If Bypass Image Hotlink is enabled, send image to the page and quit. |
| | 231 | if (isset($_GET['js'])) { |
| | 232 | |
| | 233 | // JavaScript for the Odeo Player |
| | 234 | $embed=''; |
| | 235 | $embed.='function embed_odeo(link) {'; |
| | 236 | $embed.='document.writeln(\''; |
| | 237 | $embed.='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '; |
| | 238 | $embed.=' codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" '; |
| | 239 | $embed.=' width="440" '; |
| | 240 | $embed.=' height="80" '; |
| | 241 | $embed.=' align="middle">'; |
| | 242 | $embed.='<param name="movie" value="http://odeo.com/flash/audio_player_fullsize.swf" />'; |
| | 243 | $embed.='<param name="allowScriptAccess" value="any" />'; |
| | 244 | $embed.='<param name="quality" value="high">'; |
| | 245 | $embed.='<param name="wmode" value="transparent">'; |
| | 246 | $embed.='<param name="flashvars" value="valid_sample_rate=true&external_url=\'+link+\'" />'; |
| | 247 | $embed.='<embed src="http://odeo.com/flash/audio_player_fullsize.swf" '; |
| | 248 | $embed.=' pluginspage="http://www.macromedia.com/go/getflashplayer" '; |
| | 249 | $embed.=' type="application/x-shockwave-flash" '; |
| | 250 | $embed.=' quality="high" '; |
| | 251 | $embed.=' width="440" '; |
| | 252 | $embed.=' height="80" '; |
| | 253 | $embed.=' wmode="transparent" '; |
| | 254 | $embed.=' allowScriptAccess="any" '; |
| | 255 | $embed.=' flashvars="valid_sample_rate=true&external_url=\'+link+\'">'; |
| | 256 | $embed.='</embed>'; |
| | 257 | $embed.='</object>'; |
| | 258 | $embed.='\');'; |
| | 259 | $embed.='}'; |
| | 260 | |
| | 261 | $embed.="\r\n"; |
| | 262 | |
| | 263 | $embed.='function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {'; |
| | 264 | $embed.='document.writeln(\''; |
| | 265 | $embed.='<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" '; |
| | 266 | $embed.=' style="cursor:hand; cursor:pointer;" '; |
| | 267 | $embed.=' type="\'+type+\'" '; |
| | 268 | $embed.=' codebase="http://www.apple.com/qtactivex/qtplugin.cab" '; |
| | 269 | $embed.=' bgcolor="\'+bgcolor+\'" '; |
| | 270 | $embed.=' width="\'+width+\'" '; |
| | 271 | $embed.=' height="\'+height+\'">'; |
| | 272 | $embed.='<param name="href" value="\'+link+\'" />'; |
| | 273 | $embed.='<param name="src" value="\'+placeholder+\'" />'; |
| | 274 | $embed.='<param name="autoplay" value="false" />'; |
| | 275 | $embed.='<param name="target" value="myself" />'; |
| | 276 | $embed.='<param name="controller" value="false" />'; |
| | 277 | $embed.='<param name="loop" value="\'+loop+\'" />'; |
| | 278 | $embed.='<param name="scale" value="aspect" />'; |
| | 279 | $embed.='<param name="bgcolor" value="\'+bgcolor+\'">'; |
| | 280 | $embed.='<embed type="\'+type+\'" '; |
| | 281 | $embed.=' style="cursor:hand; cursor:pointer;" '; |
| | 282 | $embed.=' href="\'+link+\'" '; |
| | 283 | $embed.=' src="\'+placeholder+\'"'; |
| | 284 | $embed.=' width="\'+width+\'" '; |
| | 285 | $embed.=' height="\'+height+\'" '; |
| | 286 | $embed.=' autoplay="false" '; |
| | 287 | $embed.=' target="myself" '; |
| | 288 | $embed.=' controller="false" '; |
| | 289 | $embed.=' loop="\'+loop+\'" '; |
| | 290 | $embed.=' scale="aspect" '; |
| | 291 | $embed.=' bgcolor="\'+bgcolor+\'" '; |
| | 292 | $embed.=' pluginspage="http://www.apple.com/quicktime/download/">'; |
| | 293 | $embed.='</embed>'; |
| | 294 | $embed.='</object>'; |
| | 295 | $embed.='\');'; |
| | 296 | $embed.='}'; |
| | 297 | |
| | 298 | $embed.="\r\n"; |
| | 299 | |
| | 300 | $embed.='function embed_flash(bgcolor, width, height, link, loop, type) {'; |
| | 301 | $embed.='document.writeln(\''; |
| | 302 | $embed.='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" '; |
| | 303 | $embed.=' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" '; |
| | 304 | $embed.=' bgcolor="\'+bgcolor+\'" '; |
| | 305 | $embed.=' width="\'+width+\'" '; |
| | 306 | $embed.=' height="\'+height+\'">'; |
| | 307 | $embed.='<param name="movie" value="\'+link+\'">'; |
| | 308 | $embed.='<param name="quality" value="high">'; |
| | 309 | $embed.='<param name="loop" value="\'+loop+\'">'; |
| | 310 | $embed.='<param name="bgcolor" value="\'+bgcolor+\'">'; |
| | 311 | $embed.='<embed src="\'+link+\'" '; |
| | 312 | $embed.=' pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" '; |
| | 313 | $embed.=' type="\'+type+\'" '; |
| | 314 | $embed.=' quality="high" '; |
| | 315 | $embed.=' width="\'+width+\'" '; |
| | 316 | $embed.=' height="\'+height+\'" '; |
| | 317 | $embed.=' bgcolor="\'+bgcolor+\'" '; |
| | 318 | $embed.=' loop="\'+loop+\'">'; |
| | 319 | $embed.='</embed>'; |
| | 320 | $embed.='</object>'; |
| | 321 | $embed.='\');'; |
| | 322 | $embed.='}'; |
| | 323 | |
| | 324 | $embed.="\r\n"; |
| | 325 | |
| | 326 | $embed.='function embed_wmedia(width, height, link) {'; |
| | 327 | $embed.='document.writeln(\''; |
| | 328 | $embed.='<object classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95"'; |
| | 329 | $embed.=' type="application/x-oleobject"'; |
| | 330 | $embed.=' width="\'+width+\'"'; |
| | 331 | $embed.=' height="\'+height+\'"'; |
| | 332 | $embed.=' standby="Loading Windows Media Player...">'; |
| | 333 | $embed.='<param name="FileName" value="\'+link+\'">'; |
| | 334 | $embed.='<param name="autosize" value="true">'; |
| | 335 | $embed.='<param name="ShowControls" value="true">'; |
| | 336 | $embed.='<param name="ShowStatusBar" value="false">'; |
| | 337 | $embed.='<param name="ShowDisplay" value="false">'; |
| | 338 | $embed.='<param name="autostart" value="false">'; |
| | 339 | $embed.='<embed type="application/x-mplayer2" '; |
| | 340 | $embed.=' src="\'+link+\'" '; |
| | 341 | $embed.=' autosize="1" '; |
| | 342 | $embed.=' width="\'+width+\'" '; |
| | 343 | $embed.=' height="\'+height+\'" '; |
| | 344 | $embed.=' showcontrols="1" '; |
| | 345 | $embed.=' showstatusbar="0" '; |
| | 346 | $embed.=' showdisplay="0" '; |
| | 347 | $embed.=' autostart="0">'; |
| | 348 | $embed.='</embed>'; |
| | 349 | $embed.='</object>'; |
| | 350 | $embed.='\');'; |
| | 351 | $embed.='}'; |
| | 352 | |
| | 353 | $embed.="\r\n"; |
| | 354 | |
| | 355 | // enable gzip compression |
| | 356 | ob_start ("ob_gzhandler"); |
| | 357 | header("Content-type: text/javascript; charset: UTF-8"); |
| | 358 | header("Cache-Control: must-revalidate"); |
| | 359 | header("Expires: " . gmdate("D, d M Y H:i:s", time() + 60*60*24) . " GMT"); |
| | 360 | echo $embed; |
| | 361 | exit; |
| | 362 | } |
| | 363 | |
| | 364 | // Clear all outdated cache from the server's cache folder |
| | 365 | $this->clear_cache($this->cache_location, $this->max_minutes); |
| | 366 | |
| | 367 | if (!empty($this->rss_url)) { |
| | 368 | // Read the XML file for processing. |
| | 369 | $cache_filename = $this->cache_location . '/' . urlencode($this->rss_url) . '.spc'; |
| | 370 | if ($this->caching && !$this->xml_dump && substr($this->rss_url, 0, 7) == 'http://' && file_exists($cache_filename)) { |
| | 371 | if ($fp = fopen($cache_filename, 'r')) { |
| | 372 | $data = ''; |
| | 373 | while (!feof($fp)) { |
| | 374 | $data .= fread($fp, 2048); |
| | 375 | } |
| | 376 | fclose($fp); |
| | 377 | $mp_rss = unserialize($data); |
| | 378 | if (empty($mp_rss)) { |
| | 379 | $this->caching = false; |
| | 380 | return $this->init(); |
| | 381 | } else if (isset($mp_rss['feed_url'])) { |
| | 382 | $this->rss_url = $mp_rss['feed_url']; |
| | 383 | return $this->init(); |
| | 384 | } else { |
| | 385 | $this->data = $mp_rss; |
| | 386 | return true; |
| | 387 | } |
| | 388 | } else { |
| | 389 | $this->caching = false; |
| | 390 | return $this->init(); |
| | 391 | } |
| | 392 | } else { |
| | 393 | // Get the file |
| | 394 | $mp_rss = $this->get_file($this->rss_url); |
| | 395 | |
| | 396 | // Check if file is a feed or a webpage |
| | 397 | // If it's a webpage, auto-discover the feed and re-pass it to init() |
| | 398 | $discovery = $this->rss_locator($mp_rss, $this->rss_url); |
| | 399 | if ($discovery) { |
| | 400 | if ($discovery != $this->rss_url) { |
| | 401 | $this->rss_url = $discovery; |
| | 402 | if ($this->caching && substr($this->rss_url, 0, 7) == 'http://') { |
| | 403 | if ($this->is_writeable_createable($cache_filename)) { |
| | 404 | $fp = fopen($cache_filename, 'w'); |
| | 405 | fwrite($fp, serialize(array('feed_url' => $discovery))); |
| | 406 | fclose($fp); |
| | 407 | } |
| | 408 | else trigger_error("$cache_filename is not writeable", E_USER_WARNING); |
| | 409 | } |
| | 410 | return $this->init(); |
| | 411 | } |
| | 412 | } else { |
| | 413 | $this->sp_error("A feed could not be found at $this->rss_url", E_USER_WARNING, __FILE__, __LINE__); |
| | 414 | return false; |
| | 415 | } |
| | 416 | |
| | 417 | // Trim out any whitespace at the beginning or the end of the file |
| | 418 | $mp_rss = trim($mp_rss); |
| | 419 | |
| | 420 | // Get encoding |
| | 421 | // Attempt to support everything from libiconv (http://www.gnu.org/software/libiconv/) |
| | 422 | // Support everything from mbstring (http://www.php.net/manual/en/ref.mbstring.php#mbstring.supported-encodings) |
| | 423 | $use_iconv = false; |
| | 424 | $use_mbstring = false; |
| | 425 | $utf8_fail = true; |
| | 426 | if (preg_match('/encoding=["|\'](.*)["|\']/Ui', $mp_rss, $encoding)) { |
| | 427 | $match = $encoding; |
| | 428 | switch (strtolower($encoding[1])) { |
| | 429 | |
| | 430 | // 7bit |
| | 431 | case '7bit': |
| | 432 | case '7-bit': |
| | 433 | $encoding = '7bit'; |
| | 434 | $use_mbstring = true; |
| | 435 | break; |
| | 436 | |
| | 437 | // 8bit |
| | 438 | case '8bit': |
| | 439 | case '8-bit': |
| | 440 | $encoding = '8bit'; |
| | 441 | $use_mbstring = true; |
| | 442 | break; |
| | 443 | |
| | 444 | // ARMSCII-8 |
| | 445 | case 'armscii-8': |
| | 446 | case 'armscii': |
| | 447 | $encoding = 'ARMSCII-8'; |
| | 448 | $use_iconv = true; |
| | 449 | break; |
| | 450 | |
| | 451 | // ASCII |
| | 452 | case 'us-ascii': |
| | 453 | case 'ascii': |
| | 454 | $encoding = 'US-ASCII'; |
| | 455 | $use_iconv = true; |
| | 456 | $use_mbstring = true; |
| | 457 | $utf8_fail = false; |
| | 458 | break; |
| | 459 | |
| | 460 | // BASE64 |
| | 461 | case 'base64': |
| | 462 | case 'base-64': |
| | 463 | $encoding = 'BASE64'; |
| | 464 | $use_mbstring = true; |
| | 465 | break; |
| | 466 | |
| | 467 | // Big5 - Traditional Chinese, mainly used in Taiwan |
| | 468 | case 'big5': |
| | 469 | case '950': |
| | 470 | $encoding = 'BIG5'; |