diff options
| author | 2017-05-28 02:02:21 +0200 | |
|---|---|---|
| committer | 2017-05-28 02:02:21 +0200 | |
| commit | dd65cb0f9c85b261af6db85d0ea7c06b9e0bc6ec (patch) | |
| tree | 153b3b52b9d1e3c387310a3e413444cb3af22630 /lib | |
| parent | b49bd3f3378102dc8cfc5986cc3a3f969005f5f1 (diff) | |
Manual merge upstream SimplePie
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/SimplePie/SimplePie.php | 243 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Category.php | 65 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Content/Type/Sniffer.php | 11 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/File.php | 11 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/HTTP/Parser.php | 18 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Item.php | 130 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Locator.php | 27 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Misc.php | 20 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Parser.php | 229 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Sanitize.php | 28 | ||||
| -rw-r--r-- | lib/lib_rss.php | 2 |
11 files changed, 581 insertions, 203 deletions
diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index ec3ef1c77..5cd445b6d 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2017, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,8 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev-FreshRSS - * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @version 1.5 + * @copyright 2004-2017 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -50,7 +50,7 @@ define('SIMPLEPIE_NAME', 'SimplePie'); /** * SimplePie Version */ -define('SIMPLEPIE_VERSION', '1.4-dev-FreshRSS'); +define('SIMPLEPIE_VERSION', '1.5'); /** * SimplePie Build @@ -510,6 +510,14 @@ class SimplePie public $cache = true; /** + * @var bool Force SimplePie to fallback to expired cache, if enabled, + * when feed is unavailable. + * @see SimplePie::force_cache_fallback() + * @access private + */ + public $force_cache_fallback = false; + + /** * @var int Cache duration (in seconds) * @see SimplePie::set_cache_duration() * @access private @@ -642,6 +650,12 @@ class SimplePie public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); /** + * @var bool Should we throw exceptions, or use the old-style error property? + * @access private + */ + public $enable_exceptions = false; + + /** * Use syslog to report HTTP requests done by SimplePie. * @see SimplePie::set_syslog() */ @@ -860,6 +874,21 @@ class SimplePie } /** + * SimplePie to continue to fall back to expired cache, if enabled, when + * feed is unavailable. + * + * This tells SimplePie to ignore any file errors and fall back to cache + * instead. This only works if caching is enabled and cached content + * still exists. + + * @param bool $enable Force use of cache on fail. + */ + public function force_cache_fallback($enable = false) + { + $this->force_cache_fallback= (bool) $enable; + } + + /** * Set the length of time (in seconds) that the contents of a feed will be * cached * @@ -1387,7 +1416,6 @@ class SimplePie return $this->data['mtime']; } elseif ($fetched === false) { - $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); return false; } @@ -1398,6 +1426,13 @@ class SimplePie $md5 = $this->data['md5']; } } + + // Empty response check + if(empty($this->raw_data)){ + $this->error = "A feed could not be found at `$this->feed_url`. Empty body."; + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); + return false; + } // Set up array of possible encodings $encodings = array(); @@ -1440,7 +1475,7 @@ class SimplePie // Text MIME-type default elseif (substr($sniffed, 0, 5) === 'text/') { - $encodings[] = 'US-ASCII'; + $encodings[] = 'UTF-8'; } } @@ -1500,12 +1535,20 @@ class SimplePie else { $this->error = 'The data could not be converted to UTF-8.'; - if (!extension_loaded('mbstring') && !extension_loaded('iconv')) { - $this->error .= ' You MUST have either the iconv or mbstring extension installed.'; - } elseif (!extension_loaded('mbstring')) { - $this->error .= ' Try installing the mbstring extension.'; - } elseif (!extension_loaded('iconv')) { - $this->error .= ' Try installing the iconv extension.'; + if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) { + $this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.'; + } else { + $missingExtensions = array(); + if (!extension_loaded('iconv')) { + $missingExtensions[] = 'iconv'; + } + if (!extension_loaded('mbstring')) { + $missingExtensions[] = 'mbstring'; + } + if (!class_exists('\UConverter')) { + $missingExtensions[] = 'intl (PHP 5.5+)'; + } + $this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.'; } } @@ -1896,14 +1939,19 @@ class SimplePie { if ($this->permanent_url !== null) { - return $this->sanitize($this->permanent_url, SIMPLEPIE_CONSTRUCT_IRI); + // sanitize encodes ampersands which are required when used in a url. + return str_replace('&', '&', + $this->sanitize($this->permanent_url, + SIMPLEPIE_CONSTRUCT_IRI)); } } else { if ($this->feed_url !== null) { - return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + return str_replace('&', '&', + $this->sanitize($this->feed_url, + SIMPLEPIE_CONSTRUCT_IRI)); } } return null; @@ -2565,6 +2613,12 @@ class SimplePie { return $this->data['links'][$rel]; } + else if (isset($this->data['headers']['link']) && + preg_match('/<([^>]+)>; rel='.preg_quote($rel).'/', + $this->data['headers']['link'], $match)) + { + return array($match[1]); + } else { return null; @@ -3002,96 +3056,81 @@ class SimplePie if (!empty($this->multifeed_objects)) { $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); + if (empty($this->data['items'])) + { + return array(); + } + return $this->data['items']; } - else + $this->data['items'] = array(); + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) { - $this->data['items'] = array(); - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) + $keys = array_keys($items); + foreach ($keys as $key) { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); - } + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); - } + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); - } + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); - } + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } - if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) + } + if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); - } + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } } - if (!empty($this->data['items'])) + if (empty($this->data['items'])) { - // If we want to order it by date, check if all items have a date, and then sort it - if ($this->order_by_date && empty($this->multifeed_objects)) - { - if (!isset($this->data['ordered_items'])) - { - $do_sort = true; - foreach ($this->data['items'] as $item) - { - if (!$item->get_date('U')) - { - $do_sort = false; - break; - } - } - $item = null; - $this->data['ordered_items'] = $this->data['items']; - if ($do_sort) - { - usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); - } - } - $items = $this->data['ordered_items']; - } - else - { - $items = $this->data['items']; - } + return array(); + } - // Slice the data as desired - if ($end === 0) - { - return array_slice($items, $start); - } - else + if ($this->order_by_date) + { + if (!isset($this->data['ordered_items'])) { - return array_slice($items, $start, $end); - } + $this->data['ordered_items'] = $this->data['items']; + usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); + } + $items = $this->data['ordered_items']; } else { - return array(); + $items = $this->data['items']; + } + // Slice the data as desired + if ($end === 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); } } @@ -3226,4 +3265,42 @@ class SimplePie return array(); } } + + /** + * Store PubSubHubbub links as headers + * + * There is no way to find PuSH links in the body of a microformats feed, + * so they are added to the headers when found, to be used later by get_links. + * @param SimplePie_File $file + * @param string $hub + * @param string $self + */ + private function store_links(&$file, $hub, $self) { + if (isset($file->headers['link']['hub']) || + (isset($file->headers['link']) && + preg_match('/rel=hub/', $file->headers['link']))) + { + return; + } + + if ($hub) + { + if (isset($file->headers['link'])) + { + if ($file->headers['link'] !== '') + { + $file->headers['link'] = ', '; + } + } + else + { + $file->headers['link'] = ''; + } + $file->headers['link'] .= '<'.$hub.'>; rel=hub'; + if ($self) + { + $file->headers['link'] .= ', <'.$self.'>; rel=self'; + } + } + } } diff --git a/lib/SimplePie/SimplePie/Category.php b/lib/SimplePie/SimplePie/Category.php index 92d511e1a..df0f13f9a 100644 --- a/lib/SimplePie/SimplePie/Category.php +++ b/lib/SimplePie/SimplePie/Category.php @@ -56,7 +56,7 @@ class SimplePie_Category /** * Category identifier * - * @var string + * @var string|null * @see get_term */ var $term; @@ -64,7 +64,7 @@ class SimplePie_Category /** * Categorization scheme identifier * - * @var string + * @var string|null * @see get_scheme() */ var $scheme; @@ -72,23 +72,36 @@ class SimplePie_Category /** * Human readable label * - * @var string + * @var string|null * @see get_label() */ var $label; /** + * Category type + * + * category for <category> + * subject for <dc:subject> + * + * @var string|null + * @see get_type() + */ + var $type; + + /** * Constructor, used to input the data * - * @param string $term - * @param string $scheme - * @param string $label + * @param string|null $term + * @param string|null $scheme + * @param string|null $label + * @param string|null $type */ - public function __construct($term = null, $scheme = null, $label = null) + public function __construct($term = null, $scheme = null, $label = null, $type = null) { $this->term = $term; $this->scheme = $scheme; $this->label = $label; + $this->type = $type; } /** @@ -109,14 +122,7 @@ class SimplePie_Category */ public function get_term() { - if ($this->term !== null) - { - return $this->term; - } - else - { - return null; - } + return $this->term; } /** @@ -126,31 +132,32 @@ class SimplePie_Category */ public function get_scheme() { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } + return $this->scheme; } /** * Get the human readable label * + * @param bool $strict * @return string|null */ - public function get_label() + public function get_label($strict = false) { - if ($this->label !== null) - { - return $this->label; - } - else + if ($this->label === null && $strict !== true) { return $this->get_term(); } + return $this->label; + } + + /** + * Get the category type + * + * @return string|null + */ + public function get_type() + { + return $this->type; } } diff --git a/lib/SimplePie/SimplePie/Content/Type/Sniffer.php b/lib/SimplePie/SimplePie/Content/Type/Sniffer.php index b68b73134..6caf80f33 100644 --- a/lib/SimplePie/SimplePie/Content/Type/Sniffer.php +++ b/lib/SimplePie/SimplePie/Content/Type/Sniffer.php @@ -124,8 +124,8 @@ class SimplePie_Content_Type_Sniffer } } elseif ($official === 'text/html' - || $official === 'text/xml' - || $official === 'application/xml') + || $official === 'text/xml' //FreshRSS + || $official === 'application/xml') //FreshRSS { return $this->feed_or_html(); } @@ -255,12 +255,7 @@ class SimplePie_Content_Type_Sniffer public function feed_or_html() { $len = strlen($this->file->body); - $pos = 0; - if (isset($this->file->body[2]) && $this->file->body[0] === "\xEF" && - $this->file->body[1] === "\xBB" && $this->file->body[2] === "\xBF") { - $pos += 3; //UTF-8 BOM - } - $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); + $pos = strspn($this->file->body, "\x09\x0A\x0D\x20\xEF\xBB\xBF"); while ($pos < $len) { diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index c1fab42dc..8be38f145 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -118,8 +118,7 @@ class SimplePie_File curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); } - foreach ($curl_options as $curl_param => $curl_value) - { + foreach ($curl_options as $curl_param => $curl_value) { curl_setopt($fp, $curl_param, $curl_value); } @@ -136,10 +135,12 @@ class SimplePie_File } else { - $info = curl_getinfo($fp); + // Use the updated url provided by curl_getinfo after any redirects. + if ($info = curl_getinfo($fp)) { + $this->url = $info['url']; + } curl_close($fp); - $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); - $this->headers = array_pop($this->headers); + $this->headers = SimplePie_HTTP_Parser::prepareHeaders($this->headers, $info['redirect_count'] + 1); $parser = new SimplePie_HTTP_Parser($this->headers); if ($parser->parse()) { diff --git a/lib/SimplePie/SimplePie/HTTP/Parser.php b/lib/SimplePie/SimplePie/HTTP/Parser.php index 63ae1e03d..3899c53fa 100644 --- a/lib/SimplePie/SimplePie/HTTP/Parser.php +++ b/lib/SimplePie/SimplePie/HTTP/Parser.php @@ -496,4 +496,22 @@ class SimplePie_HTTP_Parser } } } + + /** + * Prepare headers (take care of proxies headers) + * + * @param string $headers Raw headers + * @param integer $count Redirection count. Default to 1. + * + * @return string + */ + static public function prepareHeaders($headers, $count = 1) + { + $data = explode("\r\n\r\n", $headers, $count); + $data = array_pop($data); + if (false !== stripos($data, "HTTP/1.0 200 Connection established\r\n\r\n")) { + $data = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $data); + } + return $data; + } } diff --git a/lib/SimplePie/SimplePie/Item.php b/lib/SimplePie/SimplePie/Item.php index daecf0a15..425538606 100644 --- a/lib/SimplePie/SimplePie/Item.php +++ b/lib/SimplePie/SimplePie/Item.php @@ -206,9 +206,10 @@ class SimplePie_Item * * @since Beta 2 * @param boolean $hash Should we force using a hash instead of the supplied ID? - * @return string + * @param string|false $fn User-supplied function to generate an hash + * @return string|null */ - public function get_id($hash = false, $fn = '') + public function get_id($hash = false, $fn = 'md5') { if (!$hash) { @@ -237,7 +238,15 @@ class SimplePie_Item return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT); } } - if ($fn === '' || !is_callable($fn)) $fn = 'md5'; + if ($fn === false) + { + return null; + } + elseif (!is_callable($fn)) + { + trigger_error('User-supplied function $fn must be callable', E_USER_WARNING); + $fn = 'md5'; + } return call_user_func($fn, $this->get_permalink().$this->get_title().$this->get_content()); } @@ -307,41 +316,50 @@ class SimplePie_Item */ public function get_description($description_only = false) { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) + if (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) && + ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) && + ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML); + return $return; } elseif (!$description_only) @@ -370,17 +388,20 @@ class SimplePie_Item */ public function get_content($content_only = false) { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) + if (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) && + ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) && + ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + return $return; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) + elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) && + ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0])))) { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + return $return; } elseif (!$content_only) { @@ -448,47 +469,50 @@ class SimplePie_Item { $categories = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + $type = 'category'; + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, $type) as $category) { $term = null; $scheme = null; $label = null; if (isset($category['attribs']['']['term'])) { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_HTML); } if (isset($category['attribs']['']['scheme'])) { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_HTML); } if (isset($category['attribs']['']['label'])) { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_HTML); } - $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + $categories[] = $this->registry->create('Category', array($term, $scheme, $label, $type)); } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, $type) 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); + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_HTML); if (isset($category['attribs']['']['domain'])) { - $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); + $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_HTML); } else { $scheme = null; } - $categories[] = $this->registry->create('Category', array($term, $scheme, null)); + $categories[] = $this->registry->create('Category', array($term, $scheme, null, $type)); } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + + $type = 'subject'; + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, $type) as $category) { - $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null, $type)); } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, $type) as $category) { - $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null, $type)); } if (!empty($categories)) @@ -625,7 +649,7 @@ class SimplePie_Item $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); + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { @@ -633,7 +657,7 @@ class SimplePie_Item } 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); + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML); } if ($name !== null || $email !== null || $uri !== null) { @@ -647,7 +671,7 @@ class SimplePie_Item $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); + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { @@ -655,7 +679,7 @@ class SimplePie_Item } 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); + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_HTML); } if ($name !== null || $email !== null || $url !== null) { @@ -664,19 +688,19 @@ class SimplePie_Item } if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author')) { - $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))); + $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_HTML))); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) { - $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) { - $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) { - $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_HTML), null, null)); } if (!empty($authors)) @@ -2802,9 +2826,17 @@ class SimplePie_Item { $length = ceil($link['attribs']['']['length']); } + if (isset($link['attribs']['']['title'])) + { + $title = $this->sanitize($link['attribs']['']['title'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width)); } } diff --git a/lib/SimplePie/SimplePie/Locator.php b/lib/SimplePie/SimplePie/Locator.php index 36bc02895..bc314c2cd 100644 --- a/lib/SimplePie/SimplePie/Locator.php +++ b/lib/SimplePie/SimplePie/Locator.php @@ -120,34 +120,41 @@ class SimplePie_Locator { if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) { - return $working; + return $working[0]; } if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) { - return $working; + return $working[0]; } if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) { - return $working; + return $working[0]; } if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) { - return $working; + return $working[0]; } } return null; } - public function is_feed($file) + public function is_feed($file, $check_html = false) { 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', 'application/x-rss+xml'))) + $mime_types = array('application/rss+xml', 'application/rdf+xml', + 'text/rdf', 'application/atom+xml', 'text/xml', + 'application/xml', 'application/x-rss+xml'); + if ($check_html) + { + $mime_types[] = 'text/html'; + } + if (in_array($sniffed, $mime_types)) { return true; } @@ -241,14 +248,14 @@ class SimplePie_Locator 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])) + 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('text/html', '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)) + 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, true)) { $feeds[$href] = $feed; } @@ -380,7 +387,7 @@ class SimplePie_Locator $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; + return array($feed); } else { @@ -408,7 +415,7 @@ class SimplePie_Locator $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; + return array($feed); } else { diff --git a/lib/SimplePie/SimplePie/Misc.php b/lib/SimplePie/SimplePie/Misc.php index ca2810611..40477c01e 100644 --- a/lib/SimplePie/SimplePie/Misc.php +++ b/lib/SimplePie/SimplePie/Misc.php @@ -127,7 +127,7 @@ class SimplePie_Misc { $attribs[$j][2] = $attribs[$j][1]; } - $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); + $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j])); } } } @@ -337,11 +337,16 @@ class SimplePie_Misc { return $return; } - // This is last, as behaviour of this varies with OS userland and PHP version + // This is third, as behaviour of this varies with OS userland and PHP version elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output))) { return $return; } + // This is last, as behaviour of this varies with OS userland and PHP version + elseif (class_exists('\UConverter') && ($return = SimplePie_Misc::change_encoding_uconverter($data, $input, $output))) + { + return $return; + } // If we can't do anything, just fail else { @@ -393,6 +398,17 @@ class SimplePie_Misc } /** + * @param string $data + * @param string $input + * @param string $output + * @return string|false + */ + protected static function change_encoding_uconverter($data, $input, $output) + { + return @\UConverter::transcode($data, $output, $input); + } + + /** * Normalize an encoding name * * This is automatically generated by create.php diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index e3966218c..9348382ad 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -434,4 +434,231 @@ class SimplePie_Parser } return $cache[$string]; } -} + + private function parse_hcard($data, $category = false) { + $name = ''; + $link = ''; + // Check if h-card is set and pass that information on in the link. + if (isset($data['type']) && in_array('h-card', $data['type'])) { + if (isset($data['properties']['name'][0])) { + $name = $data['properties']['name'][0]; + } + if (isset($data['properties']['url'][0])) { + $link = $data['properties']['url'][0]; + if ($name === '') { + $name = $link; + } + else { + // can't have commas in categories. + $name = str_replace(',', '', $name); + } + $person_tag = $category ? '<span class="person-tag"></span>' : ''; + return '<a class="h-card" href="'.$link.'">'.$person_tag.$name.'</a>'; + } + } + return isset($data['value']) ? $data['value'] : ''; + } + + private function parse_microformats(&$data, $url) { + $feed_title = ''; + $feed_author = NULL; + $author_cache = array(); + $items = array(); + $entries = array(); + $mf = Mf2\parse($data, $url); + // First look for an h-feed. + $h_feed = array(); + foreach ($mf['items'] as $mf_item) { + if (in_array('h-feed', $mf_item['type'])) { + $h_feed = $mf_item; + break; + } + // Also look for an h-feed in the children of each top level item. + if (!isset($mf_item['children'][0]['type'])) continue; + if (in_array('h-feed', $mf_item['children'][0]['type'])) { + $h_feed = $mf_item['children'][0]; + // In this case the parent of the h-feed may be an h-card, so use it as + // the feed_author. + if (in_array('h-card', $mf_item['type'])) $feed_author = $mf_item; + break; + } + } + if (isset($h_feed['children'])) { + $entries = $h_feed['children']; + // Also set the feed title and store author from the h-feed if available. + if (isset($mf['items'][0]['properties']['name'][0])) { + $feed_title = $mf['items'][0]['properties']['name'][0]; + } + if (isset($mf['items'][0]['properties']['author'][0])) { + $feed_author = $mf['items'][0]['properties']['author'][0]; + } + } + else { + $entries = $mf['items']; + } + for ($i = 0; $i < count($entries); $i++) { + $entry = $entries[$i]; + if (in_array('h-entry', $entry['type'])) { + $item = array(); + $title = ''; + $description = ''; + if (isset($entry['properties']['url'][0])) { + $link = $entry['properties']['url'][0]; + if (isset($link['value'])) $link = $link['value']; + $item['link'] = array(array('data' => $link)); + } + if (isset($entry['properties']['uid'][0])) { + $guid = $entry['properties']['uid'][0]; + if (isset($guid['value'])) $guid = $guid['value']; + $item['guid'] = array(array('data' => $guid)); + } + if (isset($entry['properties']['name'][0])) { + $title = $entry['properties']['name'][0]; + if (isset($title['value'])) $title = $title['value']; + $item['title'] = array(array('data' => $title)); + } + if (isset($entry['properties']['author'][0]) || isset($feed_author)) { + // author is a special case, it can be plain text or an h-card array. + // If it's plain text it can also be a url that should be followed to + // get the actual h-card. + $author = isset($entry['properties']['author'][0]) ? + $entry['properties']['author'][0] : $feed_author; + if (!is_string($author)) { + $author = $this->parse_hcard($author); + } + else if (strpos($author, 'http') === 0) { + if (isset($author_cache[$author])) { + $author = $author_cache[$author]; + } + else { + $mf = Mf2\fetch($author); + foreach ($mf['items'] as $hcard) { + // Only interested in an h-card by itself in this case. + if (!in_array('h-card', $hcard['type'])) { + continue; + } + // It must have a url property matching what we fetched. + if (!isset($hcard['properties']['url']) || + !(in_array($author, $hcard['properties']['url']))) { + continue; + } + // Save parse_hcard the trouble of finding the correct url. + $hcard['properties']['url'][0] = $author; + // Cache this h-card for the next h-entry to check. + $author_cache[$author] = $this->parse_hcard($hcard); + $author = $author_cache[$author]; + break; + } + } + } + $item['author'] = array(array('data' => $author)); + } + if (isset($entry['properties']['photo'][0])) { + // If a photo is also in content, don't need to add it again here. + $content = ''; + if (isset($entry['properties']['content'][0]['html'])) { + $content = $entry['properties']['content'][0]['html']; + } + $photo_list = array(); + for ($j = 0; $j < count($entry['properties']['photo']); $j++) { + $photo = $entry['properties']['photo'][$j]; + if (strpos($content, $photo) === false) { + $photo_list[] = $photo; + } + } + // When there's more than one photo show the first and use a lightbox. + $count = count($photo_list); + if ($count > 1) { + $description = '<p>'; + for ($j = 0; $j < $count; $j++) { + $hidden = $j === 0 ? '' : 'class="hidden" '; + $description .= '<a href="'.$photo_list[$j].'" '.$hidden. + 'data-lightbox="image-set-'.$i.'">'. + '<img src="'.$photo_list[$j].'"></a>'; + } + $description .= '<br><b>'.$count.' photos</b></p>'; + } + else if ($count == 1) { + $description = '<p><img src="'.$photo_list[0].'"></p>'; + } + } + if (isset($entry['properties']['content'][0]['html'])) { + // e-content['value'] is the same as p-name when they are on the same + // element. Use this to replace title with a strip_tags version so + // that alt text from images is not included in the title. + if ($entry['properties']['content'][0]['value'] === $title) { + $title = strip_tags($entry['properties']['content'][0]['html']); + $item['title'] = array(array('data' => $title)); + } + $description .= $entry['properties']['content'][0]['html']; + if (isset($entry['properties']['in-reply-to'][0]['value'])) { + $in_reply_to = $entry['properties']['in-reply-to'][0]['value']; + $description .= '<p><span class="in-reply-to"></span> '. + '<a href="'.$in_reply_to.'">'.$in_reply_to.'</a><p>'; + } + $item['description'] = array(array('data' => $description)); + } + if (isset($entry['properties']['category'])) { + $category_csv = ''; + // Categories can also contain h-cards. + foreach ($entry['properties']['category'] as $category) { + if ($category_csv !== '') $category_csv .= ', '; + if (is_string($category)) { + // Can't have commas in categories. + $category_csv .= str_replace(',', '', $category); + } + else { + $category_csv .= $this->parse_hcard($category, true); + } + } + $item['category'] = array(array('data' => $category_csv)); + } + if (isset($entry['properties']['published'][0])) { + $timestamp = strtotime($entry['properties']['published'][0]); + $pub_date = date('F j Y g:ia', $timestamp).' GMT'; + $item['pubDate'] = array(array('data' => $pub_date)); + } + // The title and description are set to the empty string to represent + // a deleted item (which also makes it an invalid rss item). + if (isset($entry['properties']['deleted'][0])) { + $item['title'] = array(array('data' => '')); + $item['description'] = array(array('data' => '')); + } + $items[] = array('child' => array('' => $item)); + } + } + // Mimic RSS data format when storing microformats. + $link = array(array('data' => $url)); + $image = ''; + if (!is_string($feed_author) && + isset($feed_author['properties']['photo'][0])) { + $image = array(array('child' => array('' => array('url' => + array(array('data' => $feed_author['properties']['photo'][0])))))); + } + // Use the a name given for the h-feed, or get the title from the html. + if ($feed_title !== '') { + $feed_title = array(array('data' => htmlspecialchars($feed_title))); + } + else if ($position = strpos($data, '<title>')) { + $start = $position < 200 ? 0 : $position - 200; + $check = substr($data, $start, 400); + $matches = array(); + if (preg_match('/<title>(.+)<\/title>/', $check, $matches)) { + $feed_title = array(array('data' => htmlspecialchars($matches[1]))); + } + } + $channel = array('channel' => array(array('child' => array('' => + array('link' => $link, 'image' => $image, 'title' => $feed_title, + 'item' => $items))))); + $rss = array(array('attribs' => array('' => array('version' => '2.0')), + 'child' => array('' => $channel))); + $this->data = array('child' => array('' => array('rss' => $rss))); + return true; + } + + private function declare_html_entities() { + // This is required because the RSS specification says that entity-encoded + // html is allowed, but the xml specification says they must be declared. + return '<!DOCTYPE html [ <!ENTITY nbsp " "> <!ENTITY iexcl "¡"> <!ENTITY cent "¢"> <!ENTITY pound "£"> <!ENTITY curren "¤"> <!ENTITY yen "¥"> <!ENTITY brvbar "¦"> <!ENTITY sect "§"> <!ENTITY uml "¨"> <!ENTITY copy "©"> <!ENTITY ordf "ª"> <!ENTITY laquo "«"> <!ENTITY not "¬"> <!ENTITY shy "­"> <!ENTITY reg "®"> <!ENTITY macr "¯"> <!ENTITY deg "°"> <!ENTITY plusmn "±"> <!ENTITY sup2 "²"> <!ENTITY sup3 "³"> <!ENTITY acute "´"> <!ENTITY micro "µ"> <!ENTITY para "¶"> <!ENTITY middot "·"> <!ENTITY cedil "¸"> <!ENTITY sup1 "¹"> <!ENTITY ordm "º"> <!ENTITY raquo "»"> <!ENTITY frac14 "¼"> <!ENTITY frac12 "½"> <!ENTITY frac34 "¾"> <!ENTITY iquest "¿"> <!ENTITY Agrave "À"> <!ENTITY Aacute "Á"> <!ENTITY Acirc "Â"> <!ENTITY Atilde "Ã"> <!ENTITY Auml "Ä"> <!ENTITY Aring "Å"> <!ENTITY AElig "Æ"> <!ENTITY Ccedil "Ç"> <!ENTITY Egrave "È"> <!ENTITY Eacute "É"> <!ENTITY Ecirc "Ê"> <!ENTITY Euml "Ë"> <!ENTITY Igrave "Ì"> <!ENTITY Iacute "Í"> <!ENTITY Icirc "Î"> <!ENTITY Iuml "Ï"> <!ENTITY ETH "Ð"> <!ENTITY Ntilde "Ñ"> <!ENTITY Ograve "Ò"> <!ENTITY Oacute "Ó"> <!ENTITY Ocirc "Ô"> <!ENTITY Otilde "Õ"> <!ENTITY Ouml "Ö"> <!ENTITY times "×"> <!ENTITY Oslash "Ø"> <!ENTITY Ugrave "Ù"> <!ENTITY Uacute "Ú"> <!ENTITY Ucirc "Û"> <!ENTITY Uuml "Ü"> <!ENTITY Yacute "Ý"> <!ENTITY THORN "Þ"> <!ENTITY szlig "ß"> <!ENTITY agrave "à"> <!ENTITY aacute "á"> <!ENTITY acirc "â"> <!ENTITY atilde "ã"> <!ENTITY auml "ä"> <!ENTITY aring "å"> <!ENTITY aelig "æ"> <!ENTITY ccedil "ç"> <!ENTITY egrave "è"> <!ENTITY eacute "é"> <!ENTITY ecirc "ê"> <!ENTITY euml "ë"> <!ENTITY igrave "ì"> <!ENTITY iacute "í"> <!ENTITY icirc "î"> <!ENTITY iuml "ï"> <!ENTITY eth "ð"> <!ENTITY ntilde "ñ"> <!ENTITY ograve "ò"> <!ENTITY oacute "ó"> <!ENTITY ocirc "ô"> <!ENTITY otilde "õ"> <!ENTITY ouml "ö"> <!ENTITY divide "÷"> <!ENTITY oslash "ø"> <!ENTITY ugrave "ù"> <!ENTITY uacute "ú"> <!ENTITY ucirc "û"> <!ENTITY uuml "ü"> <!ENTITY yacute "ý"> <!ENTITY thorn "þ"> <!ENTITY yuml "ÿ"> <!ENTITY OElig "Œ"> <!ENTITY oelig "œ"> <!ENTITY Scaron "Š"> <!ENTITY scaron "š"> <!ENTITY Yuml "Ÿ"> <!ENTITY fnof "ƒ"> <!ENTITY circ "ˆ"> <!ENTITY tilde "˜"> <!ENTITY Alpha "Α"> <!ENTITY Beta "Β"> <!ENTITY Gamma "Γ"> <!ENTITY Epsilon "Ε"> <!ENTITY Zeta "Ζ"> <!ENTITY Eta "Η"> <!ENTITY Theta "Θ"> <!ENTITY Iota "Ι"> <!ENTITY Kappa "Κ"> <!ENTITY Lambda "Λ"> <!ENTITY Mu "Μ"> <!ENTITY Nu "Ν"> <!ENTITY Xi "Ξ"> <!ENTITY Omicron "Ο"> <!ENTITY Pi "Π"> <!ENTITY Rho "Ρ"> <!ENTITY Sigma "Σ"> <!ENTITY Tau "Τ"> <!ENTITY Upsilon "Υ"> <!ENTITY Phi "Φ"> <!ENTITY Chi "Χ"> <!ENTITY Psi "Ψ"> <!ENTITY Omega "Ω"> <!ENTITY alpha "α"> <!ENTITY beta "β"> <!ENTITY gamma "γ"> <!ENTITY delta "δ"> <!ENTITY epsilon "ε"> <!ENTITY zeta "ζ"> <!ENTITY eta "η"> <!ENTITY theta "θ"> <!ENTITY iota "ι"> <!ENTITY kappa "κ"> <!ENTITY lambda "λ"> <!ENTITY mu "μ"> <!ENTITY nu "ν"> <!ENTITY xi "ξ"> <!ENTITY omicron "ο"> <!ENTITY pi "π"> <!ENTITY rho "ρ"> <!ENTITY sigmaf "ς"> <!ENTITY sigma "σ"> <!ENTITY tau "τ"> <!ENTITY upsilon "υ"> <!ENTITY phi "φ"> <!ENTITY chi "χ"> <!ENTITY psi "ψ"> <!ENTITY omega "ω"> <!ENTITY thetasym "ϑ"> <!ENTITY upsih "ϒ"> <!ENTITY piv "ϖ"> <!ENTITY ensp " "> <!ENTITY emsp " "> <!ENTITY thinsp " "> <!ENTITY zwnj "‌"> <!ENTITY zwj "‍"> <!ENTITY lrm "‎"> <!ENTITY rlm "‏"> <!ENTITY ndash "–"> <!ENTITY mdash "—"> <!ENTITY lsquo "‘"> <!ENTITY rsquo "’"> <!ENTITY sbquo "‚"> <!ENTITY ldquo "“"> <!ENTITY rdquo "”"> <!ENTITY bdquo "„"> <!ENTITY dagger "†"> <!ENTITY Dagger "‡"> <!ENTITY bull "•"> <!ENTITY hellip "…"> <!ENTITY permil "‰"> <!ENTITY prime "′"> <!ENTITY Prime "″"> <!ENTITY lsaquo "‹"> <!ENTITY rsaquo "›"> <!ENTITY oline "‾"> <!ENTITY frasl "⁄"> <!ENTITY euro "€"> <!ENTITY image "ℑ"> <!ENTITY weierp "℘"> <!ENTITY real "ℜ"> <!ENTITY trade "™"> <!ENTITY alefsym "ℵ"> <!ENTITY larr "←"> <!ENTITY uarr "↑"> <!ENTITY rarr "→"> <!ENTITY darr "↓"> <!ENTITY harr "↔"> <!ENTITY crarr "↵"> <!ENTITY lArr "⇐"> <!ENTITY uArr "⇑"> <!ENTITY rArr "⇒"> <!ENTITY dArr "⇓"> <!ENTITY hArr "⇔"> <!ENTITY forall "∀"> <!ENTITY part "∂"> <!ENTITY exist "∃"> <!ENTITY empty "∅"> <!ENTITY nabla "∇"> <!ENTITY isin "∈"> <!ENTITY notin "∉"> <!ENTITY ni "∋"> <!ENTITY prod "∏"> <!ENTITY sum "∑"> <!ENTITY minus "−"> <!ENTITY lowast "∗"> <!ENTITY radic "√"> <!ENTITY prop "∝"> <!ENTITY infin "∞"> <!ENTITY ang "∠"> <!ENTITY and "∧"> <!ENTITY or "∨"> <!ENTITY cap "∩"> <!ENTITY cup "∪"> <!ENTITY int "∫"> <!ENTITY there4 "∴"> <!ENTITY sim "∼"> <!ENTITY cong "≅"> <!ENTITY asymp "≈"> <!ENTITY ne "≠"> <!ENTITY equiv "≡"> <!ENTITY le "≤"> <!ENTITY ge "≥"> <!ENTITY sub "⊂"> <!ENTITY sup "⊃"> <!ENTITY nsub "⊄"> <!ENTITY sube "⊆"> <!ENTITY supe "⊇"> <!ENTITY oplus "⊕"> <!ENTITY otimes "⊗"> <!ENTITY perp "⊥"> <!ENTITY sdot "⋅"> <!ENTITY lceil "⌈"> <!ENTITY rceil "⌉"> <!ENTITY lfloor "⌊"> <!ENTITY rfloor "⌋"> <!ENTITY lang "〈"> <!ENTITY rang "〉"> <!ENTITY loz "◊"> <!ENTITY spades "♠"> <!ENTITY clubs "♣"> <!ENTITY hearts "♥"> <!ENTITY diams "♦"> ]>'; + } +}
\ No newline at end of file diff --git a/lib/SimplePie/SimplePie/Sanitize.php b/lib/SimplePie/SimplePie/Sanitize.php index 49fe5dbd5..c55ee50b7 100644 --- a/lib/SimplePie/SimplePie/Sanitize.php +++ b/lib/SimplePie/SimplePie/Sanitize.php @@ -60,8 +60,8 @@ class SimplePie_Sanitize 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 $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); //FreshRSS + var $strip_attributes = array('bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + var $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); var $strip_comments = false; var $output_encoding = 'UTF-8'; var $enable_cache = true; @@ -169,7 +169,7 @@ class SimplePie_Sanitize $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')) + public function strip_attributes($attribs = array('bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) { if ($attribs) { @@ -322,7 +322,7 @@ class SimplePie_Sanitize { if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) { - $data = htmlspecialchars_decode($data, ENT_QUOTES); + $data = htmlspecialchars_decode($data, ENT_QUOTES); //FreshRSS 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; @@ -437,19 +437,17 @@ class SimplePie_Sanitize } } - // Remove the DOCTYPE - // Seems to cause segfaulting if we don't do this - if ($document->firstChild instanceof DOMDocumentType) + // Get content node + $div = $document->getElementsByTagName('body')->item(0)->firstChild; + // Finally, convert to a HTML string + if (version_compare(PHP_VERSION, '5.3.6', '>=')) { - $document->removeChild($document->firstChild); + $data = trim($document->saveHTML($div)); + } + else + { + $data = trim($document->saveXML($div)); } - - // 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) { diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 7e14e638d..22136854e 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -183,7 +183,7 @@ function customSimplePie() { 'object', 'param', 'plaintext', 'script', 'style', )); $simplePie->strip_attributes(array_merge($simplePie->strip_attributes, array( - 'autoplay', 'onload', 'onunload', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', + 'autoplay', 'class', 'onload', 'onunload', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur', 'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless', 'sizes', 'srcset'))); $simplePie->add_attributes(array( |
