1
0
mirror of https://github.com/moparisthebest/wallabag synced 2024-12-18 05:32:23 -05:00

Add full-text-rss lib

This commit is contained in:
Vincent Jousse 2013-12-10 16:19:15 +01:00
parent 6667c8562e
commit fffaf558fc
912 changed files with 48902 additions and 0 deletions

View File

@ -0,0 +1,16 @@
<?php
namespace Poche\Util;
class DummySingleItem {
public $url;
function __construct($url) { $this->url = $url; }
public function get_permalink() { return $this->url; }
public function get_title() { return null; }
public function get_date($format='') { return false; }
public function get_author($key=0) { return null; }
public function get_authors() { return null; }
public function get_description() { return ''; }
public function get_enclosure($key=0, $prefer=null) { return null; }
public function get_enclosures() { return null; }
public function get_categories() { return null; }
}

View File

@ -0,0 +1,13 @@
<?php
namespace Poche\Util;
class DummySingleItemFeed {
public $item;
function __construct($url) { $this->item = new DummySingleItem($url); }
public function get_title() { return ''; }
public function get_description() { return 'Content extracted from '.$this->item->url; }
public function get_link() { return $this->item->url; }
public function get_language() { return false; }
public function get_image_url() { return false; }
public function get_items($start=0, $max=1) { return array(0=>$this->item); }
}

264
src/Poche/Util/Encoding.php Normal file
View File

@ -0,0 +1,264 @@
<?php
/**
* @author "Sebastián Grignoli" <grignoli@framework2.com.ar>
* @package Encoding
* @version 1.1
* @link http://www.framework2.com.ar/dzone/forceUTF8-es/
* @example http://www.framework2.com.ar/dzone/forceUTF8-es/
*/
namespace Poche\Util;
class Encoding {
protected static $win1252ToUtf8 = array(
128 => "\xe2\x82\xac",
130 => "\xe2\x80\x9a",
131 => "\xc6\x92",
132 => "\xe2\x80\x9e",
133 => "\xe2\x80\xa6",
134 => "\xe2\x80\xa0",
135 => "\xe2\x80\xa1",
136 => "\xcb\x86",
137 => "\xe2\x80\xb0",
138 => "\xc5\xa0",
139 => "\xe2\x80\xb9",
140 => "\xc5\x92",
142 => "\xc5\xbd",
145 => "\xe2\x80\x98",
146 => "\xe2\x80\x99",
147 => "\xe2\x80\x9c",
148 => "\xe2\x80\x9d",
149 => "\xe2\x80\xa2",
150 => "\xe2\x80\x93",
151 => "\xe2\x80\x94",
152 => "\xcb\x9c",
153 => "\xe2\x84\xa2",
154 => "\xc5\xa1",
155 => "\xe2\x80\xba",
156 => "\xc5\x93",
158 => "\xc5\xbe",
159 => "\xc5\xb8"
);
protected static $brokenUtf8ToUtf8 = array(
"\xc2\x80" => "\xe2\x82\xac",
"\xc2\x82" => "\xe2\x80\x9a",
"\xc2\x83" => "\xc6\x92",
"\xc2\x84" => "\xe2\x80\x9e",
"\xc2\x85" => "\xe2\x80\xa6",
"\xc2\x86" => "\xe2\x80\xa0",
"\xc2\x87" => "\xe2\x80\xa1",
"\xc2\x88" => "\xcb\x86",
"\xc2\x89" => "\xe2\x80\xb0",
"\xc2\x8a" => "\xc5\xa0",
"\xc2\x8b" => "\xe2\x80\xb9",
"\xc2\x8c" => "\xc5\x92",
"\xc2\x8e" => "\xc5\xbd",
"\xc2\x91" => "\xe2\x80\x98",
"\xc2\x92" => "\xe2\x80\x99",
"\xc2\x93" => "\xe2\x80\x9c",
"\xc2\x94" => "\xe2\x80\x9d",
"\xc2\x95" => "\xe2\x80\xa2",
"\xc2\x96" => "\xe2\x80\x93",
"\xc2\x97" => "\xe2\x80\x94",
"\xc2\x98" => "\xcb\x9c",
"\xc2\x99" => "\xe2\x84\xa2",
"\xc2\x9a" => "\xc5\xa1",
"\xc2\x9b" => "\xe2\x80\xba",
"\xc2\x9c" => "\xc5\x93",
"\xc2\x9e" => "\xc5\xbe",
"\xc2\x9f" => "\xc5\xb8"
);
protected static $utf8ToWin1252 = array(
"\xe2\x82\xac" => "\x80",
"\xe2\x80\x9a" => "\x82",
"\xc6\x92" => "\x83",
"\xe2\x80\x9e" => "\x84",
"\xe2\x80\xa6" => "\x85",
"\xe2\x80\xa0" => "\x86",
"\xe2\x80\xa1" => "\x87",
"\xcb\x86" => "\x88",
"\xe2\x80\xb0" => "\x89",
"\xc5\xa0" => "\x8a",
"\xe2\x80\xb9" => "\x8b",
"\xc5\x92" => "\x8c",
"\xc5\xbd" => "\x8e",
"\xe2\x80\x98" => "\x91",
"\xe2\x80\x99" => "\x92",
"\xe2\x80\x9c" => "\x93",
"\xe2\x80\x9d" => "\x94",
"\xe2\x80\xa2" => "\x95",
"\xe2\x80\x93" => "\x96",
"\xe2\x80\x94" => "\x97",
"\xcb\x9c" => "\x98",
"\xe2\x84\xa2" => "\x99",
"\xc5\xa1" => "\x9a",
"\xe2\x80\xba" => "\x9b",
"\xc5\x93" => "\x9c",
"\xc5\xbe" => "\x9e",
"\xc5\xb8" => "\x9f"
);
static function toUTF8($text){
/**
* Function Encoding::toUTF8
*
* This function leaves UTF8 characters alone, while converting almost all non-UTF8 to UTF8.
*
* It assumes that the encoding of the original string is either Windows-1252 or ISO 8859-1.
*
* It may fail to convert characters to UTF-8 if they fall into one of these scenarios:
*
* 1) when any of these characters: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
* are followed by any of these: ("group B")
* ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶•¸¹º»¼½¾¿
* For example: %ABREPRESENT%C9%BB. «REPRESENTÉ»
* The "«" (%AB) character will be converted, but the "É" followed by "»" (%C9%BB)
* is also a valid unicode character, and will be left unchanged.
*
* 2) when any of these: àáâãäåæçèéêëìíîï are followed by TWO chars from group B,
* 3) when any of these: ðñòó are followed by THREE chars from group B.
*
* @name toUTF8
* @param string $text Any string.
* @return string The same string, UTF8 encoded
*
*/
if(is_array($text))
{
foreach($text as $k => $v)
{
$text[$k] = self::toUTF8($v);
}
return $text;
} elseif(is_string($text)) {
$max = strlen($text);
$buf = "";
for($i = 0; $i < $max; $i++){
$c1 = $text{$i};
if($c1>="\xc0"){ //Should be converted to UTF8, if it's not UTF8 already
$c2 = $i+1 >= $max? "\x00" : $text{$i+1};
$c3 = $i+2 >= $max? "\x00" : $text{$i+2};
$c4 = $i+3 >= $max? "\x00" : $text{$i+3};
if($c1 >= "\xc0" & $c1 <= "\xdf"){ //looks like 2 bytes UTF8
if($c2 >= "\x80" && $c2 <= "\xbf"){ //yeah, almost sure it's UTF8 already
$buf .= $c1 . $c2;
$i++;
} else { //not valid UTF8. Convert it.
$cc1 = (chr(ord($c1) / 64) | "\xc0");
$cc2 = ($c1 & "\x3f") | "\x80";
$buf .= $cc1 . $cc2;
}
} elseif($c1 >= "\xe0" & $c1 <= "\xef"){ //looks like 3 bytes UTF8
if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf"){ //yeah, almost sure it's UTF8 already
$buf .= $c1 . $c2 . $c3;
$i = $i + 2;
} else { //not valid UTF8. Convert it.
$cc1 = (chr(ord($c1) / 64) | "\xc0");
$cc2 = ($c1 & "\x3f") | "\x80";
$buf .= $cc1 . $cc2;
}
} elseif($c1 >= "\xf0" & $c1 <= "\xf7"){ //looks like 4 bytes UTF8
if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf" && $c4 >= "\x80" && $c4 <= "\xbf"){ //yeah, almost sure it's UTF8 already
$buf .= $c1 . $c2 . $c3;
$i = $i + 2;
} else { //not valid UTF8. Convert it.
$cc1 = (chr(ord($c1) / 64) | "\xc0");
$cc2 = ($c1 & "\x3f") | "\x80";
$buf .= $cc1 . $cc2;
}
} else { //doesn't look like UTF8, but should be converted
$cc1 = (chr(ord($c1) / 64) | "\xc0");
$cc2 = (($c1 & "\x3f") | "\x80");
$buf .= $cc1 . $cc2;
}
} elseif(($c1 & "\xc0") == "\x80"){ // needs conversion
if(isset(self::$win1252ToUtf8[ord($c1)])) { //found in Windows-1252 special cases
$buf .= self::$win1252ToUtf8[ord($c1)];
} else {
$cc1 = (chr(ord($c1) / 64) | "\xc0");
$cc2 = (($c1 & "\x3f") | "\x80");
$buf .= $cc1 . $cc2;
}
} else { // it doesn't need convesion
$buf .= $c1;
}
}
return $buf;
} else {
return $text;
}
}
static function toWin1252($text) {
if(is_array($text)) {
foreach($text as $k => $v) {
$text[$k] = self::toWin1252($v);
}
return $text;
} elseif(is_string($text)) {
return utf8_decode(str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), self::toUTF8($text)));
} else {
return $text;
}
}
static function toISO8859($text) {
return self::toWin1252($text);
}
static function toLatin1($text) {
return self::toWin1252($text);
}
static function fixUTF8($text){
if(is_array($text)) {
foreach($text as $k => $v) {
$text[$k] = self::fixUTF8($v);
}
return $text;
}
$last = "";
while($last <> $text){
$last = $text;
$text = self::toUTF8(utf8_decode(str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), $text)));
}
$text = self::toUTF8(utf8_decode(str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), $text)));
return $text;
}
static function UTF8FixWin1252Chars($text){
// If you received an UTF-8 string that was converted from Windows-1252 as it was ISO8859-1
// (ignoring Windows-1252 chars from 80 to 9F) use this function to fix it.
// See: http://en.wikipedia.org/wiki/Windows-1252
return str_replace(array_keys(self::$brokenUtf8ToUtf8), array_values(self::$brokenUtf8ToUtf8), $text);
}
static function removeBOM($str=""){
if(substr($str, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
$str=substr($str, 3);
}
return $str;
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Poche\Util;
class PocheReadability extends Readability
{
/**
* Get the article title as an H1.
*
* @return DOMElement
*/
protected function getArticleTitle() {
$curTitle = '';
$origTitle = '';
try {
$curTitle = $origTitle = $this->getInnerText($this->dom->getElementsByTagName('title')->item(0));
} catch(Exception $e) {}
if (preg_match('/ [\|\-] /', $curTitle))
{
$curTitle = preg_replace('/(.*)[\|\-] .*/i', '$1', $origTitle);
if (count(explode(' ', $curTitle)) < 3) {
$curTitle = preg_replace('/[^\|\-]*[\|\-](.*)/i', '$1', $origTitle);
}
}
else if(strlen($curTitle) > 150 || strlen($curTitle) < 15)
{
$hOnes = $this->dom->getElementsByTagName('h1');
if($hOnes->length == 1)
{
$curTitle = $this->getInnerText($hOnes->item(0));
}
}
$curTitle = trim($curTitle);
if (count(explode(' ', $curTitle)) <= 4) {
$curTitle = $origTitle;
}
$articleTitle = $this->dom->createElement('h1');
$articleTitle->innerHTML = $curTitle;
return $articleTitle;
}
}

397
src/Poche/Util/Url.php Normal file
View File

@ -0,0 +1,397 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
*/
namespace Poche\Util;
class Url
{
public $url;
private $fingerprints = array(
// Posterous
'<meta name="generator" content="Posterous"' => array('hostname'=>'fingerprint.posterous.com', 'head'=>true),
// Blogger
'<meta content=\'blogger\' name=\'generator\'' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
'<meta name="generator" content="Blogger"' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
// WordPress (self-hosted and hosted)
'<meta name="generator" content="WordPress' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true)
);
private $user_agents = array( 'lifehacker.com' => 'PHP/5.2',
'gawker.com' => 'PHP/5.2',
'deadspin.com' => 'PHP/5.2',
'kotaku.com' => 'PHP/5.2',
'jezebel.com' => 'PHP/5.2',
'io9.com' => 'PHP/5.2',
'jalopnik.com' => 'PHP/5.2',
'gizmodo.com' => 'PHP/5.2',
'.wikipedia.org' => 'Mozilla/5.2'
);
private $content_type_exc = array(
'application/pdf' => array('action'=>'link', 'name'=>'PDF'),
'image' => array('action'=>'link', 'name'=>'Image'),
'audio' => array('action'=>'link', 'name'=>'Audio'),
'video' => array('action'=>'link', 'name'=>'Video')
);
private $rewrite_url = array(
// Rewrite public Google Docs URLs to point to HTML view:
// if a URL contains docs.google.com, replace /Doc? with /View?
'docs.google.com' => array('/Doc?' => '/View?'),
'tnr.com' => array('tnr.com/article/' => 'tnr.com/print/article/'),
'.m.wikipedia.org' => array('.m.wikipedia.org' => '.wikipedia.org')
);
private $rewrite_relative_urls = true;
private $error_message = '[unable to retrieve full-text content]';
function __construct($url)
{
$this->url = base64_decode($url);
}
public function getUrl() {
return $this->url;
}
public function setUrl($url) {
$this->url = $url;
}
public function isCorrect() {
return filter_var($this->url, FILTER_VALIDATE_URL) !== FALSE;
}
public function extract() {
global $http, $extractor;
$extractor = new \ContentExtractor(dirname(__FILE__).'/../3rdparty/site_config/custom', dirname(__FILE__).'/../3rdparty/site_config/standard');
$extractor->fingerprints = $this->fingerprints;
$http = new \HumbleHttpAgent();
$http->userAgentMap = $this->user_agents;
$http->headerOnlyTypes = array_keys($this->content_type_exc);
$http->rewriteUrls = $this->rewrite_url;
$http->userAgentDefault = \HumbleHttpAgent::UA_PHP;
// configure SimplePie HTTP extension class to use our HumbleHttpAgent instance
\SimplePie_HumbleHttpAgent::set_agent($http);
$feed = new \SimplePie();
// some feeds use the text/html content type - force_feed tells SimplePie to process anyway
$feed->force_feed(true);
$feed->set_file_class('SimplePie_HumbleHttpAgent');
$feed->feed_url = $this->url;
$feed->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE);
$feed->set_timeout(20);
$feed->enable_cache(false);
$feed->set_stupidly_fast(true);
$feed->enable_order_by_date(false); // we don't want to do anything to the feed
$feed->set_url_replacements(array());
// initialise the feed
// the @ suppresses notices which on some servers causes a 500 internal server error
$result = @$feed->init();
if ($result && (!is_array($feed->data) || count($feed->data) == 0)) {
die('Sorry, no feed items found');
}
// from now on, we'll identify ourselves as a browser
$http->userAgentDefault = \HumbleHttpAgent::UA_BROWSER;
unset($feed, $result);
$feed = new DummySingleItemFeed($this->url);
$items = $feed->get_items(0, 1);
// Request all feed items in parallel (if supported)
$urls_sanitized = array();
$urls = array();
foreach ($items as $key => $item) {
$permalink = htmlspecialchars_decode($item->get_permalink());
// Colons in URL path segments get encoded by SimplePie, yet some sites expect them unencoded
$permalink = str_replace('%3A', ':', $permalink);
if ($permalink) {
$urls_sanitized[] = $permalink;
}
$urls[$key] = $permalink;
}
$http->fetchAll($urls_sanitized);
foreach ($items as $key => $item) {
$do_content_extraction = true;
$extract_result = false;
$permalink = $urls[$key];
// TODO: Allow error codes - some sites return correct content with error status
// e.g. prospectmagazine.co.uk returns 403
if ($permalink && ($response = $http->get($permalink, true)) && ($response['status_code'] < 300 || $response['status_code'] > 400)) {
$effective_url = $response['effective_url'];
// check if action defined for returned Content-Type
$type = null;
if (preg_match('!^Content-Type:\s*(([-\w]+)/([-\w\+]+))!im', $response['headers'], $match)) {
// look for full mime type (e.g. image/jpeg) or just type (e.g. image)
$match[1] = strtolower(trim($match[1]));
$match[2] = strtolower(trim($match[2]));
foreach (array($match[1], $match[2]) as $_mime) {
if (isset($this->content_type_exc[$_mime])) {
$type = $match[1];
$_act = $this->content_type_exc[$_mime]['action'];
$_name = $this->content_type_exc[$_mime]['name'];
if ($_act == 'exclude') {
continue 2; // skip this feed item entry
} elseif ($_act == 'link') {
if ($match[2] == 'image') {
$html = "<a href=\"$effective_url\"><img src=\"$effective_url\" alt=\"$_name\" /></a>";
} else {
$html = "<a href=\"$effective_url\">Download $_name</a>";
}
$title = $_name;
$do_content_extraction = false;
break;
}
}
}
unset($_mime, $_act, $_name, $match);
}
if ($do_content_extraction) {
$html = $response['body'];
// remove strange things
$html = str_replace('</[>', '', $html);
$html = $this->convert_to_utf8($html, $response['headers']);
// check site config for single page URL - fetch it if found
if ($single_page_response = $this->getSinglePage($item, $html, $effective_url)) {
$html = $single_page_response['body'];
// remove strange things
$html = str_replace('</[>', '', $html);
$html = $this->convert_to_utf8($html, $single_page_response['headers']);
$effective_url = $single_page_response['effective_url'];
unset($single_page_response);
}
$extract_result = $extractor->process($html, $effective_url);
$readability = $extractor->readability;
$content_block = ($extract_result) ? $extractor->getContent() : null;
}
}
if ($do_content_extraction) {
// if we failed to extract content...
if (!$extract_result) {
$html = $this->error_message;
// keep the original item description
$html .= $item->get_description();
} else {
$readability->clean($content_block, 'select');
if ($this->rewrite_relative_urls) $this->makeAbsolute($effective_url, $content_block);
if ($content_block->childNodes->length == 1 && $content_block->firstChild->nodeType === XML_ELEMENT_NODE) {
$html = $content_block->firstChild->innerHTML;
} else {
$html = $content_block->innerHTML;
}
// post-processing cleanup
$html = preg_replace('!<p>[\s\h\v]*</p>!u', '', $html);
}
}
}
$title = ($extractor->getTitle() != '' ? $extractor->getTitle() : _('Untitled'));
$content = array ('title' => $title, 'body' => $html);
return $content;
}
private function convert_to_utf8($html, $header=null)
{
$encoding = null;
if ($html || $header) {
if (is_array($header)) $header = implode("\n", $header);
if (!$header || !preg_match_all('/^Content-Type:\s+([^;]+)(?:;\s*charset=["\']?([^;"\'\n]*))?/im', $header, $match, PREG_SET_ORDER)) {
// error parsing the response
} else {
$match = end($match); // get last matched element (in case of redirects)
if (isset($match[2])) $encoding = trim($match[2], "\"' \r\n\0\x0B\t");
}
// TODO: check to see if encoding is supported (can we convert it?)
// If it's not, result will be empty string.
// For now we'll check for invalid encoding types returned by some sites, e.g. 'none'
// Problem URL: http://facta.co.jp/blog/archives/20111026001026.html
if (!$encoding || $encoding == 'none') {
// search for encoding in HTML - only look at the first 35000 characters
$html_head = substr($html, 0, 40000);
if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $html_head, $match)) {
$encoding = trim($match[1], '"\'');
} elseif (preg_match('/<meta\s+http-equiv=["\']?Content-Type["\']? content=["\'][^;]+;\s*charset=["\']?([^;"\'>]+)/i', $html_head, $match)) {
$encoding = trim($match[1]);
} elseif (preg_match_all('/<meta\s+([^>]+)>/i', $html_head, $match)) {
foreach ($match[1] as $_test) {
if (preg_match('/charset=["\']?([^"\']+)/i', $_test, $_m)) {
$encoding = trim($_m[1]);
break;
}
}
}
}
if (isset($encoding)) $encoding = trim($encoding);
// trim is important here!
if (!$encoding || (strtolower($encoding) == 'iso-8859-1')) {
// replace MS Word smart qutoes
$trans = array();
$trans[chr(130)] = '&sbquo;'; // Single Low-9 Quotation Mark
$trans[chr(131)] = '&fnof;'; // Latin Small Letter F With Hook
$trans[chr(132)] = '&bdquo;'; // Double Low-9 Quotation Mark
$trans[chr(133)] = '&hellip;'; // Horizontal Ellipsis
$trans[chr(134)] = '&dagger;'; // Dagger
$trans[chr(135)] = '&Dagger;'; // Double Dagger
$trans[chr(136)] = '&circ;'; // Modifier Letter Circumflex Accent
$trans[chr(137)] = '&permil;'; // Per Mille Sign
$trans[chr(138)] = '&Scaron;'; // Latin Capital Letter S With Caron
$trans[chr(139)] = '&lsaquo;'; // Single Left-Pointing Angle Quotation Mark
$trans[chr(140)] = '&OElig;'; // Latin Capital Ligature OE
$trans[chr(145)] = '&lsquo;'; // Left Single Quotation Mark
$trans[chr(146)] = '&rsquo;'; // Right Single Quotation Mark
$trans[chr(147)] = '&ldquo;'; // Left Double Quotation Mark
$trans[chr(148)] = '&rdquo;'; // Right Double Quotation Mark
$trans[chr(149)] = '&bull;'; // Bullet
$trans[chr(150)] = '&ndash;'; // En Dash
$trans[chr(151)] = '&mdash;'; // Em Dash
$trans[chr(152)] = '&tilde;'; // Small Tilde
$trans[chr(153)] = '&trade;'; // Trade Mark Sign
$trans[chr(154)] = '&scaron;'; // Latin Small Letter S With Caron
$trans[chr(155)] = '&rsaquo;'; // Single Right-Pointing Angle Quotation Mark
$trans[chr(156)] = '&oelig;'; // Latin Small Ligature OE
$trans[chr(159)] = '&Yuml;'; // Latin Capital Letter Y With Diaeresis
$html = strtr($html, $trans);
}
if (!$encoding) {
$encoding = 'utf-8';
} else {
if (strtolower($encoding) != 'utf-8') {
$html = \SimplePie_Misc::change_encoding($html, $encoding, 'utf-8');
/*
if (function_exists('iconv')) {
// iconv appears to handle certain character encodings better than mb_convert_encoding
$html = iconv($encoding, 'utf-8', $html);
} else {
$html = mb_convert_encoding($html, 'utf-8', $encoding);
}
*/
}
}
}
return $html;
}
private function makeAbsolute($base, $elem) {
$base = new \SimplePie_IRI($base);
// remove '//' in URL path (used to prevent URLs from resolving properly)
// TODO: check if this is still the case
if (isset($base->path)) $base->path = preg_replace('!//+!', '/', $base->path);
foreach(array('a'=>'href', 'img'=>'src') as $tag => $attr) {
$elems = $elem->getElementsByTagName($tag);
for ($i = $elems->length-1; $i >= 0; $i--) {
$e = $elems->item($i);
//$e->parentNode->replaceChild($articleContent->ownerDocument->createTextNode($e->textContent), $e);
$this->makeAbsoluteAttr($base, $e, $attr);
}
if (strtolower($elem->tagName) == $tag) $this->makeAbsoluteAttr($base, $elem, $attr);
}
}
private function makeAbsoluteAttr($base, $e, $attr) {
if ($e->hasAttribute($attr)) {
// Trim leading and trailing white space. I don't really like this but
// unfortunately it does appear on some sites. e.g. <img src=" /path/to/image.jpg" />
$url = trim(str_replace('%20', ' ', $e->getAttribute($attr)));
$url = str_replace(' ', '%20', $url);
if (!preg_match('!https?://!i', $url)) {
if ($absolute = \SimplePie_IRI::absolutize($base, $url)) {
$e->setAttribute($attr, $absolute);
}
}
}
}
private function makeAbsoluteStr($base, $url) {
$base = new \SimplePie_IRI($base);
// remove '//' in URL path (causes URLs not to resolve properly)
if (isset($base->path)) $base->path = preg_replace('!//+!', '/', $base->path);
if (preg_match('!^https?://!i', $url)) {
// already absolute
return $url;
} else {
if ($absolute = \SimplePie_IRI::absolutize($base, $url)) {
return $absolute;
}
return false;
}
}
// returns single page response, or false if not found
private function getSinglePage($item, $html, $url) {
global $http, $extractor;
$host = @parse_url($url, PHP_URL_HOST);
$site_config = \SiteConfig::build($host);
if ($site_config === false) {
// check for fingerprints
if (!empty($extractor->fingerprints) && ($_fphost = $extractor->findHostUsingFingerprints($html))) {
$site_config = \SiteConfig::build($_fphost);
}
if ($site_config === false) $site_config = new \SiteConfig();
\SiteConfig::add_to_cache($host, $site_config);
return false;
} else {
\SiteConfig::add_to_cache($host, $site_config);
}
$splink = null;
if (!empty($site_config->single_page_link)) {
$splink = $site_config->single_page_link;
} elseif (!empty($site_config->single_page_link_in_feed)) {
// single page link xpath is targeted at feed
$splink = $site_config->single_page_link_in_feed;
// so let's replace HTML with feed item description
$html = $item->get_description();
}
if (isset($splink)) {
// Build DOM tree from HTML
$readability = new PocheReadability($html, $url);
$xpath = new DOMXPath($readability->dom);
// Loop through single_page_link xpath expressions
$single_page_url = null;
foreach ($splink as $pattern) {
$elems = @$xpath->evaluate($pattern, $readability->dom);
if (is_string($elems)) {
$single_page_url = trim($elems);
break;
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
foreach ($elems as $item) {
if ($item instanceof DOMElement && $item->hasAttribute('href')) {
$single_page_url = $item->getAttribute('href');
break;
} elseif ($item instanceof DOMAttr && $item->value) {
$single_page_url = $item->value;
break;
}
}
}
}
// If we've got URL, resolve against $url
if (isset($single_page_url) && ($single_page_url = $this->makeAbsoluteStr($url, $single_page_url))) {
// check it's not what we have already!
if ($single_page_url != $url) {
// it's not, so let's try to fetch it...
$_prev_ref = $http->referer;
$http->referer = $single_page_url;
if (($response = $http->get($single_page_url, true)) && $response['status_code'] < 300) {
$http->referer = $_prev_ref;
return $response;
}
$http->referer = $_prev_ref;
}
}
}
return false;
}
}

View File

@ -1,4 +1,5 @@
<?php <?php
require __DIR__.'/../../vendor/autoload.php'; require __DIR__.'/../../vendor/autoload.php';
require __DIR__.'/../../vendor/full-text-rss/autoload.php';
require __DIR__.'/Fixtures.php'; require __DIR__.'/Fixtures.php';
require __DIR__.'/PocheWebTestCase.php'; require __DIR__.'/PocheWebTestCase.php';

38
vendor/full-text-rss/README.txt vendored Normal file
View File

@ -0,0 +1,38 @@
Full-Text RSS
=============
About
-----
See http://fivefilters.org/content-only/ for a description of the code.
Installation
------------
1. Extract the files in this ZIP archive to a folder on your computer.
2. FTP the files up to your server
3. Access index.php through your browser. E.g. http://example.org/full-text-rss/index.php
4. Enter a URL in the form field to test the code
5. If you get an RSS feed with full-text content, all is working well. :)
Configuration (optional)
------------------------
1. Save a copy of config.php as custom_config.php and edit custom_config.php
2. If you decide to enable caching, make sure the cache folder (and its 2 sub folders) is writable.
(You might need to change the permissions of these folders to 777 through your FTP client.)
3. If you want to use the admin area to edit/update your site config files, make sure the
site_config folder (and its 2 sub folders) is writable. (You might need to change the permissions
of these folders to 777 through your FTP client.)
Help
----
Please visit http://help.fivefilters.org

35
vendor/full-text-rss/UPDATING.txt vendored Normal file
View File

@ -0,0 +1,35 @@
Updating Full-Text RSS
======================
To update your copy of Full-Text RSS to ensure feeds continue to be processed as they were before, we suggest the following steps:
1. Keep your current installation in place for now (we'll deal with it later)
2. Extract this updated package to a new folder -- for example, if the last version is in a folder called 'full-text-rss', extract this version to a new folder called 'full-text-rss-updated'
3. Upload the new folder to your server
4. Access index.php in the new folder through your browser -- for example http://example.org/full-text-rss-updated/index.php
5. Enter a URL in the form field to test the updated code
6. If you'd configured the last version, copy custom_config.php from your old version to the new installation.
7. If you'd added custom site config files (in site_config/custom/), copy those over to the new installation.
8. If you'd enabled caching, make sure the cache folder (and its 2 sub folders) is writable. (You might need to change the permissions of these folders to 777 through your FTP client.)
9. Test the new copy again to make sure the config values are now applied to the new version.
10. Now simply rename the folder with your old copy to 'full-text-rss-old' and then rename the folder with the new copy to 'full-text-rss' (or whatever name you'd given the original folder).
That's all that's needed. Your feeds should continue to work as they did before. Let us know if you have any trouble: help@fivefilters.org.
Updating Site Patterns
======================
Site patterns are used by Full-Text RSS to improve extraction for certain sites. These are simple text files which are updated more frequently than the Full-Text RSS software itself.
To make sure your copy of Full-Text RSS is using the latest site patterns, We've created a simple tool to help you get the latest copy of these site patterns from FiveFilters.org. To use it, access the admin/ folder in your browser and follow the instructions.
Alternatively, you can download the latest set of site config files via GitHub: https://github.com/fivefilters/ftr-site-config

View File

@ -0,0 +1,169 @@
.CodeMirror {
line-height: 1em;
font-family: monospace;
/* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */
position: relative;
/* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */
overflow: hidden;
}
.CodeMirror-scroll {
overflow-x: auto;
overflow-y: hidden;
height: 300px;
/* This is needed to prevent an IE[67] bug where the scrolled content
is visible outside of the scrolling box. */
position: relative;
outline: none;
}
/* Vertical scrollbar */
.CodeMirror-scrollbar {
float: right;
overflow-x: hidden;
overflow-y: scroll;
/* This corrects for the 1px gap introduced to the left of the scrollbar
by the rule for .CodeMirror-scrollbar-inner. */
margin-left: -1px;
}
.CodeMirror-scrollbar-inner {
/* This needs to have a nonzero width in order for the scrollbar to appear
in Firefox and IE9. */
width: 1px;
}
.CodeMirror-scrollbar.cm-sb-overlap {
/* Ensure that the scrollbar appears in Lion, and that it overlaps the content
rather than sitting to the right of it. */
position: absolute;
z-index: 1;
float: none;
right: 0;
min-width: 12px;
}
.CodeMirror-scrollbar.cm-sb-nonoverlap {
min-width: 12px;
}
.CodeMirror-scrollbar.cm-sb-ie7 {
min-width: 18px;
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
z-index: 10;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
height: 100%;
}
.CodeMirror-gutter-text {
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
white-space: pre !important;
cursor: default;
}
.CodeMirror-lines {
padding: .4em;
white-space: pre;
cursor: text;
}
.CodeMirror-lines * {
/* Necessary for throw-scrolling to decelerate properly on Safari. */
pointer-events: none;
}
.CodeMirror pre {
-moz-border-radius: 0;
-webkit-border-radius: 0;
-o-border-radius: 0;
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
font-size: inherit;
padding: 0; margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror textarea {
outline: none !important;
}
.CodeMirror pre.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
border-left: 1px solid black;
border-right: none;
width: 0;
}
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
width: auto;
border: 0;
background: transparent;
background: rgba(0, 200, 0, .4);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
}
/* Kludge to turn off filter in ie9+, which also accepts rgba */
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
div.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Default theme */
.cm-s-default span.cm-keyword {color: #708;}
.cm-s-default span.cm-atom {color: #219;}
.cm-s-default span.cm-number {color: #164;}
.cm-s-default span.cm-def {color: #00f;}
.cm-s-default span.cm-variable {color: black;}
.cm-s-default span.cm-variable-2 {color: #05a;}
.cm-s-default span.cm-variable-3 {color: #085;}
.cm-s-default span.cm-property {color: black;}
.cm-s-default span.cm-operator {color: black;}
.cm-s-default span.cm-comment {color: #a50;}
.cm-s-default span.cm-string {color: #a11;}
.cm-s-default span.cm-string-2 {color: #f50;}
.cm-s-default span.cm-meta {color: #555;}
.cm-s-default span.cm-error {color: #f00;}
.cm-s-default span.cm-qualifier {color: #555;}
.cm-s-default span.cm-builtin {color: #30a;}
.cm-s-default span.cm-bracket {color: #cc7;}
.cm-s-default span.cm-tag {color: #170;}
.cm-s-default span.cm-attribute {color: #00c;}
.cm-s-default span.cm-header {color: blue;}
.cm-s-default span.cm-quote {color: #090;}
.cm-s-default span.cm-hr {color: #999;}
.cm-s-default span.cm-link {color: #00c;}
span.cm-header, span.cm-strong {font-weight: bold;}
span.cm-em {font-style: italic;}
span.cm-emstrong {font-style: italic; font-weight: bold;}
span.cm-link {text-decoration: underline;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,63 @@
CodeMirror.defineMode("properties", function() {
return {
token: function(stream, state) {
var sol = stream.sol() || state.afterSection;
var eol = stream.eol();
state.afterSection = false;
if (sol) {
if (state.nextMultiline) {
state.inMultiline = true;
state.nextMultiline = false;
} else {
state.position = "def";
}
}
if (eol && ! state.nextMultiline) {
state.inMultiline = false;
state.position = "def";
}
if (sol) {
while(stream.eatSpace());
}
var ch = stream.next();
if (sol && (ch === "#")) {
state.position = "comment";
stream.skipToEnd();
return "comment";
} else if (sol && ch === "[") {
state.afterSection = true;
stream.skipTo("]"); stream.eat("]");
return "header";
} else if (ch === ":") {
state.position = "quote";
return null;
} else if (ch === "\\" && state.position === "quote") {
if (stream.next() !== "u") { // u = Unicode sequence \u1234
// Multiline value
state.nextMultiline = true;
}
}
return state.position;
},
startState: function() {
return {
position : "def", // Current position, "def", "quote" or "comment"
nextMultiline : false, // Is the next line multiline value
inMultiline : false, // Is the current line a multiline value
afterSection : false // Did we just open a section
};
}
};
});
CodeMirror.defineMIME("text/x-properties", "properties");
CodeMirror.defineMIME("text/x-ini", "properties");

View File

@ -0,0 +1,320 @@
<?php
// Edit site config files for Full-Text RSS
// Author: Keyvan Minoukadeh
// Copyright (c) 2013 Keyvan Minoukadeh
// License: AGPLv3
// Date: 2013-02-25
// More info: http://fivefilters.org/content-only/
// Help: http://help.fivefilters.org
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Usage
// -----
// Access this file in your browser and follow the instructions to update your site config files.
error_reporting(E_ALL ^ E_NOTICE);
ini_set("display_errors", 1);
@set_time_limit(120);
if (get_magic_quotes_gpc()) {
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
require_once '../libraries/content-extractor/SiteConfig.php';
////////////////////////////////
// Load config file
////////////////////////////////
$admin_page = 'edit-pattern';
require_once('../config.php');
require_once('require_login.php');
require_once('template.php');
tpl_header('Edit site patterns');
$version = include('../site_config/standard/version.php');
function filter_only_text($filename) {
return (strtolower(substr($filename, -4)) == '.txt');
}
function is_valid_hostname($host) {
return preg_match('!^[a-z0-9_.-]+$!i', $host);
}
/////////////////////////////////
// Process changes
/////////////////////////////////
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// DELETE
if (@$_POST['delete'] != '' && @$_POST['delete_dir'] != '') {
if (is_valid_hostname($_POST['delete'])) {
$delete = $_POST['delete'];
if ($_POST['delete_dir'] == 'standard') {
$delete = '../site_config/standard/'.$delete;
} else {
$delete = '../site_config/custom/'.$delete;
}
if (@unlink($delete)) {
echo 'Deleted <strong>'.$delete.'</strong>';
} else {
echo 'Failed to delete <strong>'.$delete.'</strong>';
}
}
exit;
}
// SAVE
if (@$_POST['save'] != '' && isset($_POST['contents'])) {
if (is_valid_hostname(trim($_POST['save']))) {
$save = strtolower(trim($_POST['save']));
if (@$_POST['save_dir'] == 'standard') {
$savepath = '../site_config/standard/'.$save.'.txt';
} else {
$savepath = '../site_config/custom/'.$save.'.txt';
}
// TODO: check if file exists, if it does, prompt user whether to overwrite
if (file_put_contents($savepath, $_POST['contents']) !== false) {
echo '<p>Saved to <strong>'.$savepath.'</strong></p>';
// check caching
if ($options->caching) {
echo '<p>Note: caching is enabled &mdash; you may have to disable caching or delete cache files to see changes.<p>';
}
if ($options->apc && function_exists('apc_delete') && function_exists('apc_cache_info')) {
$_apc_data = apc_cache_info('user');
foreach ($_apc_data['cache_list'] as $_apc_item) {
if (substr($_apc_item['info'], 0, 3) == 'sc.') {
apc_delete($_apc_item['info']);
}
}
echo '<p>Cleared site config cache in APC.</p>';
}
SiteConfig::set_config_path(dirname($savepath));
$sconfig = SiteConfig::build($save, $exact_host_match=true);
if ($sconfig) {
if (!empty($sconfig->test_url)) {
echo '<h4>Test URLs</h4>';
echo '<ul>';
foreach ($sconfig->test_url as $test_url) {
$ftr_test_url = $test_url;
if (strtolower(substr($ftr_test_url, 0, 7)) == 'http://') {
$ftr_test_url = substr($ftr_test_url, 7);
}
$ftr_test_url = '../makefulltextfeed.php?url='.urlencode($ftr_test_url);
echo '<li>';
echo '<a href="'.htmlspecialchars($test_url).'" target="_blank">'.htmlspecialchars($test_url).'</a>';
echo ' | <a href="'.$ftr_test_url.'" target="_blank">Full-Text RSS result</a>';
echo ' | <a href="'.$ftr_test_url.'&debug" target="_blank">Debug</a>';
echo '</li>';
}
echo '</ul>';
} else {
echo '<p>No test URLs found in config, if you supply one we\'ll give you a link to test how Full-Text RSS will extract it</p>';
}
} else {
echo '<p>Could not load/parse config file</p>';
}
} else {
echo 'Failed to save <strong>'.$savepath.'</strong>. Make sure the directory is writable.';
}
}
exit;
}
}
/////////////////////////////////
// Show list of site config files
/////////////////////////////////
if (!isset($_REQUEST['url']) || trim($_REQUEST['url']) == '') {
$sc_files = array_merge(scandir('../site_config/standard/'), scandir('../site_config/custom/'));
$sc_files = array_unique(array_filter($sc_files, 'filter_only_text'));
?>
<p><strong>Note:</strong> This feature is for advanced users familiar with XPath. It allows you to override automatic article extraction and specify what Full-Text RSS should extract from specific domains. If you're uncomfortable writing your own, you can <a href="http://fivefilters.org/content-only/custom_site_patterns.php">request one</a> from us.</p>
<form class="well form-search" action="edit-pattern.php">
<input id="search" type="text" name="url" class="span8" placeholder="Enter a host name or URL, e.g. http://www.example.org/article-123.html">
<button type="submit" class="btn">Search or add</button>
</form>
<?php
echo '<ul style="-webkit-column-count: 3; -moz-column-count: 3; column-count: 3" id="list">';
foreach ($sc_files as $file) {
$file = basename($file, '.txt');
echo '<li><a href="edit-pattern.php?url='.urlencode($file).'">'.htmlspecialchars($file).'</a></li>';
}
echo '</ul>';
// adapted from http://stackoverflow.com/a/11022738/407938 ...
?>
<script>
$('input#search').bind('keyup',function(){
var inputString = $(this).val();
var items = $('ul#list li');
items.hide();
items.each(function(){
var item = $(this).text().toString();
if (item.indexOf(inputString)>=0)
$(this).show();
});
});
</script>
<?php
exit;
}
//////////////////////////////////
// Check if primary or secondary
// folder specified
//////////////////////////////////
$lookin = null;
if (isset($_REQUEST['lookin']) && in_array($_REQUEST['lookin'], array('standard', 'custom'))) {
$lookin = $_REQUEST['lookin'];
}
//////////////////////////////////
// Find file and display
//////////////////////////////////
$hostname = false;
if (is_valid_hostname($_REQUEST['url'])) {
$hostname = $_REQUEST['url'];
} else {
if ($_host = parse_url($_REQUEST['url'], PHP_URL_HOST)) {
if (is_valid_hostname($_host)) {
$hostname = $_host;
}
}
}
if (!$hostname) die('Invalid URL');
$hostname_base = ltrim($hostname, '.');
if (strtolower(substr($hostname, 0, 4)) == 'www.') {
$hostname = substr($hostname, 4);
$hostname_base = $hostname;
}
$check = array(
'../site_config/standard/'.$hostname_base.'.txt',
'../site_config/standard/.'.$hostname_base.'.txt',
'../site_config/custom/'.$hostname_base.'.txt',
'../site_config/custom/.'.$hostname_base.'.txt'
);
$related = array();
$matched = array();
$exact_match = false;
foreach ($check as $filename) {
if (file_exists($filename)) {
$related[$filename] = file_get_contents($filename);
if ($lookin === null || strpos($filename, "/$lookin/") !== false) {
$matched[$filename] = file_get_contents($filename);
if (strpos($filename, "/$hostname") !== false) $exact_match = $filename;
}
}
}
if (empty($matched)) {
$contents = "# No matching files found, you can write yours here\n\n# body: //div[@id='body']\n\n# test_url: http://...";
echo '<p>No matching files found...</p>';
} elseif ($exact_match) {
$contents = $matched[$exact_match];
$file_location = $exact_match;
echo '<p style="position: absolute;">Loaded <strong>'.htmlspecialchars($exact_match).'</strong></p>';
} else {
$contents = end($matched);
$file_location = array_pop(array_keys($matched));
echo '<p style="position: absolute;">Loaded <strong>'.htmlspecialchars($file_location).'</strong></p>';
}
if (isset($file_location)) unset($related[$file_location]);
$save_locations = array(
'custom' => 'custom (recommended)',
'standard' => 'standard'
);
echo '<form method="POST" action="edit-pattern.php">';
echo '<div style="text-align: right; margin-top: 10px; margin-bottom: 5px;"><a href="http://help.fivefilters.org/customer/portal/articles/223153-site-patterns" target="_blank">Need help?</a></div>';
echo '<textarea name="contents" class="span8" style="height: 400px;" id="config">'.htmlspecialchars($contents).'</textarea>';
echo '<div style="margin-top: 8px; margin-bottom: 4px;">';
echo '<label>Save as</label> <input type="text" name="save" value="'.htmlspecialchars($hostname).'" />.txt';
echo '<br />';
echo '<label>In directory</label> ';
echo '<select name="save_dir">';
foreach ($save_locations as $_sl_val => $_sl_display) {
echo "<option value=\"$_sl_val\">$_sl_display</option>";
}
echo '</select>';
echo '</div>';
echo '<input type="submit" class="btn btn-primary" value="Save" /> ';
echo 'or <a href="edit-pattern.php" class="btn" >Cancel and return to listing</a>';
echo '</form>';
// DELETE option
if (!empty($matched)) {
echo '<hr /><h3>Delete file?</h3>';
echo '<p>Delete <strong>'.htmlspecialchars($file_location).'</strong></p>';
echo '<form method="POST" action="edit-pattern.php" onsubmit="return confirm(\'Are you sure?\');">';
echo '<input type="hidden" name="delete" value="'.htmlspecialchars(basename($file_location)).'" />';
echo '<input type="hidden" name="delete_dir" value="'.(strpos($file_location, '/standard/') ? 'standard' : 'custom').'" />';
echo '<input type="submit" value="Delete" class="btn btn-danger" />';
echo '</form>';
}
// TEST URLs
if (!empty($matched)) {
if ($sconfig = SiteConfig::build_from_array(explode("\n", $contents))) {
if (!empty($sconfig->test_url)) {
echo '<hr /><h3>Test URLs</h3>';
echo '<ul>';
foreach ($sconfig->test_url as $test_url) {
$ftr_test_url = $test_url;
if (strtolower(substr($ftr_test_url, 0, 7)) == 'http://') {
$ftr_test_url = substr($ftr_test_url, 7);
}
$ftr_test_url = '../makefulltextfeed.php?url='.urlencode($ftr_test_url);
echo '<li>';
echo '<a href="'.htmlspecialchars($test_url).'" target="_blank">'.htmlspecialchars($test_url).'</a>';
echo ' | <a href="'.$ftr_test_url.'" target="_blank">Full-Text RSS result</a>';
echo ' | <a href="'.$ftr_test_url.'&debug" target="_blank">Debug</a>';
echo '</li>';
}
echo '</ul>';
}
}
}
// RELATED files
if (!empty($related)) {
echo '<hr /><h3>Related files</h3>';
echo '<ul>';
foreach (array_keys($related) as $_m_file) {
preg_match('!/(standard|custom)/(.+?)\.txt$!', $_m_file, $_m);
echo '<li><a href="edit-pattern.php?lookin='.$_m[1].'&url='.urlencode($_m[2]).'">'.htmlspecialchars($_m_file).'</a></li>';
}
echo '</ul>';
}
?>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("config"), {
lineNumbers: true,
theme: 'default',
lineWrapping: true
});
</script>

43
vendor/full-text-rss/admin/index.php vendored Normal file
View File

@ -0,0 +1,43 @@
<?php
// Admin page for Full-Text RSS
// Author: Keyvan Minoukadeh
// Copyright (c) 2012 Keyvan Minoukadeh
// License: AGPLv3
// Date: 2012-04-29
// More info: http://fivefilters.org/content-only/
// Help: http://help.fivefilters.org
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Usage
// -----
// Access this file in your browser
error_reporting(E_ALL ^ E_NOTICE);
ini_set("display_errors", 1);
@set_time_limit(120);
////////////////////////////////
// Load config file
////////////////////////////////
require_once('../config.php');
require_once('require_login.php');
require_once('template.php');
tpl_header('Admin');
?>
<p>The admin pages are intended to help you manage your copy of Full-Text RSS more easily.</p>
<p>We currently offer an experimental <a href="update.php">update tool</a> which you can use to update your site patterns.</p>

38
vendor/full-text-rss/admin/login.php vendored Normal file
View File

@ -0,0 +1,38 @@
<?php
session_start();
require_once(dirname(dirname(__FILE__)).'/config.php');
if (!isset($options->admin_credentials) || $options->admin_credentials['username'] == '' || $options->admin_credentials['password'] == '') {
die('<h2>Admin privileges required</h2><p>This page requires admin privileges but Full-Text RSS has not been configured with admin credentials.</p><p>If you are the administrator, please edit your <tt>custom_config.php</tt> file and enter the credentials in the appropriate section. When you\'ve done that, this page will prompt you for your admin credentials.</p>');
}
$name = @$_POST['username'];
$pass = @$_POST['pass'];
$invalid_login = false;
if ($name || $pass) {
if ($name == $options->admin_credentials['username'] && $pass == $options->admin_credentials['password']) {
// Authentication successful - set session
$_SESSION['auth'] = 1;
if (isset($_POST['redirect']) && preg_match('/^[0-9a-z]+$/', $_POST['redirect'])) {
header('Location: '.$_POST['redirect'].'.php');
} else {
header('Location: index.php');
}
exit;
}
$invalid_login = true;
}
?>
<!DOCTYPE html>
<html>
<head><title>Login</title></head>
<body>
<?php if ($invalid_login) echo '<p><strong>Invalid login, please try again.</strong> If you can\'t remember your admin credentials, open your <tt>custom_config.php</tt> and you\'ll find them in there.</p>'; ?>
<form method="post" action="login.php">
<?php if (isset($_GET['redirect'])) echo '<input type="hidden" name="redirect" value="'.htmlspecialchars($_GET['redirect']).'" />'; ?>
<label>Username: <input type="text" name="username" value="<?php echo @$_POST['username']; ?>" /></label>
<label>Password: <input type="password" name="pass" /></label>
<input type="submit" name="submit" value="Log In" />
</form>
</body>
</html>

View File

@ -0,0 +1,95 @@
<?php
// Require login for admin access
// Author: Keyvan Minoukadeh
// Copyright (c) 2012 Keyvan Minoukadeh
// License: AGPLv3
// Date: 2012-08-30
// More info: http://fivefilters.org/content-only/
// Help: http://help.fivefilters.org
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Usage
// -----
// This file is included on pages which require admin privileges - e.g. updating the software.
// The username is 'admin' by default and the password should be set in the custom_config.php file.
session_start();
require_once(dirname(dirname(__FILE__)).'/config.php');
if (isset($_GET['logout'])) $_SESSION['auth'] = 0;
if (!isset($_SESSION['auth']) || $_SESSION['auth'] != 1) {
if (isset($admin_page)) {
header('Location: login.php?redirect='.$admin_page);
} else {
header('Location: login.php');
}
exit;
}
/* HTTP DIGEST authentication - doesn't work without server tweaks in FastCGI environments
$realm = 'Restricted area';
//user => password
$users = array($options->admin_credentials['username'] => $options->admin_credentials['password']);
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
die('If you can\'t remember your admin credentials, open your custom_config.php and you\'ll find them in there.');
}
// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
die('Wrong credentials!');
// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response)
die('Wrong credentials!');
// ok, valid username & password
// echo 'Thanks! You are now logged in.';
unset($realm, $users, $data, $A1, $A2, $valid_response);
// function to parse the http auth header
function http_digest_parse($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$data = array();
$keys = implode('|', array_keys($needed_parts));
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
return $needed_parts ? false : $data;
}
*/
?>

61
vendor/full-text-rss/admin/template.php vendored Normal file
View File

@ -0,0 +1,61 @@
<?php
ob_start();
function tpl_header($title='Full-Text RSS Admin Area') {
global $admin_page;
?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title; ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="noindex, nofollow" />
<link rel="stylesheet" href="../css/bootstrap.min.css" type="text/css" media="screen" />
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script type="text/javascript" src="../js/bootstrap-tooltip.js"></script>
<script type="text/javascript" src="../js/bootstrap-popover.js"></script>
<script type="text/javascript" src="../js/bootstrap-tab.js"></script>
<script type="text/javascript" src="codemirror/codemirror.js"></script>
<script type="text/javascript" src="codemirror/properties.js"></script>
<link rel="stylesheet" href="codemirror/codemirror.css" />
<style>
html, body { background-color: #eee; }
body { margin: 0; line-height: 1.4em; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; }
label, input, select, textarea { font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; }
.actions { padding-left: 220px; }
.popover .inner { width: 200px; }
#main-container { width: 800px; padding-bottom: 60px; }
.page-header { padding-bottom: 0; }
.CodeMirror {border: 1px solid black; font-size:13px}
.cm-def {color: green !important;}
.cm-quote {color: #000 !important;}
.CodeMirror-scroll { height: auto; overflow: visible; }
</style>
</head>
<body>
<div class="container" id="main-container">
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="../">Full-Text RSS</a>
<ul class="nav">
<li <?php if (@$admin_page == 'update') echo 'class="active"'; ?>><a href="update.php">Update patterns</a></li>
<li <?php if (@$admin_page == 'edit-pattern') echo 'class="active"'; ?>><a href="edit-pattern.php">Edit patterns</a></li>
<li><a href="index.php?logout">Logout</a></li>
</ul>
</div>
</div>
</div>
<div class="page-header"><h1><?php echo $title; ?></h1></div>
<?php
}
function tpl_footer() {
?>
</div> <!-- close container -->
</body>
</html>
<?php
}
register_shutdown_function('tpl_footer');

191
vendor/full-text-rss/admin/update.php vendored Normal file
View File

@ -0,0 +1,191 @@
<?php
// Update site config files for Full-Text RSS
// Author: Keyvan Minoukadeh
// Copyright (c) 2012 Keyvan Minoukadeh
// License: AGPLv3
// Date: 2012-04-13
// More info: http://fivefilters.org/content-only/
// Help: http://help.fivefilters.org
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Usage
// -----
// Access this file in your browser and follow the instructions to update your site config files.
error_reporting(E_ALL ^ E_NOTICE);
ini_set("display_errors", 1);
@set_time_limit(120);
////////////////////////////////
// Load config file
////////////////////////////////
$admin_page = 'update';
require_once('../config.php');
require_once('require_login.php');
require_once('template.php');
tpl_header('Update site patterns');
$version = include('../site_config/standard/version.php');
/////////////////////////////////
// Check for valid update key
/////////////////////////////////
if (!isset($_REQUEST['key']) || trim($_REQUEST['key']) == '') {
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
header('Location: update.php');
exit;
}
$auto = true;
$no_auto_reasons = array();
if (!class_exists('ZipArchive')) {
$auto = false;
$no_auto_reasons[] = 'zip support (PHP\'s <a href="http://php.net/manual/en/zip.requirements.php">ZipArchive</a> class) is missing';
}
if (!is_writable('../site_config')) {
$auto = false;
$no_auto_reasons[] = 'your <tt>site_config/</tt> folder is not writable - change permissions to 777 and try again.</p>';
}
if (!file_exists('../site_config/standard/version.php')) {
die('Could not determine current version of your site pattern files (site_config/standard/version.php). Make sure you\'re using at least version 2.9.5 of Full-Text RSS.');
}
if (!@$options->registration_key) {
$input_field = '<label for="key">Registration key</label><input type="password" name="key" id="key" />';
} else {
$reg_key = preg_replace('/[^a-z0-9-]/i', '', $options->registration_key);
$input_field = '<input type="hidden" name="key" value="'.$reg_key.'" />';
}
?>
<p>You have Full-Text RSS <strong><?php echo _FF_FTR_VERSION; ?></strong>
(Site Patterns version: <strong><?php echo (isset($version) ? $version : 'Unknown'); ?></strong>)
</p>
<p>To see if you have the latest versions, <a href="http://fivefilters.org/content-only/latest_version.php?version=<?php echo urlencode(_FF_FTR_VERSION).'&site_config='.urlencode(@$version); ?>">check for updates</a>.</p>
<?php
$reg_key_info = '<h3>Registration key</h3><p>This update tool requires a registration key issued by FiveFilters.org. You do not need a registration key to use Full-Text RSS, and none of the regular funtionality is affected if you do not have one. The update tool is simply a convenience service we offer our customers.</p>';
if ($auto) {
echo '<p>This update tool will attempt to fetch the latest site patterns from FiveFilters.org and update yours.</p>';
echo '<p><strong>Important: </strong>if you\'ve modified or added your own config files in the <tt>site_config/standard/</tt> folder, please move them to <tt>site_config/custom/</tt> &mdash; the update process will attempt to replace everything in <tt>site_config/standard/</tt> with our updated version.</p>';
echo $reg_key_info;
if (!isset($reg_key)) {
echo '<p>Your registration key should be your PayPal or Avangate transaction ID. If you don\'t have a registration key, you will get one sent to you automatically when you <a href="http://fivefilters.org/content-only/">purchase Full-Text RSS</a> from FiveFilters.org.</p>';
}
echo '<form method="post" action="update.php" class="well">',$input_field,' <input type="submit" value="Update now" /></form>';
} else {
echo '<div class="notice">';
echo '<p>We cannot automatically update your site pattern files because:</p>';
echo '<ul>';
foreach ($no_auto_reasons as $reason) {
echo '<li>',$reason,'</li>';
}
echo '</ul>';
echo '<p>You can still manually update by downloading the zip file and replacing everything in your <tt>site_config/standard/</tt> folder with the contents of the zip file.</p>';
echo '</div>';
echo $reg_key_info;
if (!isset($reg_key)) {
echo '<p>Enter your registration key below to download the latest version of the site config files from FiveFilters.org</p>';
echo '<p>Your registration key should be your PayPal or Avangate transaction ID.</p>';
}
echo '<form method="post" class="well" action="http://fivefilters.org/content-only/update/get_site_config.php">',$input_field,' <input type="submit" value="Download site patterns" /></form>';
}
echo '<h3>Help</h3>';
echo '<p>If you have any trouble, please contact us via our <a href="http://help.fivefilters.org">support site</a>.</p>';
exit;
}
//////////////////////////////////
// Check for updates
//////////////////////////////////
$ff_version = (float)@file_get_contents('http://fivefilters.org/content-only/site_config/standard/version.txt');
if (version_compare($version, $ff_version) != -1) {
die('Your site config files are up to date! If you have trouble extracting from a particular site, please email us: help@fivefilters.org');
} else {
println("Updated site patterns are available at FiveFilters.org (version $ff_version)...");
}
//////////////////////////////////
// Prepare
//////////////////////////////////
$latest_remote = 'http://fivefilters.org/content-only/update/get_site_config.php?key='.urlencode($_REQUEST['key']);
$tmp_latest_local = '../site_config/latest_site_config.zip';
$tmp_latest_local_dir = '../site_config/standard_latest';
$tmp_old_local_dir = '../site_config/standard_old';
if (file_exists($tmp_latest_local)) unlink($tmp_latest_local);
if (file_exists($tmp_latest_local_dir)) rrmdir($tmp_latest_local_dir);
if (file_exists($tmp_old_local_dir)) {
rrmdir($tmp_old_local_dir);
}
$standard_local_dir = '../site_config/standard/';
//@copy($latest_remote, $tmp_latest_local);
//copy() does not appear to fill $http_response_header in certain environments
@file_put_contents($tmp_latest_local, @file_get_contents($latest_remote));
$headers = implode("\n", $http_response_header);
//var_dump($headers); exit;
if (strpos($headers, 'HTTP/1.1 403') !== false) {
println("Invalid registration key supplied");
exit;
} elseif (strpos($headers, 'HTTP/1.1 200') === false) {
println("Sorry, something went wrong. We're looking into it. Please contact us if the problem persists.");
exit;
}
if (class_exists('ZipArchive') && file_exists($tmp_latest_local)) {
println("Downloaded latest copy of the site pattern files to $tmp_latest_local");
$zip = new ZipArchive;
if ($zip->open($tmp_latest_local) === TRUE) {
$zip->extractTo($tmp_latest_local_dir);
$zip->close();
@unlink($tmp_latest_local);
if (file_exists($tmp_latest_local_dir)) {
println("Unzipped contents to $tmp_latest_local_dir");
if (!file_exists($tmp_latest_local_dir.'/version.php')) {
println("There was a problem extracting the latest site patterns archive - your current site patterns remain untouched.");
println("Please <a href=\"$latest_remote\">update manually</a>.");
exit;
}
rename($standard_local_dir, $tmp_old_local_dir);
if (file_exists($tmp_old_local_dir)) println("Renamed $standard_local_dir to $tmp_old_local_dir");
rename($tmp_latest_local_dir, $standard_local_dir);
if (file_exists($standard_local_dir)) println("Renamed $tmp_latest_local_dir to $standard_local_dir");
println("<strong style=\"color: darkgreen;\">All done!</strong> Your old site config files are in $tmp_old_local_dir &mdash; these will be removed next time you go through the update process.");
} else {
if (file_exists($tmp_latest_local)) @unlink($tmp_latest_local);
println("Failed to unzip to $tmp_latest_local_dir - your current site patterns remain untouched");
}
} else {
if (file_exists($tmp_latest_local)) @unlink($tmp_latest_local);
println("Failed to extract from $tmp_latest_local - your current site patterns remain untouched");
}
} else {
println("Could not download the latest site config files. Please <a href=\"$latest_remote\">update manually</a> - your current site patterns remain untouched.");
}
function println($txt) {
echo $txt,"<br />\n";
ob_end_flush();
ob_flush();
flush();
}
function rrmdir($dir) {
foreach(glob($dir . '/{*.txt,*.php,.*.txt,.*.php}', GLOB_BRACE|GLOB_NOSORT) as $file) {
if(is_dir($file)) {
rrmdir($file);
} else {
unlink($file);
}
}
rmdir($dir);
}
?>

17
vendor/full-text-rss/autoload.php vendored Normal file
View File

@ -0,0 +1,17 @@
<?php
require_once __DIR__ . '/libraries/readability/Readability.php';
require dirname(__FILE__).'/libraries/simplepie/autoloader.php';
require_once __DIR__ . '/libraries/content-extractor/ContentExtractor.php';
require_once __DIR__ . '/libraries/content-extractor/SiteConfig.php';
require_once __DIR__ . '/libraries/humble-http-agent/HumbleHttpAgent.php';
require_once __DIR__ . '/libraries/humble-http-agent/SimplePie_HumbleHttpAgent.php';
require_once __DIR__ . '/libraries/humble-http-agent/CookieJar.php';
////////////////////////////////
// Load config file
////////////////////////////////
require dirname(__FILE__).'/config.php';

3
vendor/full-text-rss/cache/index.php vendored Normal file
View File

@ -0,0 +1,3 @@
<?php
// this is here to prevent directory listing over the web
?>

136
vendor/full-text-rss/changelog.txt vendored Normal file
View File

@ -0,0 +1,136 @@
FiveFilters.org: Full-Text RSS
http://fivefilters.org/content-only/
CHANGELOG
------------------------------------
3.1 (2013-03-06)
- PHP Readability updated to preserve more images/videos
- Site config files updated for better extraction
- SimplePie updated
- New site config option favour_feed_titles and request parameter use_extracted_title to allow extracted titles to be used in generated feed
- Remove image lazy loading (looks for markup used by http://wordpress.org/extend/plugins/lazy-load/)
- <category> elements appearing inside <item> elements are now preserved in generated feed
- <media:thumbnail> elements now preserved
- Allow multiple <media:content> elements (previously only one was preserved)
- Bug fix: No more self-closing iframe elements
- Bug fix: Fixed manifest.yml to prevent error message when deploying to AppFog
- Other minor fixes/improvements
3.0 (2012-09-04)
- Multi-page support - next_page_link now supported in site config (enable/disable with $options->multipage)
- HTML5 parser available - use parser: html5lib in site config, also see $options->allowed_parsers
- Updated site patterns for better extraction
- New global site config to be applied to all sites (global.txt)
- APC caching of site config files to improve performance, if APC available - see $options->apc
- Site config editor in admin/ - easily find, edit, test, and test site config files, or add new ones
- Debug mode to see what's happening behind the scenes - see $options->debug
- Removed deprecated config options: restrict, message_to_prepend_with_key, message_to_append_with_key, error_message_with_key
- Removed extraction with CSS via querystring
- Removed config option: $options->alternative_url
- Bug fix: allow extraction of a single element
- Bug fix: redirect handling improved
- Strip 'http://' prefix when API key is supplied
- Site config merging (custom + standard + fingerprint + global)
- Site config command replace_string(find): replace can now be split over two lines: find_string: find, replace_string: replace
- YouTube and Vimeo URLs now return iframe embed code
- We now look for OpenGraph title and date elements
- Improved extraction from AJAX pages - we now look for AJAX triggers embedded in HTML, per Google spec
- JSONP support - use &format=json&callback=functionName in querystring
- New config option to enable Cross-Origin Resource Sharing (CORS): $option->cors
- New config option to enable XSS filtering, if required: $option->xss_filter
- Zend_Cache updated
- Smart caching - experimental feature to store cache IDs in APC first, and write output to disk on subsequent request (see $options->smart_cache)
- Easier cloud deploy - manifest.yml added for AppFog
- Override most config options with environment variables, e.g. ftr_max_entries: 3
2.9.5 (2012-04-29)
- Language detection using Text_LanguageDetect or PHP-CLD (dc:language field in output and $options->detect_language in config)
- New site patterns added and old ones updated
- Experimental tool for simpler site pattern updates (access admin/ folder)
- Plus other fixes/improvements
2.9.1 (2011-11-02)
- Fix: Character encoding issue affecting some non-English articles (makefulltextfeed.php and SimplePie/Misc.php changed)
2.9 (2011-11-01)
- New site patterns added and old ones updated
- New config option: require_key - restrict access to those with password/key
- New config option: rewrite_url - URL rewrite rules to be applied before HTTP request
- New site config options to extract author(s) and publication date (matches included in feed item as <dc:creator> and <pubDate>)
- New site config option: replace_string([string to find]): [replacement string]
- New site identification method: site fingerprints (HTML fragments linked to site config)
- Update check now also checks for new site patterns
- Effective URL (URL after redirects/rewrites) now included in feed item as <dc:identifier>
- Prevent indexing of generated feeds by search engines
- Enclosure support (enclosures preserved as <media:content> elements)
- Better handling of non-HTML content types
- Sending custom User-Agent HTTP header for matching sites now supported
- CSS extraction deprecated in favour of site patterns (still works, but form field removed and feature may disappear in 3.0)
- Fix: Improved character-encoding detection
- Fix: URL parsing issues for certain URLs (SimplePie updated)
- Fix: Author and other Dublin Core (<dc:..>) elements now appear in JSON output
- Fix: Minor fixes for PHP Readability
- Plus other minor fixes/improvements
2.8 (2011-05-30)
- Tidy no longer stripping HTML5 elements
- JSON output (pass &format=json in querystring)
- New site patterns added and old ones updated
- New site config option to force full-page retrieval on multi-page articles: single_page_link
- User Guide (PDF) now included (although still a work in progress)
- URL placeholders now accepted in message_to_prepend/append config options
- Plus minor fixes...
2.7 (2011-03-21)
- Site patterns for better control over extraction (see site_config/README.txt)
- hNews support (improves content extraction for sites using hNews microformatting)
- Cookie Jar now used to store and sends cookies when following HTTP redirects
- Better handling of certain cases where HTML Tidy fails to clean up properly
- Bug fix: curl_multi_select() timing out in certain environments (fixed in HumbleHttpAgent.php)
- Bug fix: broken HTTP header parsing in some environments (fixed in SimplePie_HumbleHttpAgent.php)
- Bug fix: invalid API URL shown (fixed in index.php)
- Plus other minor fixes...
2.6 (2011-03-02)
- Rewriting of hash-bang (#!) URLs (see http://www.tbray.org/ongoing/When/201x/2011/02/09/Hash-Blecch for an explanation)
- Improved parallel fetching support (HumbleHttpAgent uses curl_multi_* functions if PECL HTTP extension is not present)
- Improved HTTP redirect support (now handled in HumbleHttpAgent, no longer relies on PHP)
- Improved performance for single page (non-feed) requests: (SimplePie connected to HumbleHttpAgent)
- Improved memory use for processing large feeds (HumbleHttpAgent's stored responses cleared as they're retrieved)
- Bug fix: exclude on fail option no longer requires valid key
- Bug fix: workaround for PHP bug http://bugs.php.net/51192 (fixed in makefulltextfeed.php)
- Plus other minor changes...
2.5 (2011-01-08)
- New option: custom extraction pattern (CSS selectors)
- New option: allowed URLs (restrict service to pre-defined feeds/domains)
- New option: exclude items on fail (remove items from feed if content extraction fails)
- Remove 'http://' from URL before form submission (prevents errors on hosts which have overly vigilant security software)
- Allow overriding of index.php with custom_index.php
- config.php now required (override with custom_config.php)
- index.php now uses config.php to determine what to display
- Bug fix: occasional fatal error in IRI::__toString() (IRI updated)
- Bug fix: workaround for PHP bug http://bugs.php.net/51192 (fixed in HumbleHttpAgent.php)
2.2 (2010-10-30)
- Character-encoding detection improved (minor change)
- Rewriting of relative URLs improved (tracks redirect URLs)
- Minor changes to prevent errors in certain hosting environments
- Compatibility test file updated with more tests
2.1 (2010-09-13)
- Better content extraction (using PHP Readability 1.7.1)
- Parallel HTTP requests (using Humble HTTP Agent)
- Auto loading of necessary classes
- Rewriting of relative URLs (using IRI)
- Added compatibility test file (to check if server meets requirements)
- Character-encoding support improved (using SimplePie)
1.5 (2010-05-30)
- Support for PHP 5.3 (thanks Murilo!)
- Character-encoding support improved (favours iconv over mb_convert_encoding)
1.0 (2010-03-05)
- Better support for different character-encodings
- Auto-cleanup of cache files
- Very basic option for load distribution (if you're planning on installing the code on multiple servers)
- Separate config file (see config-sample.php)

108
vendor/full-text-rss/cleancache.php vendored Normal file
View File

@ -0,0 +1,108 @@
<?php
// Full-Text RSS: Clear Cache
// Author: Keyvan Minoukadeh
// Copyright (c) 2012 Keyvan Minoukadeh
// License: AGPLv3
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Usage
// -----
// Set up your scheduler (e.g. cron) to request this file periodically.
// Note: this file must _not_ be named cleancache.php so please rename it.
// We ask you to do this to prevent others from initiating
// the cache cleanup process. It will not run if it's called cleancache.php.
error_reporting(E_ALL ^ E_NOTICE);
ini_set("display_errors", 1);
@set_time_limit(120);
// check file name
if (basename(__FILE__) == 'cleancache.php') die('cleancache.php must be renamed');
// set include path
set_include_path(realpath(dirname(__FILE__).'/libraries').PATH_SEPARATOR.get_include_path());
// Autoloading of classes allows us to include files only when they're
// needed. If we've got a cached copy, for example, only Zend_Cache is loaded.
function __autoload($class_name) {
static $mapping = array(
'Zend_Cache' => 'Zend/Cache.php'
);
if (isset($mapping[$class_name])) {
//echo "Loading $class_name\n<br />";
require_once $mapping[$class_name];
return true;
} else {
return false;
}
}
require_once dirname(__FILE__).'/config.php';
if (!$options->caching) die('Caching is disabled');
// clean APC cache
if ($options->apc && function_exists('apc_delete')) {
$_apc_data = apc_cache_info('user');
foreach ($_apc_data['cache_list'] as $_apc_item) {
if ($_apc_item['ttl'] > 0 && ($_apc_item['ttl'] + $_apc_item['creation_time'] < time())) {
apc_delete($_apc_item['info']);
}
}
}
// clean rss (non-key) cache
$frontendOptions = array(
'lifetime' => 20*60,
'automatic_serialization' => false,
'write_control' => false,
'automatic_cleaning_factor' => 0,
'ignore_user_abort' => false
);
$backendOptions = array(
'cache_dir' => $options->cache_dir.'/rss/',
'file_locking' => false,
'read_control' => true,
'read_control_type' => 'strlen',
'hashed_directory_level' => $options->cache_directory_level,
'hashed_directory_perm' => 0777,
'cache_file_perm' => 0664,
'file_name_prefix' => 'ff'
);
$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
$cache->clean(Zend_Cache::CLEANING_MODE_OLD);
// clean rss (key) cache
$frontendOptions = array(
'lifetime' => 20*60,
'automatic_serialization' => false,
'write_control' => false,
'automatic_cleaning_factor' => 0,
'ignore_user_abort' => false
);
$backendOptions = array(
'cache_dir' => $options->cache_dir.'/rss-with-key/',
'file_locking' => false,
'read_control' => true,
'read_control_type' => 'strlen',
'hashed_directory_level' => $options->cache_directory_level,
'hashed_directory_perm' => 0777,
'cache_file_perm' => 0664,
'file_name_prefix' => 'ff'
);
$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
$cache->clean(Zend_Cache::CLEANING_MODE_OLD);
?>

405
vendor/full-text-rss/config.php vendored Normal file
View File

@ -0,0 +1,405 @@
<?php
/* Full-Text RSS config */
// ......IMPORTANT......................................
// .....................................................
// Please do not change this file (config.php) directly.
// Save a copy as custom_config.php and make your
// changes to that instead. It will automatically
// override anything in config.php. Because config.php
// always gets loaded anyway, you can simply specify
// options you'd like to override in custom_config.php.
// .....................................................
// Create config object
if (!isset($options)) $options = new stdClass();
// Enable service
// ----------------------
// Set this to false if you want to disable the service.
// If set to false, no feed is produced and users will
// be told that the service is disabled.
$options->enabled = true;
// Debug mode
// ----------------------
// Enable or disable debugging. When enabled debugging works by passing
// &debug to the makefulltextfeed.php querystring.
// Valid values:
// true or 'user' (default) - let user decide
// 'admin' - debug works only for logged in admin users
// false - disabled
$options->debug = true;
// Default entries (without access key)
// ----------------------
// The number of feed items to process when no API key is supplied
// and no &max=x value is supplied in the querystring.
$options->default_entries = 5;
// Max entries (without access key)
// ----------------------
// The maximum number of feed items to process when no access key is supplied.
// This limits the user-supplied &max=x value. For example, if the user
// asks for 20 items to be processed (&max=20), if max_entries is set to
// 10, only 10 will be processed.
$options->max_entries = 10;
// Rewrite relative URLs
// ----------------------
// With this enabled relative URLs found in the extracted content
// block are automatically rewritten as absolute URLs.
$options->rewrite_relative_urls = true;
// Exclude items if extraction fails
// ---------------------------------
// Excludes items from the resulting feed
// if we cannot extract any content from the
// item URL.
// Possible values...
// Enable: true
// Disable: false (default)
// User decides: 'user' (this option will appear on the form)
$options->exclude_items_on_fail = 'user';
// Enable multi-page support
// -------------------------
// If enabled, we will try to follow next page links on multi-page articles.
// Currently this only happens for sites where next_page_link has been defined
// in a site config file.
$options->multipage = true;
// Enable caching
// ----------------------
// Enable this if you'd like to cache results
// for 10 minutes. Cache files are written to disk (in cache/ subfolders
// - which must be writable).
// Initially it's best to keep this disabled to make sure everything works
// as expected. If you have APC enabled, please also see smart_cache in the
// advanced section.
$options->caching = false;
// Cache directory
// ----------------------
// Only used if caching is true
$options->cache_dir = dirname(__FILE__).'/cache';
// Message to prepend (without access key)
// ----------------------
// HTML to insert at the beginning of each feed item when no access key is supplied.
// Substitution tags:
// {url} - Feed item URL
// {effective-url} - Feed item URL after we've followed all redirects
$options->message_to_prepend = '';
// Message to append (without access key)
// ----------------------
// HTML to insert at the end of each feed item when no access key is supplied.
// Substitution tags:
// {url} - Feed item URL
// {effective-url} - Feed item URL after we've followed all redirects
$options->message_to_append = '';
// Error message when content extraction fails (without access key)
// ----------------------
$options->error_message = '[unable to retrieve full-text content]';
// Keep enclosure in feed items
// If enabled, we will try to preserve enclosures if present.
// ----------------------
$options->keep_enclosures = true;
// Detect language
// ---------------
// Should we try and find/guess the language of the article being processed?
// Values will be placed inside the <dc:language> element inside each <item> element
// Possible values:
// * Ignore language: 0
// * Use article/feed metadata (e.g. HTML lang attribute): 1 (default)
// * As above, but guess if not present: 2
// * Always guess: 3
// * User decides: 'user' (value of 0-3 can be passed in querystring: e.g. &l=2)
$options->detect_language = 1;
// Registration key
// ---------------
// The registration key is optional. It is not required to use Full-Text RSS,
// and does not affect the normal operation of Full-Text RSS. It is currently
// only used on admin pages which help you update site patterns with the
// latest version offered by FiveFilters.org. For these admin-related
// tasks to complete, we will require a valid registration key.
// If you would like one, you can purchase the latest version of Full-Text RSS
// at http://fivefilters.org/content-only/
// Your registration key will automatically be sent in the confirmation email.
// Once you have it, simply copy and paste it here.
$options->registration_key = '';
/////////////////////////////////////////////////
/// RESTRICT ACCESS /////////////////////////////
/////////////////////////////////////////////////
// Admin credentials
// ----------------------
// Certain pages/actions, e.g. updating site patterns with our online tool, will require admin credentials.
// To use these pages, enter a password here and you'll be prompted for it when you try to access those pages.
// If no password or username is set, pages requiring admin privelages will be inaccessible.
// The default username is 'admin'.
// If overriding with an environment variable, separate username and password with a colon, e.g.:
// ftr_admin_credentials: admin:my-secret-password
// Example: $options->admin_credentials = array('username'=>'admin', 'password'=>'my-secret-password');
$options->admin_credentials = array('username'=>'admin', 'password'=>'');
// URLs to allow
// ----------------------
// List of URLs (or parts of a URL) which the service will accept.
// If the list is empty, all URLs (except those specified in the blocked list below)
// will be permitted.
// Empty: array();
// Non-empty example: array('example.com', 'anothersite.org');
$options->allowed_urls = array();
// URLs to block
// ----------------------
// List of URLs (or parts of a URL) which the service will not accept.
// Note: this list is ignored if allowed_urls is not empty
$options->blocked_urls = array();
// Key holder(s) only?
// ----------------------
// Set this to true if you want to restrict access only to
// those with a key (see below to specify key(s)).
// If set to true, no feed is produced unless a valid
// key is provided.
$options->key_required = false;
// Favour item titles in feed
// ----------------------
// By default, when processing feeds, we assume item titles in the feed
// have not been truncated. So after processing web pages, the extracted titles
// are not used in the generated feed. If you prefer to have extracted titles in
// the feed you can either set this to false, in which case we will always favour
// extracted titles. Alternatively, if set to 'user' (default) we'll use the
// extracted title if you pass '&use_extracted_title' in the querystring.
// Possible values:
// * Favour feed titles: true
// * Favour extracted titles: false
// * Favour feed titles with user override: 'user' (default)
// Note: this has no effect when the input URL is to a web page - in these cases
// we always use the extracted title in the generated feed.
$options->favour_feed_titles = 'user';
// Access keys (password protected access)
// ------------------------------------
// NOTE: You do not need an API key from fivefilters.org to run your own
// copy of the code. This is here if you'd like to restrict access to
// _your_ copy.
// Keys let you group users - those with a key and those without - and
// restrict access to the service to those without a key.
// If you want everyone to access the service in the same way, you can
// leave the array below empty and ignore the access key options further down.
// The options further down let you control how the service should behave
// in each mode.
// Note: Explicitly including the index number (1 and 2 in the examples below)
// is highly recommended (when generating feeds, we encode the key and
// refer to it by index number and hash).
$options->api_keys = array();
// Example:
// $options->api_keys[1] = 'secret-key-1';
// $options->api_keys[2] = 'secret-key-2';
// Default entries (with access key)
// ----------------------
// The number of feed items to process when a valid access key is supplied.
$options->default_entries_with_key = 5;
// Max entries (with access key)
// ----------------------
// The maximum number of feed items to process when a valid access key is supplied.
$options->max_entries_with_key = 10;
/////////////////////////////////////////////////
/// ADVANCED OPTIONS ////////////////////////////
/////////////////////////////////////////////////
// Enable XSS filter?
// ----------------------
// We have not enabled this by default because we assume the majority of
// our users do not display the HTML retrieved by Full-Text RSS
// in a web page without further processing. If you subscribe to our generated
// feeds in your news reader application, it should, if it's good software, already
// filter the resulting HTML for XSS attacks, making it redundant for
// Full-Text RSS do the same. Similarly with frameworks/CMS which display
// feed content - the content should be treated like any other user-submitted content.
//
// If you are writing an application yourself which is processing feeds generated by
// Full-Text RSS, you can either filter the HTML yourself to remove potential XSS attacks
// or enable this option. This might be useful if you are processing our generated
// feeds with JavaScript on the client side - although there's client side xss
// filtering available too, e.g. https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer
//
// If enabled, we'll pass retrieved HTML content through htmLawed with
// safe flag on and style attributes denied, see
// http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm#s3.6
// Note: if enabled this will also remove certain elements you may want to preserve, such as iframes.
//
// Valid values:
// true - enabled, all content will be filtered
// 'user' (default) - user must pass &xss in makefulltextfeed.php querystring to enable
// false - disabled
$options->xss_filter = 'user';
// Allowed parsers
// ----------------------
// Full-Text RSS attempts to use PHP's libxml extension to process HTML.
// While fast, on some sites it may not always produce good results.
// For these sites, you can specify an alternative HTML parser:
// parser: html5lib
// The html5lib parser is bundled with Full-Text RSS.
// see http://code.google.com/p/html5lib/
//
// To disable HTML parsing with html5lib, you can remove it from this list.
// By default we allow both: libxml and html5lib.
$options->allowed_parsers = array('libxml', 'html5lib');
//$options->allowed_parsers = array('libxml'); //disable html5lib - forcing libxml in all cases
// Enable Cross-Origin Resource Sharing (CORS)
// ----------------------
// If enabled we'll send the following HTTP header
// Access-Control-Allow-Origin: *
// see http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
$options->cors = false;
// Use APC user cache?
// ----------------------
// If enabled we will store site config files (when requested
// for the first time) in APC's user cache. Keys prefixed with 'sc.'
// This improves performance by reducing disk access.
// Note: this has no effect if APC is unavailable on your server.
$options->apc = true;
// Smart cache (experimental)
// ----------------------
// With this option enabled we will not cache to disk immediately.
// We will store the cache key in APC and if it's requested again
// we will cache results to disk. Keys prefixed with 'cache.'
// This improves performance by reducing disk access.
// Note: this has no effect if APC is disabled or unavailable on your server,
// or if you have caching disabled.
$options->smart_cache = true;
// Fingerprints
// ----------------------
// key is fingerprint (fragment to find in HTML)
// value is host name to use for site config lookup if fingerprint matches
$options->fingerprints = array(
// Posterous
'<meta name="generator" content="Posterous"' => array('hostname'=>'fingerprint.posterous.com', 'head'=>true),
// Blogger
'<meta content=\'blogger\' name=\'generator\'' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
'<meta name="generator" content="Blogger"' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
// WordPress (hosted)
// '<meta name="generator" content="WordPress.com"' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true),
// WordPress (self-hosted and hosted)
'<meta name="generator" content="WordPress' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true)
);
// User Agent strings - mapping domain names
// ----------------------
// e.g. $options->user_agents = array('example.org' => 'PHP/5.2');
$options->user_agents = array( 'lifehacker.com' => 'PHP/5.2',
'gawker.com' => 'PHP/5.2',
'deadspin.com' => 'PHP/5.2',
'kotaku.com' => 'PHP/5.2',
'jezebel.com' => 'PHP/5.2',
'io9.com' => 'PHP/5.2',
'jalopnik.com' => 'PHP/5.2',
'gizmodo.com' => 'PHP/5.2',
'.wikipedia.org' => 'Mozilla/5.2',
'.fok.nl' => 'Googlebot/2.1',
'getpocket.com' => 'PHP/5.2'
);
// URL Rewriting
// ----------------------
// Currently allows simple string replace of URLs.
// Useful for rewriting certain URLs to point to a single page
// or HTML view. Although using the single_page_link site config
// instruction is the preferred way to do this, sometimes, as
// with Google Docs URLs, it's not possible.
// Note: this might move to the site config file at some point.
$options->rewrite_url = array(
// Rewrite public Google Docs URLs to point to HTML view:
// if a URL contains docs.google.com, replace /Doc? with /View?
'docs.google.com' => array('/Doc?' => '/View?'),
'tnr.com' => array('tnr.com/article/' => 'tnr.com/print/article/'),
'.m.wikipedia.org' => array('.m.wikipedia.org' => '.wikipedia.org'),
'm.vanityfair.com' => array('m.vanityfair.com' => 'www.vanityfair.com')
);
// Content-Type exceptions
// -----------------------
// Here you can define different actions based
// on the Content-Type header returned by server.
// MIME type as key, action as value.
// Valid actions:
// * 'exclude' - exclude this item from the result
// * 'link' - create HTML link to the item
$options->content_type_exc = array(
'application/pdf' => array('action'=>'link', 'name'=>'PDF'),
'image' => array('action'=>'link', 'name'=>'Image'),
'audio' => array('action'=>'link', 'name'=>'Audio'),
'video' => array('action'=>'link', 'name'=>'Video')
);
// Cache directory level
// ----------------------
// Spread cache files over different directories (only used if caching is enabled).
// Used to prevent large number of files in one directory.
// This corresponds to Zend_Cache's hashed_directory_level
// see http://framework.zend.com/manual/en/zend.cache.backends.html
// It's best not to change this if you're unsure.
$options->cache_directory_level = 0;
// Cache cleanup
// -------------
// 0 = script will not clean cache (rename cachecleanup.php and use it for scheduled (e.g. cron) cache cleanup)
// 1 = clean cache everytime the script runs (not recommended)
// 100 = clean cache roughly once every 100 script runs
// x = clean cache roughly once every x script runs
// ...you get the idea :)
$options->cache_cleanup = 100;
/////////////////////////////////////////////////
/// DO NOT CHANGE ANYTHING BELOW THIS ///////////
/////////////////////////////////////////////////
if (!defined('_FF_FTR_VERSION')) define('_FF_FTR_VERSION', '3.1');
if (basename(__FILE__) == 'config.php') {
if (file_exists(dirname(__FILE__).'/custom_config.php')) {
require_once dirname(__FILE__).'/custom_config.php';
}
// check for environment variables - often used on cloud platforms
// environment variables should be prefixed with 'ftr_', e.g.
// ftr_max_entries: 1
// will set the max_entries value to 1.
foreach ($options as $_key=>&$_val) {
$_key = "ftr_$_key";
if (($_env = getenv($_key)) !== false) {
if (is_array($_val)) {
if ($_key === 'ftr_admin_credentials') {
$_val = array_combine(array('username', 'password'), array_map('trim', explode(':', $_env, 2)));
if ($_val === false) $_val = array('username'=>'admin', 'password'=>'');
}
} elseif ($_env === 'true' || $_env === 'false') {
$_val = ($_env === 'true');
} elseif (is_numeric($_env)) {
$_val = (int)$_env;
} else { // string
$_val = $_env;
}
}
}
unset($_key, $_val, $_env);
}

File diff suppressed because one or more lines are too long

30
vendor/full-text-rss/css/feed.css vendored Normal file
View File

@ -0,0 +1,30 @@
/* RSS CSS Document */
* { margin:0; padding:0; }
p { padding: .5em 0; }
h1,h2,h3,h4,h5,h6 { font-size: 1em; padding: .5em 0; }
html { display:block; padding-bottom:50px; }
body { font:80% Verdana, sans-serif; color:#000; padding:25px 0 0 35px; }
a { color:#5BAB03; text-decoration:none; }
a:hover { color:#5BAB03; text-decoration: underline;}
ul { margin-left:1.5em; }
li { margin-bottom:0.4em; }
div#content>ul { list-style-type: none; }
div.article>li>a { font-weight:bold; font-size: 1.3em;}
div { line-height:1.6em; }
div#content { background:#fff; margin-right:15px; padding-left:1em;}
div#content div { margin:0 1em 1em 0; }
div#explanation { padding:1em 1em 0 1em; border:1px solid #ddd; background:#efefef; margin:0 2em 2em 0; }
div#explanation h1 { font-weight:normal; font-size:1.8em; margin-bottom:0.3em; }
div#explanation p { margin-bottom:1em; }
.small { font-size: .7em; color: #666; }

34
vendor/full-text-rss/css/feed.xsl vendored Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:variable name="title" select="/rss/channel/title"/>
<xsl:template match="/">
<html>
<head>
<title><xsl:value-of select="$title"/> (full-text feed)</title>
<style type="text/css">
@import url(css/feed.css);
</style>
</head>
<body>
<div id="explanation">
<h1><xsl:value-of select="$title"/> <span class="small"> (full-text feed)</span></h1>
<p>You are viewing an auto-generated full-text <acronym title="Really Simple Syndication">RSS</acronym> feed. RSS feeds allow you to stay up to date with the latest news and features you want from websites. To subscribe to it, you will need a News Reader or other similar device.</p>
<p>Below is the latest content available from this feed.</p>
</div>
<div id="content">
<ul>
<xsl:for-each select="rss/channel/item">
<div class="article">
<li><a href="{link}" rel="bookmark"><xsl:value-of disable-output-escaping="yes" select="title"/></a>
<div><xsl:value-of disable-output-escaping="yes" select="description" /></div>
</li>
</div>
</xsl:for-each>
</ul>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,357 @@
<?php
/*
FULL-TEXT-RSS V3 COMPATIBILITY TEST
1) Upload ftr_compatibility_test.php to the web-accessible root of your website.
For example, if your website is www.example.com, upload it so that you can get
to it at www.example.com/ftr_compatibility_test.php
2) Open your web browser and go to the page you just uploaded.
Note: This compatibility test has been borrowed (and slightly adapted) from the one supplied by
SimplePie.org. We have kept most of their checks intact as we use SimplePie in our application.
http://github.com/simplepie/simplepie/tree/master/compatibility_test/
*/
$app_name = 'Full-Text RSS 3.1';
$php_ok = (function_exists('version_compare') && version_compare(phpversion(), '5.2.0', '>='));
$pcre_ok = extension_loaded('pcre');
$zlib_ok = extension_loaded('zlib');
$mbstring_ok = extension_loaded('mbstring');
$iconv_ok = extension_loaded('iconv');
$tidy_ok = function_exists('tidy_parse_string');
$curl_ok = function_exists('curl_exec');
$parallel_ok = ((extension_loaded('http') && class_exists('HttpRequestPool')) || ($curl_ok && function_exists('curl_multi_init')));
$allow_url_fopen_ok = (bool)ini_get('allow_url_fopen');
$filter_ok = extension_loaded('filter');
if (extension_loaded('xmlreader')) {
$xml_ok = true;
} elseif (extension_loaded('xml')) {
$parser_check = xml_parser_create();
xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
xml_parser_free($parser_check);
$xml_ok = isset($values[0]['value']);
} else {
$xml_ok = false;
}
header('Content-type: text/html; charset=UTF-8');
?><!DOCTYPE html>
<html lang="en">
<head>
<title><?php echo $app_name; ?>: Server Compatibility Test</title>
<style type="text/css">
body {
font:14px/1.4em "Lucida Grande", Verdana, Arial, Helvetica, Clean, Sans, sans-serif;
letter-spacing:0px;
color:#333;
margin:0;
padding:0;
background:#fff;
}
div#site {
width:550px;
margin:20px auto 0 auto;
}
a {
color:#000;
text-decoration:underline;
padding:0 1px;
}
a:hover {
color:#fff;
background-color:#333;
text-decoration:none;
padding:0 1px;
}
p {
margin:0;
padding:5px 0;
}
em {
font-style:normal;
background-color:#ffc;
padding: 0.1em 0;
}
.success {
background-color: lightgreen;
}
.highlight {
background-color: #ffc;
}
ul, ol {
margin:10px 0 10px 20px;
padding:0 0 0 15px;
}
ul li, ol li {
margin:0 0 7px 0;
padding:0 0 0 3px;
}
h2 {
font-size:18px;
padding:0;
margin:30px 0 20px 0;
}
h3 {
font-size:16px;
padding:0;
margin:20px 0 5px 0;
}
h4 {
font-size:14px;
padding:0;
margin:15px 0 5px 0;
}
code {
font-size:1.1em;
color:#000;
}
em strong {
text-transform: uppercase;
}
table#chart {
border-collapse:collapse;
}
table#chart th {
background-color:#eee;
padding:2px 3px;
border:1px solid #fff;
}
table#chart td {
text-align:center;
padding:2px 3px;
border:1px solid #eee;
}
table#chart tr.enabled td {
/* Leave this alone */
}
table#chart tr.disabled td,
table#chart tr.disabled td a {
color:#999;
font-style:italic;
}
table#chart tr.disabled td a {
text-decoration:underline;
}
div.chunk {
margin:20px 0 0 0;
padding:0 0 10px 0;
border-bottom:1px solid #ccc;
}
.footnote,
.footnote a {
font:10px/12px verdana, sans-serif;
color:#aaa;
}
.footnote em {
background-color:transparent;
font-style:italic;
}
</style>
</head>
<body>
<div id="site">
<div id="content">
<div class="chunk">
<h2 style="text-align:center;"><?php echo $app_name; ?>: Compatibility Test</h2>
<table cellpadding="0" cellspacing="0" border="0" width="100%" id="chart">
<thead>
<tr>
<th>Test</th>
<th>Should Be</th>
<th>What You Have</th>
</tr>
</thead>
<tbody>
<tr class="<?php echo ($php_ok) ? 'enabled' : 'disabled'; ?>">
<td>PHP</td>
<td>5.2.0 or higher</td>
<td><?php echo phpversion(); ?></td>
</tr>
<tr class="<?php echo ($xml_ok) ? 'enabled, and sane' : 'disabled, or broken'; ?>">
<td><a href="http://php.net/xml">XML</a></td>
<td>Enabled</td>
<td><?php echo ($xml_ok) ? 'Enabled, and sane' : 'Disabled, or broken'; ?></td>
</tr>
<tr class="<?php echo ($pcre_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://php.net/pcre">PCRE</a></td>
<td>Enabled</td>
<td><?php echo ($pcre_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($zlib_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://php.net/zlib">Zlib</a></td>
<td>Enabled</td>
<td><?php echo ($zlib_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($mbstring_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://php.net/mbstring">mbstring</a></td>
<td>Enabled</td>
<td><?php echo ($mbstring_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($iconv_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://php.net/iconv">iconv</a></td>
<td>Enabled</td>
<td><?php echo ($iconv_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($filter_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://uk.php.net/manual/en/book.filter.php">Data filtering</a></td>
<td>Enabled</td>
<td><?php echo ($filter_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($tidy_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://php.net/tidy">Tidy</a></td>
<td>Enabled</td>
<td><?php echo ($tidy_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($curl_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://php.net/curl">cURL</a></td>
<td>Enabled</td>
<td><?php echo (extension_loaded('curl')) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($parallel_ok) ? 'enabled' : 'disabled'; ?>">
<td>Parallel URL fetching</td>
<td>Enabled</td>
<td><?php echo ($parallel_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
<tr class="<?php echo ($allow_url_fopen_ok) ? 'enabled' : 'disabled'; ?>">
<td><a href="http://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen">allow_url_fopen</a></td>
<td>Enabled</td>
<td><?php echo ($allow_url_fopen_ok) ? 'Enabled' : 'Disabled'; ?></td>
</tr>
</tbody>
</table>
</div>
<div class="chunk">
<h3>What does this mean?</h3>
<ol>
<?php if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $iconv_ok && $filter_ok && $zlib_ok && $tidy_ok && $curl_ok && $parallel_ok && $allow_url_fopen_ok): ?>
<li>You have everything you need to run <?php echo $app_name; ?> properly! Congratulations!</li>
<?php else: ?>
<?php if ($php_ok): ?>
<li><strong>PHP:</strong> You are running a supported version of PHP. No problems here.</li>
<?php if ($xml_ok): ?>
<li><strong>XML:</strong> You have XMLReader support or a version of XML support that isn't broken installed. No problems here.</li>
<?php if ($pcre_ok): ?>
<li><strong>PCRE:</strong> You have PCRE support installed. No problems here.</li>
<?php if ($allow_url_fopen_ok): ?>
<li><strong>allow_url_fopen:</strong> You have allow_url_fopen enabled. No problems here.</li>
<?php if ($filter_ok): ?>
<li><strong>Data filtering:</strong> You have the PHP filter extension enabled. No problems here.</li>
<?php if ($zlib_ok): ?>
<li><strong>Zlib:</strong> You have <code>Zlib</code> enabled. This allows SimplePie to support GZIP-encoded feeds. No problems here.</li>
<?php else: ?>
<li class="highlight"><strong>Zlib:</strong> The <code>Zlib</code> extension is not available. SimplePie will ignore any GZIP-encoding, and instead handle feeds as uncompressed text.</li>
<?php endif; ?>
<?php if ($mbstring_ok && $iconv_ok): ?>
<li><strong>mbstring and iconv:</strong> You have both <code>mbstring</code> and <code>iconv</code> installed! This will allow <?php echo $app_name; ?> to handle the greatest number of languages. No problems here.</li>
<?php elseif ($mbstring_ok): ?>
<li class="highlight"><strong>mbstring:</strong> <code>mbstring</code> is installed, but <code>iconv</code> is not.</li>
<?php elseif ($iconv_ok): ?>
<li class="highlight"><strong>iconv:</strong> <code>iconv</code> is installed, but <code>mbstring</code> is not.</li>
<?php else: ?>
<li class="highlight"><strong>mbstring and iconv:</strong> <em>You do not have either of the extensions installed.</em> This will significantly impair your ability to read non-English feeds, as well as even some English ones.</li>
<?php endif; ?>
<?php if ($tidy_ok): ?>
<li><strong>Tidy:</strong> You have <code>Tidy</code> support installed. No problems here.</li>
<?php else: ?>
<li class="highlight"><strong>Tidy:</strong> The <code>Tidy</code> extension is not available. <?php echo $app_name; ?> should still work with most feeds/articles, but you may experience problems with some. If you do, we suggest you specify parsing with html5lib.</li>
<?php endif; ?>
<?php if ($curl_ok): ?>
<li><strong>cURL:</strong> You have <code>cURL</code> support installed. No problems here.</li>
<?php else: ?>
<li class="highlight"><strong>cURL:</strong> The <code>cURL</code> extension is not available. SimplePie will use <code>fsockopen()</code> instead.</li>
<?php endif; ?>
<?php if ($parallel_ok): ?>
<li><strong>Parallel URL fetching:</strong> You have <code>HttpRequestPool</code> or <code>curl_multi</code> support installed. No problems here.</li>
<?php else: ?>
<li class="highlight"><strong>Parallel URL fetching:</strong> <code>HttpRequestPool</code> or <code>curl_multi</code> support is not available. <?php echo $app_name; ?> will use <code>file_get_contents()</code> instead to fetch URLs sequentially rather than in parallel.</li>
<?php endif; ?>
<?php else: ?>
<li class="highlight"><strong>Data filtering:</strong> Your PHP configuration has the filter extension disabled. <em><?php echo $app_name; ?> will not work here.</em></li>
<?php endif; ?>
<?php else: ?>
<li class="highlight"><strong>allow_url_fopen:</strong> Your PHP configuration has allow_url_fopen disabled. <em><?php echo $app_name; ?> will not work here.</em></li>
<?php endif; ?>
<?php else: ?>
<li class="highlight"><strong>PCRE:</strong> Your PHP installation doesn't support Perl-Compatible Regular Expressions. <em><?php echo $app_name; ?> will not work here.</em></li>
<?php endif; ?>
<?php else: ?>
<li class="highlight"><strong>XML:</strong> Your PHP installation doesn't support XML parsing. <em><?php echo $app_name; ?> will not work here.</em></li>
<?php endif; ?>
<?php else: ?>
<li class="highlight"><strong>PHP:</strong> You are running an unsupported version of PHP. <em><?php echo $app_name; ?> will not work here.</em></li>
<?php endif; ?>
<?php endif; ?>
</ol>
</div>
<div class="chunk">
<?php if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $iconv_ok && $filter_ok && $allow_url_fopen_ok) { ?>
<h3>Bottom Line: Yes, you can!</h3>
<p><em class="success">Your webhost has its act together!</em></p>
<p>You can download the latest version of <?php echo $app_name; ?> from <a href="http://fivefilters.org/content-only/#download">FiveFilters.org</a>.</p>
<p><strong>Note</strong>: Passing this test does not guarantee that <?php echo $app_name; ?> will run on your webhost &mdash; it only ensures that the basic requirements have been addressed. If you experience any problems, please let us know.</p>
<?php } else if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $allow_url_fopen_ok && $filter_ok) { ?>
<h3>Bottom Line: Yes, you can!</h3>
<p><em>For most feeds, it'll run with no problems.</em> There are certain languages that you might have a hard time with though.</p>
<p>You can download the latest version of <?php echo $app_name; ?> from <a href="http://fivefilters.org/content-only/#download">FiveFilters.org</a>.</p>
<p><strong>Note</strong>: Passing this test does not guarantee that <?php echo $app_name; ?> will run on your webhost &mdash; it only ensures that the basic requirements have been addressed. If you experience any problems, please let us know.</p>
<?php } else { ?>
<h3>Bottom Line: We're sorry…</h3>
<p><em>Your webhost does not support the minimum requirements for <?php echo $app_name; ?>.</em> It may be a good idea to contact your webhost and point them to the results of this test. They may be able to enable/install the required components.</p>
<?php } ?>
</div>
<div class="chunk">
<p class="footnote">This compatibility test has been borrowed (and slightly adapted) from the one supplied by <a href="http://simplepie.org/">SimplePie.org</a>. We have kept most of their checks intact as we use SimplePie in our application.</a></p>
</div>
</div>
</div>
</body>
</html>

BIN
vendor/full-text-rss/images/agplv3.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

288
vendor/full-text-rss/index.php vendored Normal file
View File

@ -0,0 +1,288 @@
<?php
require_once(dirname(__FILE__).'/config.php');
// check for custom index.php (custom_index.php)
if (!defined('_FF_FTR_INDEX')) {
define('_FF_FTR_INDEX', true);
if (file_exists(dirname(__FILE__).'/custom_index.php')) {
include(dirname(__FILE__).'/custom_index.php');
exit;
}
}
?><!DOCTYPE html>
<html>
<head>
<title>Full-Text RSS Feeds | from fivefilters.org</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="noindex, follow" />
<link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" media="screen" />
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap-tooltip.js"></script>
<script type="text/javascript" src="js/bootstrap-popover.js"></script>
<script type="text/javascript" src="js/bootstrap-tab.js"></script>
<script type="text/javascript">
var baseUrl = 'http://'+window.location.host+window.location.pathname.replace(/(\/index\.php|\/)$/, '');
$(document).ready(function() {
// remove http scheme from urls before submitting
$('#form').submit(function() {
$('#url').val($('#url').val().replace(/^http:\/\//i, ''));
return true;
});
// popovers
$('#url').popover({offset: 10, placement: 'left', trigger: 'focus', html: true});
$('#key').popover({offset: 10, placement: 'left', trigger: 'focus', html: true});
$('#max').popover({offset: 10, placement: 'left', trigger: 'focus', html: true});
$('#links').popover({offset: 10, placement: 'left', trigger: 'focus', html: true});
$('#exc').popover({offset: 10, placement: 'left', trigger: 'focus', html: true});
// tooltips
$('a[rel=tooltip]').tooltip();
});
</script>
<style>
html, body { background-color: #eee; }
body { margin: 0; line-height: 1.4em; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; }
label, input, select, textarea { font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; }
li { color: #404040; }
li.active a { font-weight: bold; color: #666 !important; }
form .controls { margin-left: 220px !important; }
label { width: 200px !important; }
fieldset legend { padding-left: 220px; line-height: 20px !important; margin-bottom: 10px !important;}
.form-actions { padding-left: 220px !important; }
.popover-inner { width: 205px; }
h1 { margin-bottom: 18px; }
</style>
</head>
<body>
<div class="container" style="width: 800px; padding-bottom: 60px;">
<h1 style="padding-top: 5px;">Full-Text RSS <?php echo _FF_FTR_VERSION; ?> <span style="font-size: .7em; font-weight: normal;">&mdash; from <a href="http://fivefilters.org">FiveFilters.org</a></span></h1>
<form method="get" action="makefulltextfeed.php" id="form" class="form-horizontal">
<fieldset>
<legend>Create full-text feed from feed or webpage URL</legend>
<div class="control-group">
<label class="control-label" for="url">Enter URL</label>
<div class="controls"><input type="text" id="url" name="url" style="width: 450px;" title="URL" data-content="Typically this is a URL for a partial feed which we transform into a full-text feed. But it can also be a standard web page URL, in which case we'll extract its content and return it in a 1-item feed." /></div>
</div>
</fieldset>
<fieldset>
<legend>Options</legend>
<?php if (isset($options->api_keys) && !empty($options->api_keys)) { ?>
<div class="control-group">
<label class="control-label" for="key">Access key</label>
<div class="controls">
<input type="text" id="key" name="key" class="input-medium" <?php if ($options->key_required) echo 'required'; ?> title="Access Key" data-content="<?php echo ($options->key_required) ? 'An access key is required to generate a feed' : 'If you have an access key, enter it here.'; ?>" />
</div>
</div>
<?php } ?>
<div class="control-group">
<label class="control-label" for="max">Max items</label>
<div class="controls">
<?php
// echo '<select name="max" id="max" class="input-medium">'
// for ($i = 1; $i <= $options->max_entries; $i++) {
// printf("<option value=\"%s\"%s>%s</option>\n", $i, ($i==$options->default_entries) ? ' selected="selected"' : '', $i);
// }
// echo '</select>';
if (!empty($options->api_keys)) {
$msg = 'Limit: '.$options->max_entries.' (with key: '.$options->max_entries_with_key.')';
$msg_more = 'If you need more items, change <tt>max_entries</tt> (and <tt>max_entries_with_key</tt>) in config.';
} else {
$msg = 'Limit: '.$options->max_entries;
$msg_more = 'If you need more items, change <tt>max_entries</tt> in config.';
}
?>
<input type="text" name="max" id="max" class="input-mini" value="<?php echo $options->default_entries; ?>" title="Feed item limit" data-content="Set the maximum number of feed items we should process. The smaller the number, the faster the new feed is produced.<br /><br />If your URL refers to a standard web page, this will have no effect: you will only get 1 item.<br /><br /> <?php echo $msg_more; ?>" />
<span class="help-inline" style="color: #888;"><?php echo $msg; ?></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="links">Links</label>
<div class="controls">
<select name="links" id="links" class="input-medium" title="Link handling" data-content="By default, links within the content are preserved. Change this field if you'd like links removed, or included as footnotes.">
<option value="preserve" selected="selected">preserve</option>
<option value="footnotes">add to footnotes</option>
<option value="remove">remove</option>
</select>
</div>
</div>
<?php if ($options->exclude_items_on_fail == 'user') { ?>
<div class="control-group">
<label class="control-label" for="exc">If extraction fails</label>
<div class="controls">
<select name="exc" id="exc" title="Item handling when extraction fails" data-content="If extraction fails, we can remove the item from the feed or keep it in.<br /><br />Keeping the item will keep the title, URL and original description (if any) found in the feed. In addition, we insert a message before the original description notifying you that extraction failed.">
<option value="" selected="selected">keep item in feed</option>
<option value="1">remove item from feed</option>
</select>
</div>
</div>
<?php } ?>
<div class="control-group">
<label class="control-label" for="json">JSON output</label>
<div class="controls">
<input type="checkbox" name="format" value="json" id="json" style="margin-top: 7px;" />
</div>
</div>
<div class="control-group" style="margin-top: -15px;">
<label class="control-label" for="debug">Debug</label>
<div class="controls">
<input type="checkbox" name="debug" value="1" id="debug" style="margin-top: 7px;" />
</div>
</div>
</fieldset>
<div class="form-actions">
<input type="submit" id="sudbmit" name="submit" value="Create Feed" class="btn btn-primary" />
</div>
</form>
<ul class="nav nav-tabs">
<li class="active"><a href="#start" data-toggle="tab">Getting Started</a></li>
<li><a href="#general" data-toggle="tab">General Info</a></li>
<li><a href="#updates" data-toggle="tab">Updates</a></li>
<li><a href="#license" data-toggle="tab">License</a></li>
</ul>
<div class="tab-content">
<!-- GETTING STARTED TAB -->
<div class="active tab-pane" id="start">
<h3>Quick start</h3>
<ol>
<li><a href="ftr_compatibility_test.php">Check server compatibility</a> to make sure this server meets the requirements</li>
<li>Enter a feed or article URL in the form above and click 'Create Feed' <a href="http://help.fivefilters.org/customer/portal/articles/223127-suggested-feeds-and-articles" rel="tooltip" title="Need suggestions? We've got a number of feeds and articles you can try" class="label">?</a></li>
<li>If the generated full-text feed looks okay, copy the URL from your browser's address bar and use it in your news reader or application</li>
<li><strong>That's it!</strong> (Although see below if you'd like to customise further.)</li>
</ol>
<h3>Configure</h3>
<p>In addition to the options above, Full-Text RSS comes with a configuration file which allows you to control how the application works. <a href="http://help.fivefilters.org/customer/portal/articles/223410-configure">Find out more.</a></p>
<p>Features include:</p>
<ul>
<li>Site patterns for better control over extraction (<a href="http://help.fivefilters.org/customer/portal/articles/223153-site-patterns">more info</a>)</li>
<li>Restrict access to those with an access key and/or to a pre-defined set of URLs</li>
<li>Restrict the maximum number of feed items to be processed</li>
<li>Prepend or append an HTML fragment to each feed item processed</li>
<li>Caching</li>
</ul>
<p><?php if (!file_exists('custom_config.php')) { ?>To change the configuration, save a copy of <tt>config.php</tt> as <tt>custom_config.php</tt> and make any changes you like to it.<?php } else { ?>To change the configuration, edit <tt>custom_config.php</tt> and make any changes you like.<?php } ?></p>
<h3>Manage and update site config files</h3>
<p>For best results, we suggest you update the site config files bundled with Full-Text RSS. If you've purchased Full-Text RSS from us, you'll receive an email when these are updated.</p>
<p>The easiest way to update these is via the <a href="admin/">admin area</a>. (For advanced users, you'll also be able to edit and test the extraction rules contained in the site config files from the admin area.)</p>
<h3>Customise this page</h3>
<p>If everything works fine, feel free to modify this page by following the steps below:</p>
<ol>
<li>Save a copy of <tt>index.php</tt> as <tt>custom_index.php</tt></li>
<li>Edit <tt>custom_index.php</tt></li>
</ol>
<p>Next time you load this page, it will automatically load custom_index.php instead.</p>
<h3 id="support">Support</h3>
<p>Check our <a href="http://help.fivefilters.org">help centre</a> if you need help. You can also email us at <a href="mailto:help@fivefilters.org">help@fivefilters.org</a>.</p>
<h3>Thank you!</h3>
<p>Thanks for downloading and setting up Full-Text RSS. This software is developed and maintained by FiveFilters.org. If you find it useful, but have not purchased this from us, please consider supporting us by purchasing from <a href="http://fivefilters.org/content-only/">FiveFilters.org</a>.</p>
</div>
<!-- GENERAL TAB -->
<div id="general" class="tab-pane">
<h3>About</h3>
<p>This is a free software project to enable article extraction from web pages. It can extract content from a standard HTML page and return a 1-item feed or it can transform an existing feed into a full-text feed. It is being developed as part of the <a href="http://fivefilters.org">Five Filters</a> project to promote independent, non-corporate media.</p>
<h3>Bookmarklet</h3>
<p>Rather than copying and pasting URLs into this form, you can add the bookmarklet on this page to your browser. Simply drag the link below to your browser's bookmarks toolbar.
Then whenever you'd like a full-text feed, click the bookmarklet.</p>
<p>Drag this:
<script type="text/javascript">
document.write('<a class="btn info" style="cursor: move;" onclick="alert(\'drag to bookmarks toolbar\'); return false;" href="javascript:location.href=\''+baseUrl+'/makefulltextfeed.php?url=\'+encodeURIComponent(document.location.href);">Full-Text RSS</a>');
</script>
<p>Note: This uses the default options and does not include your access key (if configured).</p>
<h3>Free Software</h3>
<p>Note: 'Free' as in 'free speech' (see the <a href="https://www.gnu.org/philosophy/free-sw.html">free software definition</a>)</p>
<p>If you're the owner of this site and you plan to offer this service to others through your hosted copy, please keep a download link so users can grab a copy of the code if they
want it (you can either offer a free download yourself, or link to the purchase option on fivefilters.org to support us).</p>
<p>For full details, please refer to the <a href="http://www.gnu.org/licenses/agpl-3.0.html" title="AGPLv3">license</a>.</p>
<p>If you're not the owner of this site (ie. you're not hosting this yourself), you do not have to rely on an external service if you don't want to. You can <a href="http://fivefilters.org/content-only/#download">download your own copy</a> of Full-Text RSS under the AGPL license.</p>
<h3 id="api">URL Construction</h3>
<p>To extract content from a web page or to transform an existing partial feed to full text, pass the URL (<a href="http://meyerweb.com/eric/tools/dencoder/">encoded</a>) in the querystring to the following URL:</p>
<ul>
<li style="font-family: monospace;"><script type="text/javascript">document.write(baseUrl);</script>/makefulltextfeed.php?url=<strong>[url]</strong></li>
</ul>
<p>All the parameters in the form above can be passed in this way. Examine the URL in the address bar after you click 'Create Feed' to see the values.</p>
<h3>Software Components</h3>
<p>Full-Text RSS is written in PHP and relies on the following <strong>primary</strong> components:</p>
<ul>
<li><a href="http://www.keyvan.net/2010/08/php-readability/">PHP Readability</a></li>
<li><a href="http://simplepie.org/">SimplePie</a></li>
<li>FeedWriter</li>
<li>Humble HTTP Agent</li>
</ul>
<p>Depending on your configuration, these <strong>secondary</strong> components may also be used:</p>
<ul>
<li><a href="http://code.google.com/p/html5lib/">html5lib</a></li>
<li><a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/">htmLawed</a></li>
<li><a href="http://code.google.com/p/rolling-curl/">Rolling Curl</a></li>
<li><a href="http://framework.zend.com/manual/en/zend.cache.introduction.html">Zend Cache</a></li>
<li><a href="http://pear.php.net/package/Text_LanguageDetect">Text_LanguageDetect</a> or <a href="https://github.com/lstrojny/php-cld">PHP-CLD</a> if available</li>
</ul>
<h3>System Requirements</h3>
<p>PHP 5.2 or above is required. A simple shared web hosting account will work fine.
The code has been tested on Windows and Linux using the Apache web server. If you're a Windows user, you can try it on your own machine using <a href="http://www.wampserver.com/en/index.php">WampServer</a>. It has also been reported as working under IIS, but we have not tested this ourselves.</p>
<h3 id="download">Download</h3>
<p>Download from <a href="http://fivefilters.org/content-only/#download">fivefilters.org</a> &mdash; old versions are available in our <a href="http://code.fivefilters.org">code repository</a>.</p>
</div>
<!-- UPDATES TAB -->
<div id="updates" class="tab-pane">
<?php
$site_config_version_file = dirname(__FILE__).'/site_config/standard/version.php';
if (file_exists($site_config_version_file)) {
$site_config_version = include($site_config_version_file);
}
?>
<p>Your version of Full-Text RSS: <strong><?php echo _FF_FTR_VERSION; ?></strong><br />
Your version of Site Patterns: <strong><?php echo (isset($site_config_version) ? $site_config_version : 'Unknown'); ?></strong>
</p>
<p>To see if you have the latest versions, <a href="http://fivefilters.org/content-only/latest_version.php?version=<?php echo urlencode(_FF_FTR_VERSION).'&site_config='.urlencode(@$site_config_version); ?>">check for updates</a>.</p>
<p>If you've purchased this from FiveFilters.org, you'll receive notification when we release a new version or update the site patterns.</p>
</div>
<!-- LICENSE TAB -->
<div id="license" class="tab-pane">
<p><a href="http://en.wikipedia.org/wiki/Affero_General_Public_License" style="border-bottom: none;"><img src="images/agplv3.png" alt="AGPL logo" /></a></p>
<p>Full-Text RSS is licensed under the <a href="http://en.wikipedia.org/wiki/Affero_General_Public_License">AGPL version 3</a> &mdash; more information about why we use this license can be found on <a href="http://fivefilters.org/content-only/http://fivefilters.org/content-only/#license">FiveFilters.org</a></p>
<p>The software components in this application are licensed as follows...</p>
<ul>
<li>PHP Readability: <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License v2</a></li>
<li>SimplePie: <a href="http://en.wikipedia.org/wiki/BSD_license">BSD</a></li>
<li>FeedWriter: <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL v2</a></li>
<li>Humble HTTP Agent: <a href="http://en.wikipedia.org/wiki/Affero_General_Public_License">AGPL v3</a></li>
<li>Zend: <a href="http://framework.zend.com/license/new-bsd">New BSD</a></li>
<li>Rolling Curl: <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License v2</a></li>
<li>html5lib: <a href="http://opensource.org/licenses/mit-license.php">MIT</a></li>
<li>htmLawed: <a href="http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License">LGPL v3</a></li>
<li>Text_LanguageDetect: <a href="http://en.wikipedia.org/wiki/BSD_license">BSD</a></li>
</ul>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,98 @@
/* ===========================================================
* bootstrap-popover.js v2.0.3
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* POPOVER PUBLIC CLASS DEFINITION
* =============================== */
var Popover = function ( element, options ) {
this.init('popover', element, options)
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
constructor: Popover
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title)
$tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content)
$tip.removeClass('fade top bottom left right in')
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
return content
}
, tip: function () {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.popover.Constructor = Popover
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, content: ''
, template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})
}(window.jQuery);

135
vendor/full-text-rss/js/bootstrap-tab.js vendored Normal file
View File

@ -0,0 +1,135 @@
/* ========================================================
* bootstrap-tab.js v2.0.3
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ======================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TAB CLASS DEFINITION
* ==================== */
var Tab = function ( element ) {
this.element = $(element)
}
Tab.prototype = {
constructor: Tab
, show: function () {
var $this = this.element
, $ul = $this.closest('ul:not(.dropdown-menu)')
, selector = $this.attr('data-target')
, previous
, $target
, e
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
if ( $this.parent('li').hasClass('active') ) return
previous = $ul.find('.active a').last()[0]
e = $.Event('show', {
relatedTarget: previous
})
$this.trigger(e)
if (e.isDefaultPrevented()) return
$target = $(selector)
this.activate($this.parent('li'), $ul)
this.activate($target, $target.parent(), function () {
$this.trigger({
type: 'shown'
, relatedTarget: previous
})
})
}
, activate: function ( element, container, callback) {
var $active = container.find('> .active')
, transition = callback
&& $.support.transition
&& $active.hasClass('fade')
function next() {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
element.addClass('active')
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in')
} else {
element.removeClass('fade')
}
if ( element.parent('.dropdown-menu') ) {
element.closest('li.dropdown').addClass('active')
}
callback && callback()
}
transition ?
$active.one($.support.transition.end, next) :
next()
$active.removeClass('in')
}
}
/* TAB PLUGIN DEFINITION
* ===================== */
$.fn.tab = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tab')
if (!data) $this.data('tab', (data = new Tab(this)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tab.Constructor = Tab
/* TAB DATA-API
* ============ */
$(function () {
$('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
e.preventDefault()
$(this).tab('show')
})
})
}(window.jQuery);

View File

@ -0,0 +1,275 @@
/* ===========================================================
* bootstrap-tooltip.js v2.0.3
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function (element, options) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function (type, element, options) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function (options) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) return self.show()
clearTimeout(this.timeout)
self.hoverState = 'in'
this.timeout = setTimeout(function() {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
, leave: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.hide) return self.hide()
clearTimeout(this.timeout)
self.hoverState = 'out'
this.timeout = setTimeout(function() {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, isHTML: function(text) {
// html string detection logic adapted from jQuery
return typeof text != 'string'
|| ( text.charAt(0) === "<"
&& text.charAt( text.length - 1 ) === ">"
&& text.length >= 3
) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
}
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
$tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, placement: 'top'
, selector: false
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
, trigger: 'hover'
, title: ''
, delay: 0
}
}(window.jQuery);

4
vendor/full-text-rss/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,250 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: Cache.php 24656 2012-02-26 06:02:53Z adamlundrigan $
*/
/**
* @package Zend_Cache
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
abstract class Zend_Cache
{
/**
* Standard frontends
*
* @var array
*/
public static $standardFrontends = array('Core', 'Output', 'Class', 'File', 'Function', 'Page');
/**
* Standard backends
*
* @var array
*/
public static $standardBackends = array('File', 'Sqlite', 'Memcached', 'Libmemcached', 'Apc', 'ZendPlatform',
'Xcache', 'TwoLevels', 'WinCache', 'ZendServer_Disk', 'ZendServer_ShMem');
/**
* Standard backends which implement the ExtendedInterface
*
* @var array
*/
public static $standardExtendedBackends = array('File', 'Apc', 'TwoLevels', 'Memcached', 'Libmemcached', 'Sqlite', 'WinCache');
/**
* Only for backward compatibility (may be removed in next major release)
*
* @var array
* @deprecated
*/
public static $availableFrontends = array('Core', 'Output', 'Class', 'File', 'Function', 'Page');
/**
* Only for backward compatibility (may be removed in next major release)
*
* @var array
* @deprecated
*/
public static $availableBackends = array('File', 'Sqlite', 'Memcached', 'Libmemcached', 'Apc', 'ZendPlatform', 'Xcache', 'WinCache', 'TwoLevels');
/**
* Consts for clean() method
*/
const CLEANING_MODE_ALL = 'all';
const CLEANING_MODE_OLD = 'old';
const CLEANING_MODE_MATCHING_TAG = 'matchingTag';
const CLEANING_MODE_NOT_MATCHING_TAG = 'notMatchingTag';
const CLEANING_MODE_MATCHING_ANY_TAG = 'matchingAnyTag';
/**
* Factory
*
* @param mixed $frontend frontend name (string) or Zend_Cache_Frontend_ object
* @param mixed $backend backend name (string) or Zend_Cache_Backend_ object
* @param array $frontendOptions associative array of options for the corresponding frontend constructor
* @param array $backendOptions associative array of options for the corresponding backend constructor
* @param boolean $customFrontendNaming if true, the frontend argument is used as a complete class name ; if false, the frontend argument is used as the end of "Zend_Cache_Frontend_[...]" class name
* @param boolean $customBackendNaming if true, the backend argument is used as a complete class name ; if false, the backend argument is used as the end of "Zend_Cache_Backend_[...]" class name
* @param boolean $autoload if true, there will no require_once for backend and frontend (useful only for custom backends/frontends)
* @throws Zend_Cache_Exception
* @return Zend_Cache_Core|Zend_Cache_Frontend
*/
public static function factory($frontend, $backend, $frontendOptions = array(), $backendOptions = array(), $customFrontendNaming = false, $customBackendNaming = false, $autoload = false)
{
if (is_string($backend)) {
$backendObject = self::_makeBackend($backend, $backendOptions, $customBackendNaming, $autoload);
} else {
if ((is_object($backend)) && (in_array('Zend_Cache_Backend_Interface', class_implements($backend)))) {
$backendObject = $backend;
} else {
self::throwException('backend must be a backend name (string) or an object which implements Zend_Cache_Backend_Interface');
}
}
if (is_string($frontend)) {
$frontendObject = self::_makeFrontend($frontend, $frontendOptions, $customFrontendNaming, $autoload);
} else {
if (is_object($frontend)) {
$frontendObject = $frontend;
} else {
self::throwException('frontend must be a frontend name (string) or an object');
}
}
$frontendObject->setBackend($backendObject);
return $frontendObject;
}
/**
* Backend Constructor
*
* @param string $backend
* @param array $backendOptions
* @param boolean $customBackendNaming
* @param boolean $autoload
* @return Zend_Cache_Backend
*/
public static function _makeBackend($backend, $backendOptions, $customBackendNaming = false, $autoload = false)
{
if (!$customBackendNaming) {
$backend = self::_normalizeName($backend);
}
if (in_array($backend, Zend_Cache::$standardBackends)) {
// we use a standard backend
$backendClass = 'Zend_Cache_Backend_' . $backend;
// security controls are explicit
require_once realpath(dirname(__FILE__).'/..').DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
} else {
// we use a custom backend
if (!preg_match('~^[\w\\\\]+$~D', $backend)) {
Zend_Cache::throwException("Invalid backend name [$backend]");
}
if (!$customBackendNaming) {
// we use this boolean to avoid an API break
$backendClass = 'Zend_Cache_Backend_' . $backend;
} else {
$backendClass = $backend;
}
if (!$autoload) {
$file = str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
if (!(self::_isReadable($file))) {
self::throwException("file $file not found in include_path");
}
require_once $file;
}
}
return new $backendClass($backendOptions);
}
/**
* Frontend Constructor
*
* @param string $frontend
* @param array $frontendOptions
* @param boolean $customFrontendNaming
* @param boolean $autoload
* @return Zend_Cache_Core|Zend_Cache_Frontend
*/
public static function _makeFrontend($frontend, $frontendOptions = array(), $customFrontendNaming = false, $autoload = false)
{
if (!$customFrontendNaming) {
$frontend = self::_normalizeName($frontend);
}
if (in_array($frontend, self::$standardFrontends)) {
// we use a standard frontend
// For perfs reasons, with frontend == 'Core', we can interact with the Core itself
$frontendClass = 'Zend_Cache_' . ($frontend != 'Core' ? 'Frontend_' : '') . $frontend;
// security controls are explicit
require_once realpath(dirname(__FILE__).'/..').DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
} else {
// we use a custom frontend
if (!preg_match('~^[\w\\\\]+$~D', $frontend)) {
Zend_Cache::throwException("Invalid frontend name [$frontend]");
}
if (!$customFrontendNaming) {
// we use this boolean to avoid an API break
$frontendClass = 'Zend_Cache_Frontend_' . $frontend;
} else {
$frontendClass = $frontend;
}
if (!$autoload) {
$file = str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
if (!(self::_isReadable($file))) {
self::throwException("file $file not found in include_path");
}
require_once $file;
}
}
return new $frontendClass($frontendOptions);
}
/**
* Throw an exception
*
* Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic
* @param string $msg Message for the exception
* @throws Zend_Cache_Exception
*/
public static function throwException($msg, Exception $e = null)
{
// For perfs reasons, we use this dynamic inclusion
require_once 'Zend/Cache/Exception.php';
throw new Zend_Cache_Exception($msg, 0, $e);
}
/**
* Normalize frontend and backend names to allow multiple words TitleCased
*
* @param string $name Name to normalize
* @return string
*/
protected static function _normalizeName($name)
{
$name = ucfirst(strtolower($name));
$name = str_replace(array('-', '_', '.'), ' ', $name);
$name = ucwords($name);
$name = str_replace(' ', '', $name);
if (stripos($name, 'ZendServer') === 0) {
$name = 'ZendServer_' . substr($name, strlen('ZendServer'));
}
return $name;
}
/**
* Returns TRUE if the $filename is readable, or FALSE otherwise.
* This function uses the PHP include_path, where PHP's is_readable()
* does not.
*
* Note : this method comes from Zend_Loader (see #ZF-2891 for details)
*
* @param string $filename
* @return boolean
*/
private static function _isReadable($filename)
{
if (!$fh = @fopen($filename, 'r', true)) {
return false;
}
@fclose($fh);
return true;
}
}

View File

@ -0,0 +1,290 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @subpackage Zend_Cache_Backend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: Backend.php 24989 2012-06-21 07:24:13Z mabe $
*/
/**
* @package Zend_Cache
* @subpackage Zend_Cache_Backend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Cache_Backend
{
/**
* Frontend or Core directives
*
* =====> (int) lifetime :
* - Cache lifetime (in seconds)
* - If null, the cache is valid forever
*
* =====> (int) logging :
* - if set to true, a logging is activated throw Zend_Log
*
* @var array directives
*/
protected $_directives = array(
'lifetime' => 3600,
'logging' => false,
'logger' => null
);
/**
* Available options
*
* @var array available options
*/
protected $_options = array();
/**
* Constructor
*
* @param array $options Associative array of options
* @throws Zend_Cache_Exception
* @return void
*/
public function __construct(array $options = array())
{
while (list($name, $value) = each($options)) {
$this->setOption($name, $value);
}
}
/**
* Set the frontend directives
*
* @param array $directives Assoc of directives
* @throws Zend_Cache_Exception
* @return void
*/
public function setDirectives($directives)
{
if (!is_array($directives)) Zend_Cache::throwException('Directives parameter must be an array');
while (list($name, $value) = each($directives)) {
if (!is_string($name)) {
Zend_Cache::throwException("Incorrect option name : $name");
}
$name = strtolower($name);
if (array_key_exists($name, $this->_directives)) {
$this->_directives[$name] = $value;
}
}
$this->_loggerSanity();
}
/**
* Set an option
*
* @param string $name
* @param mixed $value
* @throws Zend_Cache_Exception
* @return void
*/
public function setOption($name, $value)
{
if (!is_string($name)) {
Zend_Cache::throwException("Incorrect option name : $name");
}
$name = strtolower($name);
if (array_key_exists($name, $this->_options)) {
$this->_options[$name] = $value;
}
}
/**
* Returns an option
*
* @param string $name Optional, the options name to return
* @throws Zend_Cache_Exceptions
* @return mixed
*/
public function getOption($name)
{
$name = strtolower($name);
if (array_key_exists($name, $this->_options)) {
return $this->_options[$name];
}
if (array_key_exists($name, $this->_directives)) {
return $this->_directives[$name];
}
Zend_Cache::throwException("Incorrect option name : {$name}");
}
/**
* Get the life time
*
* if $specificLifetime is not false, the given specific life time is used
* else, the global lifetime is used
*
* @param int $specificLifetime
* @return int Cache life time
*/
public function getLifetime($specificLifetime)
{
if ($specificLifetime === false) {
return $this->_directives['lifetime'];
}
return $specificLifetime;
}
/**
* Return true if the automatic cleaning is available for the backend
*
* DEPRECATED : use getCapabilities() instead
*
* @deprecated
* @return boolean
*/
public function isAutomaticCleaningAvailable()
{
return true;
}
/**
* Determine system TMP directory and detect if we have read access
*
* inspired from Zend_File_Transfer_Adapter_Abstract
*
* @return string
* @throws Zend_Cache_Exception if unable to determine directory
*/
public function getTmpDir()
{
$tmpdir = array();
foreach (array($_ENV, $_SERVER) as $tab) {
foreach (array('TMPDIR', 'TEMP', 'TMP', 'windir', 'SystemRoot') as $key) {
if (isset($tab[$key]) && is_string($tab[$key])) {
if (($key == 'windir') or ($key == 'SystemRoot')) {
$dir = realpath($tab[$key] . '\\temp');
} else {
$dir = realpath($tab[$key]);
}
if ($this->_isGoodTmpDir($dir)) {
return $dir;
}
}
}
}
$upload = ini_get('upload_tmp_dir');
if ($upload) {
$dir = realpath($upload);
if ($this->_isGoodTmpDir($dir)) {
return $dir;
}
}
if (function_exists('sys_get_temp_dir')) {
$dir = sys_get_temp_dir();
if ($this->_isGoodTmpDir($dir)) {
return $dir;
}
}
// Attemp to detect by creating a temporary file
$tempFile = tempnam(md5(uniqid(rand(), TRUE)), '');
if ($tempFile) {
$dir = realpath(dirname($tempFile));
unlink($tempFile);
if ($this->_isGoodTmpDir($dir)) {
return $dir;
}
}
if ($this->_isGoodTmpDir('/tmp')) {
return '/tmp';
}
if ($this->_isGoodTmpDir('\\temp')) {
return '\\temp';
}
Zend_Cache::throwException('Could not determine temp directory, please specify a cache_dir manually');
}
/**
* Verify if the given temporary directory is readable and writable
*
* @param string $dir temporary directory
* @return boolean true if the directory is ok
*/
protected function _isGoodTmpDir($dir)
{
if (is_readable($dir)) {
if (is_writable($dir)) {
return true;
}
}
return false;
}
/**
* Make sure if we enable logging that the Zend_Log class
* is available.
* Create a default log object if none is set.
*
* @throws Zend_Cache_Exception
* @return void
*/
protected function _loggerSanity()
{
if (!isset($this->_directives['logging']) || !$this->_directives['logging']) {
return;
}
if (isset($this->_directives['logger'])) {
if ($this->_directives['logger'] instanceof Zend_Log) {
return;
}
Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.');
}
// Create a default logger to the standard output stream
require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Log/Filter/Priority.php';
$logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
$logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
$this->_directives['logger'] = $logger;
}
/**
* Log a message at the WARN (4) priority.
*
* @param string $message
* @throws Zend_Cache_Exception
* @return void
*/
protected function _log($message, $priority = 4)
{
if (!$this->_directives['logging']) {
return;
}
if (!isset($this->_directives['logger'])) {
Zend_Cache::throwException('Logging is enabled but logger is not set.');
}
$logger = $this->_directives['logger'];
if (!$logger instanceof Zend_Log) {
Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.');
}
$logger->log($message, $priority);
}
}

View File

@ -0,0 +1,127 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @subpackage Zend_Cache_Backend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: ExtendedInterface.php 24593 2012-01-05 20:35:02Z matthew $
*/
/**
* @see Zend_Cache_Backend_Interface
*/
//require_once 'Zend/Cache/Backend/Interface.php';
require_once dirname(__FILE__).'/Interface.php';
/**
* @package Zend_Cache
* @subpackage Zend_Cache_Backend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
interface Zend_Cache_Backend_ExtendedInterface extends Zend_Cache_Backend_Interface
{
/**
* Return an array of stored cache ids
*
* @return array array of stored cache ids (string)
*/
public function getIds();
/**
* Return an array of stored tags
*
* @return array array of stored tags (string)
*/
public function getTags();
/**
* Return an array of stored cache ids which match given tags
*
* In case of multiple tags, a logical AND is made between tags
*
* @param array $tags array of tags
* @return array array of matching cache ids (string)
*/
public function getIdsMatchingTags($tags = array());
/**
* Return an array of stored cache ids which don't match given tags
*
* In case of multiple tags, a logical OR is made between tags
*
* @param array $tags array of tags
* @return array array of not matching cache ids (string)
*/
public function getIdsNotMatchingTags($tags = array());
/**
* Return an array of stored cache ids which match any given tags
*
* In case of multiple tags, a logical AND is made between tags
*
* @param array $tags array of tags
* @return array array of any matching cache ids (string)
*/
public function getIdsMatchingAnyTags($tags = array());
/**
* Return the filling percentage of the backend storage
*
* @return int integer between 0 and 100
*/
public function getFillingPercentage();
/**
* Return an array of metadatas for the given cache id
*
* The array must include these keys :
* - expire : the expire timestamp
* - tags : a string array of tags
* - mtime : timestamp of last modification time
*
* @param string $id cache id
* @return array array of metadatas (false if the cache id is not found)
*/
public function getMetadatas($id);
/**
* Give (if possible) an extra lifetime to the given cache id
*
* @param string $id cache id
* @param int $extraLifetime
* @return boolean true if ok
*/
public function touch($id, $extraLifetime);
/**
* Return an associative array of capabilities (booleans) of the backend
*
* The array must include these keys :
* - automatic_cleaning (is automating cleaning necessary)
* - tags (are tags supported)
* - expired_read (is it possible to read expired cache records
* (for doNotTestCacheValidity option for example))
* - priority does the backend deal with priority when saving
* - infinite_lifetime (is infinite lifetime can work with this backend)
* - get_list (is it possible to get the list of cache ids and the complete list of tags)
*
* @return array associative of with capabilities
*/
public function getCapabilities();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @subpackage Zend_Cache_Backend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: Interface.php 24593 2012-01-05 20:35:02Z matthew $
*/
/**
* @package Zend_Cache
* @subpackage Zend_Cache_Backend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
interface Zend_Cache_Backend_Interface
{
/**
* Set the frontend directives
*
* @param array $directives assoc of directives
*/
public function setDirectives($directives);
/**
* Test if a cache is available for the given id and (if yes) return it (false else)
*
* Note : return value is always "string" (unserialization is done by the core not by the backend)
*
* @param string $id Cache id
* @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
* @return string|false cached datas
*/
public function load($id, $doNotTestCacheValidity = false);
/**
* Test if a cache is available or not (for the given id)
*
* @param string $id cache id
* @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
*/
public function test($id);
/**
* Save some string datas into a cache record
*
* Note : $data is always "string" (serialization is done by the
* core not by the backend)
*
* @param string $data Datas to cache
* @param string $id Cache id
* @param array $tags Array of strings, the cache record will be tagged by each string entry
* @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
* @return boolean true if no problem
*/
public function save($data, $id, $tags = array(), $specificLifetime = false);
/**
* Remove a cache record
*
* @param string $id Cache id
* @return boolean True if no problem
*/
public function remove($id);
/**
* Clean some cache records
*
* Available modes are :
* Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
* Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
* Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
* ($tags can be an array of strings or a single string)
* Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
* ($tags can be an array of strings or a single string)
* Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
* ($tags can be an array of strings or a single string)
*
* @param string $mode Clean mode
* @param array $tags Array of tags
* @return boolean true if no problem
*/
public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array());
}

View File

@ -0,0 +1,765 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: Core.php 24989 2012-06-21 07:24:13Z mabe $
*/
/**
* @package Zend_Cache
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Cache_Core
{
/**
* Messages
*/
const BACKEND_NOT_SUPPORTS_TAG = 'tags are not supported by the current backend';
const BACKEND_NOT_IMPLEMENTS_EXTENDED_IF = 'Current backend doesn\'t implement the Zend_Cache_Backend_ExtendedInterface, so this method is not available';
/**
* Backend Object
*
* @var Zend_Cache_Backend_Interface $_backend
*/
protected $_backend = null;
/**
* Available options
*
* ====> (boolean) write_control :
* - Enable / disable write control (the cache is read just after writing to detect corrupt entries)
* - Enable write control will lightly slow the cache writing but not the cache reading
* Write control can detect some corrupt cache files but maybe it's not a perfect control
*
* ====> (boolean) caching :
* - Enable / disable caching
* (can be very useful for the debug of cached scripts)
*
* =====> (string) cache_id_prefix :
* - prefix for cache ids (namespace)
*
* ====> (boolean) automatic_serialization :
* - Enable / disable automatic serialization
* - It can be used to save directly datas which aren't strings (but it's slower)
*
* ====> (int) automatic_cleaning_factor :
* - Disable / Tune the automatic cleaning process
* - The automatic cleaning process destroy too old (for the given life time)
* cache files when a new cache file is written :
* 0 => no automatic cache cleaning
* 1 => systematic cache cleaning
* x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
*
* ====> (int) lifetime :
* - Cache lifetime (in seconds)
* - If null, the cache is valid forever.
*
* ====> (boolean) logging :
* - If set to true, logging is activated (but the system is slower)
*
* ====> (boolean) ignore_user_abort
* - If set to true, the core will set the ignore_user_abort PHP flag inside the
* save() method to avoid cache corruptions in some cases (default false)
*
* @var array $_options available options
*/
protected $_options = array(
'write_control' => true,
'caching' => true,
'cache_id_prefix' => null,
'automatic_serialization' => false,
'automatic_cleaning_factor' => 10,
'lifetime' => 3600,
'logging' => false,
'logger' => null,
'ignore_user_abort' => false
);
/**
* Array of options which have to be transfered to backend
*
* @var array $_directivesList
*/
protected static $_directivesList = array('lifetime', 'logging', 'logger');
/**
* Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends)
*
* @var array $_specificOptions
*/
protected $_specificOptions = array();
/**
* Last used cache id
*
* @var string $_lastId
*/
private $_lastId = null;
/**
* True if the backend implements Zend_Cache_Backend_ExtendedInterface
*
* @var boolean $_extendedBackend
*/
protected $_extendedBackend = false;
/**
* Array of capabilities of the backend (only if it implements Zend_Cache_Backend_ExtendedInterface)
*
* @var array
*/
protected $_backendCapabilities = array();
/**
* Constructor
*
* @param array|Zend_Config $options Associative array of options or Zend_Config instance
* @throws Zend_Cache_Exception
* @return void
*/
public function __construct($options = array())
{
if ($options instanceof Zend_Config) {
$options = $options->toArray();
}
if (!is_array($options)) {
Zend_Cache::throwException("Options passed were not an array"
. " or Zend_Config instance.");
}
while (list($name, $value) = each($options)) {
$this->setOption($name, $value);
}
$this->_loggerSanity();
}
/**
* Set options using an instance of type Zend_Config
*
* @param Zend_Config $config
* @return Zend_Cache_Core
*/
public function setConfig(Zend_Config $config)
{
$options = $config->toArray();
while (list($name, $value) = each($options)) {
$this->setOption($name, $value);
}
return $this;
}
/**
* Set the backend
*
* @param Zend_Cache_Backend $backendObject
* @throws Zend_Cache_Exception
* @return void
*/
public function setBackend(Zend_Cache_Backend $backendObject)
{
$this->_backend= $backendObject;
// some options (listed in $_directivesList) have to be given
// to the backend too (even if they are not "backend specific")
$directives = array();
foreach (Zend_Cache_Core::$_directivesList as $directive) {
$directives[$directive] = $this->_options[$directive];
}
$this->_backend->setDirectives($directives);
if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) {
$this->_extendedBackend = true;
$this->_backendCapabilities = $this->_backend->getCapabilities();
}
}
/**
* Returns the backend
*
* @return Zend_Cache_Backend backend object
*/
public function getBackend()
{
return $this->_backend;
}
/**
* Public frontend to set an option
*
* There is an additional validation (relatively to the protected _setOption method)
*
* @param string $name Name of the option
* @param mixed $value Value of the option
* @throws Zend_Cache_Exception
* @return void
*/
public function setOption($name, $value)
{
if (!is_string($name)) {
Zend_Cache::throwException("Incorrect option name!");
}
$name = strtolower($name);
if (array_key_exists($name, $this->_options)) {
// This is a Core option
$this->_setOption($name, $value);
return;
}
if (array_key_exists($name, $this->_specificOptions)) {
// This a specic option of this frontend
$this->_specificOptions[$name] = $value;
return;
}
}
/**
* Public frontend to get an option value
*
* @param string $name Name of the option
* @throws Zend_Cache_Exception
* @return mixed option value
*/
public function getOption($name)
{
$name = strtolower($name);
if (array_key_exists($name, $this->_options)) {
// This is a Core option
return $this->_options[$name];
}
if (array_key_exists($name, $this->_specificOptions)) {
// This a specic option of this frontend
return $this->_specificOptions[$name];
}
Zend_Cache::throwException("Incorrect option name : $name");
}
/**
* Set an option
*
* @param string $name Name of the option
* @param mixed $value Value of the option
* @throws Zend_Cache_Exception
* @return void
*/
private function _setOption($name, $value)
{
if (!is_string($name) || !array_key_exists($name, $this->_options)) {
Zend_Cache::throwException("Incorrect option name : $name");
}
if ($name == 'lifetime' && empty($value)) {
$value = null;
}
$this->_options[$name] = $value;
}
/**
* Force a new lifetime
*
* The new value is set for the core/frontend but for the backend too (directive)
*
* @param int $newLifetime New lifetime (in seconds)
* @return void
*/
public function setLifetime($newLifetime)
{
$this->_options['lifetime'] = $newLifetime;
$this->_backend->setDirectives(array(
'lifetime' => $newLifetime
));
}
/**
* Test if a cache is available for the given id and (if yes) return it (false else)
*
* @param string $id Cache id
* @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
* @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use
* @return mixed|false Cached datas
*/
public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
{
if (!$this->_options['caching']) {
return false;
}
$id = $this->_id($id); // cache id may need prefix
$this->_lastId = $id;
self::_validateIdOrTag($id);
$this->_log("Zend_Cache_Core: load item '{$id}'", 7);
$data = $this->_backend->load($id, $doNotTestCacheValidity);
if ($data===false) {
// no cache available
return false;
}
if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) {
// we need to unserialize before sending the result
return unserialize($data);
}
return $data;
}
/**
* Test if a cache is available for the given id
*
* @param string $id Cache id
* @return int|false Last modified time of cache entry if it is available, false otherwise
*/
public function test($id)
{
if (!$this->_options['caching']) {
return false;
}
$id = $this->_id($id); // cache id may need prefix
self::_validateIdOrTag($id);
$this->_lastId = $id;
$this->_log("Zend_Cache_Core: test item '{$id}'", 7);
return $this->_backend->test($id);
}
/**
* Save some data in a cache
*
* @param mixed $data Data to put in cache (can be another type than string if automatic_serialization is on)
* @param string $id Cache id (if not set, the last cache id will be used)
* @param array $tags Cache tags
* @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
* @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
* @throws Zend_Cache_Exception
* @return boolean True if no problem
*/
public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8)
{
if (!$this->_options['caching']) {
return true;
}
if ($id === null) {
$id = $this->_lastId;
} else {
$id = $this->_id($id);
}
self::_validateIdOrTag($id);
self::_validateTagsArray($tags);
if ($this->_options['automatic_serialization']) {
// we need to serialize datas before storing them
$data = serialize($data);
} else {
if (!is_string($data)) {
Zend_Cache::throwException("Datas must be string or set automatic_serialization = true");
}
}
// automatic cleaning
if ($this->_options['automatic_cleaning_factor'] > 0) {
$rand = rand(1, $this->_options['automatic_cleaning_factor']);
if ($rand==1) {
// new way || deprecated way
if ($this->_extendedBackend || method_exists($this->_backend, 'isAutomaticCleaningAvailable')) {
$this->_log("Zend_Cache_Core::save(): automatic cleaning running", 7);
$this->clean(Zend_Cache::CLEANING_MODE_OLD);
} else {
$this->_log("Zend_Cache_Core::save(): automatic cleaning is not available/necessary with current backend", 4);
}
}
}
$this->_log("Zend_Cache_Core: save item '{$id}'", 7);
if ($this->_options['ignore_user_abort']) {
$abort = ignore_user_abort(true);
}
if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) {
$result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority);
} else {
$result = $this->_backend->save($data, $id, $tags, $specificLifetime);
}
if ($this->_options['ignore_user_abort']) {
ignore_user_abort($abort);
}
if (!$result) {
// maybe the cache is corrupted, so we remove it !
$this->_log("Zend_Cache_Core::save(): failed to save item '{$id}' -> removing it", 4);
$this->_backend->remove($id);
return false;
}
if ($this->_options['write_control']) {
$data2 = $this->_backend->load($id, true);
if ($data!=$data2) {
$this->_log("Zend_Cache_Core::save(): write control of item '{$id}' failed -> removing it", 4);
$this->_backend->remove($id);
return false;
}
}
return true;
}
/**
* Remove a cache
*
* @param string $id Cache id to remove
* @return boolean True if ok
*/
public function remove($id)
{
if (!$this->_options['caching']) {
return true;
}
$id = $this->_id($id); // cache id may need prefix
self::_validateIdOrTag($id);
$this->_log("Zend_Cache_Core: remove item '{$id}'", 7);
return $this->_backend->remove($id);
}
/**
* Clean cache entries
*
* Available modes are :
* 'all' (default) => remove all cache entries ($tags is not used)
* 'old' => remove too old cache entries ($tags is not used)
* 'matchingTag' => remove cache entries matching all given tags
* ($tags can be an array of strings or a single string)
* 'notMatchingTag' => remove cache entries not matching one of the given tags
* ($tags can be an array of strings or a single string)
* 'matchingAnyTag' => remove cache entries matching any given tags
* ($tags can be an array of strings or a single string)
*
* @param string $mode
* @param array|string $tags
* @throws Zend_Cache_Exception
* @return boolean True if ok
*/
public function clean($mode = 'all', $tags = array())
{
if (!$this->_options['caching']) {
return true;
}
if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL,
Zend_Cache::CLEANING_MODE_OLD,
Zend_Cache::CLEANING_MODE_MATCHING_TAG,
Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG,
Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG))) {
Zend_Cache::throwException('Invalid cleaning mode');
}
self::_validateTagsArray($tags);
return $this->_backend->clean($mode, $tags);
}
/**
* Return an array of stored cache ids which match given tags
*
* In case of multiple tags, a logical AND is made between tags
*
* @param array $tags array of tags
* @return array array of matching cache ids (string)
*/
public function getIdsMatchingTags($tags = array())
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
if (!($this->_backendCapabilities['tags'])) {
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
}
$ids = $this->_backend->getIdsMatchingTags($tags);
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
$prefix = & $this->_options['cache_id_prefix'];
$prefixLen = strlen($prefix);
foreach ($ids as &$id) {
if (strpos($id, $prefix) === 0) {
$id = substr($id, $prefixLen);
}
}
}
return $ids;
}
/**
* Return an array of stored cache ids which don't match given tags
*
* In case of multiple tags, a logical OR is made between tags
*
* @param array $tags array of tags
* @return array array of not matching cache ids (string)
*/
public function getIdsNotMatchingTags($tags = array())
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
if (!($this->_backendCapabilities['tags'])) {
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
}
$ids = $this->_backend->getIdsNotMatchingTags($tags);
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
$prefix = & $this->_options['cache_id_prefix'];
$prefixLen = strlen($prefix);
foreach ($ids as &$id) {
if (strpos($id, $prefix) === 0) {
$id = substr($id, $prefixLen);
}
}
}
return $ids;
}
/**
* Return an array of stored cache ids which match any given tags
*
* In case of multiple tags, a logical OR is made between tags
*
* @param array $tags array of tags
* @return array array of matching any cache ids (string)
*/
public function getIdsMatchingAnyTags($tags = array())
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
if (!($this->_backendCapabilities['tags'])) {
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
}
$ids = $this->_backend->getIdsMatchingAnyTags($tags);
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
$prefix = & $this->_options['cache_id_prefix'];
$prefixLen = strlen($prefix);
foreach ($ids as &$id) {
if (strpos($id, $prefix) === 0) {
$id = substr($id, $prefixLen);
}
}
}
return $ids;
}
/**
* Return an array of stored cache ids
*
* @return array array of stored cache ids (string)
*/
public function getIds()
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
$ids = $this->_backend->getIds();
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
$prefix = & $this->_options['cache_id_prefix'];
$prefixLen = strlen($prefix);
foreach ($ids as &$id) {
if (strpos($id, $prefix) === 0) {
$id = substr($id, $prefixLen);
}
}
}
return $ids;
}
/**
* Return an array of stored tags
*
* @return array array of stored tags (string)
*/
public function getTags()
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
if (!($this->_backendCapabilities['tags'])) {
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
}
return $this->_backend->getTags();
}
/**
* Return the filling percentage of the backend storage
*
* @return int integer between 0 and 100
*/
public function getFillingPercentage()
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
return $this->_backend->getFillingPercentage();
}
/**
* Return an array of metadatas for the given cache id
*
* The array will include these keys :
* - expire : the expire timestamp
* - tags : a string array of tags
* - mtime : timestamp of last modification time
*
* @param string $id cache id
* @return array array of metadatas (false if the cache id is not found)
*/
public function getMetadatas($id)
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
$id = $this->_id($id); // cache id may need prefix
return $this->_backend->getMetadatas($id);
}
/**
* Give (if possible) an extra lifetime to the given cache id
*
* @param string $id cache id
* @param int $extraLifetime
* @return boolean true if ok
*/
public function touch($id, $extraLifetime)
{
if (!$this->_extendedBackend) {
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
}
$id = $this->_id($id); // cache id may need prefix
$this->_log("Zend_Cache_Core: touch item '{$id}'", 7);
return $this->_backend->touch($id, $extraLifetime);
}
/**
* Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
*
* Throw an exception if a problem is found
*
* @param string $string Cache id or tag
* @throws Zend_Cache_Exception
* @return void
*/
protected static function _validateIdOrTag($string)
{
if (!is_string($string)) {
Zend_Cache::throwException('Invalid id or tag : must be a string');
}
if (substr($string, 0, 9) == 'internal-') {
Zend_Cache::throwException('"internal-*" ids or tags are reserved');
}
if (!preg_match('~^[a-zA-Z0-9_]+$~D', $string)) {
Zend_Cache::throwException("Invalid id or tag '$string' : must use only [a-zA-Z0-9_]");
}
}
/**
* Validate a tags array (security, reliable filenames, reserved prefixes...)
*
* Throw an exception if a problem is found
*
* @param array $tags Array of tags
* @throws Zend_Cache_Exception
* @return void
*/
protected static function _validateTagsArray($tags)
{
if (!is_array($tags)) {
Zend_Cache::throwException('Invalid tags array : must be an array');
}
foreach($tags as $tag) {
self::_validateIdOrTag($tag);
}
reset($tags);
}
/**
* Make sure if we enable logging that the Zend_Log class
* is available.
* Create a default log object if none is set.
*
* @throws Zend_Cache_Exception
* @return void
*/
protected function _loggerSanity()
{
if (!isset($this->_options['logging']) || !$this->_options['logging']) {
return;
}
if (isset($this->_options['logger']) && $this->_options['logger'] instanceof Zend_Log) {
return;
}
// Create a default logger to the standard output stream
require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Log/Filter/Priority.php';
$logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
$logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
$this->_options['logger'] = $logger;
}
/**
* Log a message at the WARN (4) priority.
*
* @param string $message
* @throws Zend_Cache_Exception
* @return void
*/
protected function _log($message, $priority = 4)
{
if (!$this->_options['logging']) {
return;
}
if (!(isset($this->_options['logger']) || $this->_options['logger'] instanceof Zend_Log)) {
Zend_Cache::throwException('Logging is enabled but logger is not set');
}
$logger = $this->_options['logger'];
$logger->log($message, $priority);
}
/**
* Make and return a cache id
*
* Checks 'cache_id_prefix' and returns new id with prefix or simply the id if null
*
* @param string $id Cache id
* @return string Cache id (with or without prefix)
*/
protected function _id($id)
{
if (($id !== null) && isset($this->_options['cache_id_prefix'])) {
return $this->_options['cache_id_prefix'] . $id; // return with prefix
}
return $id; // no prefix, just return the $id passed
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Cache
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: Exception.php 24593 2012-01-05 20:35:02Z matthew $
*/
/**
* @see Zend_Exception
*/
require_once 'Zend/Exception.php';
/**
* @package Zend_Cache
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Cache_Exception extends Zend_Exception {}

View File

@ -0,0 +1,96 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id: Exception.php 24593 2012-01-05 20:35:02Z matthew $
*/
/**
* @category Zend
* @package Zend
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Exception extends Exception
{
/**
* @var null|Exception
*/
private $_previous = null;
/**
* Construct the exception
*
* @param string $msg
* @param int $code
* @param Exception $previous
* @return void
*/
public function __construct($msg = '', $code = 0, Exception $previous = null)
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
parent::__construct($msg, (int) $code);
$this->_previous = $previous;
} else {
parent::__construct($msg, (int) $code, $previous);
}
}
/**
* Overloading
*
* For PHP < 5.3.0, provides access to the getPrevious() method.
*
* @param string $method
* @param array $args
* @return mixed
*/
public function __call($method, array $args)
{
if ('getprevious' == strtolower($method)) {
return $this->_getPrevious();
}
return null;
}
/**
* String representation of the exception
*
* @return string
*/
public function __toString()
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
if (null !== ($e = $this->getPrevious())) {
return $e->__toString()
. "\n\nNext "
. parent::__toString();
}
}
return parent::__toString();
}
/**
* Returns previous Exception
*
* @return Exception|null
*/
protected function _getPrevious()
{
return $this->_previous;
}
}

View File

@ -0,0 +1,728 @@
<?php
/**
* Content Extractor
*
* Uses patterns specified in site config files and auto detection (hNews/PHP Readability)
* to extract content from HTML files.
*
* @version 1.0
* @date 2013-02-05
* @author Keyvan Minoukadeh
* @copyright 2013 Keyvan Minoukadeh
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
*/
class ContentExtractor
{
protected static $tidy_config = array(
'clean' => true,
'output-xhtml' => true,
'logical-emphasis' => true,
'show-body-only' => false,
'new-blocklevel-tags' => 'article, aside, footer, header, hgroup, menu, nav, section, details, datagrid',
'new-inline-tags' => 'mark, time, meter, progress, data',
'wrap' => 0,
'drop-empty-paras' => true,
'drop-proprietary-attributes' => false,
'enclose-text' => true,
'enclose-block-text' => true,
'merge-divs' => true,
'merge-spans' => true,
'char-encoding' => 'utf8',
'hide-comments' => true
);
protected $html;
protected $config;
protected $title;
protected $author = array();
protected $language;
protected $date;
protected $body;
protected $success = false;
protected $nextPageUrl;
public $allowedParsers = array('libxml', 'html5lib');
public $fingerprints = array();
public $readability;
public $debug = false;
public $debugVerbose = false;
function __construct($path, $fallback=null) {
SiteConfig::set_config_path($path, $fallback);
}
protected function debug($msg) {
if ($this->debug) {
$mem = round(memory_get_usage()/1024, 2);
$memPeak = round(memory_get_peak_usage()/1024, 2);
echo '* ',$msg;
if ($this->debugVerbose) echo ' - mem used: ',$mem," (peak: $memPeak)";
echo "\n";
ob_flush();
flush();
}
}
public function reset() {
$this->html = null;
$this->readability = null;
$this->config = null;
$this->title = null;
$this->body = null;
$this->author = array();
$this->language = null;
$this->date = null;
$this->nextPageUrl = null;
$this->success = false;
}
public function findHostUsingFingerprints($html) {
$this->debug('Checking fingerprints...');
$head = substr($html, 0, 8000);
foreach ($this->fingerprints as $_fp => $_fphost) {
$lookin = 'html';
if (is_array($_fphost)) {
if (isset($_fphost['head']) && $_fphost['head']) {
$lookin = 'head';
}
$_fphost = $_fphost['hostname'];
}
if (strpos($$lookin, $_fp) !== false) {
$this->debug("Found match: $_fphost");
return $_fphost;
}
}
$this->debug('No fingerprint matches');
return false;
}
// returns SiteConfig instance (joined in order: exact match, wildcard, fingerprint, global, default)
public function buildSiteConfig($url, $html='', $add_to_cache=true) {
// extract host name
$host = @parse_url($url, PHP_URL_HOST);
$host = strtolower($host);
if (substr($host, 0, 4) == 'www.') $host = substr($host, 4);
// is merged version already cached?
if (SiteConfig::is_cached("$host.merged")) {
$this->debug("Returning cached and merged site config for $host");
return SiteConfig::build("$host.merged");
}
// let's build from site_config/custom/ and standard/
$config = SiteConfig::build($host);
if ($add_to_cache && $config && !SiteConfig::is_cached("$host")) {
SiteConfig::add_to_cache($host, $config);
}
// if no match, use defaults
if (!$config) $config = new SiteConfig();
// load fingerprint config?
if ($config->autodetect_on_failure()) {
// check HTML for fingerprints
if (!empty($this->fingerprints) && ($_fphost = $this->findHostUsingFingerprints($html))) {
if ($config_fingerprint = SiteConfig::build($_fphost)) {
$this->debug("Appending site config settings from $_fphost (fingerprint match)");
$config->append($config_fingerprint);
if ($add_to_cache && !SiteConfig::is_cached($_fphost)) {
//$config_fingerprint->cache_in_apc = true;
SiteConfig::add_to_cache($_fphost, $config_fingerprint);
}
}
}
}
// load global config?
if ($config->autodetect_on_failure()) {
if ($config_global = SiteConfig::build('global', true)) {
$this->debug('Appending site config settings from global.txt');
$config->append($config_global);
if ($add_to_cache && !SiteConfig::is_cached('global')) {
//$config_global->cache_in_apc = true;
SiteConfig::add_to_cache('global', $config_global);
}
}
}
// store copy of merged config
if ($add_to_cache) {
// do not store in APC if wildcard match
$use_apc = ($host == $config->cache_key);
$config->cache_key = null;
SiteConfig::add_to_cache("$host.merged", $config, $use_apc);
}
return $config;
}
// returns true on success, false on failure
// $smart_tidy indicates that if tidy is used and no results are produced, we will
// try again without it. Tidy helps us deal with PHP's patchy HTML parsing most of the time
// but it has problems of its own which we try to avoid with this option.
public function process($html, $url, $smart_tidy=true) {
$this->reset();
$this->config = $this->buildSiteConfig($url, $html);
// do string replacements
if (!empty($this->config->find_string)) {
if (count($this->config->find_string) == count($this->config->replace_string)) {
$html = str_replace($this->config->find_string, $this->config->replace_string, $html, $_count);
$this->debug("Strings replaced: $_count (find_string and/or replace_string)");
} else {
$this->debug('Skipped string replacement - incorrect number of find-replace strings in site config');
}
unset($_count);
}
// use tidy (if it exists)?
// This fixes problems with some sites which would otherwise
// trouble DOMDocument's HTML parsing. (Although sometimes it
// makes matters worse, which is why you can override it in site config files.)
$tidied = false;
if ($this->config->tidy() && function_exists('tidy_parse_string') && $smart_tidy) {
$this->debug('Using Tidy');
$tidy = tidy_parse_string($html, self::$tidy_config, 'UTF8');
if (tidy_clean_repair($tidy)) {
$original_html = $html;
$tidied = true;
$html = $tidy->value;
}
unset($tidy);
}
// load and parse html
$_parser = $this->config->parser();
if (!in_array($_parser, $this->allowedParsers)) {
$this->debug("HTML parser $_parser not listed, using libxml instead");
$_parser = 'libxml';
}
$this->debug("Attempting to parse HTML with $_parser");
$this->readability = new Readability($html, $url, $_parser);
// we use xpath to find elements in the given HTML document
// see http://en.wikipedia.org/wiki/XPath_1.0
$xpath = new DOMXPath($this->readability->dom);
// try to get next page link
foreach ($this->config->next_page_link as $pattern) {
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
if (is_string($elems)) {
$this->nextPageUrl = trim($elems);
break;
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
foreach ($elems as $item) {
if ($item instanceof DOMElement && $item->hasAttribute('href')) {
$this->nextPageUrl = $item->getAttribute('href');
break 2;
} elseif ($item instanceof DOMAttr && $item->value) {
$this->nextPageUrl = $item->value;
break 2;
}
}
}
}
// try to get title
foreach ($this->config->title as $pattern) {
// $this->debug("Trying $pattern");
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
if (is_string($elems)) {
$this->title = trim($elems);
$this->debug('Title expression evaluated as string: '.$this->title);
$this->debug("...XPath match: $pattern");
break;
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
$this->title = $elems->item(0)->textContent;
$this->debug('Title matched: '.$this->title);
$this->debug("...XPath match: $pattern");
// remove title from document
try {
$elems->item(0)->parentNode->removeChild($elems->item(0));
} catch (DOMException $e) {
// do nothing
}
break;
}
}
// try to get author (if it hasn't already been set)
if (empty($this->author)) {
foreach ($this->config->author as $pattern) {
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
if (is_string($elems)) {
if (trim($elems) != '') {
$this->author[] = trim($elems);
$this->debug('Author expression evaluated as string: '.trim($elems));
$this->debug("...XPath match: $pattern");
break;
}
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
foreach ($elems as $elem) {
if (!isset($elem->parentNode)) continue;
$this->author[] = trim($elem->textContent);
$this->debug('Author matched: '.trim($elem->textContent));
}
if (!empty($this->author)) {
$this->debug("...XPath match: $pattern");
break;
}
}
}
}
// try to get language
$_lang_xpath = array('//html[@lang]/@lang', '//meta[@name="DC.language"]/@content');
foreach ($_lang_xpath as $pattern) {
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
if (is_string($elems)) {
if (trim($elems) != '') {
$this->language = trim($elems);
$this->debug('Language matched: '.$this->language);
break;
}
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
foreach ($elems as $elem) {
if (!isset($elem->parentNode)) continue;
$this->language = trim($elem->textContent);
$this->debug('Language matched: '.$this->language);
}
if ($this->language) break;
}
}
// try to get date
foreach ($this->config->date as $pattern) {
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
if (is_string($elems)) {
$this->date = strtotime(trim($elems, "; \t\n\r\0\x0B"));
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
$this->date = $elems->item(0)->textContent;
$this->date = strtotime(trim($this->date, "; \t\n\r\0\x0B"));
// remove date from document
// $elems->item(0)->parentNode->removeChild($elems->item(0));
}
if (!$this->date) {
$this->date = null;
} else {
$this->debug('Date matched: '.date('Y-m-d H:i:s', $this->date));
$this->debug("...XPath match: $pattern");
break;
}
}
// strip elements (using xpath expressions)
foreach ($this->config->strip as $pattern) {
$elems = @$xpath->query($pattern, $this->readability->dom);
// check for matches
if ($elems && $elems->length > 0) {
$this->debug('Stripping '.$elems->length.' elements (strip)');
for ($i=$elems->length-1; $i >= 0; $i--) {
$elems->item($i)->parentNode->removeChild($elems->item($i));
}
}
}
// strip elements (using id and class attribute values)
foreach ($this->config->strip_id_or_class as $string) {
$string = strtr($string, array("'"=>'', '"'=>''));
$elems = @$xpath->query("//*[contains(@class, '$string') or contains(@id, '$string')]", $this->readability->dom);
// check for matches
if ($elems && $elems->length > 0) {
$this->debug('Stripping '.$elems->length.' elements (strip_id_or_class)');
for ($i=$elems->length-1; $i >= 0; $i--) {
$elems->item($i)->parentNode->removeChild($elems->item($i));
}
}
}
// strip images (using src attribute values)
foreach ($this->config->strip_image_src as $string) {
$string = strtr($string, array("'"=>'', '"'=>''));
$elems = @$xpath->query("//img[contains(@src, '$string')]", $this->readability->dom);
// check for matches
if ($elems && $elems->length > 0) {
$this->debug('Stripping '.$elems->length.' image elements');
for ($i=$elems->length-1; $i >= 0; $i--) {
$elems->item($i)->parentNode->removeChild($elems->item($i));
}
}
}
// strip elements using Readability.com and Instapaper.com ignore class names
// .entry-unrelated and .instapaper_ignore
// See https://www.readability.com/publishers/guidelines/#view-plainGuidelines
// and http://blog.instapaper.com/post/730281947
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' entry-unrelated ') or contains(concat(' ',normalize-space(@class),' '),' instapaper_ignore ')]", $this->readability->dom);
// check for matches
if ($elems && $elems->length > 0) {
$this->debug('Stripping '.$elems->length.' .entry-unrelated,.instapaper_ignore elements');
for ($i=$elems->length-1; $i >= 0; $i--) {
$elems->item($i)->parentNode->removeChild($elems->item($i));
}
}
// strip elements that contain style="display: none;"
$elems = @$xpath->query("//*[contains(@style,'display:none')]", $this->readability->dom);
// check for matches
if ($elems && $elems->length > 0) {
$this->debug('Stripping '.$elems->length.' elements with inline display:none style');
for ($i=$elems->length-1; $i >= 0; $i--) {
$elems->item($i)->parentNode->removeChild($elems->item($i));
}
}
// try to get body
foreach ($this->config->body as $pattern) {
$elems = @$xpath->query($pattern, $this->readability->dom);
// check for matches
if ($elems && $elems->length > 0) {
$this->debug('Body matched');
$this->debug("...XPath match: $pattern");
if ($elems->length == 1) {
$this->body = $elems->item(0);
// prune (clean up elements that may not be content)
if ($this->config->prune()) {
$this->debug('...pruning content');
$this->readability->prepArticle($this->body);
}
break;
} else {
$this->body = $this->readability->dom->createElement('div');
$this->debug($elems->length.' body elems found');
foreach ($elems as $elem) {
if (!isset($elem->parentNode)) continue;
$isDescendant = false;
foreach ($this->body->childNodes as $parent) {
if ($this->isDescendant($parent, $elem)) {
$isDescendant = true;
break;
}
}
if ($isDescendant) {
$this->debug('...element is child of another body element, skipping.');
} else {
// prune (clean up elements that may not be content)
if ($this->config->prune()) {
$this->debug('Pruning content');
$this->readability->prepArticle($elem);
}
$this->debug('...element added to body');
$this->body->appendChild($elem);
}
}
if ($this->body->hasChildNodes()) break;
}
}
}
// auto detect?
$detect_title = $detect_body = $detect_author = $detect_date = false;
// detect title?
if (!isset($this->title)) {
if (empty($this->config->title) || $this->config->autodetect_on_failure()) {
$detect_title = true;
}
}
// detect body?
if (!isset($this->body)) {
if (empty($this->config->body) || $this->config->autodetect_on_failure()) {
$detect_body = true;
}
}
// detect author?
if (empty($this->author)) {
if (empty($this->config->author) || $this->config->autodetect_on_failure()) {
$detect_author = true;
}
}
// detect date?
if (!isset($this->date)) {
if (empty($this->config->date) || $this->config->autodetect_on_failure()) {
$detect_date = true;
}
}
// check for hNews
if ($detect_title || $detect_body) {
// check for hentry
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' hentry ')]", $this->readability->dom);
if ($elems && $elems->length > 0) {
$this->debug('hNews: found hentry');
$hentry = $elems->item(0);
if ($detect_title) {
// check for entry-title
$elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' entry-title ')]", $hentry);
if ($elems && $elems->length > 0) {
$this->title = $elems->item(0)->textContent;
$this->debug('hNews: found entry-title: '.$this->title);
// remove title from document
$elems->item(0)->parentNode->removeChild($elems->item(0));
$detect_title = false;
}
}
if ($detect_date) {
// check for time element with pubdate attribute
$elems = @$xpath->query(".//time[@pubdate] | .//abbr[contains(concat(' ',normalize-space(@class),' '),' published ')]", $hentry);
if ($elems && $elems->length > 0) {
$this->date = strtotime(trim($elems->item(0)->textContent));
// remove date from document
//$elems->item(0)->parentNode->removeChild($elems->item(0));
if ($this->date) {
$this->debug('hNews: found publication date: '.date('Y-m-d H:i:s', $this->date));
$detect_date = false;
} else {
$this->date = null;
}
}
}
if ($detect_author) {
// check for time element with pubdate attribute
$elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' vcard ') and (contains(concat(' ',normalize-space(@class),' '),' author ') or contains(concat(' ',normalize-space(@class),' '),' byline '))]", $hentry);
if ($elems && $elems->length > 0) {
$author = $elems->item(0);
$fn = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' fn ')]", $author);
if ($fn && $fn->length > 0) {
foreach ($fn as $_fn) {
if (trim($_fn->textContent) != '') {
$this->author[] = trim($_fn->textContent);
$this->debug('hNews: found author: '.trim($_fn->textContent));
}
}
} else {
if (trim($author->textContent) != '') {
$this->author[] = trim($author->textContent);
$this->debug('hNews: found author: '.trim($author->textContent));
}
}
$detect_author = empty($this->author);
}
}
// check for entry-content.
// according to hAtom spec, if there are multiple elements marked entry-content,
// we include all of these in the order they appear - see http://microformats.org/wiki/hatom#Entry_Content
if ($detect_body) {
$elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' entry-content ')]", $hentry);
if ($elems && $elems->length > 0) {
$this->debug('hNews: found entry-content');
if ($elems->length == 1) {
// what if it's empty? (some sites misuse hNews - place their content outside an empty entry-content element)
$e = $elems->item(0);
if (($e->tagName == 'img') || (trim($e->textContent) != '')) {
$this->body = $elems->item(0);
// prune (clean up elements that may not be content)
if ($this->config->prune()) {
$this->debug('Pruning content');
$this->readability->prepArticle($this->body);
}
$detect_body = false;
} else {
$this->debug('hNews: skipping entry-content - appears not to contain content');
}
unset($e);
} else {
$this->body = $this->readability->dom->createElement('div');
$this->debug($elems->length.' entry-content elems found');
foreach ($elems as $elem) {
if (!isset($elem->parentNode)) continue;
$isDescendant = false;
foreach ($this->body->childNodes as $parent) {
if ($this->isDescendant($parent, $elem)) {
$isDescendant = true;
break;
}
}
if ($isDescendant) {
$this->debug('Element is child of another body element, skipping.');
} else {
// prune (clean up elements that may not be content)
if ($this->config->prune()) {
$this->debug('Pruning content');
$this->readability->prepArticle($elem);
}
$this->debug('Element added to body');
$this->body->appendChild($elem);
}
}
$detect_body = false;
}
}
}
}
}
// check for elements marked with instapaper_title
if ($detect_title) {
// check for instapaper_title
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' instapaper_title ')]", $this->readability->dom);
if ($elems && $elems->length > 0) {
$this->title = $elems->item(0)->textContent;
$this->debug('Title found (.instapaper_title): '.$this->title);
// remove title from document
$elems->item(0)->parentNode->removeChild($elems->item(0));
$detect_title = false;
}
}
// check for elements marked with instapaper_body
if ($detect_body) {
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' instapaper_body ')]", $this->readability->dom);
if ($elems && $elems->length > 0) {
$this->debug('body found (.instapaper_body)');
$this->body = $elems->item(0);
// prune (clean up elements that may not be content)
if ($this->config->prune()) {
$this->debug('Pruning content');
$this->readability->prepArticle($this->body);
}
$detect_body = false;
}
}
// Find author in rel="author" marked element
// We only use this if there's exactly one.
// If there's more than one, it could indicate more than
// one author, but it could also indicate that we're processing
// a page listing different articles with different authors.
if ($detect_author) {
$elems = @$xpath->query("//a[contains(concat(' ',normalize-space(@rel),' '),' author ')]", $this->readability->dom);
if ($elems && $elems->length == 1) {
$author = trim($elems->item(0)->textContent);
if ($author != '') {
$this->debug("Author found (rel=\"author\"): $author");
$this->author[] = $author;
$detect_author = false;
}
}
}
// Find date in pubdate marked time element
// For the same reason given above, we only use this
// if there's exactly one element.
if ($detect_date) {
$elems = @$xpath->query("//time[@pubdate]", $this->readability->dom);
if ($elems && $elems->length == 1) {
$this->date = strtotime(trim($elems->item(0)->textContent));
// remove date from document
//$elems->item(0)->parentNode->removeChild($elems->item(0));
if ($this->date) {
$this->debug('Date found (pubdate marked time element): '.date('Y-m-d H:i:s', $this->date));
$detect_date = false;
} else {
$this->date = null;
}
}
}
// still missing title or body, so we detect using Readability
if ($detect_title || $detect_body) {
$this->debug('Using Readability');
// clone body if we're only using Readability for title (otherwise it may interfere with body element)
if (isset($this->body)) $this->body = $this->body->cloneNode(true);
$success = $this->readability->init();
}
if ($detect_title) {
$this->debug('Detecting title');
$this->title = $this->readability->getTitle()->textContent;
}
if ($detect_body && $success) {
$this->debug('Detecting body');
$this->body = $this->readability->getContent();
if ($this->body->childNodes->length == 1 && $this->body->firstChild->nodeType === XML_ELEMENT_NODE) {
$this->body = $this->body->firstChild;
}
// prune (clean up elements that may not be content)
if ($this->config->prune()) {
$this->debug('Pruning content');
$this->readability->prepArticle($this->body);
}
}
if (isset($this->body)) {
// remove scripts
$this->readability->removeScripts($this->body);
// remove any h1-h6 elements that appear as first thing in the body
// and which match our title
if (isset($this->title) && ($this->title != '')) {
$firstChild = $this->body->firstChild;
while ($firstChild->nodeType && ($firstChild->nodeType !== XML_ELEMENT_NODE)) {
$firstChild = $firstChild->nextSibling;
}
if (($firstChild->nodeType === XML_ELEMENT_NODE)
&& in_array(strtolower($firstChild->tagName), array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))
&& (strtolower(trim($firstChild->textContent)) == strtolower(trim($this->title)))) {
$this->body->removeChild($firstChild);
}
}
// prevent self-closing iframes
$elems = $this->body->getElementsByTagName('iframe');
for ($i = $elems->length-1; $i >= 0; $i--) {
$e = $elems->item($i);
if (!$e->hasChildNodes()) {
$e->appendChild($this->body->ownerDocument->createTextNode('[embedded content]'));
}
}
// remove image lazy loading - WordPress plugin http://wordpress.org/extend/plugins/lazy-load/
// the plugin replaces the src attribute to point to a 1x1 gif and puts the original src
// inside the data-lazy-src attribute. It also places the original image inside a noscript element
// next to the amended one.
$elems = @$xpath->query("//img[@data-lazy-src]", $this->body);
for ($i = $elems->length-1; $i >= 0; $i--) {
$e = $elems->item($i);
// let's see if we can grab image from noscript
if ($e->nextSibling !== null && $e->nextSibling->nodeName === 'noscript') {
$_new_elem = $e->ownerDocument->createDocumentFragment();
@$_new_elem->appendXML($e->nextSibling->innerHTML);
$e->nextSibling->parentNode->replaceChild($_new_elem, $e->nextSibling);
$e->parentNode->removeChild($e);
} else {
// Use data-lazy-src as src value
$e->setAttribute('src', $e->getAttribute('data-lazy-src'));
$e->removeAttribute('data-lazy-src');
}
}
$this->success = true;
}
// if we've had no success and we've used tidy, there's a chance
// that tidy has messed up. So let's try again without tidy...
if (!$this->success && $tidied && $smart_tidy) {
$this->debug('Trying again without tidy');
$this->process($original_html, $url, false);
}
return $this->success;
}
private function isDescendant(DOMElement $parent, DOMElement $child) {
$node = $child->parentNode;
while ($node != null) {
if ($node->isSameNode($parent)) return true;
$node = $node->parentNode;
}
return false;
}
public function getContent() {
return $this->body;
}
public function getTitle() {
return $this->title;
}
public function getAuthors() {
return $this->author;
}
public function getLanguage() {
return $this->language;
}
public function getDate() {
return $this->date;
}
public function getSiteConfig() {
return $this->config;
}
public function getNextPageUrl() {
return $this->nextPageUrl;
}
}
?>

View File

@ -0,0 +1,338 @@
<?php
/**
* Site Config
*
* Each instance of this class should hold extraction patterns and other directives
* for a website. See ContentExtractor class to see how it's used.
*
* @version 0.7
* @date 2012-08-27
* @author Keyvan Minoukadeh
* @copyright 2012 Keyvan Minoukadeh
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
*/
class SiteConfig
{
// Use first matching element as title (0 or more xpath expressions)
public $title = array();
// Use first matching element as body (0 or more xpath expressions)
public $body = array();
// Use first matching element as author (0 or more xpath expressions)
public $author = array();
// Use first matching element as date (0 or more xpath expressions)
public $date = array();
// Strip elements matching these xpath expressions (0 or more)
public $strip = array();
// Strip elements which contain these strings (0 or more) in the id or class attribute
public $strip_id_or_class = array();
// Strip images which contain these strings (0 or more) in the src attribute
public $strip_image_src = array();
// Additional HTTP headers to send
// NOT YET USED
public $http_header = array();
// Process HTML with tidy before creating DOM (bool or null if undeclared)
public $tidy = null;
protected $default_tidy = true; // used if undeclared
// Autodetect title/body if xpath expressions fail to produce results.
// Note that this applies to title and body separately, ie.
// * if we get a body match but no title match, this option will determine whether we autodetect title
// * if neither match, this determines whether we autodetect title and body.
// Also note that this only applies when there is at least one xpath expression in title or body, ie.
// * if title and body are both empty (no xpath expressions), this option has no effect (both title and body will be auto-detected)
// * if there's an xpath expression for title and none for body, body will be auto-detected and this option will determine whether we auto-detect title if the xpath expression for it fails to produce results.
// Usage scenario: you want to extract something specific from a set of URLs, e.g. a table, and if the table is not found, you want to ignore the entry completely. Auto-detection is unlikely to succeed here, so you construct your patterns and set this option to false. Another scenario may be a site where auto-detection has proven to fail (or worse, picked up the wrong content).
// bool or null if undeclared
public $autodetect_on_failure = null;
protected $default_autodetect_on_failure = true; // used if undeclared
// Clean up content block - attempt to remove elements that appear to be superfluous
// bool or null if undeclared
public $prune = null;
protected $default_prune = true; // used if undeclared
// Test URL - if present, can be used to test the config above
public $test_url = array();
// Single-page link - should identify a link element or URL pointing to the page holding the entire article
// This is useful for sites which split their articles across multiple pages. Links to such pages tend to
// display the first page with links to the other pages at the bottom. Often there is also a link to a page
// which displays the entire article on one page (e.g. 'print view').
// This should be an XPath expression identifying the link to that page. If present and we find a match,
// we will retrieve that page and the rest of the options in this config will be applied to the new page.
public $single_page_link = array();
public $next_page_link = array();
// Single-page link in feed? - same as above, but patterns applied to item description HTML taken from feed
public $single_page_link_in_feed = array();
// Which parser to use for turning raw HTML into a DOMDocument (either 'libxml' or 'html5lib')
// string or null if undeclared
public $parser = null;
protected $default_parser = 'libxml'; // used if undeclared
// Strings to search for in HTML before processing begins (used with $replace_string)
public $find_string = array();
// Strings to replace those found in $find_string before HTML processing begins
public $replace_string = array();
// the options below cannot be set in the config files which this class represents
//public $cache_in_apc = false; // used to decide if we should cache in apc or not
public $cache_key = null;
public static $debug = false;
protected static $apc = false;
protected static $config_path;
protected static $config_path_fallback;
protected static $config_cache = array();
const HOSTNAME_REGEX = '/^(([a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9-]*[A-Za-z0-9])$/';
protected static function debug($msg) {
if (self::$debug) {
//$mem = round(memory_get_usage()/1024, 2);
//$memPeak = round(memory_get_peak_usage()/1024, 2);
echo '* ',$msg;
//echo ' - mem used: ',$mem," (peak: $memPeak)\n";
echo "\n";
ob_flush();
flush();
}
}
// enable APC caching of certain site config files?
// If enabled the following site config files will be
// cached in APC cache (when requested for first time):
// * anything in site_config/custom/ and its corresponding file in site_config/standard/
// * the site config files associated with HTML fingerprints
// * the global site config file
// returns true if enabled, false otherwise
public static function use_apc($apc=true) {
if (!function_exists('apc_add')) {
if ($apc) self::debug('APC will not be used (function apc_add does not exist)');
return false;
}
self::$apc = $apc;
return $apc;
}
// return bool or null
public function tidy($use_default=true) {
if ($use_default) return (isset($this->tidy)) ? $this->tidy : $this->default_tidy;
return $this->tidy;
}
// return bool or null
public function prune($use_default=true) {
if ($use_default) return (isset($this->prune)) ? $this->prune : $this->default_prune;
return $this->prune;
}
// return string or null
public function parser($use_default=true) {
if ($use_default) return (isset($this->parser)) ? $this->parser : $this->default_parser;
return $this->parser;
}
// return bool or null
public function autodetect_on_failure($use_default=true) {
if ($use_default) return (isset($this->autodetect_on_failure)) ? $this->autodetect_on_failure : $this->default_autodetect_on_failure;
return $this->autodetect_on_failure;
}
public static function set_config_path($path, $fallback=null) {
self::$config_path = $path;
self::$config_path_fallback = $fallback;
}
public static function add_to_cache($key, SiteConfig $config, $use_apc=true) {
$key = strtolower($key);
if (substr($key, 0, 4) == 'www.') $key = substr($key, 4);
if ($config->cache_key) $key = $config->cache_key;
self::$config_cache[$key] = $config;
if (self::$apc && $use_apc) {
self::debug("Adding site config to APC cache with key sc.$key");
apc_add("sc.$key", $config);
}
self::debug("Cached site config with key $key");
}
public static function is_cached($key) {
$key = strtolower($key);
if (substr($key, 0, 4) == 'www.') $key = substr($key, 4);
if (array_key_exists($key, self::$config_cache)) {
return true;
} elseif (self::$apc && (bool)apc_fetch("sc.$key")) {
return true;
}
return false;
}
public function append(SiteConfig $newconfig) {
// check for commands where we accept multiple statements (no test_url)
foreach (array('title', 'body', 'author', 'date', 'strip', 'strip_id_or_class', 'strip_image_src', 'single_page_link', 'single_page_link_in_feed', 'next_page_link', 'http_header', 'find_string', 'replace_string') as $var) {
// append array elements for this config variable from $newconfig to this config
//$this->$var = $this->$var + $newconfig->$var;
$this->$var = array_unique(array_merge($this->$var, $newconfig->$var));
}
// check for single statement commands
// we do not overwrite existing non null values
foreach (array('tidy', 'prune', 'parser', 'autodetect_on_failure') as $var) {
if ($this->$var === null) $this->$var = $newconfig->$var;
}
}
// returns SiteConfig instance if an appropriate one is found, false otherwise
// if $exact_host_match is true, we will not look for wildcard config matches
// by default if host is 'test.example.org' we will look for and load '.example.org.txt' if it exists
public static function build($host, $exact_host_match=false) {
$host = strtolower($host);
if (substr($host, 0, 4) == 'www.') $host = substr($host, 4);
if (!$host || (strlen($host) > 200) || !preg_match(self::HOSTNAME_REGEX, ltrim($host, '.'))) return false;
// check for site configuration
$try = array($host);
// should we look for wildcard matches
if (!$exact_host_match) {
$split = explode('.', $host);
if (count($split) > 1) {
array_shift($split);
$try[] = '.'.implode('.', $split);
}
}
// look for site config file in primary folder
self::debug(". looking for site config for $host in primary folder");
foreach ($try as $h) {
if (array_key_exists($h, self::$config_cache)) {
self::debug("... site config for $h already loaded in this request");
return self::$config_cache[$h];
} elseif (self::$apc && ($sconfig = apc_fetch("sc.$h"))) {
self::debug("... site config for $h in APC cache");
return $sconfig;
} elseif (file_exists(self::$config_path."/$h.txt")) {
self::debug("... found site config ($h.txt)");
$file_primary = self::$config_path."/$h.txt";
$matched_name = $h;
break;
}
}
// if we found site config, process it
if (isset($file_primary)) {
$config_lines = file($file_primary, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!$config_lines || !is_array($config_lines)) return false;
$config = self::build_from_array($config_lines);
// if APC caching is available and enabled, mark this for cache
//$config->cache_in_apc = true;
$config->cache_key = $matched_name;
// if autodetec on failure is off (on by default) we do not need to look
// in secondary folder
if (!$config->autodetect_on_failure()) {
self::debug('... autodetect on failure is disabled (no other site config files will be loaded)');
return $config;
}
}
// look for site config file in secondary folder
if (isset(self::$config_path_fallback)) {
self::debug(". looking for site config for $host in secondary folder");
foreach ($try as $h) {
if (file_exists(self::$config_path_fallback."/$h.txt")) {
self::debug("... found site config in secondary folder ($h.txt)");
$file_secondary = self::$config_path_fallback."/$h.txt";
$matched_name = $h;
break;
}
}
if (!isset($file_secondary)) {
self::debug("... no site config match in secondary folder");
}
}
// return false if no config file found
if (!isset($file_primary) && !isset($file_secondary)) {
self::debug("... no site config match for $host");
return false;
}
// return primary config if secondary not found
if (!isset($file_secondary) && isset($config)) {
return $config;
}
// process secondary config file
$config_lines = file($file_secondary, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!$config_lines || !is_array($config_lines)) {
// failed to process secondary
if (isset($config)) {
// return primary config
return $config;
} else {
return false;
}
}
// merge with primary and return
if (isset($config)) {
self::debug('. merging config files');
$config->append(self::build_from_array($config_lines));
return $config;
} else {
// return just secondary
$config = self::build_from_array($config_lines);
// if APC caching is available and enabled, mark this for cache
//$config->cache_in_apc = true;
$config->cache_key = $matched_name;
return $config;
}
}
public static function build_from_array(array $lines) {
$config = new SiteConfig();
foreach ($lines as $line) {
$line = trim($line);
// skip comments, empty lines
if ($line == '' || $line[0] == '#') continue;
// get command
$command = explode(':', $line, 2);
// if there's no colon ':', skip this line
if (count($command) != 2) continue;
$val = trim($command[1]);
$command = trim($command[0]);
if ($command == '' || $val == '') continue;
// check for commands where we accept multiple statements
if (in_array($command, array('title', 'body', 'author', 'date', 'strip', 'strip_id_or_class', 'strip_image_src', 'single_page_link', 'single_page_link_in_feed', 'next_page_link', 'http_header', 'test_url', 'find_string', 'replace_string'))) {
array_push($config->$command, $val);
// check for single statement commands that evaluate to true or false
} elseif (in_array($command, array('tidy', 'prune', 'autodetect_on_failure'))) {
$config->$command = ($val == 'yes');
// check for single statement commands stored as strings
} elseif (in_array($command, array('parser'))) {
$config->$command = $val;
// check for replace_string(find): replace
} elseif ((substr($command, -1) == ')') && preg_match('!^([a-z0-9_]+)\((.*?)\)$!i', $command, $match)) {
if (in_array($match[1], array('replace_string'))) {
$command = $match[1];
array_push($config->find_string, $match[2]);
array_push($config->$command, $val);
}
}
}
return $config;
}
}
?>

View File

@ -0,0 +1,190 @@
<?php
/**
* Univarsel Feed Writer
*
* FeedItem class - Used as feed element in FeedWriter class
*
* @package UnivarselFeedWriter
* @author Anis uddin Ahmad <anisniit@gmail.com>
* @link http://www.ajaxray.com/projects/rss
*/
class FeedItem
{
private $elements = array(); //Collection of feed elements
private $version;
/**
* Constructor
*
* @param contant (RSS1/RSS2/ATOM) RSS2 is default.
*/
function __construct($version = RSS2)
{
$this->version = $version;
}
/**
* Set element (overwrites existing elements with $elementName)
*
* @access public
* @param srting The tag name of an element
* @param srting The content of tag
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
* @return void
*/
public function setElement($elementName, $content, $attributes = null)
{
if (isset($this->elements[$elementName])) {
unset($this->elements[$elementName]);
}
$this->addElement($elementName, $content, $attributes);
}
/**
* Add an element to elements array
*
* @access public
* @param srting The tag name of an element
* @param srting The content of tag
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
* @return void
*/
public function addElement($elementName, $content, $attributes = null)
{
$i = 0;
if (isset($this->elements[$elementName])) {
$i = count($this->elements[$elementName]);
} else {
$this->elements[$elementName] = array();
}
$this->elements[$elementName][$i]['name'] = $elementName;
$this->elements[$elementName][$i]['content'] = $content;
$this->elements[$elementName][$i]['attributes'] = $attributes;
}
/**
* Set multiple feed elements from an array.
* Elements which have attributes cannot be added by this method
*
* @access public
* @param array array of elements in 'tagName' => 'tagContent' format.
* @return void
*/
public function addElementArray($elementArray)
{
if(! is_array($elementArray)) return;
foreach ($elementArray as $elementName => $content)
{
$this->addElement($elementName, $content);
}
}
/**
* Return the collection of elements in this feed item
*
* @access public
* @return array
*/
public function getElements()
{
return $this->elements;
}
// Wrapper functions ------------------------------------------------------
/**
* Set the 'dscription' element of feed item
*
* @access public
* @param string The content of 'description' element
* @return void
*/
public function setDescription($description)
{
$tag = ($this->version == ATOM)? 'summary' : 'description';
$this->setElement($tag, $description);
}
/**
* @desc Set the 'title' element of feed item
* @access public
* @param string The content of 'title' element
* @return void
*/
public function setTitle($title)
{
$this->setElement('title', $title);
}
/**
* Set the 'date' element of feed item
*
* @access public
* @param string The content of 'date' element
* @return void
*/
public function setDate($date)
{
if(! is_numeric($date))
{
$date = strtotime($date);
}
if($this->version == ATOM)
{
$tag = 'updated';
$value = date(DATE_ATOM, $date);
}
elseif($this->version == RSS2)
{
$tag = 'pubDate';
$value = date(DATE_RSS, $date);
}
else
{
$tag = 'dc:date';
$value = date("Y-m-d", $date);
}
$this->setElement($tag, $value);
}
/**
* Set the 'link' element of feed item
*
* @access public
* @param string The content of 'link' element
* @return void
*/
public function setLink($link)
{
if($this->version == RSS2 || $this->version == RSS1)
{
$this->setElement('link', $link);
}
else
{
$this->setElement('link','',array('href'=>$link));
$this->setElement('id', FeedWriter::uuid($link,'urn:uuid:'));
}
}
/**
* Set the 'encloser' element of feed item
* For RSS 2.0 only
*
* @access public
* @param string The url attribute of encloser tag
* @param string The length attribute of encloser tag
* @param string The type attribute of encloser tag
* @return void
*/
public function setEncloser($url, $length, $type)
{
$attributes = array('url'=>$url, 'length'=>$length, 'type'=>$type);
$this->setElement('enclosure','',$attributes);
}
} // end of class FeedItem
?>

View File

@ -0,0 +1,441 @@
<?php
define('RSS2', 1, true);
define('JSON', 2, true);
define('JSONP', 3, true);
/**
* Univarsel Feed Writer class
*
* Genarate RSS2 or JSON (original: RSS 1.0, RSS2.0 and ATOM Feed)
*
* Modified for FiveFilters.org's Full-Text RSS project
* to allow for inclusion of hubs, JSON output.
* Stripped RSS1 and ATOM support.
*
* @package UnivarselFeedWriter
* @author Anis uddin Ahmad <anisniit@gmail.com>
* @link http://www.ajaxray.com/projects/rss
*/
class FeedWriter
{
private $self = null; // self URL - http://feed2.w3.org/docs/warning/MissingAtomSelfLink.html
private $hubs = array(); // PubSubHubbub hubs
private $channels = array(); // Collection of channel elements
private $items = array(); // Collection of items as object of FeedItem class.
private $data = array(); // Store some other version wise data
private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA
private $xsl = null; // stylesheet to render RSS (used by Chrome)
private $json = null; // JSON object
private $version = null;
/**
* Constructor
*
* @param constant the version constant (RSS2 or JSON).
*/
function __construct($version = RSS2)
{
$this->version = $version;
// Setting default value for assential channel elements
$this->channels['title'] = $version . ' Feed';
$this->channels['link'] = 'http://www.ajaxray.com/blog';
//Tag names to encode in CDATA
$this->CDATAEncoding = array('description', 'content:encoded', 'content', 'subtitle', 'summary');
}
public function setFormat($format) {
$this->version = $format;
}
// Start # public functions ---------------------------------------------
/**
* Set a channel element
* @access public
* @param srting name of the channel tag
* @param string content of the channel tag
* @return void
*/
public function setChannelElement($elementName, $content)
{
$this->channels[$elementName] = $content ;
}
/**
* Set multiple channel elements from an array. Array elements
* should be 'channelName' => 'channelContent' format.
*
* @access public
* @param array array of channels
* @return void
*/
public function setChannelElementsFromArray($elementArray)
{
if(! is_array($elementArray)) return;
foreach ($elementArray as $elementName => $content)
{
$this->setChannelElement($elementName, $content);
}
}
/**
* Genarate the actual RSS/JSON file
*
* @access public
* @return void
*/
public function genarateFeed()
{
if ($this->version == RSS2) {
header('Content-type: text/xml; charset=UTF-8');
// this line prevents Chrome 20 from prompting download
// used by Google: https://news.google.com/news/feeds?ned=us&topic=b&output=rss
header('X-content-type-options: nosniff');
} elseif ($this->version == JSON) {
header('Content-type: application/json; charset=UTF-8');
$this->json = new stdClass();
} elseif ($this->version == JSONP) {
header('Content-type: application/javascript; charset=UTF-8');
$this->json = new stdClass();
}
$this->printHead();
$this->printChannels();
$this->printItems();
$this->printTale();
if ($this->version == JSON || $this->version == JSONP) {
echo json_encode($this->json);
}
}
/**
* Create a new FeedItem.
*
* @access public
* @return object instance of FeedItem class
*/
public function createNewItem()
{
$Item = new FeedItem($this->version);
return $Item;
}
/**
* Add a FeedItem to the main class
*
* @access public
* @param object instance of FeedItem class
* @return void
*/
public function addItem($feedItem)
{
$this->items[] = $feedItem;
}
// Wrapper functions -------------------------------------------------------------------
/**
* Set the 'title' channel element
*
* @access public
* @param srting value of 'title' channel tag
* @return void
*/
public function setTitle($title)
{
$this->setChannelElement('title', $title);
}
/**
* Add a hub to the channel element
*
* @access public
* @param string URL
* @return void
*/
public function addHub($hub)
{
$this->hubs[] = $hub;
}
/**
* Set XSL URL
*
* @access public
* @param string URL
* @return void
*/
public function setXsl($xsl)
{
$this->xsl = $xsl;
}
/**
* Set self URL
*
* @access public
* @param string URL
* @return void
*/
public function setSelf($self)
{
$this->self = $self;
}
/**
* Set the 'description' channel element
*
* @access public
* @param srting value of 'description' channel tag
* @return void
*/
public function setDescription($desciption)
{
$tag = ($this->version == ATOM)? 'subtitle' : 'description';
$this->setChannelElement($tag, $desciption);
}
/**
* Set the 'link' channel element
*
* @access public
* @param srting value of 'link' channel tag
* @return void
*/
public function setLink($link)
{
$this->setChannelElement('link', $link);
}
/**
* Set the 'image' channel element
*
* @access public
* @param srting title of image
* @param srting link url of the imahe
* @param srting path url of the image
* @return void
*/
public function setImage($title, $link, $url)
{
$this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
}
// End # public functions ----------------------------------------------
// Start # private functions ----------------------------------------------
/**
* Prints the xml and rss namespace
*
* @access private
* @return void
*/
private function printHead()
{
if ($this->version == RSS2)
{
$out = '<?xml version="1.0" encoding="utf-8"?>'."\n";
if ($this->xsl) $out .= '<?xml-stylesheet type="text/xsl" href="'.htmlspecialchars($this->xsl).'"?>' . PHP_EOL;
$out .= '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">' . PHP_EOL;
echo $out;
}
elseif ($this->version == JSON || $this->version == JSONP)
{
$this->json->rss = array('@attributes' => array('version' => '2.0'));
}
}
/**
* Closes the open tags at the end of file
*
* @access private
* @return void
*/
private function printTale()
{
if ($this->version == RSS2)
{
echo '</channel>',PHP_EOL,'</rss>';
}
// do nothing for JSON
}
/**
* Creates a single node as xml format
*
* @access private
* @param string name of the tag
* @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
* @return string formatted xml tag
*/
private function makeNode($tagName, $tagContent, $attributes = null)
{
if ($this->version == RSS2)
{
$nodeText = '';
$attrText = '';
if (is_array($attributes))
{
foreach ($attributes as $key => $value)
{
$attrText .= " $key=\"$value\" ";
}
}
$nodeText .= "<{$tagName}{$attrText}>";
if (is_array($tagContent))
{
foreach ($tagContent as $key => $value)
{
$nodeText .= $this->makeNode($key, $value);
}
}
else
{
//$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
$nodeText .= htmlspecialchars($tagContent);
}
//$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>";
$nodeText .= "</$tagName>";
return $nodeText . PHP_EOL;
}
elseif ($this->version == JSON || $this->version == JSONP)
{
$tagName = (string)$tagName;
$tagName = strtr($tagName, ':', '_');
$node = null;
if (!$tagContent && is_array($attributes) && count($attributes))
{
$node = array('@attributes' => $this->json_keys($attributes));
} else {
if (is_array($tagContent)) {
$node = $this->json_keys($tagContent);
} else {
$node = $tagContent;
}
}
return $node;
}
return ''; // should not get here
}
private function json_keys(array $array) {
$new = array();
foreach ($array as $key => $val) {
if (is_string($key)) $key = strtr($key, ':', '_');
if (is_array($val)) {
$new[$key] = $this->json_keys($val);
} else {
$new[$key] = $val;
}
}
return $new;
}
/**
* @desc Print channels
* @access private
* @return void
*/
private function printChannels()
{
//Start channel tag
if ($this->version == RSS2) {
echo '<channel>' . PHP_EOL;
// add hubs
foreach ($this->hubs as $hub) {
//echo $this->makeNode('link', '', array('rel'=>'hub', 'href'=>$hub, 'xmlns'=>'http://www.w3.org/2005/Atom'));
echo '<link rel="hub" href="'.htmlspecialchars($hub).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
}
// add self
if (isset($this->self)) {
//echo $this->makeNode('link', '', array('rel'=>'self', 'href'=>$this->self, 'xmlns'=>'http://www.w3.org/2005/Atom'));
echo '<link rel="self" href="'.htmlspecialchars($this->self).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
}
//Print Items of channel
foreach ($this->channels as $key => $value)
{
echo $this->makeNode($key, $value);
}
} elseif ($this->version == JSON || $this->version == JSONP) {
$this->json->rss['channel'] = (object)$this->json_keys($this->channels);
}
}
/**
* Prints formatted feed items
*
* @access private
* @return void
*/
private function printItems()
{
foreach ($this->items as $item) {
$itemElements = $item->getElements();
echo $this->startItem();
if ($this->version == JSON || $this->version == JSONP) {
$json_item = array();
}
foreach ($itemElements as $thisElement) {
foreach ($thisElement as $instance) {
if ($this->version == RSS2) {
echo $this->makeNode($instance['name'], $instance['content'], $instance['attributes']);
} elseif ($this->version == JSON || $this->version == JSONP) {
$_json_node = $this->makeNode($instance['name'], $instance['content'], $instance['attributes']);
if (count($thisElement) > 1) {
$json_item[strtr($instance['name'], ':', '_')][] = $_json_node;
} else {
$json_item[strtr($instance['name'], ':', '_')] = $_json_node;
}
}
}
}
echo $this->endItem();
if ($this->version == JSON || $this->version == JSONP) {
if (count($this->items) > 1) {
$this->json->rss['channel']->item[] = $json_item;
} else {
$this->json->rss['channel']->item = $json_item;
}
}
}
}
/**
* Make the starting tag of channels
*
* @access private
* @return void
*/
private function startItem()
{
if ($this->version == RSS2)
{
echo '<item>' . PHP_EOL;
}
// nothing for JSON
}
/**
* Closes feed item tag
*
* @access private
* @return void
*/
private function endItem()
{
if ($this->version == RSS2)
{
echo '</item>' . PHP_EOL;
}
// nothing for JSON
}
// End # private functions ----------------------------------------------
}

View File

@ -0,0 +1,728 @@
<?php
/*
htmLawed 1.1.14, 8 August 2012
OOP code, 8 August 2012
Copyright Santosh Patnaik
Dual LGPL v3 and GPL v2+ license
A PHP Labware internal utility; www.bioinformatics.org/phplabware/internal_utilities/htmLawed
See htmLawed_README.txt/htm
*/
class htmLawed{
// begin class
public static function hl($t, $C=1, $S=array()){
$C = is_array($C) ? $C : array();
if(!empty($C['valid_xhtml'])){
$C['elements'] = empty($C['elements']) ? '*-center-dir-font-isindex-menu-s-strike-u' : $C['elements'];
$C['make_tag_strict'] = isset($C['make_tag_strict']) ? $C['make_tag_strict'] : 2;
$C['xml:lang'] = isset($C['xml:lang']) ? $C['xml:lang'] : 2;
}
// config eles
$e = array('a'=>1, 'abbr'=>1, 'acronym'=>1, 'address'=>1, 'applet'=>1, 'area'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'blockquote'=>1, 'br'=>1, 'button'=>1, 'caption'=>1, 'center'=>1, 'cite'=>1, 'code'=>1, 'col'=>1, 'colgroup'=>1, 'dd'=>1, 'del'=>1, 'dfn'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'dt'=>1, 'em'=>1, 'embed'=>1, 'fieldset'=>1, 'font'=>1, 'form'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'i'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'ins'=>1, 'isindex'=>1, 'kbd'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'object'=>1, 'ol'=>1, 'optgroup'=>1, 'option'=>1, 'p'=>1, 'param'=>1, 'pre'=>1, 'q'=>1, 'rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1, 'ruby'=>1, 's'=>1, 'samp'=>1, 'script'=>1, 'select'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'table'=>1, 'tbody'=>1, 'td'=>1, 'textarea'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1, 'tt'=>1, 'u'=>1, 'ul'=>1, 'var'=>1); // 86/deprecated+embed+ruby
if(!empty($C['safe'])){
unset($e['applet'], $e['embed'], $e['iframe'], $e['object'], $e['script']);
}
$x = !empty($C['elements']) ? str_replace(array("\n", "\r", "\t", ' '), '', $C['elements']) : '*';
if($x == '-*'){$e = array();}
elseif(strpos($x, '*') === false){$e = array_flip(explode(',', $x));}
else{
if(isset($x[1])){
preg_match_all('`(?:^|-|\+)[^\-+]+?(?=-|\+|$)`', $x, $m, PREG_SET_ORDER);
for($i=count($m); --$i>=0;){$m[$i] = $m[$i][0];}
foreach($m as $v){
if($v[0] == '+'){$e[substr($v, 1)] = 1;}
if($v[0] == '-' && isset($e[($v = substr($v, 1))]) && !in_array('+'. $v, $m)){unset($e[$v]);}
}
}
}
$C['elements'] =& $e;
// config attrs
$x = !empty($C['deny_attribute']) ? str_replace(array("\n", "\r", "\t", ' '), '', $C['deny_attribute']) : '';
$x = array_flip((isset($x[0]) && $x[0] == '*') ? explode('-', $x) : explode(',', $x. (!empty($C['safe']) ? ',on*' : '')));
if(isset($x['on*'])){
unset($x['on*']);
$x += array('onblur'=>1, 'onchange'=>1, 'onclick'=>1, 'ondblclick'=>1, 'onfocus'=>1, 'onkeydown'=>1, 'onkeypress'=>1, 'onkeyup'=>1, 'onmousedown'=>1, 'onmousemove'=>1, 'onmouseout'=>1, 'onmouseover'=>1, 'onmouseup'=>1, 'onreset'=>1, 'onselect'=>1, 'onsubmit'=>1);
}
$C['deny_attribute'] = $x;
// config URL
$x = (isset($C['schemes'][2]) && strpos($C['schemes'], ':')) ? strtolower($C['schemes']) : 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https';
$C['schemes'] = array();
foreach(explode(';', str_replace(array(' ', "\t", "\r", "\n"), '', $x)) as $v){
$x = $x2 = null; list($x, $x2) = explode(':', $v, 2);
if($x2){$C['schemes'][$x] = array_flip(explode(',', $x2));}
}
if(!isset($C['schemes']['*'])){$C['schemes']['*'] = array('file'=>1, 'http'=>1, 'https'=>1,);}
if(!empty($C['safe']) && empty($C['schemes']['style'])){$C['schemes']['style'] = array('!'=>1);}
$C['abs_url'] = isset($C['abs_url']) ? $C['abs_url'] : 0;
if(!isset($C['base_url']) or !preg_match('`^[a-zA-Z\d.+\-]+://[^/]+/(.+?/)?$`', $C['base_url'])){
$C['base_url'] = $C['abs_url'] = 0;
}
// config rest
$C['and_mark'] = empty($C['and_mark']) ? 0 : 1;
$C['anti_link_spam'] = (isset($C['anti_link_spam']) && is_array($C['anti_link_spam']) && count($C['anti_link_spam']) == 2 && (empty($C['anti_link_spam'][0]) or htmLawed::hl_regex($C['anti_link_spam'][0])) && (empty($C['anti_link_spam'][1]) or htmLawed::hl_regex($C['anti_link_spam'][1]))) ? $C['anti_link_spam'] : 0;
$C['anti_mail_spam'] = isset($C['anti_mail_spam']) ? $C['anti_mail_spam'] : 0;
$C['balance'] = isset($C['balance']) ? (bool)$C['balance'] : 1;
$C['cdata'] = isset($C['cdata']) ? $C['cdata'] : (empty($C['safe']) ? 3 : 0);
$C['clean_ms_char'] = empty($C['clean_ms_char']) ? 0 : $C['clean_ms_char'];
$C['comment'] = isset($C['comment']) ? $C['comment'] : (empty($C['safe']) ? 3 : 0);
$C['css_expression'] = empty($C['css_expression']) ? 0 : 1;
$C['direct_list_nest'] = empty($C['direct_list_nest']) ? 0 : 1;
$C['hexdec_entity'] = isset($C['hexdec_entity']) ? $C['hexdec_entity'] : 1;
$C['hook'] = (!empty($C['hook']) && function_exists($C['hook'])) ? $C['hook'] : 0;
$C['hook_tag'] = (!empty($C['hook_tag']) && function_exists($C['hook_tag'])) ? $C['hook_tag'] : 0;
$C['keep_bad'] = isset($C['keep_bad']) ? $C['keep_bad'] : 6;
$C['lc_std_val'] = isset($C['lc_std_val']) ? (bool)$C['lc_std_val'] : 1;
$C['make_tag_strict'] = isset($C['make_tag_strict']) ? $C['make_tag_strict'] : 1;
$C['named_entity'] = isset($C['named_entity']) ? (bool)$C['named_entity'] : 1;
$C['no_deprecated_attr'] = isset($C['no_deprecated_attr']) ? $C['no_deprecated_attr'] : 1;
$C['parent'] = isset($C['parent'][0]) ? strtolower($C['parent']) : 'body';
$C['show_setting'] = !empty($C['show_setting']) ? $C['show_setting'] : 0;
$C['style_pass'] = empty($C['style_pass']) ? 0 : 1;
$C['tidy'] = empty($C['tidy']) ? 0 : $C['tidy'];
$C['unique_ids'] = isset($C['unique_ids']) ? $C['unique_ids'] : 1;
$C['xml:lang'] = isset($C['xml:lang']) ? $C['xml:lang'] : 0;
if(isset($GLOBALS['C'])){$reC = $GLOBALS['C'];}
$GLOBALS['C'] = $C;
$S = is_array($S) ? $S : htmLawed::hl_spec($S);
if(isset($GLOBALS['S'])){$reS = $GLOBALS['S'];}
$GLOBALS['S'] = $S;
$t = preg_replace('`[\x00-\x08\x0b-\x0c\x0e-\x1f]`', '', $t);
if($C['clean_ms_char']){
$x = array("\x7f"=>'', "\x80"=>'&#8364;', "\x81"=>'', "\x83"=>'&#402;', "\x85"=>'&#8230;', "\x86"=>'&#8224;', "\x87"=>'&#8225;', "\x88"=>'&#710;', "\x89"=>'&#8240;', "\x8a"=>'&#352;', "\x8b"=>'&#8249;', "\x8c"=>'&#338;', "\x8d"=>'', "\x8e"=>'&#381;', "\x8f"=>'', "\x90"=>'', "\x95"=>'&#8226;', "\x96"=>'&#8211;', "\x97"=>'&#8212;', "\x98"=>'&#732;', "\x99"=>'&#8482;', "\x9a"=>'&#353;', "\x9b"=>'&#8250;', "\x9c"=>'&#339;', "\x9d"=>'', "\x9e"=>'&#382;', "\x9f"=>'&#376;');
$x = $x + ($C['clean_ms_char'] == 1 ? array("\x82"=>'&#8218;', "\x84"=>'&#8222;', "\x91"=>'&#8216;', "\x92"=>'&#8217;', "\x93"=>'&#8220;', "\x94"=>'&#8221;') : array("\x82"=>'\'', "\x84"=>'"', "\x91"=>'\'', "\x92"=>'\'', "\x93"=>'"', "\x94"=>'"'));
$t = strtr($t, $x);
}
if($C['cdata'] or $C['comment']){$t = preg_replace_callback('`<!(?:(?:--.*?--)|(?:\[CDATA\[.*?\]\]))>`sm', 'htmLawed::hl_cmtcd', $t);}
$t = preg_replace_callback('`&amp;([A-Za-z][A-Za-z0-9]{1,30}|#(?:[0-9]{1,8}|[Xx][0-9A-Fa-f]{1,7}));`', 'htmLawed::hl_ent', str_replace('&', '&amp;', $t));
if($C['unique_ids'] && !isset($GLOBALS['hl_Ids'])){$GLOBALS['hl_Ids'] = array();}
if($C['hook']){$t = $C['hook']($t, $C, $S);}
if($C['show_setting'] && preg_match('`^[a-z][a-z0-9_]*$`i', $C['show_setting'])){
$GLOBALS[$C['show_setting']] = array('config'=>$C, 'spec'=>$S, 'time'=>microtime());
}
// main
$t = preg_replace_callback('`<(?:(?:\s|$)|(?:[^>]*(?:>|$)))|>`m', 'htmLawed::hl_tag', $t);
$t = $C['balance'] ? htmLawed::hl_bal($t, $C['keep_bad'], $C['parent']) : $t;
$t = (($C['cdata'] or $C['comment']) && strpos($t, "\x01") !== false) ? str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05"), array('', '', '&', '<', '>'), $t) : $t;
$t = $C['tidy'] ? htmLawed::hl_tidy($t, $C['tidy'], $C['parent']) : $t;
unset($C, $e);
if(isset($reC)){$GLOBALS['C'] = $reC;}
if(isset($reS)){$GLOBALS['S'] = $reS;}
return $t;
// eof
}
public static function hl_attrval($t, $p){
// check attr val against $S
$o = 1; $l = strlen($t);
foreach($p as $k=>$v){
switch($k){
case 'maxlen':if($l > $v){$o = 0;}
break; case 'minlen': if($l < $v){$o = 0;}
break; case 'maxval': if((float)($t) > $v){$o = 0;}
break; case 'minval': if((float)($t) < $v){$o = 0;}
break; case 'match': if(!preg_match($v, $t)){$o = 0;}
break; case 'nomatch': if(preg_match($v, $t)){$o = 0;}
break; case 'oneof':
$m = 0;
foreach(explode('|', $v) as $n){if($t == $n){$m = 1; break;}}
$o = $m;
break; case 'noneof':
$m = 1;
foreach(explode('|', $v) as $n){if($t == $n){$m = 0; break;}}
$o = $m;
break; default:
break;
}
if(!$o){break;}
}
return ($o ? $t : (isset($p['default']) ? $p['default'] : 0));
// eof
}
public static function hl_bal($t, $do=1, $in='div'){
// balance tags
// by content
$cB = array('blockquote'=>1, 'form'=>1, 'map'=>1, 'noscript'=>1); // Block
$cE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1); // Empty
$cF = array('button'=>1, 'del'=>1, 'div'=>1, 'dd'=>1, 'fieldset'=>1, 'iframe'=>1, 'ins'=>1, 'li'=>1, 'noscript'=>1, 'object'=>1, 'td'=>1, 'th'=>1); // Flow; later context-wise dynamic move of ins & del to $cI
$cI = array('a'=>1, 'abbr'=>1, 'acronym'=>1, 'address'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'caption'=>1, 'cite'=>1, 'code'=>1, 'dfn'=>1, 'dt'=>1, 'em'=>1, 'font'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'i'=>1, 'kbd'=>1, 'label'=>1, 'legend'=>1, 'p'=>1, 'pre'=>1, 'q'=>1, 'rb'=>1, 'rt'=>1, 's'=>1, 'samp'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'tt'=>1, 'u'=>1, 'var'=>1); // Inline
$cN = array('a'=>array('a'=>1), 'button'=>array('a'=>1, 'button'=>1, 'fieldset'=>1, 'form'=>1, 'iframe'=>1, 'input'=>1, 'label'=>1, 'select'=>1, 'textarea'=>1), 'fieldset'=>array('fieldset'=>1), 'form'=>array('form'=>1), 'label'=>array('label'=>1), 'noscript'=>array('script'=>1), 'pre'=>array('big'=>1, 'font'=>1, 'img'=>1, 'object'=>1, 'script'=>1, 'small'=>1, 'sub'=>1, 'sup'=>1), 'rb'=>array('ruby'=>1), 'rt'=>array('ruby'=>1)); // Illegal
$cN2 = array_keys($cN);
$cR = array('blockquote'=>1, 'dir'=>1, 'dl'=>1, 'form'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
$cS = array('colgroup'=>array('col'=>1), 'dir'=>array('li'=>1), 'dl'=>array('dd'=>1, 'dt'=>1), 'menu'=>array('li'=>1), 'ol'=>array('li'=>1), 'optgroup'=>array('option'=>1), 'option'=>array('#pcdata'=>1), 'rbc'=>array('rb'=>1), 'rp'=>array('#pcdata'=>1), 'rtc'=>array('rt'=>1), 'ruby'=>array('rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1), 'select'=>array('optgroup'=>1, 'option'=>1), 'script'=>array('#pcdata'=>1), 'table'=>array('caption'=>1, 'col'=>1, 'colgroup'=>1, 'tfoot'=>1, 'tbody'=>1, 'tr'=>1, 'thead'=>1), 'tbody'=>array('tr'=>1), 'tfoot'=>array('tr'=>1), 'textarea'=>array('#pcdata'=>1), 'thead'=>array('tr'=>1), 'tr'=>array('td'=>1, 'th'=>1), 'ul'=>array('li'=>1)); // Specific - immediate parent-child
if($GLOBALS['C']['direct_list_nest']){$cS['ol'] = $cS['ul'] += array('ol'=>1, 'ul'=>1);}
$cO = array('address'=>array('p'=>1), 'applet'=>array('param'=>1), 'blockquote'=>array('script'=>1), 'fieldset'=>array('legend'=>1, '#pcdata'=>1), 'form'=>array('script'=>1), 'map'=>array('area'=>1), 'object'=>array('param'=>1, 'embed'=>1)); // Other
$cT = array('colgroup'=>1, 'dd'=>1, 'dt'=>1, 'li'=>1, 'option'=>1, 'p'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1); // Omitable closing
// block/inline type; ins & del both type; #pcdata: text
$eB = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'del'=>1, 'dir'=>1, 'dl'=>1, 'div'=>1, 'fieldset'=>1, 'form'=>1, 'ins'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'isindex'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'p'=>1, 'pre'=>1, 'table'=>1, 'ul'=>1);
$eI = array('#pcdata'=>1, 'a'=>1, 'abbr'=>1, 'acronym'=>1, 'applet'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'br'=>1, 'button'=>1, 'cite'=>1, 'code'=>1, 'del'=>1, 'dfn'=>1, 'em'=>1, 'embed'=>1, 'font'=>1, 'i'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'ins'=>1, 'kbd'=>1, 'label'=>1, 'map'=>1, 'object'=>1, 'q'=>1, 'ruby'=>1, 's'=>1, 'samp'=>1, 'select'=>1, 'script'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'textarea'=>1, 'tt'=>1, 'u'=>1, 'var'=>1);
$eN = array('a'=>1, 'big'=>1, 'button'=>1, 'fieldset'=>1, 'font'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'label'=>1, 'object'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'small'=>1, 'sub'=>1, 'sup'=>1, 'textarea'=>1); // Exclude from specific ele; $cN values
$eO = array('area'=>1, 'caption'=>1, 'col'=>1, 'colgroup'=>1, 'dd'=>1, 'dt'=>1, 'legend'=>1, 'li'=>1, 'optgroup'=>1, 'option'=>1, 'param'=>1, 'rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1, 'script'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'thead'=>1, 'th'=>1, 'tr'=>1); // Missing in $eB & $eI
$eF = $eB + $eI;
// $in sets allowed child
$in = ((isset($eF[$in]) && $in != '#pcdata') or isset($eO[$in])) ? $in : 'div';
if(isset($cE[$in])){
return (!$do ? '' : str_replace(array('<', '>'), array('&lt;', '&gt;'), $t));
}
if(isset($cS[$in])){$inOk = $cS[$in];}
elseif(isset($cI[$in])){$inOk = $eI; $cI['del'] = 1; $cI['ins'] = 1;}
elseif(isset($cF[$in])){$inOk = $eF; unset($cI['del'], $cI['ins']);}
elseif(isset($cB[$in])){$inOk = $eB; unset($cI['del'], $cI['ins']);}
if(isset($cO[$in])){$inOk = $inOk + $cO[$in];}
if(isset($cN[$in])){$inOk = array_diff_assoc($inOk, $cN[$in]);}
$t = explode('<', $t);
$ok = $q = array(); // $q seq list of open non-empty ele
ob_start();
for($i=-1, $ci=count($t); ++$i<$ci;){
// allowed $ok in parent $p
if($ql = count($q)){
$p = array_pop($q);
$q[] = $p;
if(isset($cS[$p])){$ok = $cS[$p];}
elseif(isset($cI[$p])){$ok = $eI; $cI['del'] = 1; $cI['ins'] = 1;}
elseif(isset($cF[$p])){$ok = $eF; unset($cI['del'], $cI['ins']);}
elseif(isset($cB[$p])){$ok = $eB; unset($cI['del'], $cI['ins']);}
if(isset($cO[$p])){$ok = $ok + $cO[$p];}
if(isset($cN[$p])){$ok = array_diff_assoc($ok, $cN[$p]);}
}else{$ok = $inOk; unset($cI['del'], $cI['ins']);}
// bad tags, & ele content
if(isset($e) && ($do == 1 or (isset($ok['#pcdata']) && ($do == 3 or $do == 5)))){
echo '&lt;', $s, $e, $a, '&gt;';
}
if(isset($x[0])){
if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){
echo '<div>', $x, '</div>';
}
elseif($do < 3 or isset($ok['#pcdata'])){echo $x;}
elseif(strpos($x, "\x02\x04")){
foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){
echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
}
}elseif($do > 4){echo preg_replace('`\S`', '', $x);}
}
// get markup
if(!preg_match('`^(/?)([a-z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
$s = null; $e = null; $a = null; $x = null; list($all, $s, $e, $a, $x) = $r;
// close tag
if($s){
if(isset($cE[$e]) or !in_array($e, $q)){continue;} // Empty/unopen
if($p == $e){array_pop($q); echo '</', $e, '>'; unset($e); continue;} // Last open
$add = ''; // Nesting - close open tags that need to be
for($j=-1, $cj=count($q); ++$j<$cj;){
if(($d = array_pop($q)) == $e){break;}
else{$add .= "</{$d}>";}
}
echo $add, '</', $e, '>'; unset($e); continue;
}
// open tag
// $cB ele needs $eB ele as child
if(isset($cB[$e]) && strlen(trim($x))){
$t[$i] = "{$e}{$a}>";
array_splice($t, $i+1, 0, 'div>'. $x); unset($e, $x); ++$ci; --$i; continue;
}
if((($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql)) && !isset($eB[$e]) && !isset($ok[$e])){
array_splice($t, $i, 0, 'div>'); unset($e, $x); ++$ci; --$i; continue;
}
// if no open ele, $in = parent; mostly immediate parent-child relation should hold
if(!$ql or !isset($eN[$e]) or !array_intersect($q, $cN2)){
if(!isset($ok[$e])){
if($ql && isset($cT[$p])){echo '</', array_pop($q), '>'; unset($e, $x); --$i;}
continue;
}
if(!isset($cE[$e])){$q[] = $e;}
echo '<', $e, $a, '>'; unset($e); continue;
}
// specific parent-child
if(isset($cS[$p][$e])){
if(!isset($cE[$e])){$q[] = $e;}
echo '<', $e, $a, '>'; unset($e); continue;
}
// nesting
$add = '';
$q2 = array();
for($k=-1, $kc=count($q); ++$k<$kc;){
$d = $q[$k];
$ok2 = array();
if(isset($cS[$d])){$q2[] = $d; continue;}
$ok2 = isset($cI[$d]) ? $eI : $eF;
if(isset($cO[$d])){$ok2 = $ok2 + $cO[$d];}
if(isset($cN[$d])){$ok2 = array_diff_assoc($ok2, $cN[$d]);}
if(!isset($ok2[$e])){
if(!$k && !isset($inOk[$e])){continue 2;}
$add = "</{$d}>";
for(;++$k<$kc;){$add = "</{$q[$k]}>{$add}";}
break;
}
else{$q2[] = $d;}
}
$q = $q2;
if(!isset($cE[$e])){$q[] = $e;}
echo $add, '<', $e, $a, '>'; unset($e); continue;
}
// end
if($ql = count($q)){
$p = array_pop($q);
$q[] = $p;
if(isset($cS[$p])){$ok = $cS[$p];}
elseif(isset($cI[$p])){$ok = $eI; $cI['del'] = 1; $cI['ins'] = 1;}
elseif(isset($cF[$p])){$ok = $eF; unset($cI['del'], $cI['ins']);}
elseif(isset($cB[$p])){$ok = $eB; unset($cI['del'], $cI['ins']);}
if(isset($cO[$p])){$ok = $ok + $cO[$p];}
if(isset($cN[$p])){$ok = array_diff_assoc($ok, $cN[$p]);}
}else{$ok = $inOk; unset($cI['del'], $cI['ins']);}
if(isset($e) && ($do == 1 or (isset($ok['#pcdata']) && ($do == 3 or $do == 5)))){
echo '&lt;', $s, $e, $a, '&gt;';
}
if(isset($x[0])){
if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){
echo '<div>', $x, '</div>';
}
elseif($do < 3 or isset($ok['#pcdata'])){echo $x;}
elseif(strpos($x, "\x02\x04")){
foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){
echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
}
}elseif($do > 4){echo preg_replace('`\S`', '', $x);}
}
while(!empty($q) && ($e = array_pop($q))){echo '</', $e, '>';}
$o = ob_get_contents();
ob_end_clean();
return $o;
// eof
}
public static function hl_cmtcd($t){
// comment/CDATA sec handler
$t = $t[0];
global $C;
if(!($v = $C[$n = $t[3] == '-' ? 'comment' : 'cdata'])){return $t;}
if($v == 1){return '';}
if($n == 'comment'){
if(substr(($t = preg_replace('`--+`', '-', substr($t, 4, -3))), -1) != ' '){$t .= ' ';}
}
else{$t = substr($t, 1, -1);}
$t = $v == 2 ? str_replace(array('&', '<', '>'), array('&amp;', '&lt;', '&gt;'), $t) : $t;
return str_replace(array('&', '<', '>'), array("\x03", "\x04", "\x05"), ($n == 'comment' ? "\x01\x02\x04!--$t--\x05\x02\x01" : "\x01\x01\x04$t\x05\x01\x01"));
// eof
}
public static function hl_ent($t){
// entitity handler
global $C;
$t = $t[1];
static $U = array('quot'=>1,'amp'=>1,'lt'=>1,'gt'=>1);
static $N = array('fnof'=>'402', 'Alpha'=>'913', 'Beta'=>'914', 'Gamma'=>'915', 'Delta'=>'916', 'Epsilon'=>'917', 'Zeta'=>'918', 'Eta'=>'919', 'Theta'=>'920', 'Iota'=>'921', 'Kappa'=>'922', 'Lambda'=>'923', 'Mu'=>'924', 'Nu'=>'925', 'Xi'=>'926', 'Omicron'=>'927', 'Pi'=>'928', 'Rho'=>'929', 'Sigma'=>'931', 'Tau'=>'932', 'Upsilon'=>'933', 'Phi'=>'934', 'Chi'=>'935', 'Psi'=>'936', 'Omega'=>'937', 'alpha'=>'945', 'beta'=>'946', 'gamma'=>'947', 'delta'=>'948', 'epsilon'=>'949', 'zeta'=>'950', 'eta'=>'951', 'theta'=>'952', 'iota'=>'953', 'kappa'=>'954', 'lambda'=>'955', 'mu'=>'956', 'nu'=>'957', 'xi'=>'958', 'omicron'=>'959', 'pi'=>'960', 'rho'=>'961', 'sigmaf'=>'962', 'sigma'=>'963', 'tau'=>'964', 'upsilon'=>'965', 'phi'=>'966', 'chi'=>'967', 'psi'=>'968', 'omega'=>'969', 'thetasym'=>'977', 'upsih'=>'978', 'piv'=>'982', 'bull'=>'8226', 'hellip'=>'8230', 'prime'=>'8242', 'Prime'=>'8243', 'oline'=>'8254', 'frasl'=>'8260', 'weierp'=>'8472', 'image'=>'8465', 'real'=>'8476', 'trade'=>'8482', 'alefsym'=>'8501', 'larr'=>'8592', 'uarr'=>'8593', 'rarr'=>'8594', 'darr'=>'8595', 'harr'=>'8596', 'crarr'=>'8629', 'lArr'=>'8656', 'uArr'=>'8657', 'rArr'=>'8658', 'dArr'=>'8659', 'hArr'=>'8660', 'forall'=>'8704', 'part'=>'8706', 'exist'=>'8707', 'empty'=>'8709', 'nabla'=>'8711', 'isin'=>'8712', 'notin'=>'8713', 'ni'=>'8715', 'prod'=>'8719', 'sum'=>'8721', 'minus'=>'8722', 'lowast'=>'8727', 'radic'=>'8730', 'prop'=>'8733', 'infin'=>'8734', 'ang'=>'8736', 'and'=>'8743', 'or'=>'8744', 'cap'=>'8745', 'cup'=>'8746', 'int'=>'8747', 'there4'=>'8756', 'sim'=>'8764', 'cong'=>'8773', 'asymp'=>'8776', 'ne'=>'8800', 'equiv'=>'8801', 'le'=>'8804', 'ge'=>'8805', 'sub'=>'8834', 'sup'=>'8835', 'nsub'=>'8836', 'sube'=>'8838', 'supe'=>'8839', 'oplus'=>'8853', 'otimes'=>'8855', 'perp'=>'8869', 'sdot'=>'8901', 'lceil'=>'8968', 'rceil'=>'8969', 'lfloor'=>'8970', 'rfloor'=>'8971', 'lang'=>'9001', 'rang'=>'9002', 'loz'=>'9674', 'spades'=>'9824', 'clubs'=>'9827', 'hearts'=>'9829', 'diams'=>'9830', 'apos'=>'39', 'OElig'=>'338', 'oelig'=>'339', 'Scaron'=>'352', 'scaron'=>'353', 'Yuml'=>'376', 'circ'=>'710', 'tilde'=>'732', 'ensp'=>'8194', 'emsp'=>'8195', 'thinsp'=>'8201', 'zwnj'=>'8204', 'zwj'=>'8205', 'lrm'=>'8206', 'rlm'=>'8207', 'ndash'=>'8211', 'mdash'=>'8212', 'lsquo'=>'8216', 'rsquo'=>'8217', 'sbquo'=>'8218', 'ldquo'=>'8220', 'rdquo'=>'8221', 'bdquo'=>'8222', 'dagger'=>'8224', 'Dagger'=>'8225', 'permil'=>'8240', 'lsaquo'=>'8249', 'rsaquo'=>'8250', 'euro'=>'8364', 'nbsp'=>'160', 'iexcl'=>'161', 'cent'=>'162', 'pound'=>'163', 'curren'=>'164', 'yen'=>'165', 'brvbar'=>'166', 'sect'=>'167', 'uml'=>'168', 'copy'=>'169', 'ordf'=>'170', 'laquo'=>'171', 'not'=>'172', 'shy'=>'173', 'reg'=>'174', 'macr'=>'175', 'deg'=>'176', 'plusmn'=>'177', 'sup2'=>'178', 'sup3'=>'179', 'acute'=>'180', 'micro'=>'181', 'para'=>'182', 'middot'=>'183', 'cedil'=>'184', 'sup1'=>'185', 'ordm'=>'186', 'raquo'=>'187', 'frac14'=>'188', 'frac12'=>'189', 'frac34'=>'190', 'iquest'=>'191', 'Agrave'=>'192', 'Aacute'=>'193', 'Acirc'=>'194', 'Atilde'=>'195', 'Auml'=>'196', 'Aring'=>'197', 'AElig'=>'198', 'Ccedil'=>'199', 'Egrave'=>'200', 'Eacute'=>'201', 'Ecirc'=>'202', 'Euml'=>'203', 'Igrave'=>'204', 'Iacute'=>'205', 'Icirc'=>'206', 'Iuml'=>'207', 'ETH'=>'208', 'Ntilde'=>'209', 'Ograve'=>'210', 'Oacute'=>'211', 'Ocirc'=>'212', 'Otilde'=>'213', 'Ouml'=>'214', 'times'=>'215', 'Oslash'=>'216', 'Ugrave'=>'217', 'Uacute'=>'218', 'Ucirc'=>'219', 'Uuml'=>'220', 'Yacute'=>'221', 'THORN'=>'222', 'szlig'=>'223', 'agrave'=>'224', 'aacute'=>'225', 'acirc'=>'226', 'atilde'=>'227', 'auml'=>'228', 'aring'=>'229', 'aelig'=>'230', 'ccedil'=>'231', 'egrave'=>'232', 'eacute'=>'233', 'ecirc'=>'234', 'euml'=>'235', 'igrave'=>'236', 'iacute'=>'237', 'icirc'=>'238', 'iuml'=>'239', 'eth'=>'240', 'ntilde'=>'241', 'ograve'=>'242', 'oacute'=>'243', 'ocirc'=>'244', 'otilde'=>'245', 'ouml'=>'246', 'divide'=>'247', 'oslash'=>'248', 'ugrave'=>'249', 'uacute'=>'250', 'ucirc'=>'251', 'uuml'=>'252', 'yacute'=>'253', 'thorn'=>'254', 'yuml'=>'255');
if($t[0] != '#'){
return ($C['and_mark'] ? "\x06" : '&'). (isset($U[$t]) ? $t : (isset($N[$t]) ? (!$C['named_entity'] ? '#'. ($C['hexdec_entity'] > 1 ? 'x'. dechex($N[$t]) : $N[$t]) : $t) : 'amp;'. $t)). ';';
}
if(($n = ctype_digit($t = substr($t, 1)) ? intval($t) : hexdec(substr($t, 1))) < 9 or ($n > 13 && $n < 32) or $n == 11 or $n == 12 or ($n > 126 && $n < 160 && $n != 133) or ($n > 55295 && ($n < 57344 or ($n > 64975 && $n < 64992) or $n == 65534 or $n == 65535 or $n > 1114111))){
return ($C['and_mark'] ? "\x06" : '&'). "amp;#{$t};";
}
return ($C['and_mark'] ? "\x06" : '&'). '#'. (((ctype_digit($t) && $C['hexdec_entity'] < 2) or !$C['hexdec_entity']) ? $n : 'x'. dechex($n)). ';';
// eof
}
public static function hl_prot($p, $c=null){
// check URL scheme
global $C;
$b = $a = '';
if($c == null){$c = 'style'; $b = $p[1]; $a = $p[3]; $p = trim($p[2]);}
$c = isset($C['schemes'][$c]) ? $C['schemes'][$c] : $C['schemes']['*'];
static $d = 'denied:';
if(isset($c['!']) && substr($p, 0, 7) != $d){$p = "$d$p";}
if(isset($c['*']) or !strcspn($p, '#?;') or (substr($p, 0, 7) == $d)){return "{$b}{$p}{$a}";} // All ok, frag, query, param
if(preg_match('`^([a-z\d\-+.&#; ]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
return "{$b}{$d}{$p}{$a}";
}
if($C['abs_url']){
if($C['abs_url'] == -1 && strpos($p, $C['base_url']) === 0){ // Make url rel
$p = substr($p, strlen($C['base_url']));
}elseif(empty($m[1])){ // Make URL abs
if(substr($p, 0, 2) == '//'){$p = substr($C['base_url'], 0, strpos($C['base_url'], ':')+1). $p;}
elseif($p[0] == '/'){$p = preg_replace('`(^.+?://[^/]+)(.*)`', '$1', $C['base_url']). $p;}
elseif(strcspn($p, './')){$p = $C['base_url']. $p;}
else{
preg_match('`^([a-zA-Z\d\-+.]+://[^/]+)(.*)`', $C['base_url'], $m);
$p = preg_replace('`(?<=/)\./`', '', $m[2]. $p);
while(preg_match('`(?<=/)([^/]{3,}|[^/.]+?|\.[^/.]|[^/.]\.)/\.\./`', $p)){
$p = preg_replace('`(?<=/)([^/]{3,}|[^/.]+?|\.[^/.]|[^/.]\.)/\.\./`', '', $p);
}
$p = $m[1]. $p;
}
}
}
return "{$b}{$p}{$a}";
// eof
}
public static function hl_regex($p){
// ?regex
if(empty($p)){return 0;}
if($t = ini_get('track_errors')){$o = isset($php_errormsg) ? $php_errormsg : null;}
else{ini_set('track_errors', 1);}
unset($php_errormsg);
if(($d = ini_get('display_errors'))){ini_set('display_errors', 0);}
preg_match($p, '');
if($d){ini_set('display_errors', 1);}
$r = isset($php_errormsg) ? 0 : 1;
if($t){$php_errormsg = isset($o) ? $o : null;}
else{ini_set('track_errors', 0);}
return $r;
// eof
}
public static function hl_spec($t){
// final $spec
$s = array();
$t = str_replace(array("\t", "\r", "\n", ' '), '', preg_replace('/"(?>(`.|[^"])*)"/sme', 'substr(str_replace(array(";", "|", "~", " ", ",", "/", "(", ")", \'`"\'), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\""), "$0"), 1, -1)', trim($t)));
for($i = count(($t = explode(';', $t))); --$i>=0;){
$w = $t[$i];
if(empty($w) or ($e = strpos($w, '=')) === false or !strlen(($a = substr($w, $e+1)))){continue;}
$y = $n = array();
foreach(explode(',', $a) as $v){
if(!preg_match('`^([a-z:\-\*]+)(?:\((.*?)\))?`i', $v, $m)){continue;}
if(($x = strtolower($m[1])) == '-*'){$n['*'] = 1; continue;}
if($x[0] == '-'){$n[substr($x, 1)] = 1; continue;}
if(!isset($m[2])){$y[$x] = 1; continue;}
foreach(explode('/', $m[2]) as $m){
if(empty($m) or ($p = strpos($m, '=')) == 0 or $p < 5){$y[$x] = 1; continue;}
$y[$x][strtolower(substr($m, 0, $p))] = str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08"), array(";", "|", "~", " ", ",", "/", "(", ")"), substr($m, $p+1));
}
if(isset($y[$x]['match']) && !htmLawed::hl_regex($y[$x]['match'])){unset($y[$x]['match']);}
if(isset($y[$x]['nomatch']) && !htmLawed::hl_regex($y[$x]['nomatch'])){unset($y[$x]['nomatch']);}
}
if(!count($y) && !count($n)){continue;}
foreach(explode(',', substr($w, 0, $e)) as $v){
if(!strlen(($v = strtolower($v)))){continue;}
if(count($y)){$s[$v] = $y;}
if(count($n)){$s[$v]['n'] = $n;}
}
}
return $s;
// eof
}
public static function hl_tag($t){
// tag/attribute handler
global $C;
$t = $t[0];
// invalid < >
if($t == '< '){return '&lt; ';}
if($t == '>'){return '&gt;';}
if(!preg_match('`^<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>$`m', $t, $m)){
return str_replace(array('<', '>'), array('&lt;', '&gt;'), $t);
}elseif(!isset($C['elements'][($e = strtolower($m[2]))])){
return (($C['keep_bad']%2) ? str_replace(array('<', '>'), array('&lt;', '&gt;'), $t) : '');
}
// attr string
$a = str_replace(array("\n", "\r", "\t"), ' ', trim($m[3]));
// tag transform
static $eD = array('applet'=>1, 'center'=>1, 'dir'=>1, 'embed'=>1, 'font'=>1, 'isindex'=>1, 'menu'=>1, 's'=>1, 'strike'=>1, 'u'=>1); // Deprecated
if($C['make_tag_strict'] && isset($eD[$e])){
$trt = htmLawed::hl_tag2($e, $a, $C['make_tag_strict']);
if(!$e){return (($C['keep_bad']%2) ? str_replace(array('<', '>'), array('&lt;', '&gt;'), $t) : '');}
}
// close tag
static $eE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1); // Empty ele
if(!empty($m[1])){
return (!isset($eE[$e]) ? (empty($C['hook_tag']) ? "</$e>" : $C['hook_tag']($e)) : (($C['keep_bad'])%2 ? str_replace(array('<', '>'), array('&lt;', '&gt;'), $t) : ''));
}
// open tag & attr
static $aN = array('abbr'=>array('td'=>1, 'th'=>1), 'accept-charset'=>array('form'=>1), 'accept'=>array('form'=>1, 'input'=>1), 'accesskey'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'label'=>1, 'legend'=>1, 'textarea'=>1), 'action'=>array('form'=>1), 'align'=>array('caption'=>1, 'embed'=>1, 'applet'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'object'=>1, 'legend'=>1, 'table'=>1, 'hr'=>1, 'div'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'p'=>1, 'col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'alt'=>array('applet'=>1, 'area'=>1, 'img'=>1, 'input'=>1), 'archive'=>array('applet'=>1, 'object'=>1), 'axis'=>array('td'=>1, 'th'=>1), 'bgcolor'=>array('embed'=>1, 'table'=>1, 'tr'=>1, 'td'=>1, 'th'=>1), 'border'=>array('table'=>1, 'img'=>1, 'object'=>1), 'bordercolor'=>array('table'=>1, 'td'=>1, 'tr'=>1), 'cellpadding'=>array('table'=>1), 'cellspacing'=>array('table'=>1), 'char'=>array('col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'charoff'=>array('col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'charset'=>array('a'=>1, 'script'=>1), 'checked'=>array('input'=>1), 'cite'=>array('blockquote'=>1, 'q'=>1, 'del'=>1, 'ins'=>1), 'classid'=>array('object'=>1), 'clear'=>array('br'=>1), 'code'=>array('applet'=>1), 'codebase'=>array('object'=>1, 'applet'=>1), 'codetype'=>array('object'=>1), 'color'=>array('font'=>1), 'cols'=>array('textarea'=>1), 'colspan'=>array('td'=>1, 'th'=>1), 'compact'=>array('dir'=>1, 'dl'=>1, 'menu'=>1, 'ol'=>1, 'ul'=>1), 'coords'=>array('area'=>1, 'a'=>1), 'data'=>array('object'=>1), 'datetime'=>array('del'=>1, 'ins'=>1), 'declare'=>array('object'=>1), 'defer'=>array('script'=>1), 'dir'=>array('bdo'=>1), 'disabled'=>array('button'=>1, 'input'=>1, 'optgroup'=>1, 'option'=>1, 'select'=>1, 'textarea'=>1), 'enctype'=>array('form'=>1), 'face'=>array('font'=>1), 'for'=>array('label'=>1), 'frame'=>array('table'=>1), 'frameborder'=>array('iframe'=>1), 'headers'=>array('td'=>1, 'th'=>1), 'height'=>array('embed'=>1, 'iframe'=>1, 'td'=>1, 'th'=>1, 'img'=>1, 'object'=>1, 'applet'=>1), 'href'=>array('a'=>1, 'area'=>1), 'hreflang'=>array('a'=>1), 'hspace'=>array('applet'=>1, 'img'=>1, 'object'=>1), 'ismap'=>array('img'=>1, 'input'=>1), 'label'=>array('option'=>1, 'optgroup'=>1), 'language'=>array('script'=>1), 'longdesc'=>array('img'=>1, 'iframe'=>1), 'marginheight'=>array('iframe'=>1), 'marginwidth'=>array('iframe'=>1), 'maxlength'=>array('input'=>1), 'method'=>array('form'=>1), 'model'=>array('embed'=>1), 'multiple'=>array('select'=>1), 'name'=>array('button'=>1, 'embed'=>1, 'textarea'=>1, 'applet'=>1, 'select'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'a'=>1, 'input'=>1, 'object'=>1, 'map'=>1, 'param'=>1), 'nohref'=>array('area'=>1), 'noshade'=>array('hr'=>1), 'nowrap'=>array('td'=>1, 'th'=>1), 'object'=>array('applet'=>1), 'onblur'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'label'=>1, 'select'=>1, 'textarea'=>1), 'onchange'=>array('input'=>1, 'select'=>1, 'textarea'=>1), 'onfocus'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'label'=>1, 'select'=>1, 'textarea'=>1), 'onreset'=>array('form'=>1), 'onselect'=>array('input'=>1, 'textarea'=>1), 'onsubmit'=>array('form'=>1), 'pluginspage'=>array('embed'=>1), 'pluginurl'=>array('embed'=>1), 'prompt'=>array('isindex'=>1), 'readonly'=>array('textarea'=>1, 'input'=>1), 'rel'=>array('a'=>1), 'rev'=>array('a'=>1), 'rows'=>array('textarea'=>1), 'rowspan'=>array('td'=>1, 'th'=>1), 'rules'=>array('table'=>1), 'scope'=>array('td'=>1, 'th'=>1), 'scrolling'=>array('iframe'=>1), 'selected'=>array('option'=>1), 'shape'=>array('area'=>1, 'a'=>1), 'size'=>array('hr'=>1, 'font'=>1, 'input'=>1, 'select'=>1), 'span'=>array('col'=>1, 'colgroup'=>1), 'src'=>array('embed'=>1, 'script'=>1, 'input'=>1, 'iframe'=>1, 'img'=>1), 'standby'=>array('object'=>1), 'start'=>array('ol'=>1), 'summary'=>array('table'=>1), 'tabindex'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'object'=>1, 'select'=>1, 'textarea'=>1), 'target'=>array('a'=>1, 'area'=>1, 'form'=>1), 'type'=>array('a'=>1, 'embed'=>1, 'object'=>1, 'param'=>1, 'script'=>1, 'input'=>1, 'li'=>1, 'ol'=>1, 'ul'=>1, 'button'=>1), 'usemap'=>array('img'=>1, 'input'=>1, 'object'=>1), 'valign'=>array('col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'value'=>array('input'=>1, 'option'=>1, 'param'=>1, 'button'=>1, 'li'=>1), 'valuetype'=>array('param'=>1), 'vspace'=>array('applet'=>1, 'img'=>1, 'object'=>1), 'width'=>array('embed'=>1, 'hr'=>1, 'iframe'=>1, 'img'=>1, 'object'=>1, 'table'=>1, 'td'=>1, 'th'=>1, 'applet'=>1, 'col'=>1, 'colgroup'=>1, 'pre'=>1), 'wmode'=>array('embed'=>1), 'xml:space'=>array('pre'=>1, 'script'=>1, 'style'=>1)); // Ele-specific
static $aNE = array('checked'=>1, 'compact'=>1, 'declare'=>1, 'defer'=>1, 'disabled'=>1, 'ismap'=>1, 'multiple'=>1, 'nohref'=>1, 'noresize'=>1, 'noshade'=>1, 'nowrap'=>1, 'readonly'=>1, 'selected'=>1); // Empty
static $aNP = array('action'=>1, 'cite'=>1, 'classid'=>1, 'codebase'=>1, 'data'=>1, 'href'=>1, 'longdesc'=>1, 'model'=>1, 'pluginspage'=>1, 'pluginurl'=>1, 'usemap'=>1); // Need scheme check; excludes style, on* & src
static $aNU = array('class'=>array('param'=>1, 'script'=>1), 'dir'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'iframe'=>1, 'param'=>1, 'script'=>1), 'id'=>array('script'=>1), 'lang'=>array('applet'=>1, 'br'=>1, 'iframe'=>1, 'param'=>1, 'script'=>1), 'xml:lang'=>array('applet'=>1, 'br'=>1, 'iframe'=>1, 'param'=>1, 'script'=>1), 'onclick'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'ondblclick'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onkeydown'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onkeypress'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onkeyup'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmousedown'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmousemove'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmouseout'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmouseover'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmouseup'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'style'=>array('param'=>1, 'script'=>1), 'title'=>array('param'=>1, 'script'=>1)); // Univ & exceptions
if($C['lc_std_val']){
// predef attr vals for $eAL & $aNE ele
static $aNL = array('all'=>1, 'baseline'=>1, 'bottom'=>1, 'button'=>1, 'center'=>1, 'char'=>1, 'checkbox'=>1, 'circle'=>1, 'col'=>1, 'colgroup'=>1, 'cols'=>1, 'data'=>1, 'default'=>1, 'file'=>1, 'get'=>1, 'groups'=>1, 'hidden'=>1, 'image'=>1, 'justify'=>1, 'left'=>1, 'ltr'=>1, 'middle'=>1, 'none'=>1, 'object'=>1, 'password'=>1, 'poly'=>1, 'post'=>1, 'preserve'=>1, 'radio'=>1, 'rect'=>1, 'ref'=>1, 'reset'=>1, 'right'=>1, 'row'=>1, 'rowgroup'=>1, 'rows'=>1, 'rtl'=>1, 'submit'=>1, 'text'=>1, 'top'=>1);
static $eAL = array('a'=>1, 'area'=>1, 'bdo'=>1, 'button'=>1, 'col'=>1, 'form'=>1, 'img'=>1, 'input'=>1, 'object'=>1, 'optgroup'=>1, 'option'=>1, 'param'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1, 'xml:space'=>1);
$lcase = isset($eAL[$e]) ? 1 : 0;
}
$depTr = 0;
if($C['no_deprecated_attr']){
// dep attr:applicable ele
static $aND = array('align'=>array('caption'=>1, 'div'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'legend'=>1, 'object'=>1, 'p'=>1, 'table'=>1), 'bgcolor'=>array('table'=>1, 'td'=>1, 'th'=>1, 'tr'=>1), 'border'=>array('img'=>1, 'object'=>1), 'bordercolor'=>array('table'=>1, 'td'=>1, 'tr'=>1), 'clear'=>array('br'=>1), 'compact'=>array('dl'=>1, 'ol'=>1, 'ul'=>1), 'height'=>array('td'=>1, 'th'=>1), 'hspace'=>array('img'=>1, 'object'=>1), 'language'=>array('script'=>1), 'name'=>array('a'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'map'=>1), 'noshade'=>array('hr'=>1), 'nowrap'=>array('td'=>1, 'th'=>1), 'size'=>array('hr'=>1), 'start'=>array('ol'=>1), 'type'=>array('li'=>1, 'ol'=>1, 'ul'=>1), 'value'=>array('li'=>1), 'vspace'=>array('img'=>1, 'object'=>1), 'width'=>array('hr'=>1, 'pre'=>1, 'td'=>1, 'th'=>1));
static $eAD = array('a'=>1, 'br'=>1, 'caption'=>1, 'div'=>1, 'dl'=>1, 'form'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'legend'=>1, 'li'=>1, 'map'=>1, 'object'=>1, 'ol'=>1, 'p'=>1, 'pre'=>1, 'script'=>1, 'table'=>1, 'td'=>1, 'th'=>1, 'tr'=>1, 'ul'=>1);
$depTr = isset($eAD[$e]) ? 1 : 0;
}
// attr name-vals
if(strpos($a, "\x01") !== false){$a = preg_replace('`\x01[^\x01]*\x01`', '', $a);} // No comment/CDATA sec
$mode = 0; $a = trim($a, ' /'); $aA = array();
while(strlen($a)){
$w = 0;
switch($mode){
case 0: // Name
if(preg_match('`^[a-zA-Z][\-a-zA-Z:]+`', $a, $m)){
$nm = strtolower($m[0]);
$w = $mode = 1; $a = ltrim(substr_replace($a, '', 0, strlen($m[0])));
}
break; case 1:
if($a[0] == '='){ // =
$w = 1; $mode = 2; $a = ltrim($a, '= ');
}else{ // No val
$w = 1; $mode = 0; $a = ltrim($a);
$aA[$nm] = '';
}
break; case 2: // Val
if(preg_match('`^((?:"[^"]*")|(?:\'[^\']*\')|(?:\s*[^\s"\']+))(.*)`', $a, $m)){
$a = ltrim($m[2]); $m = $m[1]; $w = 1; $mode = 0;
$aA[$nm] = trim(($m[0] == '"' or $m[0] == '\'') ? substr($m, 1, -1) : $m);
}
break;
}
if($w == 0){ // Parse errs, deal with space, " & '
$a = preg_replace('`^(?:"[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*`', '', $a);
$mode = 0;
}
}
if($mode == 1){$aA[$nm] = '';}
// clean attrs
global $S;
$rl = isset($S[$e]) ? $S[$e] : array();
$a = array(); $nfr = 0;
foreach($aA as $k=>$v){
if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e]))) && !isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])){
if(isset($aNE[$k])){$v = $k;}
elseif(!empty($lcase) && (($e != 'button' or $e != 'input') or $k == 'type')){ // Rather loose but ?not cause issues
$v = (isset($aNL[($v2 = strtolower($v))])) ? $v2 : $v;
}
if($k == 'style' && !$C['style_pass']){
if(false !== strpos($v, '&#')){
static $sC = array('&#x20;'=>' ', '&#32;'=>' ', '&#x45;'=>'e', '&#69;'=>'e', '&#x65;'=>'e', '&#101;'=>'e', '&#x58;'=>'x', '&#88;'=>'x', '&#x78;'=>'x', '&#120;'=>'x', '&#x50;'=>'p', '&#80;'=>'p', '&#x70;'=>'p', '&#112;'=>'p', '&#x53;'=>'s', '&#83;'=>'s', '&#x73;'=>'s', '&#115;'=>'s', '&#x49;'=>'i', '&#73;'=>'i', '&#x69;'=>'i', '&#105;'=>'i', '&#x4f;'=>'o', '&#79;'=>'o', '&#x6f;'=>'o', '&#111;'=>'o', '&#x4e;'=>'n', '&#78;'=>'n', '&#x6e;'=>'n', '&#110;'=>'n', '&#x55;'=>'u', '&#85;'=>'u', '&#x75;'=>'u', '&#117;'=>'u', '&#x52;'=>'r', '&#82;'=>'r', '&#x72;'=>'r', '&#114;'=>'r', '&#x4c;'=>'l', '&#76;'=>'l', '&#x6c;'=>'l', '&#108;'=>'l', '&#x28;'=>'(', '&#40;'=>'(', '&#x29;'=>')', '&#41;'=>')', '&#x20;'=>':', '&#32;'=>':', '&#x22;'=>'"', '&#34;'=>'"', '&#x27;'=>"'", '&#39;'=>"'", '&#x2f;'=>'/', '&#47;'=>'/', '&#x2a;'=>'*', '&#42;'=>'*', '&#x5c;'=>'\\', '&#92;'=>'\\');
$v = strtr($v, $sC);
}
$v = preg_replace_callback('`(url(?:\()(?: )*(?:\'|"|&(?:quot|apos);)?)(.+?)((?:\'|"|&(?:quot|apos);)?(?: )*(?:\)))`iS', 'htmLawed::hl_prot', $v);
$v = !$C['css_expression'] ? preg_replace('`expression`i', ' ', preg_replace('`\\\\\S|(/|(%2f))(\*|(%2a))`i', ' ', $v)) : $v;
}elseif(isset($aNP[$k]) or strpos($k, 'src') !== false or $k[0] == 'o'){
$v = str_replace("\xad", ' ', (strpos($v, '&') !== false ? str_replace(array('&#xad;', '&#173;', '&shy;'), ' ', $v) : $v));
$v = htmLawed::hl_prot($v, $k);
if($k == 'href'){ // X-spam
if($C['anti_mail_spam'] && strpos($v, 'mailto:') === 0){
$v = str_replace('@', htmlspecialchars($C['anti_mail_spam']), $v);
}elseif($C['anti_link_spam']){
$r1 = $C['anti_link_spam'][1];
if(!empty($r1) && preg_match($r1, $v)){continue;}
$r0 = $C['anti_link_spam'][0];
if(!empty($r0) && preg_match($r0, $v)){
if(isset($a['rel'])){
if(!preg_match('`\bnofollow\b`i', $a['rel'])){$a['rel'] .= ' nofollow';}
}elseif(isset($aA['rel'])){
if(!preg_match('`\bnofollow\b`i', $aA['rel'])){$nfr = 1;}
}else{$a['rel'] = 'nofollow';}
}
}
}
}
if(isset($rl[$k]) && is_array($rl[$k]) && ($v = htmLawed::hl_attrval($v, $rl[$k])) === 0){continue;}
$a[$k] = str_replace('"', '&quot;', $v);
}
}
if($nfr){$a['rel'] = isset($a['rel']) ? $a['rel']. ' nofollow' : 'nofollow';}
// rqd attr
static $eAR = array('area'=>array('alt'=>'area'), 'bdo'=>array('dir'=>'ltr'), 'form'=>array('action'=>''), 'img'=>array('src'=>'', 'alt'=>'image'), 'map'=>array('name'=>''), 'optgroup'=>array('label'=>''), 'param'=>array('name'=>''), 'script'=>array('type'=>'text/javascript'), 'textarea'=>array('rows'=>'10', 'cols'=>'50'));
if(isset($eAR[$e])){
foreach($eAR[$e] as $k=>$v){
if(!isset($a[$k])){$a[$k] = isset($v[0]) ? $v : $k;}
}
}
// depr attrs
if($depTr){
$c = array();
foreach($a as $k=>$v){
if($k == 'style' or !isset($aND[$k][$e])){continue;}
if($k == 'align'){
unset($a['align']);
if($e == 'img' && ($v == 'left' or $v == 'right')){$c[] = 'float: '. $v;}
elseif(($e == 'div' or $e == 'table') && $v == 'center'){$c[] = 'margin: auto';}
else{$c[] = 'text-align: '. $v;}
}elseif($k == 'bgcolor'){
unset($a['bgcolor']);
$c[] = 'background-color: '. $v;
}elseif($k == 'border'){
unset($a['border']); $c[] = "border: {$v}px";
}elseif($k == 'bordercolor'){
unset($a['bordercolor']); $c[] = 'border-color: '. $v;
}elseif($k == 'clear'){
unset($a['clear']); $c[] = 'clear: '. ($v != 'all' ? $v : 'both');
}elseif($k == 'compact'){
unset($a['compact']); $c[] = 'font-size: 85%';
}elseif($k == 'height' or $k == 'width'){
unset($a[$k]); $c[] = $k. ': '. ($v[0] != '*' ? $v. (ctype_digit($v) ? 'px' : '') : 'auto');
}elseif($k == 'hspace'){
unset($a['hspace']); $c[] = "margin-left: {$v}px; margin-right: {$v}px";
}elseif($k == 'language' && !isset($a['type'])){
unset($a['language']);
$a['type'] = 'text/'. strtolower($v);
}elseif($k == 'name'){
if($C['no_deprecated_attr'] == 2 or ($e != 'a' && $e != 'map')){unset($a['name']);}
if(!isset($a['id']) && preg_match('`[a-zA-Z][a-zA-Z\d.:_\-]*`', $v)){$a['id'] = $v;}
}elseif($k == 'noshade'){
unset($a['noshade']); $c[] = 'border-style: none; border: 0; background-color: gray; color: gray';
}elseif($k == 'nowrap'){
unset($a['nowrap']); $c[] = 'white-space: nowrap';
}elseif($k == 'size'){
unset($a['size']); $c[] = 'size: '. $v. 'px';
}elseif($k == 'start' or $k == 'value'){
unset($a[$k]);
}elseif($k == 'type'){
unset($a['type']);
static $ol_type = array('i'=>'lower-roman', 'I'=>'upper-roman', 'a'=>'lower-latin', 'A'=>'upper-latin', '1'=>'decimal');
$c[] = 'list-style-type: '. (isset($ol_type[$v]) ? $ol_type[$v] : 'decimal');
}elseif($k == 'vspace'){
unset($a['vspace']); $c[] = "margin-top: {$v}px; margin-bottom: {$v}px";
}
}
if(count($c)){
$c = implode('; ', $c);
$a['style'] = isset($a['style']) ? rtrim($a['style'], ' ;'). '; '. $c. ';': $c. ';';
}
}
// unique ID
if($C['unique_ids'] && isset($a['id'])){
if(!preg_match('`^[A-Za-z][A-Za-z0-9_\-.:]*$`', ($id = $a['id'])) or (isset($GLOBALS['hl_Ids'][$id]) && $C['unique_ids'] == 1)){unset($a['id']);
}else{
while(isset($GLOBALS['hl_Ids'][$id])){$id = $C['unique_ids']. $id;}
$GLOBALS['hl_Ids'][($a['id'] = $id)] = 1;
}
}
// xml:lang
if($C['xml:lang'] && isset($a['lang'])){
$a['xml:lang'] = isset($a['xml:lang']) ? $a['xml:lang'] : $a['lang'];
if($C['xml:lang'] == 2){unset($a['lang']);}
}
// for transformed tag
if(!empty($trt)){
$a['style'] = isset($a['style']) ? rtrim($a['style'], ' ;'). '; '. $trt : $trt;
}
// return with empty ele /
if(empty($C['hook_tag'])){
$aA = '';
foreach($a as $k=>$v){$aA .= " {$k}=\"{$v}\"";}
return "<{$e}{$aA}". (isset($eE[$e]) ? ' /' : ''). '>';
}
else{return $C['hook_tag']($e, $a);}
// eof
}
public static function hl_tag2(&$e, &$a, $t=1){
// transform tag
if($e == 'center'){$e = 'div'; return 'text-align: center;';}
if($e == 'dir' or $e == 'menu'){$e = 'ul'; return '';}
if($e == 's' or $e == 'strike'){$e = 'span'; return 'text-decoration: line-through;';}
if($e == 'u'){$e = 'span'; return 'text-decoration: underline;';}
static $fs = array('0'=>'xx-small', '1'=>'xx-small', '2'=>'small', '3'=>'medium', '4'=>'large', '5'=>'x-large', '6'=>'xx-large', '7'=>'300%', '-1'=>'smaller', '-2'=>'60%', '+1'=>'larger', '+2'=>'150%', '+3'=>'200%', '+4'=>'300%');
if($e == 'font'){
$a2 = '';
if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=(\s*)(\S+)`i', $a, $m)){
$a2 .= ' font-family: '. str_replace('"', '\'', trim($m[2])). ';';
}
if(preg_match('`color\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m)){
$a2 .= ' color: '. trim($m[2]). ';';
}
if(preg_match('`size\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m) && isset($fs[($m = trim($m[2]))])){
$a2 .= ' font-size: '. $fs[$m]. ';';
}
$e = 'span'; return ltrim($a2);
}
if($t == 2){$e = 0; return 0;}
return '';
// eof
}
public static function hl_tidy($t, $w, $p){
// Tidy/compact HTM
if(strpos(' pre,script,textarea', "$p,")){return $t;}
$t = str_replace(' </', '</', preg_replace(array('`(<\w[^>]*(?<!/)>)\s+`', '`\s+`', '`(<\w[^>]*(?<!/)>) `'), array(' $1', ' ', '$1'), preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t)));
if(($w = strtolower($w)) == -1){
return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t);
}
$s = strpos(" $w", 't') ? "\t" : ' ';
$s = preg_match('`\d`', $w, $m) ? str_repeat($s, $m[0]) : str_repeat($s, ($s == "\t" ? 1 : 2));
$N = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
$a = array('br'=>1);
$b = array('button'=>1, 'input'=>1, 'option'=>1);
$c = array('caption'=>1, 'dd'=>1, 'dt'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'isindex'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'object'=>1, 'p'=>1, 'pre'=>1, 'td'=>1, 'textarea'=>1, 'th'=>1);
$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
$T = explode('<', $t);
$X = 1;
while($X){
$n = $N;
$t = $T;
ob_start();
if(isset($d[$p])){echo str_repeat($s, ++$n);}
echo ltrim(array_shift($t));
for($i=-1, $j=count($t); ++$i<$j;){
$r = ''; list($e, $r) = explode('>', $t[$i]);
$x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
$y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
$e = "<$e>";
if(isset($d[$y])){
if(!$x){
if($n){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
else{++$N; ob_end_clean(); continue 2;}
}
else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
echo ltrim($r); continue;
}
$f = "\n". str_repeat($s, $n);
if(isset($c[$y])){
if(!$x){echo $e, $f, ltrim($r);}
else{echo $f, $e, $r;}
}elseif(isset($b[$y])){echo $f, $e, $r;
}elseif(isset($a[$y])){echo $e, $f, ltrim($r);
}elseif(!$y){echo $f, $e, $f, ltrim($r);
}else{echo $e, $r;}
}
$X = 0;
}
$t = preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents());
ob_end_clean();
if(($l = strpos(" $w", 'r') ? (strpos(" $w", 'n') ? "\r\n" : "\r") : 0)){
$t = str_replace("\n", $l, $t);
}
return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t);
// eof
}
public static function hl_version(){
// rel
return '1.1.14';
// eof
}
public static function kses($t, $h, $p=array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'gopher', 'mailto')){
// kses compat
foreach($h as $k=>$v){
$h[$k]['n']['*'] = 1;
}
$C['cdata'] = $C['comment'] = $C['make_tag_strict'] = $C['no_deprecated_attr'] = $C['unique_ids'] = 0;
$C['keep_bad'] = 1;
$C['elements'] = count($h) ? strtolower(implode(',', array_keys($h))) : '-*';
$C['hook'] = 'htmLawed::kses_hook';
$C['schemes'] = '*:'. implode(',', $p);
return htmLawed::hl($t, $C, $h);
// eof
}
public static function kses_hook($t, &$C, &$S){
// kses compat
return $t;
// eof
}
// end class
}

View File

@ -0,0 +1,114 @@
<?php
// warning: this file is encoded in UTF-8!
class HTML5_Data
{
// at some point this should be moved to a .ser file. Another
// possible optimization is to give UTF-8 bytes, not Unicode
// codepoints
// XXX: Not quite sure why it's named this; this is
// actually the numeric entity dereference table.
protected static $realCodepointTable = array(
0x00 => 0xFFFD, // REPLACEMENT CHARACTER
0x0D => 0x000A, // LINE FEED (LF)
0x80 => 0x20AC, // EURO SIGN ('€')
0x81 => 0x0081, // <control>
0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('')
0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ')
0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„')
0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…')
0x86 => 0x2020, // DAGGER ('†')
0x87 => 0x2021, // DOUBLE DAGGER ('‡')
0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ')
0x89 => 0x2030, // PER MILLE SIGN ('‰')
0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š')
0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('')
0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ')
0x8D => 0x008D, // <control>
0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž')
0x8F => 0x008F, // <control>
0x90 => 0x0090, // <control>
0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('')
0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('')
0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“')
0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”')
0x95 => 0x2022, // BULLET ('•')
0x96 => 0x2013, // EN DASH ('')
0x97 => 0x2014, // EM DASH ('—')
0x98 => 0x02DC, // SMALL TILDE ('˜')
0x99 => 0x2122, // TRADE MARK SIGN ('™')
0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š')
0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('')
0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ')
0x9D => 0x009D, // <control>
0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž')
0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ')
);
protected static $namedCharacterReferences;
protected static $namedCharacterReferenceMaxLength;
/**
* Returns the "real" Unicode codepoint of a malformed character
* reference.
*/
public static function getRealCodepoint($ref) {
if (!isset(self::$realCodepointTable[$ref])) return false;
else return self::$realCodepointTable[$ref];
}
public static function getNamedCharacterReferences() {
if (!self::$namedCharacterReferences) {
self::$namedCharacterReferences = unserialize(
file_get_contents(dirname(__FILE__) . '/named-character-references.ser'));
}
return self::$namedCharacterReferences;
}
/**
* Converts a Unicode codepoint to sequence of UTF-8 bytes.
* @note Shamelessly stolen from HTML Purifier, which is also
* shamelessly stolen from Feyd (which is in public domain).
*/
public static function utf8chr($code) {
/* We don't care: we live dangerously
* if($code > 0x10FFFF or $code < 0x0 or
($code >= 0xD800 and $code <= 0xDFFF) ) {
// bits are set outside the "valid" range as defined
// by UNICODE 4.1.0
return "\xEF\xBF\xBD";
}*/
$x = $y = $z = $w = 0;
if ($code < 0x80) {
// regular ASCII character
$x = $code;
} else {
// set up bits for UTF-8
$x = ($code & 0x3F) | 0x80;
if ($code < 0x800) {
$y = (($code & 0x7FF) >> 6) | 0xC0;
} else {
$y = (($code & 0xFC0) >> 6) | 0x80;
if($code < 0x10000) {
$z = (($code >> 12) & 0x0F) | 0xE0;
} else {
$z = (($code >> 12) & 0x3F) | 0x80;
$w = (($code >> 18) & 0x07) | 0xF0;
}
}
}
// set up the actual character
$ret = '';
if($w) $ret .= chr($w);
if($z) $ret .= chr($z);
if($y) $ret .= chr($y);
$ret .= chr($x);
return $ret;
}
}

View File

@ -0,0 +1,284 @@
<?php
/*
Copyright 2009 Geoffrey Sneddon <http://gsnedders.com/>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Some conventions:
// /* */ indicates verbatim text from the HTML 5 specification
// // indicates regular comments
class HTML5_InputStream {
/**
* The string data we're parsing.
*/
private $data;
/**
* The current integer byte position we are in $data
*/
private $char;
/**
* Length of $data; when $char === $data, we are at the end-of-file.
*/
private $EOF;
/**
* Parse errors.
*/
public $errors = array();
/**
* @param $data Data to parse
*/
public function __construct($data) {
/* Given an encoding, the bytes in the input stream must be
converted to Unicode characters for the tokeniser, as
described by the rules for that encoding, except that the
leading U+FEFF BYTE ORDER MARK character, if any, must not
be stripped by the encoding layer (it is stripped by the rule below).
Bytes or sequences of bytes in the original byte stream that
could not be converted to Unicode characters must be converted
to U+FFFD REPLACEMENT CHARACTER code points. */
// XXX currently assuming input data is UTF-8; once we
// build encoding detection this will no longer be the case
//
// We previously had an mbstring implementation here, but that
// implementation is heavily non-conforming, so it's been
// omitted.
if (extension_loaded('iconv')) {
// non-conforming
$data = @iconv('UTF-8', 'UTF-8//IGNORE', $data);
} else {
// we can make a conforming native implementation
throw new Exception('Not implemented, please install mbstring or iconv');
}
/* One leading U+FEFF BYTE ORDER MARK character must be
ignored if any are present. */
if (substr($data, 0, 3) === "\xEF\xBB\xBF") {
$data = substr($data, 3);
}
/* All U+0000 NULL characters in the input must be replaced
by U+FFFD REPLACEMENT CHARACTERs. Any occurrences of such
characters is a parse error. */
for ($i = 0, $count = substr_count($data, "\0"); $i < $count; $i++) {
$this->errors[] = array(
'type' => HTML5_Tokenizer::PARSEERROR,
'data' => 'null-character'
);
}
/* U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED
(LF) characters are treated specially. Any CR characters
that are followed by LF characters must be removed, and any
CR characters not followed by LF characters must be converted
to LF characters. Thus, newlines in HTML DOMs are represented
by LF characters, and there are never any CR characters in the
input to the tokenization stage. */
$data = str_replace(
array(
"\0",
"\r\n",
"\r"
),
array(
"\xEF\xBF\xBD",
"\n",
"\n"
),
$data
);
/* Any occurrences of any characters in the ranges U+0001 to
U+0008, U+000B, U+000E to U+001F, U+007F to U+009F,
U+D800 to U+DFFF , U+FDD0 to U+FDEF, and
characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, U+2FFFE, U+2FFFF,
U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE,
U+6FFFF, U+7FFFE, U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF,
U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE,
U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and
U+10FFFF are parse errors. (These are all control characters
or permanently undefined Unicode characters.) */
// Check PCRE is loaded.
if (extension_loaded('pcre')) {
$count = preg_match_all(
'/(?:
[\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F
|
\xC2[\x80-\x9F] # U+0080 to U+009F
|
\xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF
|
\xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF
|
\xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF
|
[\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16})
)/x',
$data,
$matches
);
for ($i = 0; $i < $count; $i++) {
$this->errors[] = array(
'type' => HTML5_Tokenizer::PARSEERROR,
'data' => 'invalid-codepoint'
);
}
} else {
// XXX: Need non-PCRE impl, probably using substr_count
}
$this->data = $data;
$this->char = 0;
$this->EOF = strlen($data);
}
/**
* Returns the current line that the tokenizer is at.
*/
public function getCurrentLine() {
// Check the string isn't empty
if($this->EOF) {
// Add one to $this->char because we want the number for the next
// byte to be processed.
return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1;
} else {
// If the string is empty, we are on the first line (sorta).
return 1;
}
}
/**
* Returns the current column of the current line that the tokenizer is at.
*/
public function getColumnOffset() {
// strrpos is weird, and the offset needs to be negative for what we
// want (i.e., the last \n before $this->char). This needs to not have
// one (to make it point to the next character, the one we want the
// position of) added to it because strrpos's behaviour includes the
// final offset byte.
$lastLine = strrpos($this->data, "\n", $this->char - 1 - strlen($this->data));
// However, for here we want the length up until the next byte to be
// processed, so add one to the current byte ($this->char).
if($lastLine !== false) {
$findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine);
} else {
$findLengthOf = substr($this->data, 0, $this->char);
}
// Get the length for the string we need.
if(extension_loaded('iconv')) {
return iconv_strlen($findLengthOf, 'utf-8');
} elseif(extension_loaded('mbstring')) {
return mb_strlen($findLengthOf, 'utf-8');
} elseif(extension_loaded('xml')) {
return strlen(utf8_decode($findLengthOf));
} else {
$count = count_chars($findLengthOf);
// 0x80 = 0x7F - 0 + 1 (one added to get inclusive range)
// 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range)
return array_sum(array_slice($count, 0, 0x80)) +
array_sum(array_slice($count, 0xC2, 0x33));
}
}
/**
* Retrieve the currently consume character.
* @note This performs bounds checking
*/
public function char() {
return ($this->char++ < $this->EOF)
? $this->data[$this->char - 1]
: false;
}
/**
* Get all characters until EOF.
* @note This performs bounds checking
*/
public function remainingChars() {
if($this->char < $this->EOF) {
$data = substr($this->data, $this->char);
$this->char = $this->EOF;
return $data;
} else {
return false;
}
}
/**
* Matches as far as possible until we reach a certain set of bytes
* and returns the matched substring.
* @param $bytes Bytes to match.
*/
public function charsUntil($bytes, $max = null) {
if ($this->char < $this->EOF) {
if ($max === 0 || $max) {
$len = strcspn($this->data, $bytes, $this->char, $max);
} else {
$len = strcspn($this->data, $bytes, $this->char);
}
$string = (string) substr($this->data, $this->char, $len);
$this->char += $len;
return $string;
} else {
return false;
}
}
/**
* Matches as far as possible with a certain set of bytes
* and returns the matched substring.
* @param $bytes Bytes to match.
*/
public function charsWhile($bytes, $max = null) {
if ($this->char < $this->EOF) {
if ($max === 0 || $max) {
$len = strspn($this->data, $bytes, $this->char, $max);
} else {
$len = strspn($this->data, $bytes, $this->char);
}
$string = (string) substr($this->data, $this->char, $len);
$this->char += $len;
return $string;
} else {
return false;
}
}
/**
* Unconsume one character.
*/
public function unget() {
if ($this->char <= $this->EOF) {
$this->char--;
}
}
}

View File

@ -0,0 +1,36 @@
<?php
require_once dirname(__FILE__) . '/Data.php';
require_once dirname(__FILE__) . '/InputStream.php';
require_once dirname(__FILE__) . '/TreeBuilder.php';
require_once dirname(__FILE__) . '/Tokenizer.php';
/**
* Outwards facing interface for HTML5.
*/
class HTML5_Parser
{
/**
* Parses a full HTML document.
* @param $text HTML text to parse
* @param $builder Custom builder implementation
* @return Parsed HTML as DOMDocument
*/
static public function parse($text, $builder = null) {
$tokenizer = new HTML5_Tokenizer($text, $builder);
$tokenizer->parse();
return $tokenizer->save();
}
/**
* Parses an HTML fragment.
* @param $text HTML text to parse
* @param $context String name of context element to pretend parsing is in.
* @param $builder Custom builder implementation
* @return Parsed HTML as DOMDocument
*/
static public function parseFragment($text, $context = null, $builder = null) {
$tokenizer = new HTML5_Tokenizer($text, $builder);
$tokenizer->parseFragment($context);
return $tokenizer->save();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,404 @@
<?php
/**
* Cookie Jar
*
* PHP class for handling cookies, as defined by the Netscape spec:
* <http://curl.haxx.se/rfc/cookie_spec.html>
*
* This class should be used to handle cookies (storing cookies from HTTP response messages, and
* sending out cookies in HTTP request messages). This has been adapted for FiveFilters.org
* from the original version used in HTTP Navigator. See http://www.keyvan.net/code/http-navigator/
*
* This class is mainly based on Cookies.pm <http://search.cpan.org/author/GAAS/libwww-perl-5.65/
* lib/HTTP/Cookies.pm> from the libwww-perl collection <http://www.linpro.no/lwp/>.
* Unlike Cookies.pm, this class only supports the Netscape cookie spec, not RFC 2965.
*
* @version 0.5
* @date 2011-03-15
* @see http://php.net/HttpRequestPool
* @author Keyvan Minoukadeh
* @copyright 2011 Keyvan Minoukadeh
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
*/
class CookieJar
{
/**
* Cookies - array containing all cookies.
*
* <pre>
* Cookies are stored like this:
* [domain][path][name] = array
* where array is:
* 0 => value, 1 => secure, 2 => expires
* </pre>
* @var array
* @access private
*/
public $cookies = array();
public $debug = false;
/**
* Constructor
*/
function __construct() {
}
protected function debug($msg, $file=null, $line=null) {
if ($this->debug) {
$mem = round(memory_get_usage()/1024, 2);
$memPeak = round(memory_get_peak_usage()/1024, 2);
echo '* ',$msg;
if (isset($file, $line)) echo " ($file line $line)";
echo ' - mem used: ',$mem," (peak: $memPeak)\n";
ob_flush();
flush();
}
}
/**
* Get matching cookies
*
* Only use this method if you cannot use add_cookie_header(), for example, if you want to use
* this cookie jar class without using the request class.
*
* @param array $param associative array containing 'domain', 'path', 'secure' keys
* @return string
* @see add_cookie_header()
*/
public function getMatchingCookies($url)
{
if (($parts = @parse_url($url)) && isset($parts['scheme'], $parts['host'], $parts['path'])) {
$param['domain'] = $parts['host'];
$param['path'] = $parts['path'];
$param['secure'] = (strtolower($parts['scheme']) == 'https');
unset($parts);
} else {
return false;
}
// RFC 2965 notes:
// If multiple cookies satisfy the criteria above, they are ordered in
// the Cookie header such that those with more specific Path attributes
// precede those with less specific. Ordering with respect to other
// attributes (e.g., Domain) is unspecified.
$domain = $param['domain'];
if (strpos($domain, '.') === false) $domain .= '.local';
$request_path = $param['path'];
if ($request_path == '') $request_path = '/';
$request_secure = $param['secure'];
$now = time();
$matched_cookies = array();
// domain - find matching domains
$this->debug('Finding matching domains for '.$domain, __FILE__, __LINE__);
while (strpos($domain, '.') !== false) {
if (isset($this->cookies[$domain])) {
$this->debug(' domain match found: '.$domain);
$cookies =& $this->cookies[$domain];
} else {
$domain = $this->_reduce_domain($domain);
continue;
}
// paths - find matching paths starting from most specific
$this->debug(' - Finding matching paths for '.$request_path);
$paths = array_keys($cookies);
usort($paths, array($this, '_cmp_length'));
foreach ($paths as $path) {
// continue to next cookie if request path does not path-match cookie path
if (!$this->_path_match($request_path, $path)) continue;
// loop through cookie names
$this->debug(' path match found: '.$path);
foreach ($cookies[$path] as $name => $values) {
// if this cookie is secure but request isn't, continue to next cookie
if ($values[1] && !$request_secure) continue;
// if cookie is not a session cookie and has expired, continue to next cookie
if (is_int($values[2]) && ($values[2] < $now)) continue;
// cookie matches request
$this->debug(' cookie match: '.$name.'='.$values[0]);
$matched_cookies[] = $name.'='.$values[0];
}
}
$domain = $this->_reduce_domain($domain);
}
// return cookies
return implode('; ', $matched_cookies);
}
/**
* Parse Set-Cookie values.
*
* Only use this method if you cannot use extract_cookies(), for example, if you want to use
* this cookie jar class without using the response class.
*
* @param array $set_cookies array holding 1 or more "Set-Cookie" header values
* @param array $param associative array containing 'host', 'path' keys
* @return void
* @see extract_cookies()
*/
public function storeCookies($url, $set_cookies)
{
if (count($set_cookies) == 0) return;
$param = @parse_url($url);
if (!is_array($param) || !isset($param['host'])) return;
$request_host = $param['host'];
if (strpos($request_host, '.') === false) $request_host .= '.local';
$request_path = @$param['path'];
if ($request_path == '') $request_path = '/';
//
// loop through set-cookie headers
//
foreach ($set_cookies as $set_cookie) {
$this->debug('Parsing: '.$set_cookie);
// temporary cookie store (before adding to jar)
$tmp_cookie = array();
$param = explode(';', $set_cookie);
// loop through params
for ($x=0; $x<count($param); $x++) {
$key_val = explode('=', $param[$x], 2);
if (count($key_val) != 2) {
// if the first param isn't a name=value pair, continue to the next set-cookie
// header
if ($x == 0) continue 2;
// check for secure flag
if (strtolower(trim($key_val[0])) == 'secure') $tmp_cookie['secure'] = true;
// continue to next param
continue;
}
list($key, $val) = array_map('trim', $key_val);
// first name=value pair is the cookie name and value
// the name and value are stored under 'name' and 'value' to avoid conflicts
// with later parameters.
if ($x == 0) {
$tmp_cookie = array('name'=>$key, 'value'=>$val);
continue;
}
$key = strtolower($key);
if (in_array($key, array('expires', 'path', 'domain', 'secure'))) {
$tmp_cookie[$key] = $val;
}
}
//
// set cookie
//
// check domain
if (isset($tmp_cookie['domain']) && ($tmp_cookie['domain'] != $request_host) &&
($tmp_cookie['domain'] != ".$request_host")) {
$domain = $tmp_cookie['domain'];
if ((strpos($domain, '.') === false) && ($domain != 'local')) {
$this->debug(' - domain "'.$domain.'" has no dot and is not a local domain');
continue;
}
if (preg_match('/\.[0-9]+$/', $domain)) {
$this->debug(' - domain "'.$domain.'" appears to be an ip address');
continue;
}
if (substr($domain, 0, 1) != '.') $domain = ".$domain";
if (!$this->_domain_match($request_host, $domain)) {
$this->debug(' - request host "'.$request_host.'" does not domain-match "'.$domain.'"');
continue;
}
} else {
// if domain is not specified in the set-cookie header, domain will default to
// the request host
$domain = $request_host;
}
// check path
if (isset($tmp_cookie['path']) && ($tmp_cookie['path'] != '')) {
$path = urldecode($tmp_cookie['path']);
if (!$this->_path_match($request_path, $path)) {
$this->debug(' - request path "'.$request_path.'" does not path-match "'.$path.'"');
continue;
}
} else {
$path = $request_path;
$path = substr($path, 0, strrpos($path, '/'));
if ($path == '') $path = '/';
}
// check if secure
$secure = (isset($tmp_cookie['secure'])) ? true : false;
// check expiry
if (isset($tmp_cookie['expires'])) {
if (($expires = strtotime($tmp_cookie['expires'])) < 0) {
$expires = null;
}
} else {
$expires = null;
}
// set cookie
$this->set_cookie($domain, $path, $tmp_cookie['name'], $tmp_cookie['value'], $secure, $expires);
}
}
// return array of set-cookie values extracted from HTTP response headers (string $h)
public function extractCookies($h) {
$x = 0;
$lines = 0;
$headers = array();
$last_match = false;
$h = explode("\n", $h);
foreach ($h as $line) {
$line = rtrim($line);
$lines++;
$trimmed_line = trim($line);
if (isset($line_last)) {
// check if we have \r\n\r\n (indicating the end of headers)
// some servers will not use CRLF (\r\n), so we make CR (\r) optional.
// if (preg_match('/\015?\012\015?\012/', $line_last.$line)) {
// break;
// }
// As an alternative, we can check if the current trimmed line is empty
if ($trimmed_line == '') {
break;
}
// check for continuation line...
// RFC 2616 Section 2.2 "Basic Rules":
// HTTP/1.1 header field values can be folded onto multiple lines if the
// continuation line begins with a space or horizontal tab. All linear
// white space, including folding, has the same semantics as SP. A
// recipient MAY replace any linear white space with a single SP before
// interpreting the field value or forwarding the message downstream.
if ($last_match && preg_match('/^\s+(.*)/', $line, $match)) {
// append to previous header value
$headers[$x-1] .= ' '.rtrim($match[1]);
continue;
}
}
$line_last = $line;
// split header name and value
if (preg_match('/^Set-Cookie\s*:\s*(.*)/i', $line, $match)) {
$headers[$x++] = rtrim($match[1]);
$last_match = true;
} else {
$last_match = false;
}
}
return $headers;
}
/**
* Set Cookie
* @param string $domain
* @param string $path
* @param string $name cookie name
* @param string $value cookie value
* @param bool $secure
* @param int $expires expiry time (null if session cookie, <= 0 will delete cookie)
* @return void
*/
function set_cookie($domain, $path, $name, $value, $secure=false, $expires=null)
{
if ($domain == '') return;
if ($path == '') return;
if ($name == '') return;
// check if cookie needs to go
if (isset($expires) && ($expires <= 0)) {
if (isset($this->cookies[$domain][$path][$name])) unset($this->cookies[$domain][$path][$name]);
return;
}
if ($value == '') return;
$this->cookies[$domain][$path][$name] = array($value, $secure, $expires);
return;
}
/**
* Clear cookies - [domain [,path [,name]]] - call method with no arguments to clear all cookies.
* @param string $domain
* @param string $path
* @param string $name
* @return void
*/
function clear($domain=null, $path=null, $name=null)
{
if (!isset($domain)) {
$this->cookies = array();
} elseif (!isset($path)) {
if (isset($this->cookies[$domain])) unset($this->cookies[$domain]);
} elseif (!isset($name)) {
if (isset($this->cookies[$domain][$path])) unset($this->cookies[$domain][$path]);
} elseif (isset($name)) {
if (isset($this->cookies[$domain][$path][$name])) unset($this->cookies[$domain][$path][$name]);
}
}
/**
* Compare string length - used for sorting
* @access private
* @return int
*/
function _cmp_length($a, $b)
{
$la = strlen($a); $lb = strlen($b);
if ($la == $lb) return 0;
return ($la > $lb) ? -1 : 1;
}
/**
* Reduce domain
* @param string $domain
* @return string
* @access private
*/
function _reduce_domain($domain)
{
if ($domain == '') return '';
if (substr($domain, 0, 1) == '.') return substr($domain, 1);
return substr($domain, strpos($domain, '.'));
}
/**
* Path match - check if path1 path-matches path2
*
* From RFC 2965:
* <i>For two strings that represent paths, P1 and P2, P1 path-matches P2
* if P2 is a prefix of P1 (including the case where P1 and P2 string-
* compare equal). Thus, the string /tec/waldo path-matches /tec.</i>
* @param string $path1
* @param string $path2
* @return bool
* @access private
*/
function _path_match($path1, $path2)
{
return (substr($path1, 0, strlen($path2)) == $path2);
}
/**
* Domain match - check if domain1 domain-matches domain2
*
* A few extracts from RFC 2965:
* - A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com
* would be rejected, because H is y.x and contains a dot.
*
* - A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com
* would be accepted.
*
* - A Set-Cookie2 with Domain=.com or Domain=.com., will always be
* rejected, because there is no embedded dot.
*
* - A Set-Cookie2 from request-host example for Domain=.local will
* be accepted, because the effective host name for the request-
* host is example.local, and example.local domain-matches .local.
*
* I'm ignoring the first point for now (must check to see how other browsers handle
* this rule for Set-Cookie headers)
*
* @param string $domain1
* @param string $domain2
* @return bool
* @access private
*/
function _domain_match($domain1, $domain2)
{
$domain1 = strtolower($domain1);
$domain2 = strtolower($domain2);
while (strpos($domain1, '.') !== false) {
if ($domain1 == $domain2) return true;
$domain1 = $this->_reduce_domain($domain1);
continue;
}
return false;
}
}
?>

View File

@ -0,0 +1,779 @@
<?php
/**
* Humble HTTP Agent
*
* This class is designed to take advantage of parallel HTTP requests
* offered by PHP's PECL HTTP extension or the curl_multi_* functions.
* For environments which do not have these options, it reverts to standard sequential
* requests (using file_get_contents())
*
* @version 1.1
* @date 2012-08-20
* @see http://php.net/HttpRequestPool
* @author Keyvan Minoukadeh
* @copyright 2011-2012 Keyvan Minoukadeh
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
*/
class HumbleHttpAgent
{
const METHOD_REQUEST_POOL = 1;
const METHOD_CURL_MULTI = 2;
const METHOD_FILE_GET_CONTENTS = 4;
//const UA_BROWSER = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1';
const UA_BROWSER = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.92 Safari/535.2';
const UA_PHP = 'PHP/5.2';
const REF_GOOGLE = 'http://www.google.co.uk/url?sa=t&source=web&cd=1';
protected $requests = array();
protected $redirectQueue = array();
protected $requestOptions;
protected $maxParallelRequests = 5;
protected $cache = null; //TODO
protected $httpContext;
protected $minimiseMemoryUse = false; //TODO
protected $method;
protected $cookieJar;
public $debug = false;
public $debugVerbose = false;
public $rewriteHashbangFragment = true; // see http://code.google.com/web/ajaxcrawling/docs/specification.html
public $maxRedirects = 5;
public $userAgentMap = array();
public $rewriteUrls = array();
public $userAgentDefault;
public $referer;
//public $userAgent = 'Mozilla/5.0';
// Prevent certain file/mime types
// HTTP responses which match these content types will
// be returned without body.
public $headerOnlyTypes = array();
// URLs ending with one of these extensions will
// prompt Humble HTTP Agent to send a HEAD request first
// to see if returned content type matches $headerOnlyTypes.
public $headerOnlyClues = array('pdf','mp3','zip','exe','gif','gzip','gz','jpeg','jpg','mpg','mpeg','png','ppt','mov');
// AJAX triggers to search for.
// for AJAX sites, e.g. Blogger with its dynamic views templates.
public $ajaxTriggers = array("<meta name='fragment' content='!'",'<meta name="fragment" content="!"',"<meta content='!' name='fragment'",'<meta content="!" name="fragment"');
//TODO: set max file size
//TODO: normalise headers
function __construct($requestOptions=null, $method=null) {
$this->userAgentDefault = self::UA_BROWSER;
$this->referer = self::REF_GOOGLE;
// set the request method
if (in_array($method, array(1,2,4))) {
$this->method = $method;
} else {
if (class_exists('HttpRequestPool')) {
$this->method = self::METHOD_REQUEST_POOL;
} elseif (function_exists('curl_multi_init')) {
$this->method = self::METHOD_CURL_MULTI;
} else {
$this->method = self::METHOD_FILE_GET_CONTENTS;
}
}
if ($this->method == self::METHOD_CURL_MULTI) {
require_once(dirname(__FILE__).'/RollingCurl.php');
}
// create cookie jar
$this->cookieJar = new CookieJar();
// set request options (redirect must be 0)
$this->requestOptions = array(
'timeout' => 15,
'redirect' => 0 // we handle redirects manually so we can rewrite the new hashbang URLs that are creeping up over the web
// TODO: test onprogress?
);
if (is_array($requestOptions)) {
$this->requestOptions = array_merge($this->requestOptions, $requestOptions);
}
$this->httpContext = array(
'http' => array(
'ignore_errors' => true,
'timeout' => $this->requestOptions['timeout'],
'max_redirects' => $this->requestOptions['redirect'],
'header' => "Accept: */*\r\n"
)
);
}
protected function debug($msg) {
if ($this->debug) {
$mem = round(memory_get_usage()/1024, 2);
$memPeak = round(memory_get_peak_usage()/1024, 2);
echo '* ',$msg;
if ($this->debugVerbose) echo ' - mem used: ',$mem," (peak: $memPeak)";
echo "\n";
ob_flush();
flush();
}
}
protected function getUserAgent($url, $asArray=false) {
$host = @parse_url($url, PHP_URL_HOST);
if (strtolower(substr($host, 0, 4)) == 'www.') {
$host = substr($host, 4);
}
if ($host) {
$try = array($host);
$split = explode('.', $host);
if (count($split) > 1) {
array_shift($split);
$try[] = '.'.implode('.', $split);
}
foreach ($try as $h) {
if (isset($this->userAgentMap[$h])) {
$ua = $this->userAgentMap[$h];
break;
}
}
}
if (!isset($ua)) $ua = $this->userAgentDefault;
if ($asArray) {
return array('User-Agent' => $ua);
} else {
return 'User-Agent: '.$ua;
}
}
public function rewriteHashbangFragment($url) {
// return $url if there's no '#!'
if (strpos($url, '#!') === false) return $url;
// split $url and rewrite
// TODO: is SimplePie_IRI included?
$iri = new SimplePie_IRI($url);
$fragment = substr($iri->fragment, 1); // strip '!'
$iri->fragment = null;
if (isset($iri->query)) {
parse_str($iri->query, $query);
} else {
$query = array();
}
$query['_escaped_fragment_'] = (string)$fragment;
$iri->query = str_replace('%2F', '/', http_build_query($query)); // needed for some sites
return $iri->get_iri();
}
public function getUglyURL($url, $html) {
if ($html == '') return false;
$found = false;
foreach ($this->ajaxTriggers as $string) {
if (stripos($html, $string)) {
$found = true;
break;
}
}
if (!$found) return false;
$iri = new SimplePie_IRI($url);
if (isset($iri->query)) {
parse_str($iri->query, $query);
} else {
$query = array();
}
$query['_escaped_fragment_'] = '';
$iri->query = str_replace('%2F', '/', http_build_query($query)); // needed for some sites
return $iri->get_iri();
}
public function removeFragment($url) {
$pos = strpos($url, '#');
if ($pos === false) {
return $url;
} else {
return substr($url, 0, $pos);
}
}
public function rewriteUrls($url) {
foreach ($this->rewriteUrls as $find => $action) {
if (strpos($url, $find) !== false) {
if (is_array($action)) {
return strtr($url, $action);
}
}
}
return $url;
}
public function enableDebug($bool=true) {
$this->debug = (bool)$bool;
}
public function minimiseMemoryUse($bool = true) {
$this->minimiseMemoryUse = $bool;
}
public function setMaxParallelRequests($max) {
$this->maxParallelRequests = $max;
}
public function validateUrl($url) {
$url = filter_var($url, FILTER_SANITIZE_URL);
$test = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
// deal with bug http://bugs.php.net/51192 (present in PHP 5.2.13 and PHP 5.3.2)
if ($test === false) {
$test = filter_var(strtr($url, '-', '_'), FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
}
if ($test !== false && $test !== null && preg_match('!^https?://!', $url)) {
return $url;
} else {
return false;
}
}
public function fetchAll(array $urls) {
$this->fetchAllOnce($urls, $isRedirect=false);
$redirects = 0;
while (!empty($this->redirectQueue) && ++$redirects <= $this->maxRedirects) {
$this->debug("Following redirects #$redirects...");
$this->fetchAllOnce($this->redirectQueue, $isRedirect=true);
}
}
// fetch all URLs without following redirects
public function fetchAllOnce(array $urls, $isRedirect=false) {
if (!$isRedirect) $urls = array_unique($urls);
if (empty($urls)) return;
//////////////////////////////////////////////////////
// parallel (HttpRequestPool)
if ($this->method == self::METHOD_REQUEST_POOL) {
$this->debug('Starting parallel fetch (HttpRequestPool)');
try {
while (count($urls) > 0) {
$this->debug('Processing set of '.min($this->maxParallelRequests, count($urls)));
$subset = array_splice($urls, 0, $this->maxParallelRequests);
$pool = new HttpRequestPool();
foreach ($subset as $orig => $url) {
if (!$isRedirect) $orig = $url;
unset($this->redirectQueue[$orig]);
$this->debug("...$url");
if (!$isRedirect && isset($this->requests[$url])) {
$this->debug("......in memory");
/*
} elseif ($this->isCached($url)) {
$this->debug("......is cached");
if (!$this->minimiseMemoryUse) {
$this->requests[$url] = $this->getCached($url);
}
*/
} else {
$this->debug("......adding to pool");
$req_url = $this->rewriteUrls($url);
$req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
$req_url = $this->removeFragment($req_url);
if (!empty($this->headerOnlyTypes) && !isset($this->requests[$orig]['wrongGuess']) && $this->possibleUnsupportedType($req_url)) {
$_meth = HttpRequest::METH_HEAD;
} else {
$_meth = HttpRequest::METH_GET;
unset($this->requests[$orig]['wrongGuess']);
}
$httpRequest = new HttpRequest($req_url, $_meth, $this->requestOptions);
// send cookies, if we have any
if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
$this->debug("......sending cookies: $cookies");
$httpRequest->addHeaders(array('Cookie' => $cookies));
}
//$httpRequest->addHeaders(array('User-Agent' => $this->userAgent));
$httpRequest->addHeaders($this->getUserAgent($req_url, true));
// add referer for picky sites
$httpRequest->addheaders(array('Referer' => $this->referer));
$this->requests[$orig] = array('headers'=>null, 'body'=>null, 'httpRequest'=>$httpRequest);
$this->requests[$orig]['original_url'] = $orig;
$pool->attach($httpRequest);
}
}
// did we get anything into the pool?
if (count($pool) > 0) {
$this->debug('Sending request...');
try {
$pool->send();
} catch (HttpRequestPoolException $e) {
// do nothing
}
$this->debug('Received responses');
foreach($subset as $orig => $url) {
if (!$isRedirect) $orig = $url;
$request = $this->requests[$orig]['httpRequest'];
//$this->requests[$orig]['headers'] = $this->headersToString($request->getResponseHeader());
// getResponseHeader() doesn't return status line, so, for consistency...
$this->requests[$orig]['headers'] = substr($request->getRawResponseMessage(), 0, $request->getResponseInfo('header_size'));
// check content type
// TODO: use getResponseHeader('content-type') or getResponseInfo()
if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
$this->requests[$orig]['body'] = '';
$_header_only_type = true;
$this->debug('Header only type returned');
} else {
$this->requests[$orig]['body'] = $request->getResponseBody();
$_header_only_type = false;
}
$this->requests[$orig]['effective_url'] = $request->getResponseInfo('effective_url');
$this->requests[$orig]['status_code'] = $status_code = $request->getResponseCode();
// is redirect?
if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && $request->getResponseHeader('location')) {
$redirectURL = $request->getResponseHeader('location');
if (!preg_match('!^https?://!i', $redirectURL)) {
$redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
}
if ($this->validateURL($redirectURL)) {
$this->debug('Redirect detected. Valid URL: '.$redirectURL);
// store any cookies
$cookies = $request->getResponseHeader('set-cookie');
if ($cookies && !is_array($cookies)) $cookies = array($cookies);
if ($cookies) $this->cookieJar->storeCookies($url, $cookies);
$this->redirectQueue[$orig] = $redirectURL;
} else {
$this->debug('Redirect detected. Invalid URL: '.$redirectURL);
}
} elseif (!$_header_only_type && $request->getMethod() === HttpRequest::METH_HEAD) {
// the response content-type did not match our 'header only' types,
// but we'd issues a HEAD request because we assumed it would. So
// let's queue a proper GET request for this item...
$this->debug('Wrong guess at content-type, queing GET request');
$this->requests[$orig]['wrongGuess'] = true;
$this->redirectQueue[$orig] = $this->requests[$orig]['effective_url'];
} elseif (strpos($this->requests[$orig]['effective_url'], '_escaped_fragment_') === false) {
// check for <meta name='fragment' content='!'/>
// for AJAX sites, e.g. Blogger with its dynamic views templates.
// Based on Google's spec: https://developers.google.com/webmasters/ajax-crawling/docs/specification
if (isset($this->requests[$orig]['body'])) {
$redirectURL = $this->getUglyURL($this->requests[$orig]['effective_url'], substr($this->requests[$orig]['body'], 0, 4000));
if ($redirectURL) {
$this->debug('AJAX trigger (meta name="fragment" content="!") found. Queueing '.$redirectURL);
$this->redirectQueue[$orig] = $redirectURL;
}
}
}
//die($url.' -multi- '.$request->getResponseInfo('effective_url'));
$pool->detach($request);
unset($this->requests[$orig]['httpRequest'], $request);
/*
if ($this->minimiseMemoryUse) {
if ($this->cache($url)) {
unset($this->requests[$url]);
}
}
*/
}
}
}
} catch (HttpException $e) {
$this->debug($e);
return false;
}
}
//////////////////////////////////////////////////////////
// parallel (curl_multi_*)
elseif ($this->method == self::METHOD_CURL_MULTI) {
$this->debug('Starting parallel fetch (curl_multi_*)');
while (count($urls) > 0) {
$this->debug('Processing set of '.min($this->maxParallelRequests, count($urls)));
$subset = array_splice($urls, 0, $this->maxParallelRequests);
$pool = new RollingCurl(array($this, 'handleCurlResponse'));
$pool->window_size = count($subset);
foreach ($subset as $orig => $url) {
if (!$isRedirect) $orig = $url;
unset($this->redirectQueue[$orig]);
$this->debug("...$url");
if (!$isRedirect && isset($this->requests[$url])) {
$this->debug("......in memory");
/*
} elseif ($this->isCached($url)) {
$this->debug("......is cached");
if (!$this->minimiseMemoryUse) {
$this->requests[$url] = $this->getCached($url);
}
*/
} else {
$this->debug("......adding to pool");
$req_url = $this->rewriteUrls($url);
$req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
$req_url = $this->removeFragment($req_url);
if (!empty($this->headerOnlyTypes) && !isset($this->requests[$orig]['wrongGuess']) && $this->possibleUnsupportedType($req_url)) {
$_meth = 'HEAD';
} else {
$_meth = 'GET';
unset($this->requests[$orig]['wrongGuess']);
}
$headers = array();
//$headers[] = 'User-Agent: '.$this->userAgent;
$headers[] = $this->getUserAgent($req_url);
// add referer for picky sites
$headers[] = 'Referer: '.$this->referer;
// send cookies, if we have any
if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
$this->debug("......sending cookies: $cookies");
$headers[] = 'Cookie: '.$cookies;
}
$httpRequest = new RollingCurlRequest($req_url, $_meth, null, $headers, array(
CURLOPT_CONNECTTIMEOUT => $this->requestOptions['timeout'],
CURLOPT_TIMEOUT => $this->requestOptions['timeout']
));
$httpRequest->set_original_url($orig);
$this->requests[$orig] = array('headers'=>null, 'body'=>null, 'httpRequest'=>$httpRequest);
$this->requests[$orig]['original_url'] = $orig; // TODO: is this needed anymore?
$pool->add($httpRequest);
}
}
// did we get anything into the pool?
if (count($pool) > 0) {
$this->debug('Sending request...');
$pool->execute(); // this will call handleCurlResponse() and populate $this->requests[$orig]
$this->debug('Received responses');
foreach($subset as $orig => $url) {
if (!$isRedirect) $orig = $url;
// $this->requests[$orig]['headers']
// $this->requests[$orig]['body']
// $this->requests[$orig]['effective_url']
// check content type
if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
$this->requests[$orig]['body'] = '';
$_header_only_type = true;
$this->debug('Header only type returned');
} else {
$_header_only_type = false;
}
$status_code = $this->requests[$orig]['status_code'];
if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && isset($this->requests[$orig]['location'])) {
$redirectURL = $this->requests[$orig]['location'];
if (!preg_match('!^https?://!i', $redirectURL)) {
$redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
}
if ($this->validateURL($redirectURL)) {
$this->debug('Redirect detected. Valid URL: '.$redirectURL);
// store any cookies
$cookies = $this->cookieJar->extractCookies($this->requests[$orig]['headers']);
if (!empty($cookies)) $this->cookieJar->storeCookies($url, $cookies);
$this->redirectQueue[$orig] = $redirectURL;
} else {
$this->debug('Redirect detected. Invalid URL: '.$redirectURL);
}
} elseif (!$_header_only_type && $this->requests[$orig]['method'] == 'HEAD') {
// the response content-type did not match our 'header only' types,
// but we'd issues a HEAD request because we assumed it would. So
// let's queue a proper GET request for this item...
$this->debug('Wrong guess at content-type, queing GET request');
$this->requests[$orig]['wrongGuess'] = true;
$this->redirectQueue[$orig] = $this->requests[$orig]['effective_url'];
} elseif (strpos($this->requests[$orig]['effective_url'], '_escaped_fragment_') === false) {
// check for <meta name='fragment' content='!'/>
// for AJAX sites, e.g. Blogger with its dynamic views templates.
// Based on Google's spec: https://developers.google.com/webmasters/ajax-crawling/docs/specification
if (isset($this->requests[$orig]['body'])) {
$redirectURL = $this->getUglyURL($this->requests[$orig]['effective_url'], substr($this->requests[$orig]['body'], 0, 4000));
if ($redirectURL) {
$this->debug('AJAX trigger (meta name="fragment" content="!") found. Queueing '.$redirectURL);
$this->redirectQueue[$orig] = $redirectURL;
}
}
}
// die($url.' -multi- '.$request->getResponseInfo('effective_url'));
unset($this->requests[$orig]['httpRequest'], $this->requests[$orig]['method']);
}
}
}
}
//////////////////////////////////////////////////////
// sequential (file_get_contents)
else {
$this->debug('Starting sequential fetch (file_get_contents)');
$this->debug('Processing set of '.count($urls));
foreach ($urls as $orig => $url) {
if (!$isRedirect) $orig = $url;
unset($this->redirectQueue[$orig]);
$this->debug("...$url");
if (!$isRedirect && isset($this->requests[$url])) {
$this->debug("......in memory");
/*
} elseif ($this->isCached($url)) {
$this->debug("......is cached");
if (!$this->minimiseMemoryUse) {
$this->requests[$url] = $this->getCached($url);
}
*/
} else {
$this->debug("Sending request for $url");
$this->requests[$orig]['original_url'] = $orig;
$req_url = $this->rewriteUrls($url);
$req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
$req_url = $this->removeFragment($req_url);
// send cookies, if we have any
$httpContext = $this->httpContext;
$httpContext['http']['header'] .= $this->getUserAgent($req_url)."\r\n";
// add referer for picky sites
$httpContext['http']['header'] .= 'Referer: '.$this->referer."\r\n";
if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
$this->debug("......sending cookies: $cookies");
$httpContext['http']['header'] .= 'Cookie: '.$cookies."\r\n";
}
if (false !== ($html = @file_get_contents($req_url, false, stream_context_create($httpContext)))) {
$this->debug('Received response');
// get status code
if (!isset($http_response_header[0]) || !preg_match('!^HTTP/\d+\.\d+\s+(\d+)!', trim($http_response_header[0]), $match)) {
$this->debug('Error: no status code found');
// TODO: handle error - no status code
} else {
$this->requests[$orig]['headers'] = $this->headersToString($http_response_header, false);
// check content type
if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
$this->requests[$orig]['body'] = '';
} else {
$this->requests[$orig]['body'] = $html;
}
$this->requests[$orig]['effective_url'] = $req_url;
$this->requests[$orig]['status_code'] = $status_code = (int)$match[1];
unset($match);
// handle redirect
if (preg_match('/^Location:(.*?)$/mi', $this->requests[$orig]['headers'], $match)) {
$this->requests[$orig]['location'] = trim($match[1]);
}
if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && isset($this->requests[$orig]['location'])) {
$redirectURL = $this->requests[$orig]['location'];
if (!preg_match('!^https?://!i', $redirectURL)) {
$redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
}
if ($this->validateURL($redirectURL)) {
$this->debug('Redirect detected. Valid URL: '.$redirectURL);
// store any cookies
$cookies = $this->cookieJar->extractCookies($this->requests[$orig]['headers']);
if (!empty($cookies)) $this->cookieJar->storeCookies($url, $cookies);
$this->redirectQueue[$orig] = $redirectURL;
} else {
$this->debug('Redirect detected. Invalid URL: '.$redirectURL);
}
} elseif (strpos($this->requests[$orig]['effective_url'], '_escaped_fragment_') === false) {
// check for <meta name='fragment' content='!'/>
// for AJAX sites, e.g. Blogger with its dynamic views templates.
// Based on Google's spec: https://developers.google.com/webmasters/ajax-crawling/docs/specification
if (isset($this->requests[$orig]['body'])) {
$redirectURL = $this->getUglyURL($this->requests[$orig]['effective_url'], substr($this->requests[$orig]['body'], 0, 4000));
if ($redirectURL) {
$this->debug('AJAX trigger (meta name="fragment" content="!") found. Queueing '.$redirectURL);
$this->redirectQueue[$orig] = $redirectURL;
}
}
}
}
} else {
$this->debug('Error retrieving URL');
//print_r($req_url);
//print_r($http_response_header);
//print_r($html);
// TODO: handle error - failed to retrieve URL
}
}
}
}
}
public function handleCurlResponse($response, $info, $request) {
$orig = $request->url_original;
$this->requests[$orig]['headers'] = substr($response, 0, $info['header_size']);
$this->requests[$orig]['body'] = substr($response, $info['header_size']);
$this->requests[$orig]['method'] = $request->method;
$this->requests[$orig]['effective_url'] = $info['url'];
$this->requests[$orig]['status_code'] = (int)$info['http_code'];
if (preg_match('/^Location:(.*?)$/mi', $this->requests[$orig]['headers'], $match)) {
$this->requests[$orig]['location'] = trim($match[1]);
}
}
protected function headersToString(array $headers, $associative=true) {
if (!$associative) {
return implode("\n", $headers);
} else {
$str = '';
foreach ($headers as $key => $val) {
if (is_array($val)) {
foreach ($val as $v) $str .= "$key: $v\n";
} else {
$str .= "$key: $val\n";
}
}
return rtrim($str);
}
}
public function get($url, $remove=false, $gzdecode=true) {
$url = "$url";
if (isset($this->requests[$url]) && isset($this->requests[$url]['body'])) {
$this->debug("URL already fetched - in memory ($url, effective: {$this->requests[$url]['effective_url']})");
$response = $this->requests[$url];
/*
} elseif ($this->isCached($url)) {
$this->debug("URL already fetched - in disk cache ($url)");
$response = $this->getCached($url);
$this->requests[$url] = $response;
*/
} else {
$this->debug("Fetching URL ($url)");
$this->fetchAll(array($url));
if (isset($this->requests[$url]) && isset($this->requests[$url]['body'])) {
$response = $this->requests[$url];
} else {
$this->debug("Request failed");
$response = false;
}
}
/*
if ($this->minimiseMemoryUse && $response) {
$this->cache($url);
unset($this->requests[$url]);
}
*/
if ($remove && $response) unset($this->requests[$url]);
if ($gzdecode && stripos($response['headers'], 'Content-Encoding: gzip')) {
if ($html = gzdecode($response['body'])) {
$response['body'] = $html;
}
}
return $response;
}
public function parallelSupport() {
return class_exists('HttpRequestPool') || function_exists('curl_multi_init');
}
private function headerOnlyType($headers) {
if (preg_match('!^Content-Type:\s*(([a-z-]+)/([^;\r\n ]+))!im', $headers, $match)) {
// look for full mime type (e.g. image/jpeg) or just type (e.g. image)
$match[1] = strtolower(trim($match[1]));
$match[2] = strtolower(trim($match[2]));
foreach (array($match[1], $match[2]) as $mime) {
if (in_array($mime, $this->headerOnlyTypes)) return true;
}
}
return false;
}
private function possibleUnsupportedType($url) {
$path = @parse_url($url, PHP_URL_PATH);
if ($path && strpos($path, '.') !== false) {
$ext = strtolower(trim(pathinfo($path, PATHINFO_EXTENSION)));
return in_array($ext, $this->headerOnlyClues);
}
return false;
}
}
// gzdecode from http://www.php.net/manual/en/function.gzdecode.php#82930
if (!function_exists('gzdecode')) {
function gzdecode($data,&$filename='',&$error='',$maxlength=null)
{
$len = strlen($data);
if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
$error = "Not in GZIP format.";
return null; // Not GZIP format (See RFC 1952)
}
$method = ord(substr($data,2,1)); // Compression method
$flags = ord(substr($data,3,1)); // Flags
if ($flags & 31 != $flags) {
$error = "Reserved bits not allowed.";
return null;
}
// NOTE: $mtime may be negative (PHP integer limitations)
$mtime = unpack("V", substr($data,4,4));
$mtime = $mtime[1];
$xfl = substr($data,8,1);
$os = substr($data,8,1);
$headerlen = 10;
$extralen = 0;
$extra = "";
if ($flags & 4) {
// 2-byte length prefixed EXTRA data in header
if ($len - $headerlen - 2 < 8) {
return false; // invalid
}
$extralen = unpack("v",substr($data,8,2));
$extralen = $extralen[1];
if ($len - $headerlen - 2 - $extralen < 8) {
return false; // invalid
}
$extra = substr($data,10,$extralen);
$headerlen += 2 + $extralen;
}
$filenamelen = 0;
$filename = "";
if ($flags & 8) {
// C-style string
if ($len - $headerlen - 1 < 8) {
return false; // invalid
}
$filenamelen = strpos(substr($data,$headerlen),chr(0));
if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
return false; // invalid
}
$filename = substr($data,$headerlen,$filenamelen);
$headerlen += $filenamelen + 1;
}
$commentlen = 0;
$comment = "";
if ($flags & 16) {
// C-style string COMMENT data in header
if ($len - $headerlen - 1 < 8) {
return false; // invalid
}
$commentlen = strpos(substr($data,$headerlen),chr(0));
if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
return false; // Invalid header format
}
$comment = substr($data,$headerlen,$commentlen);
$headerlen += $commentlen + 1;
}
$headercrc = "";
if ($flags & 2) {
// 2-bytes (lowest order) of CRC32 on header present
if ($len - $headerlen - 2 < 8) {
return false; // invalid
}
$calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
$headercrc = unpack("v", substr($data,$headerlen,2));
$headercrc = $headercrc[1];
if ($headercrc != $calccrc) {
$error = "Header checksum failed.";
return false; // Bad header CRC
}
$headerlen += 2;
}
// GZIP FOOTER
$datacrc = unpack("V",substr($data,-8,4));
$datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF);
$isize = unpack("V",substr($data,-4));
$isize = $isize[1];
// decompression:
$bodylen = $len-$headerlen-8;
if ($bodylen < 1) {
// IMPLEMENTATION BUG!
return null;
}
$body = substr($data,$headerlen,$bodylen);
$data = "";
if ($bodylen > 0) {
switch ($method) {
case 8:
// Currently the only supported compression method:
$data = gzinflate($body,$maxlength);
break;
default:
$error = "Unknown compression method.";
return false;
}
} // zero-byte body content is allowed
// Verifiy CRC32
$crc = sprintf("%u",crc32($data));
$crcOK = $crc == $datacrc;
$lenOK = $isize == strlen($data);
if (!$lenOK || !$crcOK) {
$error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.');
return false;
}
return $data;
}
}
?>

View File

@ -0,0 +1,402 @@
<?php
/*
Authored by Josh Fraser (www.joshfraser.com)
Released under Apache License 2.0
Maintained by Alexander Makarov, http://rmcreative.ru/
Modified by Keyvan Minoukadeh for the Five Filters project: http://fivefilters.org
*/
/**
* Class that represent a single curl request
*/
class RollingCurlRequest {
public $url = false;
public $url_original = false; // used for tracking redirects
public $method = 'GET';
public $post_data = null;
public $headers = null;
public $options = null;
/**
* @param string $url
* @param string $method
* @param $post_data
* @param $headers
* @param $options
* @return void
*/
function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
$this->url = $url;
$this->url_original = $url;
$this->method = $method;
$this->post_data = $post_data;
$this->headers = $headers;
$this->options = $options;
}
/**
* @param string $url
* @return void
*/
public function set_original_url($url) {
$this->url_original = $url;
}
/**
* @return void
*/
public function __destruct() {
unset($this->url, $this->url_original, $this->method, $this->post_data, $this->headers, $this->options);
}
}
/**
* RollingCurl custom exception
*/
class RollingCurlException extends Exception {
}
/**
* Class that holds a rolling queue of curl requests.
*
* @throws RollingCurlException
*/
class RollingCurl implements Countable {
/**
* @var int
*
* Window size is the max number of simultaneous connections allowed.
*
* REMEMBER TO RESPECT THE SERVERS:
* Sending too many requests at one time can easily be perceived
* as a DOS attack. Increase this window_size if you are making requests
* to multiple servers or have permission from the receving server admins.
*/
private $window_size = 5;
/**
* @var float
*
* Timeout is the timeout used for curl_multi_select.
*/
private $timeout = 10;
/**
* @var string|array
*
* Callback function to be applied to each result.
*/
private $callback;
/**
* @var array
*
* Set your base options that you want to be used with EVERY request.
*/
protected $options = array(
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_CONNECTTIMEOUT => 30,
CURLOPT_TIMEOUT => 30
);
/**
* @var array
*/
private $headers = array();
/**
* @var Request[]
*
* The request queue
*/
private $requests = array();
/**
* @var RequestMap[]
*
* Maps handles to request indexes
*/
private $requestMap = array();
/**
* @param $callback
* Callback function to be applied to each result.
*
* Can be specified as 'my_callback_function'
* or array($object, 'my_callback_method').
*
* Function should take three parameters: $response, $info, $request.
* $response is response body, $info is additional curl info.
* $request is the original request
*
* @return void
*/
function __construct($callback = null) {
$this->callback = $callback;
}
/**
* @param string $name
* @return mixed
*/
public function __get($name) {
return (isset($this->{$name})) ? $this->{$name} : null;
}
/**
* @param string $name
* @param mixed $value
* @return bool
*/
public function __set($name, $value) {
// append the base options & headers
if ($name == "options" || $name == "headers") {
$this->{$name} = $value + $this->{$name};
} else {
$this->{$name} = $value;
}
return true;
}
/**
* Count number of requests added (Countable interface)
*
* @return int
*/
public function count() {
return count($this->requests);
}
/**
* Add a request to the request queue
*
* @param Request $request
* @return bool
*/
public function add($request) {
$this->requests[] = $request;
return true;
}
/**
* Create new Request and add it to the request queue
*
* @param string $url
* @param string $method
* @param $post_data
* @param $headers
* @param $options
* @return bool
*/
public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
$this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
return true;
}
/**
* Perform GET request
*
* @param string $url
* @param $headers
* @param $options
* @return bool
*/
public function get($url, $headers = null, $options = null) {
return $this->request($url, "GET", null, $headers, $options);
}
/**
* Perform POST request
*
* @param string $url
* @param $post_data
* @param $headers
* @param $options
* @return bool
*/
public function post($url, $post_data = null, $headers = null, $options = null) {
return $this->request($url, "POST", $post_data, $headers, $options);
}
/**
* Execute processing
*
* @param int $window_size Max number of simultaneous connections
* @return string|bool
*/
public function execute($window_size = null) {
// rolling curl window must always be greater than 1
if (sizeof($this->requests) == 1) {
return $this->single_curl();
} else {
// start the rolling curl. window_size is the max number of simultaneous connections
return $this->rolling_curl($window_size);
}
}
/**
* Performs a single curl request
*
* @access private
* @return string
*/
private function single_curl() {
$ch = curl_init();
$request = array_shift($this->requests);
$options = $this->get_options($request);
curl_setopt_array($ch, $options);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
// it's not neccesary to set a callback for one-off requests
if ($this->callback) {
$callback = $this->callback;
if (is_callable($this->callback)) {
call_user_func($callback, $output, $info, $request);
}
}
else
return $output;
return true;
}
/**
* Performs multiple curl requests
*
* @access private
* @throws RollingCurlException
* @param int $window_size Max number of simultaneous connections
* @return bool
*/
private function rolling_curl($window_size = null) {
if ($window_size)
$this->window_size = $window_size;
// make sure the rolling window isn't greater than the # of urls
if (sizeof($this->requests) < $this->window_size)
$this->window_size = sizeof($this->requests);
if ($this->window_size < 2) {
throw new RollingCurlException("Window size must be greater than 1");
}
$master = curl_multi_init();
// start the first batch of requests
for ($i = 0; $i < $this->window_size; $i++) {
$ch = curl_init();
$options = $this->get_options($this->requests[$i]);
curl_setopt_array($ch, $options);
curl_multi_add_handle($master, $ch);
// Add to our request Maps
$key = (string) $ch;
$this->requestMap[$key] = $i;
}
do {
while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
if ($execrun != CURLM_OK)
break;
// a request was just completed -- find out which one
while ($done = curl_multi_info_read($master)) {
// get the info and content returned on the request
$info = curl_getinfo($done['handle']);
$output = curl_multi_getcontent($done['handle']);
// send the return values to the callback function.
$callback = $this->callback;
if (is_callable($callback)) {
$key = (string) $done['handle'];
$request = $this->requests[$this->requestMap[$key]];
unset($this->requestMap[$key]);
call_user_func($callback, $output, $info, $request);
}
// start a new request (it's important to do this before removing the old one)
if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
$ch = curl_init();
$options = $this->get_options($this->requests[$i]);
curl_setopt_array($ch, $options);
curl_multi_add_handle($master, $ch);
// Add to our request Maps
$key = (string) $ch;
$this->requestMap[$key] = $i;
$i++;
}
// remove the curl handle that just completed
curl_multi_remove_handle($master, $done['handle']);
}
// Block for data in / output; error handling is done by curl_multi_exec
//if ($running) curl_multi_select($master, $this->timeout);
// removing timeout as it causes problems on Windows with PHP 5.3.5 and Curl 7.20.0
if ($running) curl_multi_select($master);
} while ($running);
curl_multi_close($master);
return true;
}
/**
* Helper function to set up a new request by setting the appropriate options
*
* @access private
* @param Request $request
* @return array
*/
private function get_options($request) {
// options for this entire curl object
$options = $this->__get('options');
// We're managing reirects in PHP - allows us to intervene and rewrite/block URLs
// before the next request goes out.
$options[CURLOPT_FOLLOWLOCATION] = 0;
$options[CURLOPT_MAXREDIRS] = 0;
//if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
// $options[CURLOPT_FOLLOWLOCATION] = 1;
// $options[CURLOPT_MAXREDIRS] = 5;
//}
$headers = $this->__get('headers');
// append custom headers for this specific request
if ($request->headers) {
$headers = $headers + $request->headers;
}
// append custom options for this specific request
if ($request->options) {
$options = $request->options + $options;
}
// set the request URL
$options[CURLOPT_URL] = $request->url;
if ($headers) {
$options[CURLOPT_HTTPHEADER] = $headers;
}
// return response headers
$options[CURLOPT_HEADER] = 1;
// send HEAD request?
if ($request->method == 'HEAD') {
$options[CURLOPT_NOBODY] = 1;
}
return $options;
}
/**
* @return void
*/
public function __destruct() {
unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* Humble HTTP Agent extension for SimplePie_File
*
* This class is designed to extend and override SimplePie_File
* in order to prevent duplicate HTTP requests being sent out.
* The idea is to initialise an instance of Humble HTTP Agent
* and attach it, to a static class variable, of this class.
* SimplePie will then automatically initialise this class
*
* @date 2011-02-28
*/
class SimplePie_HumbleHttpAgent extends SimplePie_File
{
protected static $agent;
var $url;
var $useragent;
var $success = true;
var $headers = array();
var $body;
var $status_code;
var $redirects = 0;
var $error;
var $method = SIMPLEPIE_FILE_SOURCE_NONE;
public static function set_agent(HumbleHttpAgent $agent) {
self::$agent = $agent;
}
public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
if (class_exists('idna_convert'))
{
$idn = new idna_convert();
$parsed = SimplePie_Misc::parse_url($url);
$url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
}
$this->url = $url;
$this->useragent = $useragent;
if (preg_match('/^http(s)?:\/\//i', $url))
{
if (!is_array($headers))
{
$headers = array();
}
$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
$headers2 = array();
foreach ($headers as $key => $value) {
$headers2[] = "$key: $value";
}
//TODO: allow for HTTP headers
// curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
$response = self::$agent->get($url);
if ($response === false || !isset($response['status_code'])) {
$this->error = 'failed to fetch URL';
$this->success = false;
} else {
// The extra lines at the end are there to satisfy SimplePie's HTTP parser.
// The class expects a full HTTP message, whereas we're giving it only
// headers - the new lines indicate the start of the body.
$parser = new SimplePie_HTTP_Parser($response['headers']."\r\n\r\n");
if ($parser->parse()) {
$this->headers = $parser->headers;
//$this->body = $parser->body;
$this->body = $response['body'];
$this->status_code = $parser->status_code;
}
}
}
else
{
$this->error = 'invalid URL';
$this->success = false;
}
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,354 @@
<?php
/**
* This class represents a text sample to be parsed.
*
* @category Text
* @package Text_LanguageDetect
* @author Nicholas Pisarro
* @copyright 2006
* @license BSD
* @version CVS: $Id: Parser.php,v 1.5 2006/03/11 05:45:05 taak Exp $
* @link http://pear.php.net/package/Text_LanguageDetect/
* @link http://langdetect.blogspot.com/
*/
/**
* This class represents a text sample to be parsed.
*
* This separates the analysis of a text sample from the primary LanguageDetect
* class. After a new profile has been built, the data can be retrieved using
* the accessor functions.
*
* This class is intended to be used by the Text_LanguageDetect class, not
* end-users.
*
* @category Text
* @package Text_LanguageDetect
* @author Nicholas Pisarro
* @copyright 2006
* @license BSD
* @version release: 0.2.3
*/
class Text_LanguageDetect_Parser extends Text_LanguageDetect
{
/**
* the piece of text being parsed
*
* @access private
* @var string
*/
var $_string;
/**
* stores the trigram frequencies of the sample
*
* @access private
* @var string
*/
var $_trigrams = array();
/**
* stores the trigram ranks of the sample
*
* @access private
* @var array
*/
var $_trigram_ranks = array();
/**
* stores the unicode blocks of the sample
*
* @access private
* @var array
*/
var $_unicode_blocks = array();
/**
* Whether the parser should compile the unicode ranges
*
* @access private
* @var bool
*/
var $_compile_unicode = false;
/**
* Whether the parser should compile trigrams
*
* @access private
* @var bool
*/
var $_compile_trigram = false;
/**
* Whether the trigram parser should pad the beginning of the string
*
* @access private
* @var bool
*/
var $_trigram_pad_start = false;
/**
* Whether the unicode parser should skip non-alphabetical ascii chars
*
* @access private
* @var bool
*/
var $_unicode_skip_symbols = true;
/**
* Constructor
*
* @access private
* @param string $string string to be parsed
*/
function Text_LanguageDetect_Parser($string, $db=null, $unicode_db=null) {
if (isset($db)) $this->_db_filename = $db;
if (isset($unicode_db)) $this->_unicode_db_filename = $unicode_db;
$this->_string = $string;
}
/**
* Returns true if a string is suitable for parsing
*
* @static
* @access public
* @param string $str input string to test
* @return bool true if acceptable, false if not
*/
function validateString($str) {
if (!empty($str) && strlen($str) > 3 && preg_match('/\S/', $str)) {
return true;
} else {
return false;
}
}
/**
* turn on/off trigram counting
*
* @access public
* @param bool $bool true for on, false for off
*/
function prepareTrigram($bool = true)
{
$this->_compile_trigram = $bool;
}
/**
* turn on/off unicode block counting
*
* @access public
* @param bool $bool true for on, false for off
*/
function prepareUnicode($bool = true)
{
$this->_compile_unicode = $bool;
}
/**
* turn on/off padding the beginning of the sample string
*
* @access public
* @param bool $bool true for on, false for off
*/
function setPadStart($bool = true)
{
$this->_trigram_pad_start = $bool;
}
/**
* Should the unicode block counter skip non-alphabetical ascii chars?
*
* @access public
* @param bool $bool true for on, false for off
*/
function setUnicodeSkipSymbols($bool = true)
{
$this->_unicode_skip_symbols = $bool;
}
/**
* Returns the trigram ranks for the text sample
*
* @access public
* @return array trigram ranks in the text sample
*/
function &getTrigramRanks()
{
return $this->_trigram_ranks;
}
/**
* Return the trigram freqency table
*
* only used in testing to make sure the parser is working
*
* @access public
* @return array trigram freqencies in the text sample
*/
function &getTrigramFreqs()
{
return $this->_trigram;
}
/**
* returns the array of unicode blocks
*
* @access public
* @return array unicode blocks in the text sample
*/
function &getUnicodeBlocks()
{
return $this->_unicode_blocks;
}
/**
* Executes the parsing operation
*
* Be sure to call the set*() functions to set options and the
* prepare*() functions first to tell it what kind of data to compute
*
* Afterwards the get*() functions can be used to access the compiled
* information.
*
* @access public
*/
function analyze()
{
$len = strlen($this->_string);
$byte_counter = 0;
// unicode startup
if ($this->_compile_unicode) {
$blocks =& $this->_read_unicode_block_db();
$block_count = count($blocks);
$skipped_count = 0;
$unicode_chars = array();
}
// trigram startup
if ($this->_compile_trigram) {
// initialize them as blank so the parser will skip the first two
// (since it skips trigrams with more than 2 contiguous spaces)
$a = ' ';
$b = ' ';
// kludge
// if it finds a valid trigram to start and the start pad option is
// off, then set a variable that will be used to reduce this
// trigram after parsing has finished
if (!$this->_trigram_pad_start) {
$a = $this->_next_char($this->_string, $byte_counter, true);
if ($a != ' ') {
$b = $this->_next_char($this->_string, $byte_counter, true);
$dropone = " $a$b";
}
$byte_counter = 0;
$a = ' ';
$b = ' ';
}
}
while ($byte_counter < $len) {
$char = $this->_next_char($this->_string, $byte_counter, true);
// language trigram detection
if ($this->_compile_trigram) {
if (!($b == ' ' && ($a == ' ' || $char == ' '))) {
if (!isset($this->_trigram[$a . $b . $char])) {
$this->_trigram[$a . $b . $char] = 1;
} else {
$this->_trigram[$a . $b . $char]++;
}
}
$a = $b;
$b = $char;
}
// unicode block detection
if ($this->_compile_unicode) {
if ($this->_unicode_skip_symbols
&& strlen($char) == 1
&& ($char < 'A' || $char > 'z'
|| ($char > 'Z' && $char < 'a'))
&& $char != "'") { // does not skip the apostrophe
// since it's included in the language
// models
$skipped_count++;
continue;
}
// build an array of all the characters
if (isset($unicode_chars[$char])) {
$unicode_chars[$char]++;
} else {
$unicode_chars[$char] = 1;
}
}
// todo: add byte detection here
}
// unicode cleanup
if ($this->_compile_unicode) {
foreach ($unicode_chars as $utf8_char => $count) {
$search_result = $this->_unicode_block_name(
$this->_utf8char2unicode($utf8_char), $blocks, $block_count);
if ($search_result != -1) {
$block_name = $search_result[2];
} else {
$block_name = '[Malformatted]';
}
if (isset($this->_unicode_blocks[$block_name])) {
$this->_unicode_blocks[$block_name] += $count;
} else {
$this->_unicode_blocks[$block_name] = $count;
}
}
}
// trigram cleanup
if ($this->_compile_trigram) {
// pad the end
if ($b != ' ') {
if (!isset($this->_trigram["$a$b "])) {
$this->_trigram["$a$b "] = 1;
} else {
$this->_trigram["$a$b "]++;
}
}
// perl compatibility; Language::Guess does not pad the beginning
// kludge
if (isset($dropone)) {
if ($this->_trigram[$dropone] == 1) {
unset($this->_trigram[$dropone]);
} else {
$this->_trigram[$dropone]--;
}
}
if (!empty($this->_trigram)) {
$this->_trigram_ranks = $this->_arr_rank($this->_trigram);
} else {
$this->_trigram_ranks = array();
}
}
}
}
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
?>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,110 @@
<?php
/**
* JavaScript-like HTML DOM Element
*
* This class extends PHP's DOMElement to allow
* users to get and set the innerHTML property of
* HTML elements in the same way it's done in
* JavaScript.
*
* Example usage:
* @code
* require_once 'JSLikeHTMLElement.php';
* header('Content-Type: text/plain');
* $doc = new DOMDocument();
* $doc->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
* $doc->loadHTML('<div><p>Para 1</p><p>Para 2</p></div>');
* $elem = $doc->getElementsByTagName('div')->item(0);
*
* // print innerHTML
* echo $elem->innerHTML; // prints '<p>Para 1</p><p>Para 2</p>'
* echo "\n\n";
*
* // set innerHTML
* $elem->innerHTML = '<a href="http://fivefilters.org">FiveFilters.org</a>';
* echo $elem->innerHTML; // prints '<a href="http://fivefilters.org">FiveFilters.org</a>'
* echo "\n\n";
*
* // print document (with our changes)
* echo $doc->saveXML();
* @endcode
*
* @author Keyvan Minoukadeh - http://www.keyvan.net - keyvan@keyvan.net
* @see http://fivefilters.org (the project this was written for)
*/
class JSLikeHTMLElement extends DOMElement
{
/**
* Used for setting innerHTML like it's done in JavaScript:
* @code
* $div->innerHTML = '<h2>Chapter 2</h2><p>The story begins...</p>';
* @endcode
*/
public function __set($name, $value) {
if ($name == 'innerHTML') {
// first, empty the element
for ($x=$this->childNodes->length-1; $x>=0; $x--) {
$this->removeChild($this->childNodes->item($x));
}
// $value holds our new inner HTML
if ($value != '') {
$f = $this->ownerDocument->createDocumentFragment();
// appendXML() expects well-formed markup (XHTML)
$result = @$f->appendXML($value); // @ to suppress PHP warnings
if ($result) {
if ($f->hasChildNodes()) $this->appendChild($f);
} else {
// $value is probably ill-formed
$f = new DOMDocument();
$value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8');
// Using <htmlfragment> will generate a warning, but so will bad HTML
// (and by this point, bad HTML is what we've got).
// We use it (and suppress the warning) because an HTML fragment will
// be wrapped around <html><body> tags which we don't really want to keep.
// Note: despite the warning, if loadHTML succeeds it will return true.
$result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>');
if ($result) {
$import = $f->getElementsByTagName('htmlfragment')->item(0);
foreach ($import->childNodes as $child) {
$importedNode = $this->ownerDocument->importNode($child, true);
$this->appendChild($importedNode);
}
} else {
// oh well, we tried, we really did. :(
// this element is now empty
}
}
}
} else {
$trace = debug_backtrace();
trigger_error('Undefined property via __set(): '.$name.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
}
}
/**
* Used for getting innerHTML like it's done in JavaScript:
* @code
* $string = $div->innerHTML;
* @endcode
*/
public function __get($name)
{
if ($name == 'innerHTML') {
$inner = '';
foreach ($this->childNodes as $child) {
$inner .= $this->ownerDocument->saveXML($child);
}
return $inner;
}
$trace = debug_backtrace();
trigger_error('Undefined property via __get(): '.$name.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
return null;
}
public function __toString()
{
return '['.$this->tagName.']';
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of the SimplePie Team nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,86 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2009, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
// autoloader
spl_autoload_register(array(new SimplePie_Autoloader(), 'autoload'));
if (!class_exists('SimplePie'))
{
trigger_error('Autoloader not registered properly', E_USER_ERROR);
}
/**
* Autoloader class
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Autoloader
{
/**
* Constructor
*/
public function __construct()
{
$this->path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library';
}
/**
* Autoloader
*
* @param string $class The name of the class to attempt to load.
*/
public function autoload($class)
{
// Only load the class if it starts with "SimplePie"
if (strpos($class, 'SimplePie') !== 0)
{
return;
}
$filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
include $filename;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,157 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Manages all author-related data
*
* Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()}
*
* This class can be overloaded with {@see SimplePie::set_author_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Author
{
/**
* Author's name
*
* @var string
* @see get_name()
*/
var $name;
/**
* Author's link
*
* @var string
* @see get_link()
*/
var $link;
/**
* Author's email address
*
* @var string
* @see get_email()
*/
var $email;
/**
* Constructor, used to input the data
*
* @param string $name
* @param string $link
* @param string $email
*/
public function __construct($name = null, $link = null, $email = null)
{
$this->name = $name;
$this->link = $link;
$this->email = $email;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Author's name
*
* @return string|null
*/
public function get_name()
{
if ($this->name !== null)
{
return $this->name;
}
else
{
return null;
}
}
/**
* Author's link
*
* @return string|null
*/
public function get_link()
{
if ($this->link !== null)
{
return $this->link;
}
else
{
return null;
}
}
/**
* Author's email address
*
* @return string|null
*/
public function get_email()
{
if ($this->email !== null)
{
return $this->email;
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,133 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Used to create cache objects
*
* This class can be overloaded with {@see SimplePie::set_cache_class()},
* although the preferred way is to create your own handler
* via {@see register()}
*
* @package SimplePie
* @subpackage Caching
*/
class SimplePie_Cache
{
/**
* Cache handler classes
*
* These receive 3 parameters to their constructor, as documented in
* {@see register()}
* @var array
*/
protected static $handlers = array(
'mysql' => 'SimplePie_Cache_MySQL',
'memcache' => 'SimplePie_Cache_Memcache',
);
/**
* Don't call the constructor. Please.
*/
private function __construct() { }
/**
* Create a new SimplePie_Cache object
*
* @param string $location URL location (scheme is used to determine handler)
* @param string $filename Unique identifier for cache object
* @param string $extension 'spi' or 'spc'
* @return SimplePie_Cache_Base Type of object depends on scheme of `$location`
*/
public static function get_handler($location, $filename, $extension)
{
$type = explode(':', $location, 2);
$type = $type[0];
if (!empty(self::$handlers[$type]))
{
$class = self::$handlers[$type];
return new $class($location, $filename, $extension);
}
return new SimplePie_Cache_File($location, $filename, $extension);
}
/**
* Create a new SimplePie_Cache object
*
* @deprecated Use {@see get_handler} instead
*/
public function create($location, $filename, $extension)
{
trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED);
return self::get_handler($location, $filename, $extension);
}
/**
* Register a handler
*
* @param string $type DSN type to register for
* @param string $class Name of handler class. Must implement SimplePie_Cache_Base
*/
public static function register($type, $class)
{
self::$handlers[$type] = $class;
}
/**
* Parse a URL into an array
*
* @param string $url
* @return array
*/
public static function parse_URL($url)
{
$params = parse_url($url);
$params['extras'] = array();
if (isset($params['query']))
{
parse_str($params['query'], $params['extras']);
}
return $params;
}
}

View File

@ -0,0 +1,114 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Base for cache objects
*
* Classes to be used with {@see SimplePie_Cache::register()} are expected
* to implement this interface.
*
* @package SimplePie
* @subpackage Caching
*/
interface SimplePie_Cache_Base
{
/**
* Feed cache type
*
* @var string
*/
const TYPE_FEED = 'spc';
/**
* Image cache type
*
* @var string
*/
const TYPE_IMAGE = 'spi';
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type);
/**
* Save data to the cache
*
* @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data);
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load();
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime();
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch();
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink();
}

View File

@ -0,0 +1,137 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Base class for database-based caches
*
* @package SimplePie
* @subpackage Caching
*/
abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base
{
/**
* Helper for database conversion
*
* Converts a given {@see SimplePie} object into data to be stored
*
* @param SimplePie $data
* @return array First item is the serialized data for storage, second item is the unique ID for this item
*/
protected static function prepare_simplepie_object_for_cache($data)
{
$items = $data->get_items();
$items_by_id = array();
if (!empty($items))
{
foreach ($items as $item)
{
$items_by_id[$item->get_id()] = $item;
}
if (count($items_by_id) !== count($items))
{
$items_by_id = array();
foreach ($items as $item)
{
$items_by_id[$item->get_id(true)] = $item;
}
}
if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
{
$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
}
elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
{
$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
}
elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
{
$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
}
elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
{
$channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
}
else
{
$channel = null;
}
if ($channel !== null)
{
if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
{
unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
}
if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
{
unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
}
if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
{
unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
}
if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
{
unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
}
if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
{
unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
}
}
if (isset($data->data['items']))
{
unset($data->data['items']);
}
if (isset($data->data['ordered_items']))
{
unset($data->data['ordered_items']);
}
}
return array(serialize($data->data), $items_by_id);
}
}

View File

@ -0,0 +1,173 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Caches data to the filesystem
*
* @package SimplePie
* @subpackage Caching
*/
class SimplePie_Cache_File implements SimplePie_Cache_Base
{
/**
* Location string
*
* @see SimplePie::$cache_location
* @var string
*/
protected $location;
/**
* Filename
*
* @var string
*/
protected $filename;
/**
* File extension
*
* @var string
*/
protected $extension;
/**
* File path
*
* @var string
*/
protected $name;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->location = $location;
$this->filename = $name;
$this->extension = $type;
$this->name = "$this->location/$this->filename.$this->extension";
}
/**
* Save data to the cache
*
* @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
{
if ($data instanceof SimplePie)
{
$data = $data->data;
}
$data = serialize($data);
return (bool) file_put_contents($this->name, $data);
}
return false;
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
if (file_exists($this->name) && is_readable($this->name))
{
return unserialize(file_get_contents($this->name));
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
if (file_exists($this->name))
{
return filemtime($this->name);
}
return false;
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
if (file_exists($this->name))
{
return touch($this->name);
}
return false;
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
if (file_exists($this->name))
{
return unlink($this->name);
}
return false;
}
}

View File

@ -0,0 +1,183 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Caches data to memcache
*
* Registered for URLs with the "memcache" protocol
*
* For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will
* connect to memcache on `localhost` on port 11211. All tables will be
* prefixed with `sp_` and data will expire after 3600 seconds
*
* @package SimplePie
* @subpackage Caching
* @uses Memcache
*/
class SimplePie_Cache_Memcache implements SimplePie_Cache_Base
{
/**
* Memcache instance
*
* @var Memcache
*/
protected $cache;
/**
* Options
*
* @var array
*/
protected $options;
/**
* Cache name
*
* @var string
*/
protected $name;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->options = array(
'host' => '127.0.0.1',
'port' => 11211,
'extras' => array(
'timeout' => 3600, // one hour
'prefix' => 'simplepie_',
),
);
$parsed = SimplePie_Cache::parse_URL($location);
$this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host'];
$this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port'];
$this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']);
$this->name = $this->options['extras']['prefix'] . md5("$name:$type");
$this->cache = new Memcache();
$this->cache->addServer($this->options['host'], (int) $this->options['port']);
}
/**
* Save data to the cache
*
* @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if ($data instanceof SimplePie)
{
$data = $data->data;
}
return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
$data = $this->cache->get($this->name);
if ($data !== false)
{
return unserialize($data);
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
$data = $this->cache->get($this->name);
if ($data !== false)
{
// essentially ignore the mtime because Memcache expires on it's own
return time();
}
return false;
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
$data = $this->cache->get($this->name);
if ($data !== false)
{
return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration);
}
return false;
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
return $this->cache->delete($this->name, 0);
}
}

View File

@ -0,0 +1,438 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Caches data to a MySQL database
*
* Registered for URLs with the "mysql" protocol
*
* For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
* connect to the `mydb` database on `localhost` on port 3306, with the user
* `root` and the password `password`. All tables will be prefixed with `sp_`
*
* @package SimplePie
* @subpackage Caching
*/
class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
{
/**
* PDO instance
*
* @var PDO
*/
protected $mysql;
/**
* Options
*
* @var array
*/
protected $options;
/**
* Cache ID
*
* @var string
*/
protected $id;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->options = array(
'user' => null,
'pass' => null,
'host' => '127.0.0.1',
'port' => '3306',
'path' => '',
'extras' => array(
'prefix' => '',
),
);
$this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
// Path is prefixed with a "/"
$this->options['dbname'] = substr($this->options['path'], 1);
try
{
$this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
}
catch (PDOException $e)
{
$this->mysql = null;
return;
}
$this->id = $name . $type;
if (!$query = $this->mysql->query('SHOW TABLES'))
{
$this->mysql = null;
return;
}
$db = array();
while ($row = $query->fetchColumn())
{
$db[] = $row;
}
if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
{
$query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
if ($query === false)
{
$this->mysql = null;
}
}
if (!in_array($this->options['extras']['prefix'] . 'items', $db))
{
$query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
if ($query === false)
{
$this->mysql = null;
}
}
}
/**
* Save data to the cache
*
* @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if ($this->mysql === null)
{
return false;
}
if ($data instanceof SimplePie)
{
$data = clone $data;
$prepared = self::prepare_simplepie_object_for_cache($data);
$query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
$query->bindValue(':feed', $this->id);
if ($query->execute())
{
if ($query->fetchColumn() > 0)
{
$items = count($prepared[1]);
if ($items)
{
$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
$query = $this->mysql->prepare($sql);
$query->bindValue(':items', $items);
}
else
{
$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
$query = $this->mysql->prepare($sql);
}
$query->bindValue(':data', $prepared[0]);
$query->bindValue(':time', time());
$query->bindValue(':feed', $this->id);
if (!$query->execute())
{
return false;
}
}
else
{
$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
$query->bindValue(':feed', $this->id);
$query->bindValue(':count', count($prepared[1]));
$query->bindValue(':data', $prepared[0]);
$query->bindValue(':time', time());
if (!$query->execute())
{
return false;
}
}
$ids = array_keys($prepared[1]);
if (!empty($ids))
{
foreach ($ids as $id)
{
$database_ids[] = $this->mysql->quote($id);
}
$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
$query->bindValue(':feed', $this->id);
if ($query->execute())
{
$existing_ids = array();
while ($row = $query->fetchColumn())
{
$existing_ids[] = $row;
}
$new_ids = array_diff($ids, $existing_ids);
foreach ($new_ids as $new_id)
{
if (!($date = $prepared[1][$new_id]->get_date('U')))
{
$date = time();
}
$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
$query->bindValue(':feed', $this->id);
$query->bindValue(':id', $new_id);
$query->bindValue(':data', serialize($prepared[1][$new_id]->data));
$query->bindValue(':date', $date);
if (!$query->execute())
{
return false;
}
}
return true;
}
}
else
{
return true;
}
}
}
else
{
$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
$query->bindValue(':feed', $this->id);
if ($query->execute())
{
if ($query->rowCount() > 0)
{
$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
$query->bindValue(':data', serialize($data));
$query->bindValue(':time', time());
$query->bindValue(':feed', $this->id);
if ($this->execute())
{
return true;
}
}
else
{
$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
$query->bindValue(':id', $this->id);
$query->bindValue(':data', serialize($data));
$query->bindValue(':time', time());
if ($query->execute())
{
return true;
}
}
}
}
return false;
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
if ($this->mysql === null)
{
return false;
}
$query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
$query->bindValue(':id', $this->id);
if ($query->execute() && ($row = $query->fetch()))
{
$data = unserialize($row[1]);
if (isset($this->options['items'][0]))
{
$items = (int) $this->options['items'][0];
}
else
{
$items = (int) $row[0];
}
if ($items !== 0)
{
if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
{
$feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
}
elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
{
$feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
}
elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
{
$feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
}
elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
{
$feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
}
else
{
$feed = null;
}
if ($feed !== null)
{
$sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
if ($items > 0)
{
$sql .= ' LIMIT ' . $items;
}
$query = $this->mysql->prepare($sql);
$query->bindValue(':feed', $this->id);
if ($query->execute())
{
while ($row = $query->fetchColumn())
{
$feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
}
}
else
{
return false;
}
}
}
return $data;
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
if ($this->mysql === null)
{
return false;
}
$query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
$query->bindValue(':id', $this->id);
if ($query->execute() && ($time = $query->fetchColumn()))
{
return $time;
}
else
{
return false;
}
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
if ($this->mysql === null)
{
return false;
}
$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
$query->bindValue(':time', time());
$query->bindValue(':id', $this->id);
if ($query->execute() && $query->rowCount() > 0)
{
return true;
}
else
{
return false;
}
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
if ($this->mysql === null)
{
return false;
}
$query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
$query->bindValue(':id', $this->id);
$query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
$query2->bindValue(':id', $this->id);
if ($query->execute() && $query2->execute())
{
return true;
}
else
{
return false;
}
}
}

View File

@ -0,0 +1,210 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Handles `<media:text>` captions as defined in Media RSS.
*
* Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()}
*
* This class can be overloaded with {@see SimplePie::set_caption_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Caption
{
/**
* Content type
*
* @var string
* @see get_type()
*/
var $type;
/**
* Language
*
* @var string
* @see get_language()
*/
var $lang;
/**
* Start time
*
* @var string
* @see get_starttime()
*/
var $startTime;
/**
* End time
*
* @var string
* @see get_endtime()
*/
var $endTime;
/**
* Caption text
*
* @var string
* @see get_text()
*/
var $text;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
{
$this->type = $type;
$this->lang = $lang;
$this->startTime = $startTime;
$this->endTime = $endTime;
$this->text = $text;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the end time
*
* @return string|null Time in the format 'hh:mm:ss.SSS'
*/
public function get_endtime()
{
if ($this->endTime !== null)
{
return $this->endTime;
}
else
{
return null;
}
}
/**
* Get the language
*
* @link http://tools.ietf.org/html/rfc3066
* @return string|null Language code as per RFC 3066
*/
public function get_language()
{
if ($this->lang !== null)
{
return $this->lang;
}
else
{
return null;
}
}
/**
* Get the start time
*
* @return string|null Time in the format 'hh:mm:ss.SSS'
*/
public function get_starttime()
{
if ($this->startTime !== null)
{
return $this->startTime;
}
else
{
return null;
}
}
/**
* Get the text of the caption
*
* @return string|null
*/
public function get_text()
{
if ($this->text !== null)
{
return $this->text;
}
else
{
return null;
}
}
/**
* Get the content type (not MIME type)
*
* @return string|null Either 'text' or 'html'
*/
public function get_type()
{
if ($this->type !== null)
{
return $this->type;
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,157 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Manages all category-related data
*
* Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()}
*
* This class can be overloaded with {@see SimplePie::set_category_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Category
{
/**
* Category identifier
*
* @var string
* @see get_term
*/
var $term;
/**
* Categorization scheme identifier
*
* @var string
* @see get_scheme()
*/
var $scheme;
/**
* Human readable label
*
* @var string
* @see get_label()
*/
var $label;
/**
* Constructor, used to input the data
*
* @param string $term
* @param string $scheme
* @param string $label
*/
public function __construct($term = null, $scheme = null, $label = null)
{
$this->term = $term;
$this->scheme = $scheme;
$this->label = $label;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the category identifier
*
* @return string|null
*/
public function get_term()
{
if ($this->term !== null)
{
return $this->term;
}
else
{
return null;
}
}
/**
* Get the categorization scheme identifier
*
* @return string|null
*/
public function get_scheme()
{
if ($this->scheme !== null)
{
return $this->scheme;
}
else
{
return null;
}
}
/**
* Get the human readable label
*
* @return string|null
*/
public function get_label()
{
if ($this->label !== null)
{
return $this->label;
}
else
{
return $this->get_term();
}
}
}

View File

@ -0,0 +1,332 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Content-type sniffing
*
* Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06
*
* This is used since we can't always trust Content-Type headers, and is based
* upon the HTML5 parsing rules.
*
*
* This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()}
*
* @package SimplePie
* @subpackage HTTP
*/
class SimplePie_Content_Type_Sniffer
{
/**
* File object
*
* @var SimplePie_File
*/
var $file;
/**
* Create an instance of the class with the input file
*
* @param SimplePie_Content_Type_Sniffer $file Input file
*/
public function __construct($file)
{
$this->file = $file;
}
/**
* Get the Content-Type of the specified file
*
* @return string Actual Content-Type
*/
public function get_type()
{
if (isset($this->file->headers['content-type']))
{
if (!isset($this->file->headers['content-encoding'])
&& ($this->file->headers['content-type'] === 'text/plain'
|| $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
|| $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
|| $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
{
return $this->text_or_binary();
}
if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
{
$official = substr($this->file->headers['content-type'], 0, $pos);
}
else
{
$official = $this->file->headers['content-type'];
}
$official = trim(strtolower($official));
if ($official === 'unknown/unknown'
|| $official === 'application/unknown')
{
return $this->unknown();
}
elseif (substr($official, -4) === '+xml'
|| $official === 'text/xml'
|| $official === 'application/xml')
{
return $official;
}
elseif (substr($official, 0, 6) === 'image/')
{
if ($return = $this->image())
{
return $return;
}
else
{
return $official;
}
}
elseif ($official === 'text/html')
{
return $this->feed_or_html();
}
else
{
return $official;
}
}
else
{
return $this->unknown();
}
}
/**
* Sniff text or binary
*
* @return string Actual Content-Type
*/
public function text_or_binary()
{
if (substr($this->file->body, 0, 2) === "\xFE\xFF"
|| substr($this->file->body, 0, 2) === "\xFF\xFE"
|| substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
|| substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
{
return 'text/plain';
}
elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
{
return 'application/octect-stream';
}
else
{
return 'text/plain';
}
}
/**
* Sniff unknown
*
* @return string Actual Content-Type
*/
public function unknown()
{
$ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
|| strtolower(substr($this->file->body, $ws, 5)) === '<html'
|| strtolower(substr($this->file->body, $ws, 7)) === '<script')
{
return 'text/html';
}
elseif (substr($this->file->body, 0, 5) === '%PDF-')
{
return 'application/pdf';
}
elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
{
return 'application/postscript';
}
elseif (substr($this->file->body, 0, 6) === 'GIF87a'
|| substr($this->file->body, 0, 6) === 'GIF89a')
{
return 'image/gif';
}
elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
{
return 'image/png';
}
elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
{
return 'image/jpeg';
}
elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
{
return 'image/bmp';
}
elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
{
return 'image/vnd.microsoft.icon';
}
else
{
return $this->text_or_binary();
}
}
/**
* Sniff images
*
* @return string Actual Content-Type
*/
public function image()
{
if (substr($this->file->body, 0, 6) === 'GIF87a'
|| substr($this->file->body, 0, 6) === 'GIF89a')
{
return 'image/gif';
}
elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
{
return 'image/png';
}
elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
{
return 'image/jpeg';
}
elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
{
return 'image/bmp';
}
elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
{
return 'image/vnd.microsoft.icon';
}
else
{
return false;
}
}
/**
* Sniff HTML
*
* @return string Actual Content-Type
*/
public function feed_or_html()
{
$len = strlen($this->file->body);
$pos = strspn($this->file->body, "\x09\x0A\x0D\x20");
while ($pos < $len)
{
switch ($this->file->body[$pos])
{
case "\x09":
case "\x0A":
case "\x0D":
case "\x20":
$pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
continue 2;
case '<':
$pos++;
break;
default:
return 'text/html';
}
if (substr($this->file->body, $pos, 3) === '!--')
{
$pos += 3;
if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
{
$pos += 3;
}
else
{
return 'text/html';
}
}
elseif (substr($this->file->body, $pos, 1) === '!')
{
if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
{
$pos++;
}
else
{
return 'text/html';
}
}
elseif (substr($this->file->body, $pos, 1) === '?')
{
if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
{
$pos += 2;
}
else
{
return 'text/html';
}
}
elseif (substr($this->file->body, $pos, 3) === 'rss'
|| substr($this->file->body, $pos, 7) === 'rdf:RDF')
{
return 'application/rss+xml';
}
elseif (substr($this->file->body, $pos, 4) === 'feed')
{
return 'application/atom+xml';
}
else
{
return 'text/html';
}
}
return 'text/html';
}
}

View File

@ -0,0 +1,130 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Manages `<media:copyright>` copyright tags as defined in Media RSS
*
* Used by {@see SimplePie_Enclosure::get_copyright()}
*
* This class can be overloaded with {@see SimplePie::set_copyright_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Copyright
{
/**
* Copyright URL
*
* @var string
* @see get_url()
*/
var $url;
/**
* Attribution
*
* @var string
* @see get_attribution()
*/
var $label;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($url = null, $label = null)
{
$this->url = $url;
$this->label = $label;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the copyright URL
*
* @return string|null URL to copyright information
*/
public function get_url()
{
if ($this->url !== null)
{
return $this->url;
}
else
{
return null;
}
}
/**
* Get the attribution text
*
* @return string|null
*/
public function get_attribution()
{
if ($this->label !== null)
{
return $this->label;
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2009, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* SimplePie class.
*
* Class for backward compatibility.
*
* @deprecated Use {@see SimplePie} directly
* @package SimplePie
* @subpackage API
*/
class SimplePie_Core extends SimplePie
{
}

View File

@ -0,0 +1,156 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Handles `<media:credit>` as defined in Media RSS
*
* Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()}
*
* This class can be overloaded with {@see SimplePie::set_credit_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Credit
{
/**
* Credited role
*
* @var string
* @see get_role()
*/
var $role;
/**
* Organizational scheme
*
* @var string
* @see get_scheme()
*/
var $scheme;
/**
* Credited name
*
* @var string
* @see get_name()
*/
var $name;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($role = null, $scheme = null, $name = null)
{
$this->role = $role;
$this->scheme = $scheme;
$this->name = $name;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the role of the person receiving credit
*
* @return string|null
*/
public function get_role()
{
if ($this->role !== null)
{
return $this->role;
}
else
{
return null;
}
}
/**
* Get the organizational scheme
*
* @return string|null
*/
public function get_scheme()
{
if ($this->scheme !== null)
{
return $this->scheme;
}
else
{
return null;
}
}
/**
* Get the credited person/entity's name
*
* @return string|null
*/
public function get_name()
{
if ($this->name !== null)
{
return $this->name;
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,617 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Decode HTML Entities
*
* This implements HTML5 as of revision 967 (2007-06-28)
*
* @deprecated Use DOMDocument instead!
* @package SimplePie
*/
class SimplePie_Decode_HTML_Entities
{
/**
* Data to be parsed
*
* @access private
* @var string
*/
var $data = '';
/**
* Currently consumed bytes
*
* @access private
* @var string
*/
var $consumed = '';
/**
* Position of the current byte being parsed
*
* @access private
* @var int
*/
var $position = 0;
/**
* Create an instance of the class with the input data
*
* @access public
* @param string $data Input data
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Parse the input data
*
* @access public
* @return string Output data
*/
public function parse()
{
while (($this->position = strpos($this->data, '&', $this->position)) !== false)
{
$this->consume();
$this->entity();
$this->consumed = '';
}
return $this->data;
}
/**
* Consume the next byte
*
* @access private
* @return mixed The next byte, or false, if there is no more data
*/
public function consume()
{
if (isset($this->data[$this->position]))
{
$this->consumed .= $this->data[$this->position];
return $this->data[$this->position++];
}
else
{
return false;
}
}
/**
* Consume a range of characters
*
* @access private
* @param string $chars Characters to consume
* @return mixed A series of characters that match the range, or false
*/
public function consume_range($chars)
{
if ($len = strspn($this->data, $chars, $this->position))
{
$data = substr($this->data, $this->position, $len);
$this->consumed .= $data;
$this->position += $len;
return $data;
}
else
{
return false;
}
}
/**
* Unconsume one byte
*
* @access private
*/
public function unconsume()
{
$this->consumed = substr($this->consumed, 0, -1);
$this->position--;
}
/**
* Decode an entity
*
* @access private
*/
public function entity()
{
switch ($this->consume())
{
case "\x09":
case "\x0A":
case "\x0B":
case "\x0B":
case "\x0C":
case "\x20":
case "\x3C":
case "\x26":
case false:
break;
case "\x23":
switch ($this->consume())
{
case "\x78":
case "\x58":
$range = '0123456789ABCDEFabcdef';
$hex = true;
break;
default:
$range = '0123456789';
$hex = false;
$this->unconsume();
break;
}
if ($codepoint = $this->consume_range($range))
{
static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8");
if ($hex)
{
$codepoint = hexdec($codepoint);
}
else
{
$codepoint = intval($codepoint);
}
if (isset($windows_1252_specials[$codepoint]))
{
$replacement = $windows_1252_specials[$codepoint];
}
else
{
$replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
}
if (!in_array($this->consume(), array(';', false), true))
{
$this->unconsume();
}
$consumed_length = strlen($this->consumed);
$this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
$this->position += strlen($replacement) - $consumed_length;
}
break;
default:
static $entities = array(
'Aacute' => "\xC3\x81",
'aacute' => "\xC3\xA1",
'Aacute;' => "\xC3\x81",
'aacute;' => "\xC3\xA1",
'Acirc' => "\xC3\x82",
'acirc' => "\xC3\xA2",
'Acirc;' => "\xC3\x82",
'acirc;' => "\xC3\xA2",
'acute' => "\xC2\xB4",
'acute;' => "\xC2\xB4",
'AElig' => "\xC3\x86",
'aelig' => "\xC3\xA6",
'AElig;' => "\xC3\x86",
'aelig;' => "\xC3\xA6",
'Agrave' => "\xC3\x80",
'agrave' => "\xC3\xA0",
'Agrave;' => "\xC3\x80",
'agrave;' => "\xC3\xA0",
'alefsym;' => "\xE2\x84\xB5",
'Alpha;' => "\xCE\x91",
'alpha;' => "\xCE\xB1",
'AMP' => "\x26",
'amp' => "\x26",
'AMP;' => "\x26",
'amp;' => "\x26",
'and;' => "\xE2\x88\xA7",
'ang;' => "\xE2\x88\xA0",
'apos;' => "\x27",
'Aring' => "\xC3\x85",
'aring' => "\xC3\xA5",
'Aring;' => "\xC3\x85",
'aring;' => "\xC3\xA5",
'asymp;' => "\xE2\x89\x88",
'Atilde' => "\xC3\x83",
'atilde' => "\xC3\xA3",
'Atilde;' => "\xC3\x83",
'atilde;' => "\xC3\xA3",
'Auml' => "\xC3\x84",
'auml' => "\xC3\xA4",
'Auml;' => "\xC3\x84",
'auml;' => "\xC3\xA4",
'bdquo;' => "\xE2\x80\x9E",
'Beta;' => "\xCE\x92",
'beta;' => "\xCE\xB2",
'brvbar' => "\xC2\xA6",
'brvbar;' => "\xC2\xA6",
'bull;' => "\xE2\x80\xA2",
'cap;' => "\xE2\x88\xA9",
'Ccedil' => "\xC3\x87",
'ccedil' => "\xC3\xA7",
'Ccedil;' => "\xC3\x87",
'ccedil;' => "\xC3\xA7",
'cedil' => "\xC2\xB8",
'cedil;' => "\xC2\xB8",
'cent' => "\xC2\xA2",
'cent;' => "\xC2\xA2",
'Chi;' => "\xCE\xA7",
'chi;' => "\xCF\x87",
'circ;' => "\xCB\x86",
'clubs;' => "\xE2\x99\xA3",
'cong;' => "\xE2\x89\x85",
'COPY' => "\xC2\xA9",
'copy' => "\xC2\xA9",
'COPY;' => "\xC2\xA9",
'copy;' => "\xC2\xA9",
'crarr;' => "\xE2\x86\xB5",
'cup;' => "\xE2\x88\xAA",
'curren' => "\xC2\xA4",
'curren;' => "\xC2\xA4",
'Dagger;' => "\xE2\x80\xA1",
'dagger;' => "\xE2\x80\xA0",
'dArr;' => "\xE2\x87\x93",
'darr;' => "\xE2\x86\x93",
'deg' => "\xC2\xB0",
'deg;' => "\xC2\xB0",
'Delta;' => "\xCE\x94",
'delta;' => "\xCE\xB4",
'diams;' => "\xE2\x99\xA6",
'divide' => "\xC3\xB7",
'divide;' => "\xC3\xB7",
'Eacute' => "\xC3\x89",
'eacute' => "\xC3\xA9",
'Eacute;' => "\xC3\x89",
'eacute;' => "\xC3\xA9",
'Ecirc' => "\xC3\x8A",
'ecirc' => "\xC3\xAA",
'Ecirc;' => "\xC3\x8A",
'ecirc;' => "\xC3\xAA",
'Egrave' => "\xC3\x88",
'egrave' => "\xC3\xA8",
'Egrave;' => "\xC3\x88",
'egrave;' => "\xC3\xA8",
'empty;' => "\xE2\x88\x85",
'emsp;' => "\xE2\x80\x83",
'ensp;' => "\xE2\x80\x82",
'Epsilon;' => "\xCE\x95",
'epsilon;' => "\xCE\xB5",
'equiv;' => "\xE2\x89\xA1",
'Eta;' => "\xCE\x97",
'eta;' => "\xCE\xB7",
'ETH' => "\xC3\x90",
'eth' => "\xC3\xB0",
'ETH;' => "\xC3\x90",
'eth;' => "\xC3\xB0",
'Euml' => "\xC3\x8B",
'euml' => "\xC3\xAB",
'Euml;' => "\xC3\x8B",
'euml;' => "\xC3\xAB",
'euro;' => "\xE2\x82\xAC",
'exist;' => "\xE2\x88\x83",
'fnof;' => "\xC6\x92",
'forall;' => "\xE2\x88\x80",
'frac12' => "\xC2\xBD",
'frac12;' => "\xC2\xBD",
'frac14' => "\xC2\xBC",
'frac14;' => "\xC2\xBC",
'frac34' => "\xC2\xBE",
'frac34;' => "\xC2\xBE",
'frasl;' => "\xE2\x81\x84",
'Gamma;' => "\xCE\x93",
'gamma;' => "\xCE\xB3",
'ge;' => "\xE2\x89\xA5",
'GT' => "\x3E",
'gt' => "\x3E",
'GT;' => "\x3E",
'gt;' => "\x3E",
'hArr;' => "\xE2\x87\x94",
'harr;' => "\xE2\x86\x94",
'hearts;' => "\xE2\x99\xA5",
'hellip;' => "\xE2\x80\xA6",
'Iacute' => "\xC3\x8D",
'iacute' => "\xC3\xAD",
'Iacute;' => "\xC3\x8D",
'iacute;' => "\xC3\xAD",
'Icirc' => "\xC3\x8E",
'icirc' => "\xC3\xAE",
'Icirc;' => "\xC3\x8E",
'icirc;' => "\xC3\xAE",
'iexcl' => "\xC2\xA1",
'iexcl;' => "\xC2\xA1",
'Igrave' => "\xC3\x8C",
'igrave' => "\xC3\xAC",
'Igrave;' => "\xC3\x8C",
'igrave;' => "\xC3\xAC",
'image;' => "\xE2\x84\x91",
'infin;' => "\xE2\x88\x9E",
'int;' => "\xE2\x88\xAB",
'Iota;' => "\xCE\x99",
'iota;' => "\xCE\xB9",
'iquest' => "\xC2\xBF",
'iquest;' => "\xC2\xBF",
'isin;' => "\xE2\x88\x88",
'Iuml' => "\xC3\x8F",
'iuml' => "\xC3\xAF",
'Iuml;' => "\xC3\x8F",
'iuml;' => "\xC3\xAF",
'Kappa;' => "\xCE\x9A",
'kappa;' => "\xCE\xBA",
'Lambda;' => "\xCE\x9B",
'lambda;' => "\xCE\xBB",
'lang;' => "\xE3\x80\x88",
'laquo' => "\xC2\xAB",
'laquo;' => "\xC2\xAB",
'lArr;' => "\xE2\x87\x90",
'larr;' => "\xE2\x86\x90",
'lceil;' => "\xE2\x8C\x88",
'ldquo;' => "\xE2\x80\x9C",
'le;' => "\xE2\x89\xA4",
'lfloor;' => "\xE2\x8C\x8A",
'lowast;' => "\xE2\x88\x97",
'loz;' => "\xE2\x97\x8A",
'lrm;' => "\xE2\x80\x8E",
'lsaquo;' => "\xE2\x80\xB9",
'lsquo;' => "\xE2\x80\x98",
'LT' => "\x3C",
'lt' => "\x3C",
'LT;' => "\x3C",
'lt;' => "\x3C",
'macr' => "\xC2\xAF",
'macr;' => "\xC2\xAF",
'mdash;' => "\xE2\x80\x94",
'micro' => "\xC2\xB5",
'micro;' => "\xC2\xB5",
'middot' => "\xC2\xB7",
'middot;' => "\xC2\xB7",
'minus;' => "\xE2\x88\x92",
'Mu;' => "\xCE\x9C",
'mu;' => "\xCE\xBC",
'nabla;' => "\xE2\x88\x87",
'nbsp' => "\xC2\xA0",
'nbsp;' => "\xC2\xA0",
'ndash;' => "\xE2\x80\x93",
'ne;' => "\xE2\x89\xA0",
'ni;' => "\xE2\x88\x8B",
'not' => "\xC2\xAC",
'not;' => "\xC2\xAC",
'notin;' => "\xE2\x88\x89",
'nsub;' => "\xE2\x8A\x84",
'Ntilde' => "\xC3\x91",
'ntilde' => "\xC3\xB1",
'Ntilde;' => "\xC3\x91",
'ntilde;' => "\xC3\xB1",
'Nu;' => "\xCE\x9D",
'nu;' => "\xCE\xBD",
'Oacute' => "\xC3\x93",
'oacute' => "\xC3\xB3",
'Oacute;' => "\xC3\x93",
'oacute;' => "\xC3\xB3",
'Ocirc' => "\xC3\x94",
'ocirc' => "\xC3\xB4",
'Ocirc;' => "\xC3\x94",
'ocirc;' => "\xC3\xB4",
'OElig;' => "\xC5\x92",
'oelig;' => "\xC5\x93",
'Ograve' => "\xC3\x92",
'ograve' => "\xC3\xB2",
'Ograve;' => "\xC3\x92",
'ograve;' => "\xC3\xB2",
'oline;' => "\xE2\x80\xBE",
'Omega;' => "\xCE\xA9",
'omega;' => "\xCF\x89",
'Omicron;' => "\xCE\x9F",
'omicron;' => "\xCE\xBF",
'oplus;' => "\xE2\x8A\x95",
'or;' => "\xE2\x88\xA8",
'ordf' => "\xC2\xAA",
'ordf;' => "\xC2\xAA",
'ordm' => "\xC2\xBA",
'ordm;' => "\xC2\xBA",
'Oslash' => "\xC3\x98",
'oslash' => "\xC3\xB8",
'Oslash;' => "\xC3\x98",
'oslash;' => "\xC3\xB8",
'Otilde' => "\xC3\x95",
'otilde' => "\xC3\xB5",
'Otilde;' => "\xC3\x95",
'otilde;' => "\xC3\xB5",
'otimes;' => "\xE2\x8A\x97",
'Ouml' => "\xC3\x96",
'ouml' => "\xC3\xB6",
'Ouml;' => "\xC3\x96",
'ouml;' => "\xC3\xB6",
'para' => "\xC2\xB6",
'para;' => "\xC2\xB6",
'part;' => "\xE2\x88\x82",
'permil;' => "\xE2\x80\xB0",
'perp;' => "\xE2\x8A\xA5",
'Phi;' => "\xCE\xA6",
'phi;' => "\xCF\x86",
'Pi;' => "\xCE\xA0",
'pi;' => "\xCF\x80",
'piv;' => "\xCF\x96",
'plusmn' => "\xC2\xB1",
'plusmn;' => "\xC2\xB1",
'pound' => "\xC2\xA3",
'pound;' => "\xC2\xA3",
'Prime;' => "\xE2\x80\xB3",
'prime;' => "\xE2\x80\xB2",
'prod;' => "\xE2\x88\x8F",
'prop;' => "\xE2\x88\x9D",
'Psi;' => "\xCE\xA8",
'psi;' => "\xCF\x88",
'QUOT' => "\x22",
'quot' => "\x22",
'QUOT;' => "\x22",
'quot;' => "\x22",
'radic;' => "\xE2\x88\x9A",
'rang;' => "\xE3\x80\x89",
'raquo' => "\xC2\xBB",
'raquo;' => "\xC2\xBB",
'rArr;' => "\xE2\x87\x92",
'rarr;' => "\xE2\x86\x92",
'rceil;' => "\xE2\x8C\x89",
'rdquo;' => "\xE2\x80\x9D",
'real;' => "\xE2\x84\x9C",
'REG' => "\xC2\xAE",
'reg' => "\xC2\xAE",
'REG;' => "\xC2\xAE",
'reg;' => "\xC2\xAE",
'rfloor;' => "\xE2\x8C\x8B",
'Rho;' => "\xCE\xA1",
'rho;' => "\xCF\x81",
'rlm;' => "\xE2\x80\x8F",
'rsaquo;' => "\xE2\x80\xBA",
'rsquo;' => "\xE2\x80\x99",
'sbquo;' => "\xE2\x80\x9A",
'Scaron;' => "\xC5\xA0",
'scaron;' => "\xC5\xA1",
'sdot;' => "\xE2\x8B\x85",
'sect' => "\xC2\xA7",
'sect;' => "\xC2\xA7",
'shy' => "\xC2\xAD",
'shy;' => "\xC2\xAD",
'Sigma;' => "\xCE\xA3",
'sigma;' => "\xCF\x83",
'sigmaf;' => "\xCF\x82",
'sim;' => "\xE2\x88\xBC",
'spades;' => "\xE2\x99\xA0",
'sub;' => "\xE2\x8A\x82",
'sube;' => "\xE2\x8A\x86",
'sum;' => "\xE2\x88\x91",
'sup;' => "\xE2\x8A\x83",
'sup1' => "\xC2\xB9",
'sup1;' => "\xC2\xB9",
'sup2' => "\xC2\xB2",
'sup2;' => "\xC2\xB2",
'sup3' => "\xC2\xB3",
'sup3;' => "\xC2\xB3",
'supe;' => "\xE2\x8A\x87",
'szlig' => "\xC3\x9F",
'szlig;' => "\xC3\x9F",
'Tau;' => "\xCE\xA4",
'tau;' => "\xCF\x84",
'there4;' => "\xE2\x88\xB4",
'Theta;' => "\xCE\x98",
'theta;' => "\xCE\xB8",
'thetasym;' => "\xCF\x91",
'thinsp;' => "\xE2\x80\x89",
'THORN' => "\xC3\x9E",
'thorn' => "\xC3\xBE",
'THORN;' => "\xC3\x9E",
'thorn;' => "\xC3\xBE",
'tilde;' => "\xCB\x9C",
'times' => "\xC3\x97",
'times;' => "\xC3\x97",
'TRADE;' => "\xE2\x84\xA2",
'trade;' => "\xE2\x84\xA2",
'Uacute' => "\xC3\x9A",
'uacute' => "\xC3\xBA",
'Uacute;' => "\xC3\x9A",
'uacute;' => "\xC3\xBA",
'uArr;' => "\xE2\x87\x91",
'uarr;' => "\xE2\x86\x91",
'Ucirc' => "\xC3\x9B",
'ucirc' => "\xC3\xBB",
'Ucirc;' => "\xC3\x9B",
'ucirc;' => "\xC3\xBB",
'Ugrave' => "\xC3\x99",
'ugrave' => "\xC3\xB9",
'Ugrave;' => "\xC3\x99",
'ugrave;' => "\xC3\xB9",
'uml' => "\xC2\xA8",
'uml;' => "\xC2\xA8",
'upsih;' => "\xCF\x92",
'Upsilon;' => "\xCE\xA5",
'upsilon;' => "\xCF\x85",
'Uuml' => "\xC3\x9C",
'uuml' => "\xC3\xBC",
'Uuml;' => "\xC3\x9C",
'uuml;' => "\xC3\xBC",
'weierp;' => "\xE2\x84\x98",
'Xi;' => "\xCE\x9E",
'xi;' => "\xCE\xBE",
'Yacute' => "\xC3\x9D",
'yacute' => "\xC3\xBD",
'Yacute;' => "\xC3\x9D",
'yacute;' => "\xC3\xBD",
'yen' => "\xC2\xA5",
'yen;' => "\xC2\xA5",
'yuml' => "\xC3\xBF",
'Yuml;' => "\xC5\xB8",
'yuml;' => "\xC3\xBF",
'Zeta;' => "\xCE\x96",
'zeta;' => "\xCE\xB6",
'zwj;' => "\xE2\x80\x8D",
'zwnj;' => "\xE2\x80\x8C"
);
for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
{
$consumed = substr($this->consumed, 1);
if (isset($entities[$consumed]))
{
$match = $consumed;
}
}
if ($match !== null)
{
$this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
$this->position += strlen($entities[$match]) - strlen($consumed) - 1;
}
break;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.4-dev
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* General SimplePie exception class
*
* @package SimplePie
*/
class SimplePie_Exception extends Exception
{
}

View File

@ -0,0 +1,292 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Used for fetching remote files and reading local files
*
* Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support
*
* This class can be overloaded with {@see SimplePie::set_file_class()}
*
* @package SimplePie
* @subpackage HTTP
* @todo Move to properly supporting RFC2616 (HTTP/1.1)
*/
class SimplePie_File
{
var $url;
var $useragent;
var $success = true;
var $headers = array();
var $body;
var $status_code;
var $redirects = 0;
var $error;
var $method = SIMPLEPIE_FILE_SOURCE_NONE;
public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
{
if (class_exists('idna_convert'))
{
$idn = new idna_convert();
$parsed = SimplePie_Misc::parse_url($url);
$url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
}
$this->url = $url;
$this->useragent = $useragent;
if (preg_match('/^http(s)?:\/\//i', $url))
{
if ($useragent === null)
{
$useragent = ini_get('user_agent');
$this->useragent = $useragent;
}
if (!is_array($headers))
{
$headers = array();
}
if (!$force_fsockopen && function_exists('curl_exec'))
{
$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
$fp = curl_init();
$headers2 = array();
foreach ($headers as $key => $value)
{
$headers2[] = "$key: $value";
}
if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
{
curl_setopt($fp, CURLOPT_ENCODING, '');
}
curl_setopt($fp, CURLOPT_URL, $url);
curl_setopt($fp, CURLOPT_HEADER, 1);
curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($fp, CURLOPT_REFERER, $url);
curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
{
curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
}
$this->headers = curl_exec($fp);
if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
{
curl_setopt($fp, CURLOPT_ENCODING, 'none');
$this->headers = curl_exec($fp);
}
if (curl_errno($fp))
{
$this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
$this->success = false;
}
else
{
$info = curl_getinfo($fp);
curl_close($fp);
$this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
$this->headers = array_pop($this->headers);
$parser = new SimplePie_HTTP_Parser($this->headers);
if ($parser->parse())
{
$this->headers = $parser->headers;
$this->body = $parser->body;
$this->status_code = $parser->status_code;
if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
{
$this->redirects++;
$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
}
}
}
}
else
{
$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
$url_parts = parse_url($url);
$socket_host = $url_parts['host'];
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
{
$socket_host = "ssl://$url_parts[host]";
$url_parts['port'] = 443;
}
if (!isset($url_parts['port']))
{
$url_parts['port'] = 80;
}
$fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
if (!$fp)
{
$this->error = 'fsockopen error: ' . $errstr;
$this->success = false;
}
else
{
stream_set_timeout($fp, $timeout);
if (isset($url_parts['path']))
{
if (isset($url_parts['query']))
{
$get = "$url_parts[path]?$url_parts[query]";
}
else
{
$get = $url_parts['path'];
}
}
else
{
$get = '/';
}
$out = "GET $get HTTP/1.1\r\n";
$out .= "Host: $url_parts[host]\r\n";
$out .= "User-Agent: $useragent\r\n";
if (extension_loaded('zlib'))
{
$out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
}
if (isset($url_parts['user']) && isset($url_parts['pass']))
{
$out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
}
foreach ($headers as $key => $value)
{
$out .= "$key: $value\r\n";
}
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
$info = stream_get_meta_data($fp);
$this->headers = '';
while (!$info['eof'] && !$info['timed_out'])
{
$this->headers .= fread($fp, 1160);
$info = stream_get_meta_data($fp);
}
if (!$info['timed_out'])
{
$parser = new SimplePie_HTTP_Parser($this->headers);
if ($parser->parse())
{
$this->headers = $parser->headers;
$this->body = $parser->body;
$this->status_code = $parser->status_code;
if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
{
$this->redirects++;
$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
}
if (isset($this->headers['content-encoding']))
{
// Hey, we act dumb elsewhere, so let's do that here too
switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
{
case 'gzip':
case 'x-gzip':
$decoder = new SimplePie_gzdecode($this->body);
if (!$decoder->parse())
{
$this->error = 'Unable to decode HTTP "gzip" stream';
$this->success = false;
}
else
{
$this->body = $decoder->data;
}
break;
case 'deflate':
if (($decompressed = gzinflate($this->body)) !== false)
{
$this->body = $decompressed;
}
else if (($decompressed = gzuncompress($this->body)) !== false)
{
$this->body = $decompressed;
}
else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false)
{
$this->body = $decompressed;
}
else
{
$this->error = 'Unable to decode HTTP "deflate" stream';
$this->success = false;
}
break;
default:
$this->error = 'Unknown content coding';
$this->success = false;
}
}
}
}
else
{
$this->error = 'fsocket timed out';
$this->success = false;
}
fclose($fp);
}
}
}
else
{
$this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
if (!$this->body = file_get_contents($url))
{
$this->error = 'file_get_contents could not read the file';
$this->success = false;
}
}
}
}

View File

@ -0,0 +1,500 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* HTTP Response Parser
*
* @package SimplePie
* @subpackage HTTP
*/
class SimplePie_HTTP_Parser
{
/**
* HTTP Version
*
* @var float
*/
public $http_version = 0.0;
/**
* Status code
*
* @var int
*/
public $status_code = 0;
/**
* Reason phrase
*
* @var string
*/
public $reason = '';
/**
* Key/value pairs of the headers
*
* @var array
*/
public $headers = array();
/**
* Body of the response
*
* @var string
*/
public $body = '';
/**
* Current state of the state machine
*
* @var string
*/
protected $state = 'http_version';
/**
* Input data
*
* @var string
*/
protected $data = '';
/**
* Input data length (to avoid calling strlen() everytime this is needed)
*
* @var int
*/
protected $data_length = 0;
/**
* Current position of the pointer
*
* @var int
*/
protected $position = 0;
/**
* Name of the hedaer currently being parsed
*
* @var string
*/
protected $name = '';
/**
* Value of the hedaer currently being parsed
*
* @var string
*/
protected $value = '';
/**
* Create an instance of the class with the input data
*
* @param string $data Input data
*/
public function __construct($data)
{
$this->data = $data;
$this->data_length = strlen($this->data);
}
/**
* Parse the input data
*
* @return bool true on success, false on failure
*/
public function parse()
{
while ($this->state && $this->state !== 'emit' && $this->has_data())
{
$state = $this->state;
$this->$state();
}
$this->data = '';
if ($this->state === 'emit' || $this->state === 'body')
{
return true;
}
else
{
$this->http_version = '';
$this->status_code = '';
$this->reason = '';
$this->headers = array();
$this->body = '';
return false;
}
}
/**
* Check whether there is data beyond the pointer
*
* @return bool true if there is further data, false if not
*/
protected function has_data()
{
return (bool) ($this->position < $this->data_length);
}
/**
* See if the next character is LWS
*
* @return bool true if the next character is LWS, false if not
*/
protected function is_linear_whitespace()
{
return (bool) ($this->data[$this->position] === "\x09"
|| $this->data[$this->position] === "\x20"
|| ($this->data[$this->position] === "\x0A"
&& isset($this->data[$this->position + 1])
&& ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
}
/**
* Parse the HTTP version
*/
protected function http_version()
{
if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
{
$len = strspn($this->data, '0123456789.', 5);
$this->http_version = substr($this->data, 5, $len);
$this->position += 5 + $len;
if (substr_count($this->http_version, '.') <= 1)
{
$this->http_version = (float) $this->http_version;
$this->position += strspn($this->data, "\x09\x20", $this->position);
$this->state = 'status';
}
else
{
$this->state = false;
}
}
else
{
$this->state = false;
}
}
/**
* Parse the status code
*/
protected function status()
{
if ($len = strspn($this->data, '0123456789', $this->position))
{
$this->status_code = (int) substr($this->data, $this->position, $len);
$this->position += $len;
$this->state = 'reason';
}
else
{
$this->state = false;
}
}
/**
* Parse the reason phrase
*/
protected function reason()
{
$len = strcspn($this->data, "\x0A", $this->position);
$this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
$this->position += $len + 1;
$this->state = 'new_line';
}
/**
* Deal with a new line, shifting data around as needed
*/
protected function new_line()
{
$this->value = trim($this->value, "\x0D\x20");
if ($this->name !== '' && $this->value !== '')
{
$this->name = strtolower($this->name);
// We should only use the last Content-Type header. c.f. issue #1
if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
{
$this->headers[$this->name] .= ', ' . $this->value;
}
else
{
$this->headers[$this->name] = $this->value;
}
}
$this->name = '';
$this->value = '';
if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
{
$this->position += 2;
$this->state = 'body';
}
elseif ($this->data[$this->position] === "\x0A")
{
$this->position++;
$this->state = 'body';
}
else
{
$this->state = 'name';
}
}
/**
* Parse a header name
*/
protected function name()
{
$len = strcspn($this->data, "\x0A:", $this->position);
if (isset($this->data[$this->position + $len]))
{
if ($this->data[$this->position + $len] === "\x0A")
{
$this->position += $len;
$this->state = 'new_line';
}
else
{
$this->name = substr($this->data, $this->position, $len);
$this->position += $len + 1;
$this->state = 'value';
}
}
else
{
$this->state = false;
}
}
/**
* Parse LWS, replacing consecutive LWS characters with a single space
*/
protected function linear_whitespace()
{
do
{
if (substr($this->data, $this->position, 2) === "\x0D\x0A")
{
$this->position += 2;
}
elseif ($this->data[$this->position] === "\x0A")
{
$this->position++;
}
$this->position += strspn($this->data, "\x09\x20", $this->position);
} while ($this->has_data() && $this->is_linear_whitespace());
$this->value .= "\x20";
}
/**
* See what state to move to while within non-quoted header values
*/
protected function value()
{
if ($this->is_linear_whitespace())
{
$this->linear_whitespace();
}
else
{
switch ($this->data[$this->position])
{
case '"':
// Workaround for ETags: we have to include the quotes as
// part of the tag.
if (strtolower($this->name) === 'etag')
{
$this->value .= '"';
$this->position++;
$this->state = 'value_char';
break;
}
$this->position++;
$this->state = 'quote';
break;
case "\x0A":
$this->position++;
$this->state = 'new_line';
break;
default:
$this->state = 'value_char';
break;
}
}
}
/**
* Parse a header value while outside quotes
*/
protected function value_char()
{
$len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
$this->value .= substr($this->data, $this->position, $len);
$this->position += $len;
$this->state = 'value';
}
/**
* See what state to move to while within quoted header values
*/
protected function quote()
{
if ($this->is_linear_whitespace())
{
$this->linear_whitespace();
}
else
{
switch ($this->data[$this->position])
{
case '"':
$this->position++;
$this->state = 'value';
break;
case "\x0A":
$this->position++;
$this->state = 'new_line';
break;
case '\\':
$this->position++;
$this->state = 'quote_escaped';
break;
default:
$this->state = 'quote_char';
break;
}
}
}
/**
* Parse a header value while within quotes
*/
protected function quote_char()
{
$len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
$this->value .= substr($this->data, $this->position, $len);
$this->position += $len;
$this->state = 'value';
}
/**
* Parse an escaped character within quotes
*/
protected function quote_escaped()
{
$this->value .= $this->data[$this->position];
$this->position++;
$this->state = 'quote';
}
/**
* Parse the body
*/
protected function body()
{
$this->body = substr($this->data, $this->position);
if (!empty($this->headers['transfer-encoding']))
{
unset($this->headers['transfer-encoding']);
$this->state = 'chunked';
}
else
{
$this->state = 'emit';
}
}
/**
* Parsed a "Transfer-Encoding: chunked" body
*/
protected function chunked()
{
if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
{
$this->state = 'emit';
return;
}
$decoded = '';
$encoded = $this->body;
while (true)
{
$is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
if (!$is_chunked)
{
// Looks like it's not chunked after all
$this->state = 'emit';
return;
}
$length = hexdec(trim($matches[1]));
if ($length === 0)
{
// Ignore trailer headers
$this->state = 'emit';
$this->body = $decoded;
return;
}
$chunk_length = strlen($matches[0]);
$decoded .= $part = substr($encoded, $chunk_length, $length);
$encoded = substr($encoded, $chunk_length + $length + 2);
if (trim($encoded) === '0' || empty($encoded))
{
$this->state = 'emit';
$this->body = $decoded;
return;
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,372 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Used for feed auto-discovery
*
*
* This class can be overloaded with {@see SimplePie::set_locator_class()}
*
* @package SimplePie
*/
class SimplePie_Locator
{
var $useragent;
var $timeout;
var $file;
var $local = array();
var $elsewhere = array();
var $cached_entities = array();
var $http_base;
var $base;
var $base_location = 0;
var $checked_feeds = 0;
var $max_checked_feeds = 10;
protected $registry;
public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10)
{
$this->file = $file;
$this->useragent = $useragent;
$this->timeout = $timeout;
$this->max_checked_feeds = $max_checked_feeds;
if (class_exists('DOMDocument'))
{
$this->dom = new DOMDocument();
set_error_handler(array('SimplePie_Misc', 'silence_errors'));
$this->dom->loadHTML($this->file->body);
restore_error_handler();
}
else
{
$this->dom = null;
}
}
public function set_registry(SimplePie_Registry $registry)
{
$this->registry = $registry;
}
public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
{
if ($this->is_feed($this->file))
{
return $this->file;
}
if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
{
$sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
if ($sniffer->get_type() !== 'text/html')
{
return null;
}
}
if ($type & ~SIMPLEPIE_LOCATOR_NONE)
{
$this->get_base();
}
if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
{
return $working[0];
}
if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
{
if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
{
return $working;
}
if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
{
return $working;
}
if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
{
return $working;
}
if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
{
return $working;
}
}
return null;
}
public function is_feed($file)
{
if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
{
$sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
$sniffed = $sniffer->get_type();
if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml')))
{
return true;
}
else
{
return false;
}
}
elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
{
return true;
}
else
{
return false;
}
}
public function get_base()
{
if ($this->dom === null)
{
throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
}
$this->http_base = $this->file->url;
$this->base = $this->http_base;
$elements = $this->dom->getElementsByTagName('base');
foreach ($elements as $element)
{
if ($element->hasAttribute('href'))
{
$base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
if ($base === false)
{
continue;
}
$this->base = $base;
$this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
break;
}
}
}
public function autodiscovery()
{
$done = array();
$feeds = array();
$feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
$feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
$feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
if (!empty($feeds))
{
return array_values($feeds);
}
else
{
return null;
}
}
protected function search_elements_by_tag($name, &$done, $feeds)
{
if ($this->dom === null)
{
throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
}
$links = $this->dom->getElementsByTagName($name);
foreach ($links as $link)
{
if ($this->checked_feeds === $this->max_checked_feeds)
{
break;
}
if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
{
$rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel')))));
$line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
if ($this->base_location < $line)
{
$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
}
else
{
$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
}
if ($href === false)
{
continue;
}
if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
{
$this->checked_feeds++;
$headers = array(
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
);
$feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent));
if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
{
$feeds[$href] = $feed;
}
}
$done[] = $href;
}
}
return $feeds;
}
public function get_links()
{
if ($this->dom === null)
{
throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
}
$links = $this->dom->getElementsByTagName('a');
foreach ($links as $link)
{
if ($link->hasAttribute('href'))
{
$href = trim($link->getAttribute('href'));
$parsed = $this->registry->call('Misc', 'parse_url', array($href));
if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme']))
{
if ($this->base_location < $link->getLineNo())
{
$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
}
else
{
$href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
}
if ($href === false)
{
continue;
}
$current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
{
$this->local[] = $href;
}
else
{
$this->elsewhere[] = $href;
}
}
}
}
$this->local = array_unique($this->local);
$this->elsewhere = array_unique($this->elsewhere);
if (!empty($this->local) || !empty($this->elsewhere))
{
return true;
}
return null;
}
public function extension(&$array)
{
foreach ($array as $key => $value)
{
if ($this->checked_feeds === $this->max_checked_feeds)
{
break;
}
if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
{
$this->checked_feeds++;
$headers = array(
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
);
$feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent));
if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
{
return $feed;
}
else
{
unset($array[$key]);
}
}
}
return null;
}
public function body(&$array)
{
foreach ($array as $key => $value)
{
if ($this->checked_feeds === $this->max_checked_feeds)
{
break;
}
if (preg_match('/(rss|rdf|atom|xml)/i', $value))
{
$this->checked_feeds++;
$headers = array(
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
);
$feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent));
if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
{
return $feed;
}
else
{
unset($array[$key]);
}
}
}
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,276 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Class to validate and to work with IPv6 addresses.
*
* @package SimplePie
* @subpackage HTTP
* @copyright 2003-2005 The PHP Group
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/package/Net_IPv6
* @author Alexander Merz <alexander.merz@web.de>
* @author elfrink at introweb dot nl
* @author Josh Peck <jmp at joshpeck dot org>
* @author Geoffrey Sneddon <geoffers@gmail.com>
*/
class SimplePie_Net_IPv6
{
/**
* Uncompresses an IPv6 address
*
* RFC 4291 allows you to compress concecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and expands the '::' to
* the required number of zero pieces.
*
* Example: FF01::101 -> FF01:0:0:0:0:0:0:101
* ::1 -> 0:0:0:0:0:0:0:1
*
* @author Alexander Merz <alexander.merz@web.de>
* @author elfrink at introweb dot nl
* @author Josh Peck <jmp at joshpeck dot org>
* @copyright 2003-2005 The PHP Group
* @license http://www.opensource.org/licenses/bsd-license.php
* @param string $ip An IPv6 address
* @return string The uncompressed IPv6 address
*/
public static function uncompress($ip)
{
$c1 = -1;
$c2 = -1;
if (substr_count($ip, '::') === 1)
{
list($ip1, $ip2) = explode('::', $ip);
if ($ip1 === '')
{
$c1 = -1;
}
else
{
$c1 = substr_count($ip1, ':');
}
if ($ip2 === '')
{
$c2 = -1;
}
else
{
$c2 = substr_count($ip2, ':');
}
if (strpos($ip2, '.') !== false)
{
$c2++;
}
// ::
if ($c1 === -1 && $c2 === -1)
{
$ip = '0:0:0:0:0:0:0:0';
}
// ::xxx
else if ($c1 === -1)
{
$fill = str_repeat('0:', 7 - $c2);
$ip = str_replace('::', $fill, $ip);
}
// xxx::
else if ($c2 === -1)
{
$fill = str_repeat(':0', 7 - $c1);
$ip = str_replace('::', $fill, $ip);
}
// xxx::xxx
else
{
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
$ip = str_replace('::', $fill, $ip);
}
}
return $ip;
}
/**
* Compresses an IPv6 address
*
* RFC 4291 allows you to compress concecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and compresses consecutive
* zero pieces to '::'.
*
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
* 0:0:0:0:0:0:0:1 -> ::1
*
* @see uncompress()
* @param string $ip An IPv6 address
* @return string The compressed IPv6 address
*/
public static function compress($ip)
{
// Prepare the IP to be compressed
$ip = self::uncompress($ip);
$ip_parts = self::split_v6_v4($ip);
// Replace all leading zeros
$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
// Find bunches of zeros
if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE))
{
$max = 0;
$pos = null;
foreach ($matches[0] as $match)
{
if (strlen($match[0]) > $max)
{
$max = strlen($match[0]);
$pos = $match[1];
}
}
$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
}
if ($ip_parts[1] !== '')
{
return implode(':', $ip_parts);
}
else
{
return $ip_parts[0];
}
}
/**
* Splits an IPv6 address into the IPv6 and IPv4 representation parts
*
* RFC 4291 allows you to represent the last two parts of an IPv6 address
* using the standard IPv4 representation
*
* Example: 0:0:0:0:0:0:13.1.68.3
* 0:0:0:0:0:FFFF:129.144.52.38
*
* @param string $ip An IPv6 address
* @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
*/
private static function split_v6_v4($ip)
{
if (strpos($ip, '.') !== false)
{
$pos = strrpos($ip, ':');
$ipv6_part = substr($ip, 0, $pos);
$ipv4_part = substr($ip, $pos + 1);
return array($ipv6_part, $ipv4_part);
}
else
{
return array($ip, '');
}
}
/**
* Checks an IPv6 address
*
* Checks if the given IP is a valid IPv6 address
*
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*/
public static function check_ipv6($ip)
{
$ip = self::uncompress($ip);
list($ipv6, $ipv4) = self::split_v6_v4($ip);
$ipv6 = explode(':', $ipv6);
$ipv4 = explode('.', $ipv4);
if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4)
{
foreach ($ipv6 as $ipv6_part)
{
// The section can't be empty
if ($ipv6_part === '')
return false;
// Nor can it be over four characters
if (strlen($ipv6_part) > 4)
return false;
// Remove leading zeros (this is safe because of the above)
$ipv6_part = ltrim($ipv6_part, '0');
if ($ipv6_part === '')
$ipv6_part = '0';
// Check the value is valid
$value = hexdec($ipv6_part);
if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF)
return false;
}
if (count($ipv4) === 4)
{
foreach ($ipv4 as $ipv4_part)
{
$value = (int) $ipv4_part;
if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF)
return false;
}
}
return true;
}
else
{
return false;
}
}
/**
* Checks if the given IP is a valid IPv6 address
*
* @codeCoverageIgnore
* @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead
* @see check_ipv6
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*/
public static function checkIPv6($ip)
{
return self::check_ipv6($ip);
}
}

View File

@ -0,0 +1,983 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Date Parser
*
* @package SimplePie
* @subpackage Parsing
*/
class SimplePie_Parse_Date
{
/**
* Input data
*
* @access protected
* @var string
*/
var $date;
/**
* List of days, calendar day name => ordinal day number in the week
*
* @access protected
* @var array
*/
var $day = array(
// English
'mon' => 1,
'monday' => 1,
'tue' => 2,
'tuesday' => 2,
'wed' => 3,
'wednesday' => 3,
'thu' => 4,
'thursday' => 4,
'fri' => 5,
'friday' => 5,
'sat' => 6,
'saturday' => 6,
'sun' => 7,
'sunday' => 7,
// Dutch
'maandag' => 1,
'dinsdag' => 2,
'woensdag' => 3,
'donderdag' => 4,
'vrijdag' => 5,
'zaterdag' => 6,
'zondag' => 7,
// French
'lundi' => 1,
'mardi' => 2,
'mercredi' => 3,
'jeudi' => 4,
'vendredi' => 5,
'samedi' => 6,
'dimanche' => 7,
// German
'montag' => 1,
'dienstag' => 2,
'mittwoch' => 3,
'donnerstag' => 4,
'freitag' => 5,
'samstag' => 6,
'sonnabend' => 6,
'sonntag' => 7,
// Italian
'lunedì' => 1,
'martedì' => 2,
'mercoledì' => 3,
'giovedì' => 4,
'venerdì' => 5,
'sabato' => 6,
'domenica' => 7,
// Spanish
'lunes' => 1,
'martes' => 2,
'miércoles' => 3,
'jueves' => 4,
'viernes' => 5,
'sábado' => 6,
'domingo' => 7,
// Finnish
'maanantai' => 1,
'tiistai' => 2,
'keskiviikko' => 3,
'torstai' => 4,
'perjantai' => 5,
'lauantai' => 6,
'sunnuntai' => 7,
// Hungarian
'hétfő' => 1,
'kedd' => 2,
'szerda' => 3,
'csütörtok' => 4,
'péntek' => 5,
'szombat' => 6,
'vasárnap' => 7,
// Greek
'Δευ' => 1,
'Τρι' => 2,
'Τετ' => 3,
'Πεμ' => 4,
'Παρ' => 5,
'Σαβ' => 6,
'Κυρ' => 7,
);
/**
* List of months, calendar month name => calendar month number
*
* @access protected
* @var array
*/
var $month = array(
// English
'jan' => 1,
'january' => 1,
'feb' => 2,
'february' => 2,
'mar' => 3,
'march' => 3,
'apr' => 4,
'april' => 4,
'may' => 5,
// No long form of May
'jun' => 6,
'june' => 6,
'jul' => 7,
'july' => 7,
'aug' => 8,
'august' => 8,
'sep' => 9,
'september' => 8,
'oct' => 10,
'october' => 10,
'nov' => 11,
'november' => 11,
'dec' => 12,
'december' => 12,
// Dutch
'januari' => 1,
'februari' => 2,
'maart' => 3,
'april' => 4,
'mei' => 5,
'juni' => 6,
'juli' => 7,
'augustus' => 8,
'september' => 9,
'oktober' => 10,
'november' => 11,
'december' => 12,
// French
'janvier' => 1,
'février' => 2,
'mars' => 3,
'avril' => 4,
'mai' => 5,
'juin' => 6,
'juillet' => 7,
'août' => 8,
'septembre' => 9,
'octobre' => 10,
'novembre' => 11,
'décembre' => 12,
// German
'januar' => 1,
'februar' => 2,
'märz' => 3,
'april' => 4,
'mai' => 5,
'juni' => 6,
'juli' => 7,
'august' => 8,
'september' => 9,
'oktober' => 10,
'november' => 11,
'dezember' => 12,
// Italian
'gennaio' => 1,
'febbraio' => 2,
'marzo' => 3,
'aprile' => 4,
'maggio' => 5,
'giugno' => 6,
'luglio' => 7,
'agosto' => 8,
'settembre' => 9,
'ottobre' => 10,
'novembre' => 11,
'dicembre' => 12,
// Spanish
'enero' => 1,
'febrero' => 2,
'marzo' => 3,
'abril' => 4,
'mayo' => 5,
'junio' => 6,
'julio' => 7,
'agosto' => 8,
'septiembre' => 9,
'setiembre' => 9,
'octubre' => 10,
'noviembre' => 11,
'diciembre' => 12,
// Finnish
'tammikuu' => 1,
'helmikuu' => 2,
'maaliskuu' => 3,
'huhtikuu' => 4,
'toukokuu' => 5,
'kesäkuu' => 6,
'heinäkuu' => 7,
'elokuu' => 8,
'suuskuu' => 9,
'lokakuu' => 10,
'marras' => 11,
'joulukuu' => 12,
// Hungarian
'január' => 1,
'február' => 2,
'március' => 3,
'április' => 4,
'május' => 5,
'június' => 6,
'július' => 7,
'augusztus' => 8,
'szeptember' => 9,
'október' => 10,
'november' => 11,
'december' => 12,
// Greek
'Ιαν' => 1,
'Φεβ' => 2,
'Μάώ' => 3,
'Μαώ' => 3,
'Απρ' => 4,
'Μάι' => 5,
'Μαϊ' => 5,
'Μαι' => 5,
'Ιούν' => 6,
'Ιον' => 6,
'Ιούλ' => 7,
'Ιολ' => 7,
'Αύγ' => 8,
'Αυγ' => 8,
'Σεπ' => 9,
'Οκτ' => 10,
'Νοέ' => 11,
'Δεκ' => 12,
);
/**
* List of timezones, abbreviation => offset from UTC
*
* @access protected
* @var array
*/
var $timezone = array(
'ACDT' => 37800,
'ACIT' => 28800,
'ACST' => 34200,
'ACT' => -18000,
'ACWDT' => 35100,
'ACWST' => 31500,
'AEDT' => 39600,
'AEST' => 36000,
'AFT' => 16200,
'AKDT' => -28800,
'AKST' => -32400,
'AMDT' => 18000,
'AMT' => -14400,
'ANAST' => 46800,
'ANAT' => 43200,
'ART' => -10800,
'AZOST' => -3600,
'AZST' => 18000,
'AZT' => 14400,
'BIOT' => 21600,
'BIT' => -43200,
'BOT' => -14400,
'BRST' => -7200,
'BRT' => -10800,
'BST' => 3600,
'BTT' => 21600,
'CAST' => 18000,
'CAT' => 7200,
'CCT' => 23400,
'CDT' => -18000,
'CEDT' => 7200,
'CET' => 3600,
'CGST' => -7200,
'CGT' => -10800,
'CHADT' => 49500,
'CHAST' => 45900,
'CIST' => -28800,
'CKT' => -36000,
'CLDT' => -10800,
'CLST' => -14400,
'COT' => -18000,
'CST' => -21600,
'CVT' => -3600,
'CXT' => 25200,
'DAVT' => 25200,
'DTAT' => 36000,
'EADT' => -18000,
'EAST' => -21600,
'EAT' => 10800,
'ECT' => -18000,
'EDT' => -14400,
'EEST' => 10800,
'EET' => 7200,
'EGT' => -3600,
'EKST' => 21600,
'EST' => -18000,
'FJT' => 43200,
'FKDT' => -10800,
'FKST' => -14400,
'FNT' => -7200,
'GALT' => -21600,
'GEDT' => 14400,
'GEST' => 10800,
'GFT' => -10800,
'GILT' => 43200,
'GIT' => -32400,
'GST' => 14400,
'GST' => -7200,
'GYT' => -14400,
'HAA' => -10800,
'HAC' => -18000,
'HADT' => -32400,
'HAE' => -14400,
'HAP' => -25200,
'HAR' => -21600,
'HAST' => -36000,
'HAT' => -9000,
'HAY' => -28800,
'HKST' => 28800,
'HMT' => 18000,
'HNA' => -14400,
'HNC' => -21600,
'HNE' => -18000,
'HNP' => -28800,
'HNR' => -25200,
'HNT' => -12600,
'HNY' => -32400,
'IRDT' => 16200,
'IRKST' => 32400,
'IRKT' => 28800,
'IRST' => 12600,
'JFDT' => -10800,
'JFST' => -14400,
'JST' => 32400,
'KGST' => 21600,
'KGT' => 18000,
'KOST' => 39600,
'KOVST' => 28800,
'KOVT' => 25200,
'KRAST' => 28800,
'KRAT' => 25200,
'KST' => 32400,
'LHDT' => 39600,
'LHST' => 37800,
'LINT' => 50400,
'LKT' => 21600,
'MAGST' => 43200,
'MAGT' => 39600,
'MAWT' => 21600,
'MDT' => -21600,
'MESZ' => 7200,
'MEZ' => 3600,
'MHT' => 43200,
'MIT' => -34200,
'MNST' => 32400,
'MSDT' => 14400,
'MSST' => 10800,
'MST' => -25200,
'MUT' => 14400,
'MVT' => 18000,
'MYT' => 28800,
'NCT' => 39600,
'NDT' => -9000,
'NFT' => 41400,
'NMIT' => 36000,
'NOVST' => 25200,
'NOVT' => 21600,
'NPT' => 20700,
'NRT' => 43200,
'NST' => -12600,
'NUT' => -39600,
'NZDT' => 46800,
'NZST' => 43200,
'OMSST' => 25200,
'OMST' => 21600,
'PDT' => -25200,
'PET' => -18000,
'PETST' => 46800,
'PETT' => 43200,
'PGT' => 36000,
'PHOT' => 46800,
'PHT' => 28800,
'PKT' => 18000,
'PMDT' => -7200,
'PMST' => -10800,
'PONT' => 39600,
'PST' => -28800,
'PWT' => 32400,
'PYST' => -10800,
'PYT' => -14400,
'RET' => 14400,
'ROTT' => -10800,
'SAMST' => 18000,
'SAMT' => 14400,
'SAST' => 7200,
'SBT' => 39600,
'SCDT' => 46800,
'SCST' => 43200,
'SCT' => 14400,
'SEST' => 3600,
'SGT' => 28800,
'SIT' => 28800,
'SRT' => -10800,
'SST' => -39600,
'SYST' => 10800,
'SYT' => 7200,
'TFT' => 18000,
'THAT' => -36000,
'TJT' => 18000,
'TKT' => -36000,
'TMT' => 18000,
'TOT' => 46800,
'TPT' => 32400,
'TRUT' => 36000,
'TVT' => 43200,
'TWT' => 28800,
'UYST' => -7200,
'UYT' => -10800,
'UZT' => 18000,
'VET' => -14400,
'VLAST' => 39600,
'VLAT' => 36000,
'VOST' => 21600,
'VUT' => 39600,
'WAST' => 7200,
'WAT' => 3600,
'WDT' => 32400,
'WEST' => 3600,
'WFT' => 43200,
'WIB' => 25200,
'WIT' => 32400,
'WITA' => 28800,
'WKST' => 18000,
'WST' => 28800,
'YAKST' => 36000,
'YAKT' => 32400,
'YAPT' => 36000,
'YEKST' => 21600,
'YEKT' => 18000,
);
/**
* Cached PCRE for SimplePie_Parse_Date::$day
*
* @access protected
* @var string
*/
var $day_pcre;
/**
* Cached PCRE for SimplePie_Parse_Date::$month
*
* @access protected
* @var string
*/
var $month_pcre;
/**
* Array of user-added callback methods
*
* @access private
* @var array
*/
var $built_in = array();
/**
* Array of user-added callback methods
*
* @access private
* @var array
*/
var $user = array();
/**
* Create new SimplePie_Parse_Date object, and set self::day_pcre,
* self::month_pcre, and self::built_in
*
* @access private
*/
public function __construct()
{
$this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
$this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
static $cache;
if (!isset($cache[get_class($this)]))
{
$all_methods = get_class_methods($this);
foreach ($all_methods as $method)
{
if (strtolower(substr($method, 0, 5)) === 'date_')
{
$cache[get_class($this)][] = $method;
}
}
}
foreach ($cache[get_class($this)] as $method)
{
$this->built_in[] = $method;
}
}
/**
* Get the object
*
* @access public
*/
public static function get()
{
static $object;
if (!$object)
{
$object = new SimplePie_Parse_Date;
}
return $object;
}
/**
* Parse a date
*
* @final
* @access public
* @param string $date Date to parse
* @return int Timestamp corresponding to date string, or false on failure
*/
public function parse($date)
{
foreach ($this->user as $method)
{
if (($returned = call_user_func($method, $date)) !== false)
{
return $returned;
}
}
foreach ($this->built_in as $method)
{
if (($returned = call_user_func(array($this, $method), $date)) !== false)
{
return $returned;
}
}
return false;
}
/**
* Add a callback method to parse a date
*
* @final
* @access public
* @param callback $callback
*/
public function add_callback($callback)
{
if (is_callable($callback))
{
$this->user[] = $callback;
}
else
{
trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
}
}
/**
* Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
* well as allowing any of upper or lower case "T", horizontal tabs, or
* spaces to be used as the time seperator (including more than one))
*
* @access protected
* @return int Timestamp
*/
public function date_w3cdtf($date)
{
static $pcre;
if (!$pcre)
{
$year = '([0-9]{4})';
$month = $day = $hour = $minute = $second = '([0-9]{2})';
$decimal = '([0-9]*)';
$zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
$pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
}
if (preg_match($pcre, $date, $match))
{
/*
Capturing subpatterns:
1: Year
2: Month
3: Day
4: Hour
5: Minute
6: Second
7: Decimal fraction of a second
8: Zulu
9: Timezone ±
10: Timezone hours
11: Timezone minutes
*/
// Fill in empty matches
for ($i = count($match); $i <= 3; $i++)
{
$match[$i] = '1';
}
for ($i = count($match); $i <= 7; $i++)
{
$match[$i] = '0';
}
// Numeric timezone
if (isset($match[9]) && $match[9] !== '')
{
$timezone = $match[10] * 3600;
$timezone += $match[11] * 60;
if ($match[9] === '-')
{
$timezone = 0 - $timezone;
}
}
else
{
$timezone = 0;
}
// Convert the number of seconds to an integer, taking decimals into account
$second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
}
else
{
return false;
}
}
/**
* Remove RFC822 comments
*
* @access protected
* @param string $data Data to strip comments from
* @return string Comment stripped string
*/
public function remove_rfc2822_comments($string)
{
$string = (string) $string;
$position = 0;
$length = strlen($string);
$depth = 0;
$output = '';
while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
{
$output .= substr($string, $position, $pos - $position);
$position = $pos + 1;
if ($string[$pos - 1] !== '\\')
{
$depth++;
while ($depth && $position < $length)
{
$position += strcspn($string, '()', $position);
if ($string[$position - 1] === '\\')
{
$position++;
continue;
}
elseif (isset($string[$position]))
{
switch ($string[$position])
{
case '(':
$depth++;
break;
case ')':
$depth--;
break;
}
$position++;
}
else
{
break;
}
}
}
else
{
$output .= '(';
}
}
$output .= substr($string, $position);
return $output;
}
/**
* Parse RFC2822's date format
*
* @access protected
* @return int Timestamp
*/
public function date_rfc2822($date)
{
static $pcre;
if (!$pcre)
{
$wsp = '[\x09\x20]';
$fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
$optional_fws = $fws . '?';
$day_name = $this->day_pcre;
$month = $this->month_pcre;
$day = '([0-9]{1,2})';
$hour = $minute = $second = '([0-9]{2})';
$year = '([0-9]{2,4})';
$num_zone = '([+\-])([0-9]{2})([0-9]{2})';
$character_zone = '([A-Z]{1,5})';
$zone = '(?:' . $num_zone . '|' . $character_zone . ')';
$pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
}
if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
{
/*
Capturing subpatterns:
1: Day name
2: Day
3: Month
4: Year
5: Hour
6: Minute
7: Second
8: Timezone ±
9: Timezone hours
10: Timezone minutes
11: Alphabetic timezone
*/
// Find the month number
$month = $this->month[strtolower($match[3])];
// Numeric timezone
if ($match[8] !== '')
{
$timezone = $match[9] * 3600;
$timezone += $match[10] * 60;
if ($match[8] === '-')
{
$timezone = 0 - $timezone;
}
}
// Character timezone
elseif (isset($this->timezone[strtoupper($match[11])]))
{
$timezone = $this->timezone[strtoupper($match[11])];
}
// Assume everything else to be -0000
else
{
$timezone = 0;
}
// Deal with 2/3 digit years
if ($match[4] < 50)
{
$match[4] += 2000;
}
elseif ($match[4] < 1000)
{
$match[4] += 1900;
}
// Second is optional, if it is empty set it to zero
if ($match[7] !== '')
{
$second = $match[7];
}
else
{
$second = 0;
}
return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
}
else
{
return false;
}
}
/**
* Parse RFC850's date format
*
* @access protected
* @return int Timestamp
*/
public function date_rfc850($date)
{
static $pcre;
if (!$pcre)
{
$space = '[\x09\x20]+';
$day_name = $this->day_pcre;
$month = $this->month_pcre;
$day = '([0-9]{1,2})';
$year = $hour = $minute = $second = '([0-9]{2})';
$zone = '([A-Z]{1,5})';
$pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
}
if (preg_match($pcre, $date, $match))
{
/*
Capturing subpatterns:
1: Day name
2: Day
3: Month
4: Year
5: Hour
6: Minute
7: Second
8: Timezone
*/
// Month
$month = $this->month[strtolower($match[3])];
// Character timezone
if (isset($this->timezone[strtoupper($match[8])]))
{
$timezone = $this->timezone[strtoupper($match[8])];
}
// Assume everything else to be -0000
else
{
$timezone = 0;
}
// Deal with 2 digit year
if ($match[4] < 50)
{
$match[4] += 2000;
}
else
{
$match[4] += 1900;
}
return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
}
else
{
return false;
}
}
/**
* Parse C99's asctime()'s date format
*
* @access protected
* @return int Timestamp
*/
public function date_asctime($date)
{
static $pcre;
if (!$pcre)
{
$space = '[\x09\x20]+';
$wday_name = $this->day_pcre;
$mon_name = $this->month_pcre;
$day = '([0-9]{1,2})';
$hour = $sec = $min = '([0-9]{2})';
$year = '([0-9]{4})';
$terminator = '\x0A?\x00?';
$pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
}
if (preg_match($pcre, $date, $match))
{
/*
Capturing subpatterns:
1: Day name
2: Month
3: Day
4: Hour
5: Minute
6: Second
7: Year
*/
$month = $this->month[strtolower($match[2])];
return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
}
else
{
return false;
}
}
/**
* Parse dates using strtotime()
*
* @access protected
* @return int Timestamp
*/
public function date_strtotime($date)
{
$strtotime = strtotime($date);
if ($strtotime === -1 || $strtotime === false)
{
return false;
}
else
{
return $strtotime;
}
}
}

View File

@ -0,0 +1,407 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Parses XML into something sane
*
*
* This class can be overloaded with {@see SimplePie::set_parser_class()}
*
* @package SimplePie
* @subpackage Parsing
*/
class SimplePie_Parser
{
var $error_code;
var $error_string;
var $current_line;
var $current_column;
var $current_byte;
var $separator = ' ';
var $namespace = array('');
var $element = array('');
var $xml_base = array('');
var $xml_base_explicit = array(false);
var $xml_lang = array('');
var $data = array();
var $datas = array(array());
var $current_xhtml_construct = -1;
var $encoding;
protected $registry;
public function set_registry(SimplePie_Registry $registry)
{
$this->registry = $registry;
}
public function parse(&$data, $encoding)
{
// Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
if (strtoupper($encoding) === 'US-ASCII')
{
$this->encoding = 'UTF-8';
}
else
{
$this->encoding = $encoding;
}
// Strip BOM:
// UTF-32 Big Endian BOM
if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
{
$data = substr($data, 4);
}
// UTF-32 Little Endian BOM
elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
{
$data = substr($data, 4);
}
// UTF-16 Big Endian BOM
elseif (substr($data, 0, 2) === "\xFE\xFF")
{
$data = substr($data, 2);
}
// UTF-16 Little Endian BOM
elseif (substr($data, 0, 2) === "\xFF\xFE")
{
$data = substr($data, 2);
}
// UTF-8 BOM
elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
{
$data = substr($data, 3);
}
if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
{
$declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
if ($declaration->parse())
{
$data = substr($data, $pos + 2);
$data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
}
else
{
$this->error_string = 'SimplePie bug! Please report this!';
return false;
}
}
$return = true;
static $xml_is_sane = null;
if ($xml_is_sane === null)
{
$parser_check = xml_parser_create();
xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
xml_parser_free($parser_check);
$xml_is_sane = isset($values[0]['value']);
}
// Create the parser
if ($xml_is_sane)
{
$xml = xml_parser_create_ns($this->encoding, $this->separator);
xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
xml_set_object($xml, $this);
xml_set_character_data_handler($xml, 'cdata');
xml_set_element_handler($xml, 'tag_open', 'tag_close');
// Parse!
if (!xml_parse($xml, $data, true))
{
$this->error_code = xml_get_error_code($xml);
$this->error_string = xml_error_string($this->error_code);
$return = false;
}
$this->current_line = xml_get_current_line_number($xml);
$this->current_column = xml_get_current_column_number($xml);
$this->current_byte = xml_get_current_byte_index($xml);
xml_parser_free($xml);
return $return;
}
else
{
libxml_clear_errors();
$xml = new XMLReader();
$xml->xml($data);
while (@$xml->read())
{
switch ($xml->nodeType)
{
case constant('XMLReader::END_ELEMENT'):
if ($xml->namespaceURI !== '')
{
$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
}
else
{
$tagName = $xml->localName;
}
$this->tag_close(null, $tagName);
break;
case constant('XMLReader::ELEMENT'):
$empty = $xml->isEmptyElement;
if ($xml->namespaceURI !== '')
{
$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
}
else
{
$tagName = $xml->localName;
}
$attributes = array();
while ($xml->moveToNextAttribute())
{
if ($xml->namespaceURI !== '')
{
$attrName = $xml->namespaceURI . $this->separator . $xml->localName;
}
else
{
$attrName = $xml->localName;
}
$attributes[$attrName] = $xml->value;
}
$this->tag_open(null, $tagName, $attributes);
if ($empty)
{
$this->tag_close(null, $tagName);
}
break;
case constant('XMLReader::TEXT'):
case constant('XMLReader::CDATA'):
$this->cdata(null, $xml->value);
break;
}
}
if ($error = libxml_get_last_error())
{
$this->error_code = $error->code;
$this->error_string = $error->message;
$this->current_line = $error->line;
$this->current_column = $error->column;
return false;
}
else
{
return true;
}
}
}
public function get_error_code()
{
return $this->error_code;
}
public function get_error_string()
{
return $this->error_string;
}
public function get_current_line()
{
return $this->current_line;
}
public function get_current_column()
{
return $this->current_column;
}
public function get_current_byte()
{
return $this->current_byte;
}
public function get_data()
{
return $this->data;
}
public function tag_open($parser, $tag, $attributes)
{
list($this->namespace[], $this->element[]) = $this->split_ns($tag);
$attribs = array();
foreach ($attributes as $name => $value)
{
list($attrib_namespace, $attribute) = $this->split_ns($name);
$attribs[$attrib_namespace][$attribute] = $value;
}
if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
{
$base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)));
if ($base !== false)
{
$this->xml_base[] = $base;
$this->xml_base_explicit[] = true;
}
}
else
{
$this->xml_base[] = end($this->xml_base);
$this->xml_base_explicit[] = end($this->xml_base_explicit);
}
if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
{
$this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
}
else
{
$this->xml_lang[] = end($this->xml_lang);
}
if ($this->current_xhtml_construct >= 0)
{
$this->current_xhtml_construct++;
if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
{
$this->data['data'] .= '<' . end($this->element);
if (isset($attribs['']))
{
foreach ($attribs[''] as $name => $value)
{
$this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
}
}
$this->data['data'] .= '>';
}
}
else
{
$this->datas[] =& $this->data;
$this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
$this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang));
if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')
|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title')))
|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title')))
|| (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title'))))
{
$this->current_xhtml_construct = 0;
}
}
}
public function cdata($parser, $cdata)
{
if ($this->current_xhtml_construct >= 0)
{
$this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
}
else
{
$this->data['data'] .= $cdata;
}
}
public function tag_close($parser, $tag)
{
if ($this->current_xhtml_construct >= 0)
{
$this->current_xhtml_construct--;
if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param')))
{
$this->data['data'] .= '</' . end($this->element) . '>';
}
}
if ($this->current_xhtml_construct === -1)
{
$this->data =& $this->datas[count($this->datas) - 1];
array_pop($this->datas);
}
array_pop($this->element);
array_pop($this->namespace);
array_pop($this->xml_base);
array_pop($this->xml_base_explicit);
array_pop($this->xml_lang);
}
public function split_ns($string)
{
static $cache = array();
if (!isset($cache[$string]))
{
if ($pos = strpos($string, $this->separator))
{
static $separator_length;
if (!$separator_length)
{
$separator_length = strlen($this->separator);
}
$namespace = substr($string, 0, $pos);
$local_name = substr($string, $pos + $separator_length);
if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
{
$namespace = SIMPLEPIE_NAMESPACE_ITUNES;
}
// Normalize the Media RSS namespaces
if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
$namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
{
$namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
}
$cache[$string] = array($namespace, $local_name);
}
else
{
$cache[$string] = array('', $string);
}
}
return $cache[$string];
}
}

View File

@ -0,0 +1,129 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
*
* Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()}
*
* This class can be overloaded with {@see SimplePie::set_rating_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Rating
{
/**
* Rating scheme
*
* @var string
* @see get_scheme()
*/
var $scheme;
/**
* Rating value
*
* @var string
* @see get_value()
*/
var $value;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($scheme = null, $value = null)
{
$this->scheme = $scheme;
$this->value = $value;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the organizational scheme for the rating
*
* @return string|null
*/
public function get_scheme()
{
if ($this->scheme !== null)
{
return $this->scheme;
}
else
{
return null;
}
}
/**
* Get the value of the rating
*
* @return string|null
*/
public function get_value()
{
if ($this->value !== null)
{
return $this->value;
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,225 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Handles creating objects and calling methods
*
* Access this via {@see SimplePie::get_registry()}
*
* @package SimplePie
*/
class SimplePie_Registry
{
/**
* Default class mapping
*
* Overriding classes *must* subclass these.
*
* @var array
*/
protected $default = array(
'Cache' => 'SimplePie_Cache',
'Locator' => 'SimplePie_Locator',
'Parser' => 'SimplePie_Parser',
'File' => 'SimplePie_File',
'Sanitize' => 'SimplePie_Sanitize',
'Item' => 'SimplePie_Item',
'Author' => 'SimplePie_Author',
'Category' => 'SimplePie_Category',
'Enclosure' => 'SimplePie_Enclosure',
'Caption' => 'SimplePie_Caption',
'Copyright' => 'SimplePie_Copyright',
'Credit' => 'SimplePie_Credit',
'Rating' => 'SimplePie_Rating',
'Restriction' => 'SimplePie_Restriction',
'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer',
'Source' => 'SimplePie_Source',
'Misc' => 'SimplePie_Misc',
'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser',
'Parse_Date' => 'SimplePie_Parse_Date',
);
/**
* Class mapping
*
* @see register()
* @var array
*/
protected $classes = array();
/**
* Legacy classes
*
* @see register()
* @var array
*/
protected $legacy = array();
/**
* Constructor
*
* No-op
*/
public function __construct() { }
/**
* Register a class
*
* @param string $type See {@see $default} for names
* @param string $class Class name, must subclass the corresponding default
* @param bool $legacy Whether to enable legacy support for this class
* @return bool Successfulness
*/
public function register($type, $class, $legacy = false)
{
if (!is_subclass_of($class, $this->default[$type]))
{
return false;
}
$this->classes[$type] = $class;
if ($legacy)
{
$this->legacy[] = $class;
}
return true;
}
/**
* Get the class registered for a type
*
* Where possible, use {@see create()} or {@see call()} instead
*
* @param string $type
* @return string|null
*/
public function get_class($type)
{
if (!empty($this->classes[$type]))
{
return $this->classes[$type];
}
if (!empty($this->default[$type]))
{
return $this->default[$type];
}
return null;
}
/**
* Create a new instance of a given type
*
* @param string $type
* @param array $parameters Parameters to pass to the constructor
* @return object Instance of class
*/
public function &create($type, $parameters = array())
{
$class = $this->get_class($type);
if (in_array($class, $this->legacy))
{
switch ($type)
{
case 'locator':
// Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class
// Specified: file, timeout, useragent, max_checked_feeds
$replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer'));
array_splice($parameters, 3, 1, $replacement);
break;
}
}
if (!method_exists($class, '__construct'))
{
$instance = new $class;
}
else
{
$reflector = new ReflectionClass($class);
$instance = $reflector->newInstanceArgs($parameters);
}
if (method_exists($instance, 'set_registry'))
{
$instance->set_registry($this);
}
return $instance;
}
/**
* Call a static method for a type
*
* @param string $type
* @param string $method
* @param array $parameters
* @return mixed
*/
public function &call($type, $method, $parameters = array())
{
$class = $this->get_class($type);
if (in_array($class, $this->legacy))
{
switch ($type)
{
case 'Cache':
// For backwards compatibility with old non-static
// Cache::create() methods
if ($method === 'get_handler')
{
$result = @call_user_func_array(array($class, 'create'), $parameters);
return $result;
}
break;
}
}
$result = call_user_func_array(array($class, $method), $parameters);
return $result;
}
}

View File

@ -0,0 +1,155 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Handles `<media:restriction>` as defined in Media RSS
*
* Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()}
*
* This class can be overloaded with {@see SimplePie::set_restriction_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Restriction
{
/**
* Relationship ('allow'/'deny')
*
* @var string
* @see get_relationship()
*/
var $relationship;
/**
* Type of restriction
*
* @var string
* @see get_type()
*/
var $type;
/**
* Restricted values
*
* @var string
* @see get_value()
*/
var $value;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($relationship = null, $type = null, $value = null)
{
$this->relationship = $relationship;
$this->type = $type;
$this->value = $value;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the relationship
*
* @return string|null Either 'allow' or 'deny'
*/
public function get_relationship()
{
if ($this->relationship !== null)
{
return $this->relationship;
}
else
{
return null;
}
}
/**
* Get the type
*
* @return string|null
*/
public function get_type()
{
if ($this->type !== null)
{
return $this->type;
}
else
{
return null;
}
}
/**
* Get the list of restricted things
*
* @return string|null
*/
public function get_value()
{
if ($this->value !== null)
{
return $this->value;
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,549 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Used for data cleanup and post-processing
*
*
* This class can be overloaded with {@see SimplePie::set_sanitize_class()}
*
* @package SimplePie
* @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
*/
class SimplePie_Sanitize
{
// Private vars
var $base;
// Options
var $remove_div = true;
var $image_handler = '';
var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
var $encode_instead_of_strip = false;
var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
var $strip_comments = false;
var $output_encoding = 'UTF-8';
var $enable_cache = true;
var $cache_location = './cache';
var $cache_name_function = 'md5';
var $timeout = 10;
var $useragent = '';
var $force_fsockopen = false;
var $replace_url_attributes = null;
public function __construct()
{
// Set defaults
$this->set_url_replacements(null);
}
public function remove_div($enable = true)
{
$this->remove_div = (bool) $enable;
}
public function set_image_handler($page = false)
{
if ($page)
{
$this->image_handler = (string) $page;
}
else
{
$this->image_handler = false;
}
}
public function set_registry(SimplePie_Registry $registry)
{
$this->registry = $registry;
}
public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
{
if (isset($enable_cache))
{
$this->enable_cache = (bool) $enable_cache;
}
if ($cache_location)
{
$this->cache_location = (string) $cache_location;
}
if ($cache_name_function)
{
$this->cache_name_function = (string) $cache_name_function;
}
}
public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
{
if ($timeout)
{
$this->timeout = (string) $timeout;
}
if ($useragent)
{
$this->useragent = (string) $useragent;
}
if ($force_fsockopen)
{
$this->force_fsockopen = (string) $force_fsockopen;
}
}
public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
{
if ($tags)
{
if (is_array($tags))
{
$this->strip_htmltags = $tags;
}
else
{
$this->strip_htmltags = explode(',', $tags);
}
}
else
{
$this->strip_htmltags = false;
}
}
public function encode_instead_of_strip($encode = false)
{
$this->encode_instead_of_strip = (bool) $encode;
}
public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
{
if ($attribs)
{
if (is_array($attribs))
{
$this->strip_attributes = $attribs;
}
else
{
$this->strip_attributes = explode(',', $attribs);
}
}
else
{
$this->strip_attributes = false;
}
}
public function strip_comments($strip = false)
{
$this->strip_comments = (bool) $strip;
}
public function set_output_encoding($encoding = 'UTF-8')
{
$this->output_encoding = (string) $encoding;
}
/**
* Set element/attribute key/value pairs of HTML attributes
* containing URLs that need to be resolved relative to the feed
*
* Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
* |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
* |q|@cite
*
* @since 1.0
* @param array|null $element_attribute Element/attribute key/value pairs, null for default
*/
public function set_url_replacements($element_attribute = null)
{
if ($element_attribute === null)
{
$element_attribute = array(
'a' => 'href',
'area' => 'href',
'blockquote' => 'cite',
'del' => 'cite',
'form' => 'action',
'img' => array(
'longdesc',
'src'
),
'input' => 'src',
'ins' => 'cite',
'q' => 'cite'
);
}
$this->replace_url_attributes = (array) $element_attribute;
}
public function sanitize($data, $type, $base = '')
{
$data = trim($data);
if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
{
if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
{
if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data))
{
$type |= SIMPLEPIE_CONSTRUCT_HTML;
}
else
{
$type |= SIMPLEPIE_CONSTRUCT_TEXT;
}
}
if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
{
$data = base64_decode($data);
}
if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
{
$document = new DOMDocument();
$document->encoding = 'UTF-8';
$data = $this->preprocess($data, $type);
set_error_handler(array('SimplePie_Misc', 'silence_errors'));
$document->loadHTML($data);
restore_error_handler();
// Strip comments
if ($this->strip_comments)
{
$xpath = new DOMXPath($document);
$comments = $xpath->query('//comment()');
foreach ($comments as $comment)
{
$comment->parentNode->removeChild($comment);
}
}
// Strip out HTML tags and attributes that might cause various security problems.
// Based on recommendations by Mark Pilgrim at:
// http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
if ($this->strip_htmltags)
{
foreach ($this->strip_htmltags as $tag)
{
$this->strip_tag($tag, $document, $type);
}
}
if ($this->strip_attributes)
{
foreach ($this->strip_attributes as $attrib)
{
$this->strip_attr($attrib, $document);
}
}
// Replace relative URLs
$this->base = $base;
foreach ($this->replace_url_attributes as $element => $attributes)
{
$this->replace_urls($document, $element, $attributes);
}
// If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
{
$images = $document->getElementsByTagName('img');
foreach ($images as $img)
{
if ($img->hasAttribute('src'))
{
$image_url = call_user_func($this->cache_name_function, $img->getAttribute('src'));
$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi'));
if ($cache->load())
{
$img->setAttribute('src', $this->image_handler . $image_url);
}
else
{
$file = $this->registry->create('File', array($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen));
$headers = $file->headers;
if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
{
if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
{
$img->setAttribute('src', $this->image_handler . $image_url);
}
else
{
trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
}
}
}
}
}
}
// Remove the DOCTYPE
// Seems to cause segfaulting if we don't do this
if ($document->firstChild instanceof DOMDocumentType)
{
$document->removeChild($document->firstChild);
}
// Move everything from the body to the root
$real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0);
$document->replaceChild($real_body, $document->firstChild);
// Finally, convert to a HTML string
$data = trim($document->saveHTML());
if ($this->remove_div)
{
$data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
$data = preg_replace('/<\/div>$/', '', $data);
}
else
{
$data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
}
}
if ($type & SIMPLEPIE_CONSTRUCT_IRI)
{
$absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
if ($absolute !== false)
{
$data = $absolute;
}
}
if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
{
$data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
}
if ($this->output_encoding !== 'UTF-8')
{
$data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding));
}
}
return $data;
}
protected function preprocess($html, $type)
{
$ret = '';
if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML)
{
// Atom XHTML constructs are wrapped with a div by default
// Note: No protection if $html contains a stray </div>!
$html = '<div>' . $html . '</div>';
$ret .= '<!DOCTYPE html>';
$content_type = 'text/html';
}
else
{
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
$content_type = 'application/xhtml+xml';
}
$ret .= '<html><head>';
$ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
$ret .= '</head><body>' . $html . '</body></html>';
return $ret;
}
public function replace_urls($document, $tag, $attributes)
{
if (!is_array($attributes))
{
$attributes = array($attributes);
}
if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
{
$elements = $document->getElementsByTagName($tag);
foreach ($elements as $element)
{
foreach ($attributes as $attribute)
{
if ($element->hasAttribute($attribute))
{
$value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base));
if ($value !== false)
{
$element->setAttribute($attribute, $value);
}
}
}
}
}
}
public function do_strip_htmltags($match)
{
if ($this->encode_instead_of_strip)
{
if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
{
$match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
$match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
}
else
{
return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
}
}
elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
{
return $match[4];
}
else
{
return '';
}
}
protected function strip_tag($tag, $document, $type)
{
$xpath = new DOMXPath($document);
$elements = $xpath->query('body//' . $tag);
if ($this->encode_instead_of_strip)
{
foreach ($elements as $element)
{
$fragment = $document->createDocumentFragment();
// For elements which aren't script or style, include the tag itself
if (!in_array($tag, array('script', 'style')))
{
$text = '<' . $tag;
if ($element->hasAttributes())
{
$attrs = array();
foreach ($element->attributes as $name => $attr)
{
$value = $attr->value;
// In XHTML, empty values should never exist, so we repeat the value
if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML))
{
$value = $name;
}
// For HTML, empty is fine
elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML))
{
$attrs[] = $name;
continue;
}
// Standard attribute text
$attrs[] = $name . '="' . $attr->value . '"';
}
$text .= ' ' . implode(' ', $attrs);
}
$text .= '>';
$fragment->appendChild(new DOMText($text));
}
$number = $element->childNodes->length;
for ($i = $number; $i > 0; $i--)
{
$child = $element->childNodes->item(0);
$fragment->appendChild($child);
}
if (!in_array($tag, array('script', 'style')))
{
$fragment->appendChild(new DOMText('</' . $tag . '>'));
}
$element->parentNode->replaceChild($fragment, $element);
}
return;
}
elseif (in_array($tag, array('script', 'style')))
{
foreach ($elements as $element)
{
$element->parentNode->removeChild($element);
}
return;
}
else
{
foreach ($elements as $element)
{
$fragment = $document->createDocumentFragment();
$number = $element->childNodes->length;
for ($i = $number; $i > 0; $i--)
{
$child = $element->childNodes->item(0);
$fragment->appendChild($child);
}
$element->parentNode->replaceChild($fragment, $element);
}
}
}
protected function strip_attr($attrib, $document)
{
$xpath = new DOMXPath($document);
$elements = $xpath->query('//*[@' . $attrib . ']');
foreach ($elements as $element)
{
$element->removeAttribute($attrib);
}
}
}

View File

@ -0,0 +1,611 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Handles `<atom:source>`
*
* Used by {@see SimplePie_Item::get_source()}
*
* This class can be overloaded with {@see SimplePie::set_source_class()}
*
* @package SimplePie
* @subpackage API
*/
class SimplePie_Source
{
var $item;
var $data = array();
protected $registry;
public function __construct($item, $data)
{
$this->item = $item;
$this->data = $data;
}
public function set_registry(SimplePie_Registry $registry)
{
$this->registry = $registry;
}
public function __toString()
{
return md5(serialize($this->data));
}
public function get_source_tags($namespace, $tag)
{
if (isset($this->data['child'][$namespace][$tag]))
{
return $this->data['child'][$namespace][$tag];
}
else
{
return null;
}
}
public function get_base($element = array())
{
return $this->item->get_base($element);
}
public function sanitize($data, $type, $base = '')
{
return $this->item->sanitize($data, $type, $base);
}
public function get_item()
{
return $this->item;
}
public function get_title()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
{
return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
{
return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
else
{
return null;
}
}
public function get_category($key = 0)
{
$categories = $this->get_categories();
if (isset($categories[$key]))
{
return $categories[$key];
}
else
{
return null;
}
}
public function get_categories()
{
$categories = array();
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
{
$term = null;
$scheme = null;
$label = null;
if (isset($category['attribs']['']['term']))
{
$term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme']))
{
$scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['label']))
{
$label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create('Category', array($term, $scheme, $label));
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
{
// This is really the label, but keep this as the term also for BC.
// Label will also work on retrieving because that falls back to term.
$term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
if (isset($category['attribs']['']['domain']))
{
$scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
}
else
{
$scheme = null;
}
$categories[] = $this->registry->create('Category', array($term, $scheme, null));
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
{
$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
{
$categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
}
if (!empty($categories))
{
return array_unique($categories);
}
else
{
return null;
}
}
public function get_author($key = 0)
{
$authors = $this->get_authors();
if (isset($authors[$key]))
{
return $authors[$key];
}
else
{
return null;
}
}
public function get_authors()
{
$authors = array();
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
{
$name = null;
$uri = null;
$email = null;
if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
{
$name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
{
$uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
{
$email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null)
{
$authors[] = $this->registry->create('Author', array($name, $uri, $email));
}
}
if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
{
$name = null;
$url = null;
$email = null;
if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
{
$name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
{
$url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
{
$email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null)
{
$authors[] = $this->registry->create('Author', array($name, $url, $email));
}
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
{
$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
{
$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
{
$authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
}
if (!empty($authors))
{
return array_unique($authors);
}
else
{
return null;
}
}
public function get_contributor($key = 0)
{
$contributors = $this->get_contributors();
if (isset($contributors[$key]))
{
return $contributors[$key];
}
else
{
return null;
}
}
public function get_contributors()
{
$contributors = array();
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
{
$name = null;
$uri = null;
$email = null;
if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
{
$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
{
$uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
{
$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null)
{
$contributors[] = $this->registry->create('Author', array($name, $uri, $email));
}
}
foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
{
$name = null;
$url = null;
$email = null;
if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
{
$name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
{
$url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
{
$email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null)
{
$contributors[] = $this->registry->create('Author', array($name, $url, $email));
}
}
if (!empty($contributors))
{
return array_unique($contributors);
}
else
{
return null;
}
}
public function get_link($key = 0, $rel = 'alternate')
{
$links = $this->get_links($rel);
if (isset($links[$key]))
{
return $links[$key];
}
else
{
return null;
}
}
/**
* Added for parity between the parent-level and the item/entry-level.
*/
public function get_permalink()
{
return $this->get_link(0);
}
public function get_links($rel = 'alternate')
{
if (!isset($this->data['links']))
{
$this->data['links'] = array();
if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
{
foreach ($links as $link)
{
if (isset($link['attribs']['']['href']))
{
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
}
}
}
if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
{
foreach ($links as $link)
{
if (isset($link['attribs']['']['href']))
{
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
}
}
}
if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
{
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
{
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
{
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
}
$keys = array_keys($this->data['links']);
foreach ($keys as $key)
{
if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
{
if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
{
$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
$this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
}
else
{
$this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
}
}
elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
{
$this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
}
$this->data['links'][$key] = array_unique($this->data['links'][$key]);
}
}
if (isset($this->data['links'][$rel]))
{
return $this->data['links'][$rel];
}
else
{
return null;
}
}
public function get_description()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
{
return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
{
return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
}
else
{
return null;
}
}
public function get_copyright()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
{
return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
{
return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
else
{
return null;
}
}
public function get_language()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
}
elseif (isset($this->data['xml_lang']))
{
return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
}
else
{
return null;
}
}
public function get_latitude()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
{
return (float) $return[0]['data'];
}
elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
{
return (float) $match[1];
}
else
{
return null;
}
}
public function get_longitude()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
{
return (float) $return[0]['data'];
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
{
return (float) $return[0]['data'];
}
elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
{
return (float) $match[2];
}
else
{
return null;
}
}
public function get_image_url()
{
if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
{
return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
}
elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
{
return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,362 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Parses the XML Declaration
*
* @package SimplePie
* @subpackage Parsing
*/
class SimplePie_XML_Declaration_Parser
{
/**
* XML Version
*
* @access public
* @var string
*/
var $version = '1.0';
/**
* Encoding
*
* @access public
* @var string
*/
var $encoding = 'UTF-8';
/**
* Standalone
*
* @access public
* @var bool
*/
var $standalone = false;
/**
* Current state of the state machine
*
* @access private
* @var string
*/
var $state = 'before_version_name';
/**
* Input data
*
* @access private
* @var string
*/
var $data = '';
/**
* Input data length (to avoid calling strlen() everytime this is needed)
*
* @access private
* @var int
*/
var $data_length = 0;
/**
* Current position of the pointer
*
* @var int
* @access private
*/
var $position = 0;
/**
* Create an instance of the class with the input data
*
* @access public
* @param string $data Input data
*/
public function __construct($data)
{
$this->data = $data;
$this->data_length = strlen($this->data);
}
/**
* Parse the input data
*
* @access public
* @return bool true on success, false on failure
*/
public function parse()
{
while ($this->state && $this->state !== 'emit' && $this->has_data())
{
$state = $this->state;
$this->$state();
}
$this->data = '';
if ($this->state === 'emit')
{
return true;
}
else
{
$this->version = '';
$this->encoding = '';
$this->standalone = '';
return false;
}
}
/**
* Check whether there is data beyond the pointer
*
* @access private
* @return bool true if there is further data, false if not
*/
public function has_data()
{
return (bool) ($this->position < $this->data_length);
}
/**
* Advance past any whitespace
*
* @return int Number of whitespace characters passed
*/
public function skip_whitespace()
{
$whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
$this->position += $whitespace;
return $whitespace;
}
/**
* Read value
*/
public function get_value()
{
$quote = substr($this->data, $this->position, 1);
if ($quote === '"' || $quote === "'")
{
$this->position++;
$len = strcspn($this->data, $quote, $this->position);
if ($this->has_data())
{
$value = substr($this->data, $this->position, $len);
$this->position += $len + 1;
return $value;
}
}
return false;
}
public function before_version_name()
{
if ($this->skip_whitespace())
{
$this->state = 'version_name';
}
else
{
$this->state = false;
}
}
public function version_name()
{
if (substr($this->data, $this->position, 7) === 'version')
{
$this->position += 7;
$this->skip_whitespace();
$this->state = 'version_equals';
}
else
{
$this->state = false;
}
}
public function version_equals()
{
if (substr($this->data, $this->position, 1) === '=')
{
$this->position++;
$this->skip_whitespace();
$this->state = 'version_value';
}
else
{
$this->state = false;
}
}
public function version_value()
{
if ($this->version = $this->get_value())
{
$this->skip_whitespace();
if ($this->has_data())
{
$this->state = 'encoding_name';
}
else
{
$this->state = 'emit';
}
}
else
{
$this->state = false;
}
}
public function encoding_name()
{
if (substr($this->data, $this->position, 8) === 'encoding')
{
$this->position += 8;
$this->skip_whitespace();
$this->state = 'encoding_equals';
}
else
{
$this->state = 'standalone_name';
}
}
public function encoding_equals()
{
if (substr($this->data, $this->position, 1) === '=')
{
$this->position++;
$this->skip_whitespace();
$this->state = 'encoding_value';
}
else
{
$this->state = false;
}
}
public function encoding_value()
{
if ($this->encoding = $this->get_value())
{
$this->skip_whitespace();
if ($this->has_data())
{
$this->state = 'standalone_name';
}
else
{
$this->state = 'emit';
}
}
else
{
$this->state = false;
}
}
public function standalone_name()
{
if (substr($this->data, $this->position, 10) === 'standalone')
{
$this->position += 10;
$this->skip_whitespace();
$this->state = 'standalone_equals';
}
else
{
$this->state = false;
}
}
public function standalone_equals()
{
if (substr($this->data, $this->position, 1) === '=')
{
$this->position++;
$this->skip_whitespace();
$this->state = 'standalone_value';
}
else
{
$this->state = false;
}
}
public function standalone_value()
{
if ($standalone = $this->get_value())
{
switch ($standalone)
{
case 'yes':
$this->standalone = true;
break;
case 'no':
$this->standalone = false;
break;
default:
$this->state = false;
return;
}
$this->skip_whitespace();
if ($this->has_data())
{
$this->state = false;
}
else
{
$this->state = 'emit';
}
}
else
{
$this->state = false;
}
}
}

View File

@ -0,0 +1,371 @@
<?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @version 1.3.1
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
* @author Ryan Parman
* @author Geoffrey Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Decode 'gzip' encoded HTTP data
*
* @package SimplePie
* @subpackage HTTP
* @link http://www.gzip.org/format.txt
*/
class SimplePie_gzdecode
{
/**
* Compressed data
*
* @access private
* @var string
* @see gzdecode::$data
*/
var $compressed_data;
/**
* Size of compressed data
*
* @access private
* @var int
*/
var $compressed_size;
/**
* Minimum size of a valid gzip string
*
* @access private
* @var int
*/
var $min_compressed_size = 18;
/**
* Current position of pointer
*
* @access private
* @var int
*/
var $position = 0;
/**
* Flags (FLG)
*
* @access private
* @var int
*/
var $flags;
/**
* Uncompressed data
*
* @access public
* @see gzdecode::$compressed_data
* @var string
*/
var $data;
/**
* Modified time
*
* @access public
* @var int
*/
var $MTIME;
/**
* Extra Flags
*
* @access public
* @var int
*/
var $XFL;
/**
* Operating System
*
* @access public
* @var int
*/
var $OS;
/**
* Subfield ID 1
*
* @access public
* @see gzdecode::$extra_field
* @see gzdecode::$SI2
* @var string
*/
var $SI1;
/**
* Subfield ID 2
*
* @access public
* @see gzdecode::$extra_field
* @see gzdecode::$SI1
* @var string
*/
var $SI2;
/**
* Extra field content
*
* @access public
* @see gzdecode::$SI1
* @see gzdecode::$SI2
* @var string
*/
var $extra_field;
/**
* Original filename
*
* @access public
* @var string
*/
var $filename;
/**
* Human readable comment
*
* @access public
* @var string
*/
var $comment;
/**
* Don't allow anything to be set
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
trigger_error("Cannot write property $name", E_USER_ERROR);
}
/**
* Set the compressed string and related properties
*
* @param string $data
*/
public function __construct($data)
{
$this->compressed_data = $data;
$this->compressed_size = strlen($data);
}
/**
* Decode the GZIP stream
*
* @return bool Successfulness
*/
public function parse()
{
if ($this->compressed_size >= $this->min_compressed_size)
{
// Check ID1, ID2, and CM
if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
{
return false;
}
// Get the FLG (FLaGs)
$this->flags = ord($this->compressed_data[3]);
// FLG bits above (1 << 4) are reserved
if ($this->flags > 0x1F)
{
return false;
}
// Advance the pointer after the above
$this->position += 4;
// MTIME
$mtime = substr($this->compressed_data, $this->position, 4);
// Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
if (current(unpack('S', "\x00\x01")) === 1)
{
$mtime = strrev($mtime);
}
$this->MTIME = current(unpack('l', $mtime));
$this->position += 4;
// Get the XFL (eXtra FLags)
$this->XFL = ord($this->compressed_data[$this->position++]);
// Get the OS (Operating System)
$this->OS = ord($this->compressed_data[$this->position++]);
// Parse the FEXTRA
if ($this->flags & 4)
{
// Read subfield IDs
$this->SI1 = $this->compressed_data[$this->position++];
$this->SI2 = $this->compressed_data[$this->position++];
// SI2 set to zero is reserved for future use
if ($this->SI2 === "\x00")
{
return false;
}
// Get the length of the extra field
$len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
$this->position += 2;
// Check the length of the string is still valid
$this->min_compressed_size += $len + 4;
if ($this->compressed_size >= $this->min_compressed_size)
{
// Set the extra field to the given data
$this->extra_field = substr($this->compressed_data, $this->position, $len);
$this->position += $len;
}
else
{
return false;
}
}
// Parse the FNAME
if ($this->flags & 8)
{
// Get the length of the filename
$len = strcspn($this->compressed_data, "\x00", $this->position);
// Check the length of the string is still valid
$this->min_compressed_size += $len + 1;
if ($this->compressed_size >= $this->min_compressed_size)
{
// Set the original filename to the given string
$this->filename = substr($this->compressed_data, $this->position, $len);
$this->position += $len + 1;
}
else
{
return false;
}
}
// Parse the FCOMMENT
if ($this->flags & 16)
{
// Get the length of the comment
$len = strcspn($this->compressed_data, "\x00", $this->position);
// Check the length of the string is still valid
$this->min_compressed_size += $len + 1;
if ($this->compressed_size >= $this->min_compressed_size)
{
// Set the original comment to the given string
$this->comment = substr($this->compressed_data, $this->position, $len);
$this->position += $len + 1;
}
else
{
return false;
}
}
// Parse the FHCRC
if ($this->flags & 2)
{
// Check the length of the string is still valid
$this->min_compressed_size += $len + 2;
if ($this->compressed_size >= $this->min_compressed_size)
{
// Read the CRC
$crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
// Check the CRC matches
if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
{
$this->position += 2;
}
else
{
return false;
}
}
else
{
return false;
}
}
// Decompress the actual data
if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
{
return false;
}
else
{
$this->position = $this->compressed_size - 8;
}
// Check CRC of data
$crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
$this->position += 4;
/*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
{
return false;
}*/
// Check ISIZE of data
$isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
$this->position += 4;
if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
{
return false;
}
// Wow, against all odds, we've actually got a valid gzip string
return true;
}
else
{
return false;
}
}
}

661
vendor/full-text-rss/license.txt vendored Normal file
View File

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

1195
vendor/full-text-rss/makefulltextfeed.php vendored Normal file

File diff suppressed because it is too large Load Diff

14
vendor/full-text-rss/manifest.yml vendored Normal file
View File

@ -0,0 +1,14 @@
---
applications:
.:
# name: full-text-rss
framework:
name: php
info:
mem: 512M
description: PHP Application
exec:
infra: aws
# url: ${name}.${target-base}
mem: 512M
instances: 1

View File

@ -0,0 +1,6 @@
Full-Text RSS Site Patterns
---------------------------
Site patterns allow you to specify what should be extracted from specific sites.
Please see http://help.fivefilters.org/customer/portal/articles/223153-site-patterns for more information.

Some files were not shown because too many files have changed in this diff Show More