зеркало из https://github.com/nextcloud/news.git
fix #734 and set a CSP on master
This commit is contained in:
Родитель
4b6e528a5f
Коммит
04dc1076f1
|
@ -57,15 +57,20 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "0a1d0d3950f7f047dc8fb1d80aa6296e15f306d0"
|
||||
"reference": "6f0ac9238dcb0899c8322933e7f4598890ecf744"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/0a1d0d3950f7f047dc8fb1d80aa6296e15f306d0",
|
||||
"reference": "0a1d0d3950f7f047dc8fb1d80aa6296e15f306d0",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/6f0ac9238dcb0899c8322933e7f4598890ecf744",
|
||||
"reference": "6f0ac9238dcb0899c8322933e7f4598890ecf744",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -86,7 +91,7 @@
|
|||
],
|
||||
"description": "Modern library to write or read feeds (RSS/Atom)",
|
||||
"homepage": "http://fguillot.github.io/picoFeed",
|
||||
"time": "2015-01-27 01:56:40"
|
||||
"time": "2015-02-18 02:47:12"
|
||||
},
|
||||
{
|
||||
"name": "pear/net_url2",
|
||||
|
|
|
@ -66,10 +66,23 @@ class PageController extends Controller {
|
|||
*/
|
||||
public function index() {
|
||||
$status = $this->statusService->getStatus();
|
||||
|
||||
return new TemplateResponse($this->appName, 'index', [
|
||||
$response = new TemplateResponse($this->appName, 'index', [
|
||||
'cronWarning' => $status['warnings']['improperlyConfiguredCron']
|
||||
]);
|
||||
|
||||
// set csp rules for ownCloud 8.1
|
||||
if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedImageDomain('*');
|
||||
$csp->addAllowedMediaDomain('*');
|
||||
$csp->addAllowedFrameDomain('https://youtube.com');
|
||||
$csp->addAllowedFrameDomain('https://www.youtube.com');
|
||||
$csp->addAllowedFrameDomain('https://player.vimeo.com');
|
||||
$csp->addAllowedFrameDomain('https://www.player.vimeo.com');
|
||||
$response->setContentSecurityPolicy($csp);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -4,4 +4,4 @@
|
|||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitd76dfeebb5ddb82dad6b87cf02d1b308::getLoader();
|
||||
return ComposerAutoloaderInit373583df083e6f26edc2b67c5b6380e9::getLoader();
|
||||
|
|
|
@ -54,6 +54,8 @@ class ClassLoader
|
|||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
|
@ -248,6 +250,27 @@ class ClassLoader
|
|||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
|
@ -299,6 +322,9 @@ class ClassLoader
|
|||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitd76dfeebb5ddb82dad6b87cf02d1b308
|
||||
class ComposerAutoloaderInit373583df083e6f26edc2b67c5b6380e9
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
|
@ -19,9 +19,9 @@ class ComposerAutoloaderInitd76dfeebb5ddb82dad6b87cf02d1b308
|
|||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitd76dfeebb5ddb82dad6b87cf02d1b308', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInit373583df083e6f26edc2b67c5b6380e9', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitd76dfeebb5ddb82dad6b87cf02d1b308', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit373583df083e6f26edc2b67c5b6380e9', 'loadClassLoader'));
|
||||
|
||||
$includePaths = require __DIR__ . '/include_paths.php';
|
||||
array_push($includePaths, get_include_path());
|
||||
|
@ -46,14 +46,14 @@ class ComposerAutoloaderInitd76dfeebb5ddb82dad6b87cf02d1b308
|
|||
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
foreach ($includeFiles as $file) {
|
||||
composerRequired76dfeebb5ddb82dad6b87cf02d1b308($file);
|
||||
composerRequire373583df083e6f26edc2b67c5b6380e9($file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequired76dfeebb5ddb82dad6b87cf02d1b308($file)
|
||||
function composerRequire373583df083e6f26edc2b67c5b6380e9($file)
|
||||
{
|
||||
require $file;
|
||||
}
|
||||
|
|
|
@ -46,45 +46,6 @@
|
|||
"html"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picofeed",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "0a1d0d3950f7f047dc8fb1d80aa6296e15f306d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/0a1d0d3950f7f047dc8fb1d80aa6296e15f306d0",
|
||||
"reference": "0a1d0d3950f7f047dc8fb1d80aa6296e15f306d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2015-01-27 01:56:40",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PicoFeed": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Unlicense"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "Modern library to write or read feeds (RSS/Atom)",
|
||||
"homepage": "http://fguillot.github.io/picoFeed"
|
||||
},
|
||||
{
|
||||
"name": "pear/net_url2",
|
||||
"version": "v2.1.1",
|
||||
|
@ -150,5 +111,49 @@
|
|||
"uri",
|
||||
"url"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picofeed",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "6f0ac9238dcb0899c8322933e7f4598890ecf744"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/6f0ac9238dcb0899c8322933e7f4598890ecf744",
|
||||
"reference": "6f0ac9238dcb0899c8322933e7f4598890ecf744",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2015-02-18 02:47:12",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PicoFeed": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Unlicense"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "Modern library to write or read feeds (RSS/Atom)",
|
||||
"homepage": "http://fguillot.github.io/picoFeed"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -33,6 +33,7 @@ Requirements
|
|||
- libxml >= 2.7
|
||||
- XML PHP extensions: DOM and SimpleXML
|
||||
- cURL or Stream Context (`allow_url_fopen=On`)
|
||||
- iconv extension
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
|
|
@ -11,7 +11,12 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
"php": ">=5.3.0",
|
||||
"ext-iconv": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-SimpleXML": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"PicoFeed": "lib/"}
|
||||
|
|
|
@ -283,4 +283,14 @@ $config->setFilterImageProxyCallback(function ($image_url) {
|
|||
$key = hash_hmac('sha1', $image_url, 'secret');
|
||||
return 'https://mypublicproxy/'.$key.'/'.urlencode($image_url);
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
### Define image proxy protocol restriction
|
||||
|
||||
- Method name: `setFilterImageProxyProtocol()`
|
||||
- Default value: Empty (all protocols)
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setFilterImageProxyProtocol('http');
|
||||
```
|
||||
|
|
|
@ -158,46 +158,21 @@ abstract class Client
|
|||
protected $status_code = 0;
|
||||
|
||||
/**
|
||||
* HTTP response body
|
||||
* Enables direct passthrough to requesting client
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
* @var bool
|
||||
*/
|
||||
protected $body = '';
|
||||
|
||||
/**
|
||||
* Body size
|
||||
*
|
||||
* @access protected
|
||||
* @var integer
|
||||
*/
|
||||
protected $body_length = 0;
|
||||
|
||||
/**
|
||||
* HTTP response headers
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = array();
|
||||
|
||||
/**
|
||||
* Counter on the number of header received
|
||||
*
|
||||
* @access protected
|
||||
* @var integer
|
||||
*/
|
||||
protected $headers_counter = 0;
|
||||
protected $passthrough = false;
|
||||
|
||||
/**
|
||||
* Do the HTTP request
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param bool $follow_location Flag used when there is an open_basedir restriction
|
||||
* @return array
|
||||
*/
|
||||
abstract public function doRequest($follow_location = true);
|
||||
abstract public function doRequest();
|
||||
|
||||
/**
|
||||
* Get client instance: curl or stream driver
|
||||
|
@ -295,48 +270,6 @@ abstract class Client
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle manually redirections when there is an open base dir restriction
|
||||
*
|
||||
* @access private
|
||||
* @param string $location Redirected URL
|
||||
* @return array
|
||||
*/
|
||||
public function handleRedirection($location)
|
||||
{
|
||||
$nb_redirects = 0;
|
||||
$result = array();
|
||||
$this->url = Url::resolve($location, $this->url);
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
$this->headers = array();
|
||||
$this->headers_counter = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
$nb_redirects++;
|
||||
|
||||
if ($nb_redirects >= $this->max_redirects) {
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
}
|
||||
|
||||
$result = $this->doRequest(false);
|
||||
|
||||
if ($result['status'] == 301 || $result['status'] == 302) {
|
||||
$this->url = $result['headers']['Location'];
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
$this->headers = array();
|
||||
$this->headers_counter = 0;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a request has been modified according to the parameters
|
||||
*
|
||||
|
@ -537,6 +470,17 @@ abstract class Client
|
|||
return $this->is_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if passthrough mode is enabled
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isPassthroughEnabled()
|
||||
{
|
||||
return $this->passthrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection timeout
|
||||
*
|
||||
|
@ -667,6 +611,30 @@ abstract class Client
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the passthrough mode
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function enablePassthroughMode()
|
||||
{
|
||||
$this->passthrough = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the passthrough mode
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function disablePassthroughMode()
|
||||
{
|
||||
$this->passthrough = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config object
|
||||
*
|
||||
|
|
|
@ -12,6 +12,38 @@ use PicoFeed\Logging\Logger;
|
|||
*/
|
||||
class Curl extends Client
|
||||
{
|
||||
/**
|
||||
* HTTP response body
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $body = '';
|
||||
|
||||
/**
|
||||
* Body size
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $body_length = 0;
|
||||
|
||||
/**
|
||||
* HTTP response headers
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $headers = array();
|
||||
|
||||
/**
|
||||
* Counter on the number of header received
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $headers_counter = 0;
|
||||
|
||||
/**
|
||||
* cURL callback to read the HTTP body
|
||||
*
|
||||
|
@ -63,6 +95,44 @@ class Curl extends Client
|
|||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* cURL callback to passthrough the HTTP status header to the client
|
||||
*
|
||||
* @access public
|
||||
* @param resource $ch cURL handler
|
||||
* @param string $buffer Header line
|
||||
* @return integer Length of the buffer
|
||||
*/
|
||||
public function passthroughHeaders($ch, $buffer)
|
||||
{
|
||||
list($status, $headers) = HttpHeaders::parse(array($buffer));
|
||||
|
||||
if ($status !== 0) {
|
||||
header(':', true, $status);
|
||||
}
|
||||
elseif (isset($headers['Content-Type'])) {
|
||||
header($buffer);
|
||||
}
|
||||
|
||||
return $this->readHeaders($ch, $buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* cURL callback to passthrough the HTTP body to the client
|
||||
*
|
||||
* If the function return -1, curl stop to read the HTTP response
|
||||
*
|
||||
* @access public
|
||||
* @param resource $ch cURL handler
|
||||
* @param string $buffer Chunk of data
|
||||
* @return integer Length of the buffer
|
||||
*/
|
||||
public function passthroughBody($ch, $buffer)
|
||||
{
|
||||
echo $buffer;
|
||||
return strlen($buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare HTTP headers
|
||||
*
|
||||
|
@ -130,6 +200,29 @@ class Curl extends Client
|
|||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set write/header functions
|
||||
*
|
||||
* @access private
|
||||
* @return resource $ch
|
||||
*/
|
||||
private function prepareDownloadMode($ch)
|
||||
{
|
||||
$write_function = 'readBody';
|
||||
$header_function = 'readHeaders';
|
||||
|
||||
if ($this->isPassthroughEnabled()) {
|
||||
$write_function = 'passthroughBody';
|
||||
$header_function = 'passthroughHeaders';
|
||||
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, $write_function));
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, $header_function));
|
||||
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare curl context
|
||||
*
|
||||
|
@ -147,12 +240,11 @@ class Curl extends Client
|
|||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, ini_get('open_basedir') === '');
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, $this->max_redirects);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, 'readBody'));
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'readHeaders'));
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory');
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory');
|
||||
curl_setopt($ch, CURLOPT_SSLVERSION, 1); // Enforce TLS v1
|
||||
|
||||
$ch = $this->prepareDownloadMode($ch);
|
||||
$ch = $this->prepareProxyContext($ch);
|
||||
$ch = $this->prepareAuthContext($ch);
|
||||
|
||||
|
@ -228,6 +320,48 @@ class Curl extends Client
|
|||
return $follow_location && ini_get('open_basedir') !== '' && ($status == 301 || $status == 302);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle manually redirections when there is an open base dir restriction
|
||||
*
|
||||
* @access private
|
||||
* @param string $location Redirected URL
|
||||
* @return array
|
||||
*/
|
||||
private function handleRedirection($location)
|
||||
{
|
||||
$nb_redirects = 0;
|
||||
$result = array();
|
||||
$this->url = Url::resolve($location, $this->url);
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
$this->headers = array();
|
||||
$this->headers_counter = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
$nb_redirects++;
|
||||
|
||||
if ($nb_redirects >= $this->max_redirects) {
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
}
|
||||
|
||||
$result = $this->doRequest(false);
|
||||
|
||||
if ($result['status'] == 301 || $result['status'] == 302) {
|
||||
$this->url = Url::resolve($result['headers']['Location'], $this->url);
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
$this->headers = array();
|
||||
$this->headers_counter = 0;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cURL errors (throw individual exceptions)
|
||||
*
|
||||
|
|
|
@ -53,12 +53,13 @@ class HttpHeaders implements ArrayAccess
|
|||
*/
|
||||
public static function parse(array $lines)
|
||||
{
|
||||
$status = 200;
|
||||
$status = 0;
|
||||
$headers = array();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
|
||||
if (strpos($line, 'HTTP') === 0) {
|
||||
$headers = array();
|
||||
$status = (int) substr($line, 9, 3);
|
||||
}
|
||||
else if (strpos($line, ':') !== false) {
|
||||
|
|
|
@ -25,7 +25,9 @@ class Stream extends Client
|
|||
'User-Agent: '.$this->user_agent,
|
||||
);
|
||||
|
||||
if (function_exists('gzdecode')) {
|
||||
// disable compression in passthrough mode. It could result in double
|
||||
// compressed content which isn't decodeable by browsers
|
||||
if (function_exists('gzdecode') && ! $this->isPassthroughEnabled()) {
|
||||
$headers[] = 'Accept-Encoding: gzip';
|
||||
}
|
||||
|
||||
|
@ -48,6 +50,23 @@ class Stream extends Client
|
|||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the final URL from location headers
|
||||
*
|
||||
* @access private
|
||||
* @param array $headers List of HTTP response header
|
||||
*/
|
||||
private function setEffectiveUrl($headers)
|
||||
{
|
||||
foreach($headers as $header) {
|
||||
if (stripos($header, 'Location') === 0) {
|
||||
list($name, $value) = explode(': ', $header);
|
||||
|
||||
$this->url = Url::resolve($value, $this->url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare stream context
|
||||
*
|
||||
|
@ -61,7 +80,7 @@ class Stream extends Client
|
|||
'method' => 'GET',
|
||||
'protocol_version' => 1.1,
|
||||
'timeout' => $this->timeout,
|
||||
'follow_location' => 0,
|
||||
'max_redirects' => $this->max_redirects,
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -89,11 +108,12 @@ class Stream extends Client
|
|||
* Do the HTTP request
|
||||
*
|
||||
* @access public
|
||||
* @param bool $follow_location Flag used when there is an open_basedir restriction
|
||||
* @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
|
||||
* @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
|
||||
*/
|
||||
public function doRequest($follow_location = false)
|
||||
public function doRequest()
|
||||
{
|
||||
$body = '';
|
||||
|
||||
// Create context
|
||||
$context = stream_context_create($this->prepareContext());
|
||||
|
||||
|
@ -103,30 +123,36 @@ class Stream extends Client
|
|||
throw new InvalidUrlException('Unable to establish a connection');
|
||||
}
|
||||
|
||||
// Get the entire body until the max size
|
||||
$body = stream_get_contents($stream, $this->max_body_size + 1);
|
||||
|
||||
// If the body size is too large abort everything
|
||||
if (strlen($body) > $this->max_body_size) {
|
||||
throw new MaxSizeException('Content size too large');
|
||||
}
|
||||
|
||||
// Get HTTP headers response
|
||||
$metadata = stream_get_meta_data($stream);
|
||||
|
||||
if ($metadata['timed_out']) {
|
||||
throw new TimeoutException('Operation timeout');
|
||||
}
|
||||
|
||||
list($status, $headers) = HttpHeaders::parse($metadata['wrapper_data']);
|
||||
|
||||
if ($this->isPassthroughEnabled()) {
|
||||
header(':', true, $status);
|
||||
|
||||
if (isset($headers['Content-Type'])) {
|
||||
header('Content-Type: '.$headers['Content-Type']);
|
||||
}
|
||||
|
||||
fpassthru($stream);
|
||||
}
|
||||
else {
|
||||
// Get the entire body until the max size
|
||||
$body = stream_get_contents($stream, $this->max_body_size + 1);
|
||||
|
||||
// If the body size is too large abort everything
|
||||
if (strlen($body) > $this->max_body_size) {
|
||||
throw new MaxSizeException('Content size too large');
|
||||
}
|
||||
|
||||
if ($metadata['timed_out']) {
|
||||
throw new TimeoutException('Operation timeout');
|
||||
}
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
|
||||
// Do redirect manual to get only the headers of the last request and
|
||||
// the final url
|
||||
if ($status == 301 || $status == 302) {
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
}
|
||||
$this->setEffectiveUrl($metadata['wrapper_data']);
|
||||
|
||||
return array(
|
||||
'status' => $status,
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace PicoFeed\Config;
|
|||
* @method \PicoFeed\Config\Config setFilterBlacklistedTags(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterImageProxyUrl($value)
|
||||
* @method \PicoFeed\Config\Config setFilterImageProxyCallback($closure)
|
||||
* @method \PicoFeed\Config\Config setFilterImageProxyProtocol($value)
|
||||
*
|
||||
* @method integer getClientTimeout()
|
||||
* @method string getClientUserAgent()
|
||||
|
@ -57,6 +58,7 @@ namespace PicoFeed\Config;
|
|||
* @method array getFilterBlacklistedTags(array $default_value)
|
||||
* @method string getFilterImageProxyUrl()
|
||||
* @method \Closure getFilterImageProxyCallback()
|
||||
* @method string getFilterImageProxyProtocol()
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
|
|
|
@ -3,165 +3,19 @@
|
|||
namespace PicoFeed\Encoding;
|
||||
|
||||
/**
|
||||
* @author "Sebastián Grignoli" <grignoli@framework2.com.ar>
|
||||
* @package Encoding
|
||||
* @version 1.2
|
||||
* @link https://github.com/neitanod/forceutf8
|
||||
* @example https://github.com/neitanod/forceutf8
|
||||
* @license Revised BSD
|
||||
* Encoding class
|
||||
*
|
||||
* @package Encoding
|
||||
*/
|
||||
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"
|
||||
);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
public static function toUTF8($text)
|
||||
{
|
||||
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.
|
||||
$buf .= self::convertInvalidCharacter($c1);
|
||||
}
|
||||
}
|
||||
else if ($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.
|
||||
$buf .= self::convertInvalidCharacter($c1);
|
||||
}
|
||||
}
|
||||
else if ($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.
|
||||
$buf .= self::convertInvalidCharacter($c1);
|
||||
}
|
||||
}
|
||||
else { //doesn't look like UTF8, but should be converted
|
||||
$buf .= self::convertInvalidCharacter($c1);
|
||||
}
|
||||
}
|
||||
elseif (($c1 & "\xc0") == "\x80") { // needs conversion
|
||||
|
||||
if (isset(self::$win1252ToUtf8[ord($c1)])) { //found in Windows-1252 special cases
|
||||
$buf .= self::$win1252ToUtf8[ord($c1)];
|
||||
}
|
||||
else {
|
||||
$buf .= self::convertInvalidCharacter($c1);
|
||||
}
|
||||
}
|
||||
else { // it doesn't need conversion
|
||||
$buf .= $c1;
|
||||
}
|
||||
}
|
||||
|
||||
return $buf;
|
||||
}
|
||||
else {
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
||||
public static function convertInvalidCharacter($c1)
|
||||
{
|
||||
$cc1 = chr(ord($c1) / 64) | "\xc0";
|
||||
$cc2 = ($c1 & "\x3f") | "\x80";
|
||||
return $cc1.$cc2;
|
||||
}
|
||||
|
||||
public static function convert($input, $encoding)
|
||||
{
|
||||
switch ($encoding) {
|
||||
case 'utf-8':
|
||||
return $input;
|
||||
case 'windows-1251':
|
||||
case 'windows-1255':
|
||||
return iconv($encoding, 'UTF-8//TRANSLIT', $input);
|
||||
default:
|
||||
return self::toUTF8($input);
|
||||
if ($encoding === 'utf-8' || $encoding === '') {
|
||||
return $input;
|
||||
}
|
||||
|
||||
// convert input to utf-8; ignore malformed characters
|
||||
return iconv($encoding, 'UTF-8//IGNORE', $input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace PicoFeed\Filter;
|
||||
|
||||
use \PicoFeed\Client\Url;
|
||||
use PicoFeed\Client\Url;
|
||||
|
||||
/**
|
||||
* Attribute Filter class
|
||||
|
@ -28,6 +28,14 @@ class Attribute
|
|||
*/
|
||||
private $image_proxy_callback = null;
|
||||
|
||||
/**
|
||||
* limits the image proxy usage to this protocol
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $image_proxy_limit_protocol = '';
|
||||
|
||||
/**
|
||||
* Tags and attribute whitelist
|
||||
*
|
||||
|
@ -225,6 +233,7 @@ class Attribute
|
|||
'filterBlacklistResourceAttribute',
|
||||
'filterProtocolUrlAttribute',
|
||||
'rewriteImageProxyUrl',
|
||||
'secureIframeSrc',
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -273,8 +282,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterEmptyAttribute($tag, $attribute, $value)
|
||||
|
@ -287,8 +296,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterAllowedAttribute($tag, $attribute, $value)
|
||||
|
@ -301,8 +310,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterIntegerAttribute($tag, $attribute, $value)
|
||||
|
@ -319,8 +328,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterIframeAttribute($tag, $attribute, $value)
|
||||
|
@ -344,8 +353,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterBlacklistResourceAttribute($tag, $attribute, $value)
|
||||
|
@ -362,8 +371,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function rewriteAbsoluteUrl($tag, $attribute, &$value)
|
||||
|
@ -376,17 +385,37 @@ class Attribute
|
|||
}
|
||||
|
||||
/**
|
||||
* Rewrite image url to use with a proxy (HTTPS resource are ignored)
|
||||
* Turns iframes' src attribute from http to https to prevent
|
||||
* mixed active content
|
||||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param array $attribute Atttributes name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function secureIframeSrc($tag, $attribute, &$value)
|
||||
{
|
||||
if ($tag === 'iframe' && $attribute === 'src' && strpos($value, 'http://') === 0) {
|
||||
$value = substr_replace($value, 's', 4, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite image url to use with a proxy
|
||||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function rewriteImageProxyUrl($tag, $attribute, &$value)
|
||||
{
|
||||
if ($tag === 'img' && $attribute === 'src' && strpos($value, 'http:') === 0) {
|
||||
if ($tag === 'img' && $attribute === 'src'
|
||||
&& ! ($this->image_proxy_limit_protocol !== '' && stripos($value, $this->image_proxy_limit_protocol.':') !== 0)) {
|
||||
|
||||
if ($this->image_proxy_url) {
|
||||
$value = sprintf($this->image_proxy_url, rawurlencode($value));
|
||||
|
@ -404,8 +433,8 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $value Attribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterProtocolUrlAttribute($tag, $attribute, $value)
|
||||
|
@ -422,7 +451,7 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param array $attributes Atttributes list
|
||||
* @param array $attributes Attributes list
|
||||
* @return array
|
||||
*/
|
||||
public function addAttributes($tag, array $attributes)
|
||||
|
@ -439,7 +468,7 @@ class Attribute
|
|||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param array $attributes Atttributes list
|
||||
* @param array $attributes Attributes list
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasRequiredAttributes($tag, array $attributes)
|
||||
|
@ -655,4 +684,17 @@ class Attribute
|
|||
$this->image_proxy_callback = $callback ?: $this->image_proxy_callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image proxy protocol restriction
|
||||
*
|
||||
* @access public
|
||||
* @param string $value
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setImageProxyProtocol($value)
|
||||
{
|
||||
$this->image_proxy_limit_protocol = $value ?: $this->image_proxy_limit_protocol;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace PicoFeed\Filter;
|
||||
|
||||
use \PicoFeed\Client\Url;
|
||||
use \PicoFeed\Parser\XmlParser;
|
||||
use PicoFeed\Client\Url;
|
||||
use PicoFeed\Parser\XmlParser;
|
||||
|
||||
/**
|
||||
* HTML Filter class
|
||||
|
@ -98,6 +98,7 @@ class Html
|
|||
if ($this->config !== null) {
|
||||
$this->attribute->setImageProxyCallback($this->config->getFilterImageProxyCallback());
|
||||
$this->attribute->setImageProxyUrl($this->config->getFilterImageProxyUrl());
|
||||
$this->attribute->setImageProxyProtocol($this->config->getFilterImageProxyProtocol());
|
||||
$this->attribute->setIframeWhitelist($this->config->getFilterIframeWhitelist(array()));
|
||||
$this->attribute->setIntegerAttributes($this->config->getFilterIntegerAttributes(array()));
|
||||
$this->attribute->setAttributeOverrides($this->config->getFilterAttributeOverrides(array()));
|
||||
|
|
|
@ -198,8 +198,10 @@ class XmlParser
|
|||
$p1 = strpos($data, 'encoding=');
|
||||
$p2 = strpos($data, '"', $p1 + 10);
|
||||
|
||||
$encoding = substr($data, $p1 + 10, $p2 - $p1 - 10);
|
||||
$encoding = strtolower($encoding);
|
||||
if ($p1 !== false && $p2 !== false) {
|
||||
$encoding = substr($data, $p1 + 10, $p2 - $p1 - 10);
|
||||
$encoding = strtolower($encoding);
|
||||
}
|
||||
}
|
||||
|
||||
return $encoding;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
return array(
|
||||
'test_url' => 'http://www.theguardian.com/law/2013/aug/31/microsoft-google-sue-us-fisa',
|
||||
'test_url' => 'http://www.theguardian.com/sustainable-business/2015/feb/02/2015-hyper-transparency-global-business',
|
||||
'body' => array(
|
||||
'//div[@id="article-wrapper"]',
|
||||
'//div[contains(@class, "content__main-column--article")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//*[contains(@class, "promo")]',
|
||||
'//div[contains(@class, "meta-container")]',
|
||||
),
|
||||
);
|
||||
);
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
return array(
|
||||
'test_url' => 'http://www.alainonline.net/news_details.php?lang=arabic&sid=18907',
|
||||
'body' => array(
|
||||
'//div[@class="news_details"]'
|
||||
),
|
||||
'strip' => array(
|
||||
'//div[@class="news_details"]/div/div[last()]',
|
||||
),
|
||||
);
|
|
@ -18,6 +18,20 @@ class ClientTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertNotEmpty($client->getLastModified());
|
||||
}
|
||||
|
||||
// // disabled due to https://github.com/sebastianbergmann/phpunit/issues/1452
|
||||
// /**
|
||||
// * @runInSeparateProcess
|
||||
// */
|
||||
// public function testPassthrough()
|
||||
// {
|
||||
// $client = Client::getInstance();
|
||||
// $client->setUrl('http://miniflux.net/favicon.ico');
|
||||
// $client->enablePassthroughMode();
|
||||
// $client->execute();
|
||||
//
|
||||
// $this->expectOutputString('no_to_be_defined');
|
||||
// }
|
||||
|
||||
public function testCacheBothHaveToMatch()
|
||||
{
|
||||
$client = Client::getInstance();
|
||||
|
|
|
@ -18,18 +18,31 @@ class CurlTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
|
||||
}
|
||||
|
||||
// // disabled due to https://github.com/sebastianbergmann/phpunit/issues/1452
|
||||
// /**
|
||||
// * @runInSeparateProcess
|
||||
// */
|
||||
// public function testPassthrough()
|
||||
// {
|
||||
// $client = new Curl;
|
||||
// $client->setUrl('http://miniflux.net/favicon.ico');
|
||||
// $client->enablePassthroughMode();
|
||||
// $client->doRequest();
|
||||
//
|
||||
// $this->expectOutputString('no_to_be_defined');
|
||||
// }
|
||||
|
||||
public function testRedirect()
|
||||
{
|
||||
$client = new Curl;
|
||||
$client->setUrl('http://www.miniflux.net/index.html');
|
||||
$client->setUrl('http://rss.feedsportal.com/c/629/f/502199/s/42e50391/sc/44/l/0L0S0A1net0N0Ceditorial0C6437220Candroid0Egoogle0Enow0Es0Eouvre0Eaux0Eapplications0Etierces0C0T0Dxtor0FRSS0E16/story01.htm');
|
||||
$result = $client->doRequest();
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals(200, $result['status']);
|
||||
$this->assertEquals('<!DOCTYPE', substr($result['body'], 0, 9));
|
||||
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
|
||||
$this->assertEquals('http://miniflux.net/', $client->getUrl());
|
||||
$this->assertEquals('text/html', $result['headers']['Content-Type']);
|
||||
$this->assertEquals('http://www.01net.com/editorial/643722/android-google-now-s-ouvre-aux-applications-tierces/', str_replace('#?xtor=RSS-16', '', $client->getUrl()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,17 +27,31 @@ class StreamTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('</html>', substr(trim($result['body']), -7));
|
||||
}
|
||||
|
||||
// // disabled due to https://github.com/sebastianbergmann/phpunit/issues/1452
|
||||
// /**
|
||||
// * @runInSeparateProcess
|
||||
// */
|
||||
// public function testPassthrough()
|
||||
// {
|
||||
// $client = new Stream;
|
||||
// $client->setUrl('http://miniflux.net/favicon.ico');
|
||||
// $client->enablePassthroughMode();
|
||||
// $client->doRequest();
|
||||
//
|
||||
// $this->expectOutputString('no_to_be_defined');
|
||||
// }
|
||||
|
||||
public function testRedirect()
|
||||
{
|
||||
$client = new Stream;
|
||||
$client->setUrl('http://www.miniflux.net/index.html');
|
||||
$client->setUrl('http://rss.feedsportal.com/c/629/f/502199/s/42e50391/sc/44/l/0L0S0A1net0N0Ceditorial0C6437220Candroid0Egoogle0Enow0Es0Eouvre0Eaux0Eapplications0Etierces0C0T0Dxtor0FRSS0E16/story01.htm');
|
||||
$result = $client->doRequest();
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals(200, $result['status']);
|
||||
$this->assertEquals('<!DOCTYPE', substr($result['body'], 0, 9));
|
||||
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
|
||||
$this->assertEquals('http://miniflux.net/', $client->getUrl());
|
||||
$this->assertEquals('text/html', $result['headers']['Content-Type']);
|
||||
$this->assertEquals('http://www.01net.com/editorial/643722/android-google-now-s-ouvre-aux-applications-tierces/#?xtor=RSS-16', $client->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,8 +39,8 @@ class AttributeFilterTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertTrue($filter->filterIntegerAttribute('iframe', 'width', '450'));
|
||||
$this->assertFalse($filter->filterIntegerAttribute('iframe', 'width', 'test'));
|
||||
|
||||
$this->assertEquals(array('width' => '10', 'src' => 'http://www.youtube.com/test'), $filter->filter('iframe', array('width' => '10', 'src' => 'http://www.youtube.com/test')));
|
||||
$this->assertEquals(array('src' => 'http://www.youtube.com/test'), $filter->filter('iframe', array('width' => 'test', 'src' => 'http://www.youtube.com/test')));
|
||||
$this->assertEquals(array('width' => '10', 'src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('width' => '10', 'src' => 'http://www.youtube.com/test')));
|
||||
$this->assertEquals(array('src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('width' => 'test', 'src' => 'http://www.youtube.com/test')));
|
||||
}
|
||||
|
||||
public function testRewriteProxyImageUrl()
|
||||
|
@ -125,7 +125,7 @@ class AttributeFilterTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertFalse($filter->filterIframeAttribute('iframe', 'src', '//www.youtube.com/test'));
|
||||
$this->assertFalse($filter->filterIframeAttribute('iframe', 'src', '//www.bidule.com/test'));
|
||||
|
||||
$this->assertEquals(array('src' => 'http://www.youtube.com/test'), $filter->filter('iframe', array('src' => '//www.youtube.com/test')));
|
||||
$this->assertEquals(array('src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('src' => '//www.youtube.com/test')));
|
||||
}
|
||||
|
||||
public function testFilterBlacklistAttribute()
|
||||
|
|
|
@ -71,10 +71,11 @@ class FilterTest extends PHPUnit_Framework_TestCase
|
|||
public function testOverrideFilters()
|
||||
{
|
||||
$data = '<iframe src="http://www.kickstarter.com/projects/lefnire/habitrpg-mobile/widget/video.html" height="480" width="640" frameborder="0"></iframe>';
|
||||
$expected = '<iframe src="https://www.kickstarter.com/projects/lefnire/habitrpg-mobile/widget/video.html" height="480" width="640" frameborder="0"></iframe>';
|
||||
|
||||
$f = Filter::html($data, 'http://blabla');
|
||||
$f->attribute->setIframeWhitelist(array('http://www.kickstarter.com'));
|
||||
$this->assertEquals($data, $f->execute());
|
||||
$this->assertEquals($expected, $f->execute());
|
||||
|
||||
$data = '<iframe src="http://www.youtube.com/bla" height="480" width="640" frameborder="0"></iframe>';
|
||||
|
||||
|
@ -90,7 +91,7 @@ class FilterTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('<p>Testboo</p>', $f->execute());
|
||||
}
|
||||
|
||||
public function testImageProxy()
|
||||
public function testNoImageProxySet()
|
||||
{
|
||||
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
|
||||
|
@ -98,11 +99,72 @@ class FilterTest extends PHPUnit_Framework_TestCase
|
|||
'<p>Image <img src="http://foo/image.png" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
// Test setFilterImageProxyUrl and HTTPS
|
||||
public function testImageProxyWithHTTPLink()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
public function testImageProxyWithHTTPSLink()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
public function testImageProxyLimitedToUnknownProtocol()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
$config->setFilterImageProxyProtocol('tripleX');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
public function testImageProxyLimitedToHTTPwithHTTPLink()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
$config->setFilterImageProxyProtocol('http');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
public function testImageProxyLimitedToHTTPwithHTTPSLink()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
$config->setFilterImageProxyProtocol('http');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
|
@ -110,20 +172,40 @@ class FilterTest extends PHPUnit_Framework_TestCase
|
|||
'<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
// Test setFilterImageProxyUrl
|
||||
public function testImageProxyLimitedToHTTPSwithHTTPLink()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
$config->setFilterImageProxyProtocol('https');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://foo/image.png').'" alt="My Image"/></p>',
|
||||
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
// Test setFilterImageProxyCallback
|
||||
public function testImageProxyLimitedToHTTPSwithHTTPSLink()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
|
||||
$config->setFilterImageProxyProtocol('https');
|
||||
|
||||
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
|
||||
$f->setConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
|
||||
$f->execute()
|
||||
);
|
||||
}
|
||||
|
||||
public function testsetFilterImageProxyCallback()
|
||||
{
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyCallback(function ($image_url) {
|
||||
$key = hash_hmac('sha1', $image_url, 'secret');
|
||||
|
|
|
@ -25,9 +25,10 @@ class HtmlFilterTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEmpty($f->execute());
|
||||
|
||||
$data = '<iframe src="http://www.youtube.com/bla" height="480" width="640" frameborder="0"></iframe>';
|
||||
$expected = '<iframe src="https://www.youtube.com/bla" height="480" width="640" frameborder="0"></iframe>';
|
||||
|
||||
$f = new Html($data, 'http://blabla');
|
||||
$this->assertEquals($data, $f->execute());
|
||||
$this->assertEquals($expected, $f->execute());
|
||||
}
|
||||
|
||||
public function testEmptyTags()
|
||||
|
@ -50,7 +51,7 @@ EOD;
|
|||
$data = '<iframe src="http://www.youtube.com/bla" height="480px" width="100%" frameborder="0"></iframe>';
|
||||
|
||||
$f = new Html($data, 'http://blabla');
|
||||
$this->assertEquals('<iframe src="http://www.youtube.com/bla" frameborder="0"></iframe>', $f->execute());
|
||||
$this->assertEquals('<iframe src="https://www.youtube.com/bla" frameborder="0"></iframe>', $f->execute());
|
||||
}
|
||||
|
||||
public function testRelativeScheme()
|
||||
|
|
|
@ -32,17 +32,17 @@ class DateParserTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('2010-08-20', date('Y-m-d', $parser->getTimestamp('2010-08-20Thh:08:ssZ')));
|
||||
$this->assertEquals(1288648057, $parser->getTimestamp('Mon, 01 Nov 2010 21:47:37 UT'));
|
||||
$this->assertEquals(1346069615, $parser->getTimestamp('Mon Aug 27 2012 12:13:35 GMT-0700 (PDT)'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Tue, 3 Febuary 2010 00:00:00 IST'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('############# EST'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Wed, 30 Nov -0001 00:00:00 +0000'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('čet, 24 maj 2012 00:00:00'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('-0-0T::Z'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Wed, 18 2012'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp("'2009-09-30 CDT16:09:54"));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('ary 8 Jan 2013 00:00:00 GMT'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Sat, 11 00:00:01 GMT'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Tue, 3 Febuary 2010 00:00:00 IST'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('############# EST'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Wed, 30 Nov -0001 00:00:00 +0000'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('čet, 24 maj 2012 00:00:00'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('-0-0T::Z'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Wed, 18 2012'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp("'2009-09-30 CDT16:09:54"), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('ary 8 Jan 2013 00:00:00 GMT'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Sat, 11 00:00:01 GMT'), '', 1);
|
||||
$this->assertEquals(1370631743, $parser->getTimestamp('Fri Jun 07 2013 19:02:23 GMT+0000 (UTC)'));
|
||||
$this->assertEquals(1377412225, $parser->getTimestamp('25/08/2013 06:30:25 م'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('+0400'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('+0400'), '', 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ class Rss20ParserTest extends PHPUnit_Framework_TestCase
|
|||
|
||||
$parser = new Rss20(file_get_contents('tests/fixtures/fulltextrss.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertEquals(time(), $feed->getDate());
|
||||
$this->assertEquals(time(), $feed->getDate(), '', 1);
|
||||
}
|
||||
|
||||
public function testFeedLanguage()
|
||||
|
@ -259,9 +259,5 @@ class Rss20ParserTest extends PHPUnit_Framework_TestCase
|
|||
$parser = new Rss20(file_get_contents('tests/fixtures/jeux-linux.fr.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
|
||||
$parser = new Rss20(file_get_contents('tests/fixtures/cercle.psy.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ class Rss91ParserTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('', $feed->getFeedUrl());
|
||||
$this->assertEquals('http://writetheweb.com/', $feed->getSiteUrl());
|
||||
$this->assertEquals('http://writetheweb.com/', $feed->getId());
|
||||
$this->assertEquals(time(), $feed->getDate());
|
||||
$this->assertEquals(time(), $feed->getDate(), '', 1);
|
||||
$this->assertEquals(6, count($feed->items));
|
||||
|
||||
$this->assertEquals('Giving the world a pluggable Gnutella', $feed->items[0]->getTitle());
|
||||
$this->assertEquals('http://writetheweb.com/read.php?item=24', $feed->items[0]->getUrl());
|
||||
$this->assertEquals('085a9133a75542f878fa73ee2afbb6a2350b6c4fb125e6d8ca09478c47702111', $feed->items[0]->getId());
|
||||
$this->assertEquals(time(), $feed->items[0]->getDate());
|
||||
$this->assertEquals(time(), $feed->items[0]->getDate(), '', 1);
|
||||
$this->assertEquals('webmaster@writetheweb.com', $feed->items[0]->getAuthor());
|
||||
$this->assertTrue(strpos($feed->items[1]->getContent(), '<p>After a period of dormancy') === 0);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class Rss92ParserTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('', $feed->getFeedUrl());
|
||||
$this->assertEquals('http://www.universfreebox.com/', $feed->getSiteUrl());
|
||||
$this->assertEquals('http://www.universfreebox.com/', $feed->getId());
|
||||
$this->assertEquals(time(), $feed->date);
|
||||
$this->assertEquals(time(), $feed->date, '', 1);
|
||||
$this->assertEquals(30, count($feed->items));
|
||||
|
||||
$this->assertEquals('Retour de Xavier Niel sur Twitter, « sans initiative privée, pas de révolution #Born2code »', $feed->items[0]->title);
|
||||
|
|
|
@ -13,6 +13,7 @@ class XmlParserTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('utf-8', XmlParser::getEncodingFromXmlTag("<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet"));
|
||||
$this->assertEquals('utf-8', XmlParser::getEncodingFromXmlTag('<?xml version="1.0" encoding="UTF-8"?><feed xml:'));
|
||||
$this->assertEquals('windows-1251', XmlParser::getEncodingFromXmlTag('<?xml version="1.0" encoding="Windows-1251"?><rss version="2.0">'));
|
||||
$this->assertEquals('', XmlParser::getEncodingFromXmlTag("<?xml version='1.0'?><?xml-stylesheet"));
|
||||
}
|
||||
|
||||
public function testScanForXEE()
|
||||
|
|
Загрузка…
Ссылка в новой задаче