whoami7 - Manager
:
/
home
/
creaupfw
/
public_html
/
wp-includes
/
assets
/
Upload File:
files >> /home/creaupfw/public_html/wp-includes/assets/src.zip
PK �ZZ ��! ! Author.phpnu �[��� <?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue * @author Ryan Parman * @author Sam Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ namespace SimplePie; /** * Manages all author-related data * * Used by {@see Item::get_author()} and {@see SimplePie::get_authors()} * * This class can be overloaded with {@see SimplePie::set_author_class()} * * @package SimplePie * @subpackage API */ class Author { /** * Author's name * * @var string * @see get_name() */ public $name; /** * Author's link * * @var string * @see get_link() */ public $link; /** * Author's email address * * @var string * @see get_email() */ public $email; /** * Constructor, used to input the data * * @param string $name * @param string $link * @param string $email */ public function __construct($name = null, $link = null, $email = null) { $this->name = $name; $this->link = $link; $this->email = $email; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Author's name * * @return string|null */ public function get_name() { if ($this->name !== null) { return $this->name; } return null; } /** * Author's link * * @return string|null */ public function get_link() { if ($this->link !== null) { return $this->link; } return null; } /** * Author's email address * * @return string|null */ public function get_email() { if ($this->email !== null) { return $this->email; } return null; } } class_alias('SimplePie\Author', 'SimplePie_Author'); PK �Zk�! �! Registry.phpnu �[��� <?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue * @author Ryan Parman * @author Sam Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ namespace SimplePie; use SimplePie\Content\Type\Sniffer; use SimplePie\Parse\Date; use SimplePie\XML\Declaration\Parser as DeclarationParser; /** * Handles creating objects and calling methods * * Access this via {@see \SimplePie\SimplePie::get_registry()} * * @package SimplePie */ class Registry { /** * Default class mapping * * Overriding classes *must* subclass these. * * @var array<class-string, class-string> */ protected $default = [ Cache::class => Cache::class, Locator::class => Locator::class, Parser::class => Parser::class, File::class => File::class, Sanitize::class => Sanitize::class, Item::class => Item::class, Author::class => Author::class, Category::class => Category::class, Enclosure::class => Enclosure::class, Caption::class => Caption::class, Copyright::class => Copyright::class, Credit::class => Credit::class, Rating::class => Rating::class, Restriction::class => Restriction::class, Sniffer::class => Sniffer::class, Source::class => Source::class, Misc::class => Misc::class, DeclarationParser::class => DeclarationParser::class, Date::class => Date::class, ]; /** * Class mapping * * @see register() * @var array */ protected $classes = []; /** * Legacy classes * * @see register() * @var array<class-string> */ protected $legacy = []; /** * Legacy types * * @see register() * @var array<string, class-string> */ private $legacyTypes = [ 'Cache' => Cache::class, 'Locator' => Locator::class, 'Parser' => Parser::class, 'File' => File::class, 'Sanitize' => Sanitize::class, 'Item' => Item::class, 'Author' => Author::class, 'Category' => Category::class, 'Enclosure' => Enclosure::class, 'Caption' => Caption::class, 'Copyright' => Copyright::class, 'Credit' => Credit::class, 'Rating' => Rating::class, 'Restriction' => Restriction::class, 'Content_Type_Sniffer' => Sniffer::class, 'Source' => Source::class, 'Misc' => Misc::class, 'XML_Declaration_Parser' => DeclarationParser::class, 'Parse_Date' => Date::class, ]; /** * Constructor * * No-op */ public function __construct() { } /** * Register a class * * @param string $type See {@see $default} for names * @param class-string $class Class name, must subclass the corresponding default * @param bool $legacy Whether to enable legacy support for this class * @return bool Successfulness */ public function register($type, $class, $legacy = false) { if (array_key_exists($type, $this->legacyTypes)) { // trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED); $type = $this->legacyTypes[$type]; } if (!array_key_exists($type, $this->default)) { return false; } if (!class_exists($class)) { return false; } /** @var string */ $base_class = $this->default[$type]; if (!is_subclass_of($class, $base_class)) { return false; } $this->classes[$type] = $class; if ($legacy) { $this->legacy[] = $class; } return true; } /** * Get the class registered for a type * * Where possible, use {@see create()} or {@see call()} instead * * @template T * @param class-string<T> $type * @return class-string<T>|null */ public function get_class($type) { if (array_key_exists($type, $this->legacyTypes)) { // trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED); $type = $this->legacyTypes[$type]; } if (!array_key_exists($type, $this->default)) { return null; } $class = $this->default[$type]; if (array_key_exists($type, $this->classes)) { $class = $this->classes[$type]; } return $class; } /** * Create a new instance of a given type * * @template T class-string $type * @param class-string<T> $type * @param array $parameters Parameters to pass to the constructor * @return T Instance of class */ public function &create($type, $parameters = []) { $class = $this->get_class($type); if (!method_exists($class, '__construct')) { $instance = new $class(); } else { $reflector = new \ReflectionClass($class); $instance = $reflector->newInstanceArgs($parameters); } if ($instance instanceof RegistryAware) { $instance->set_registry($this); } elseif (method_exists($instance, 'set_registry')) { trigger_error(sprintf('Using the method "set_registry()" without implementing "%s" is deprecated since SimplePie 1.8.0, implement "%s" in "%s".', RegistryAware::class, RegistryAware::class, $class), \E_USER_DEPRECATED); $instance->set_registry($this); } return $instance; } /** * Call a static method for a type * * @param class-string $type * @param string $method * @param array $parameters * @return mixed */ public function &call($type, $method, $parameters = []) { $class = $this->get_class($type); if (in_array($class, $this->legacy)) { switch ($type) { case Cache::class: // For backwards compatibility with old non-static // Cache::create() methods in PHP < 8.0. // No longer supported as of PHP 8.0. if ($method === 'get_handler') { $result = @call_user_func_array([$class, 'create'], $parameters); return $result; } break; } } $result = call_user_func_array([$class, $method], $parameters); return $result; } } class_alias('SimplePie\Registry', 'SimplePie_Registry'); PK �Z���� �� SimplePie.phpnu �[��� <?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue * @author Ryan Parman * @author Sam Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ namespace SimplePie; use InvalidArgumentException; use Psr\SimpleCache\CacheInterface; use SimplePie\Cache\Base; use SimplePie\Cache\BaseDataCache; use SimplePie\Cache\CallableNameFilter; use SimplePie\Cache\DataCache; use SimplePie\Cache\NameFilter; use SimplePie\Cache\Psr16; use SimplePie\Content\Type\Sniffer; /** * SimplePie * * @package SimplePie * @subpackage API */ class SimplePie { /** * SimplePie Name */ public const NAME = 'SimplePie'; /** * SimplePie Version */ public const VERSION = '1.8.0'; /** * SimplePie Website URL */ public const URL = 'http://simplepie.org'; /** * SimplePie Linkback */ public const LINKBACK = '<a href="' . self::URL . '" title="' . self::NAME . ' ' . self::VERSION . '">' . self::NAME . '</a>'; /** * No Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_NONE = 0; /** * Feed Link Element Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_AUTODISCOVERY = 1; /** * Local Feed Extension Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_LOCAL_EXTENSION = 2; /** * Local Feed Body Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_LOCAL_BODY = 4; /** * Remote Feed Extension Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_REMOTE_EXTENSION = 8; /** * Remote Feed Body Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_REMOTE_BODY = 16; /** * All Feed Autodiscovery * @see SimplePie::set_autodiscovery_level() */ public const LOCATOR_ALL = 31; /** * No known feed type */ public const TYPE_NONE = 0; /** * RSS 0.90 */ public const TYPE_RSS_090 = 1; /** * RSS 0.91 (Netscape) */ public const TYPE_RSS_091_NETSCAPE = 2; /** * RSS 0.91 (Userland) */ public const TYPE_RSS_091_USERLAND = 4; /** * RSS 0.91 (both Netscape and Userland) */ public const TYPE_RSS_091 = 6; /** * RSS 0.92 */ public const TYPE_RSS_092 = 8; /** * RSS 0.93 */ public const TYPE_RSS_093 = 16; /** * RSS 0.94 */ public const TYPE_RSS_094 = 32; /** * RSS 1.0 */ public const TYPE_RSS_10 = 64; /** * RSS 2.0 */ public const TYPE_RSS_20 = 128; /** * RDF-based RSS */ public const TYPE_RSS_RDF = 65; /** * Non-RDF-based RSS (truly intended as syndication format) */ public const TYPE_RSS_SYNDICATION = 190; /** * All RSS */ public const TYPE_RSS_ALL = 255; /** * Atom 0.3 */ public const TYPE_ATOM_03 = 256; /** * Atom 1.0 */ public const TYPE_ATOM_10 = 512; /** * All Atom */ public const TYPE_ATOM_ALL = 768; /** * All feed types */ public const TYPE_ALL = 1023; /** * No construct */ public const CONSTRUCT_NONE = 0; /** * Text construct */ public const CONSTRUCT_TEXT = 1; /** * HTML construct */ public const CONSTRUCT_HTML = 2; /** * XHTML construct */ public const CONSTRUCT_XHTML = 4; /** * base64-encoded construct */ public const CONSTRUCT_BASE64 = 8; /** * IRI construct */ public const CONSTRUCT_IRI = 16; /** * A construct that might be HTML */ public const CONSTRUCT_MAYBE_HTML = 32; /** * All constructs */ public const CONSTRUCT_ALL = 63; /** * Don't change case */ public const SAME_CASE = 1; /** * Change to lowercase */ public const LOWERCASE = 2; /** * Change to uppercase */ public const UPPERCASE = 4; /** * PCRE for HTML attributes */ public const PCRE_HTML_ATTRIBUTE = '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'; /** * PCRE for XML attributes */ public const PCRE_XML_ATTRIBUTE = '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'; /** * XML Namespace */ public const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace'; /** * Atom 1.0 Namespace */ public const NAMESPACE_ATOM_10 = 'http://www.w3.org/2005/Atom'; /** * Atom 0.3 Namespace */ public const NAMESPACE_ATOM_03 = 'http://purl.org/atom/ns#'; /** * RDF Namespace */ public const NAMESPACE_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; /** * RSS 0.90 Namespace */ public const NAMESPACE_RSS_090 = 'http://my.netscape.com/rdf/simple/0.9/'; /** * RSS 1.0 Namespace */ public const NAMESPACE_RSS_10 = 'http://purl.org/rss/1.0/'; /** * RSS 1.0 Content Module Namespace */ public const NAMESPACE_RSS_10_MODULES_CONTENT = 'http://purl.org/rss/1.0/modules/content/'; /** * RSS 2.0 Namespace * (Stupid, I know, but I'm certain it will confuse people less with support.) */ public const NAMESPACE_RSS_20 = ''; /** * DC 1.0 Namespace */ public const NAMESPACE_DC_10 = 'http://purl.org/dc/elements/1.0/'; /** * DC 1.1 Namespace */ public const NAMESPACE_DC_11 = 'http://purl.org/dc/elements/1.1/'; /** * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace */ public const NAMESPACE_W3C_BASIC_GEO = 'http://www.w3.org/2003/01/geo/wgs84_pos#'; /** * GeoRSS Namespace */ public const NAMESPACE_GEORSS = 'http://www.georss.org/georss'; /** * Media RSS Namespace */ public const NAMESPACE_MEDIARSS = 'http://search.yahoo.com/mrss/'; /** * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec. */ public const NAMESPACE_MEDIARSS_WRONG = 'http://search.yahoo.com/mrss'; /** * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5. */ public const NAMESPACE_MEDIARSS_WRONG2 = 'http://video.search.yahoo.com/mrss'; /** * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace. */ public const NAMESPACE_MEDIARSS_WRONG3 = 'http://video.search.yahoo.com/mrss/'; /** * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace. */ public const NAMESPACE_MEDIARSS_WRONG4 = 'http://www.rssboard.org/media-rss'; /** * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL. */ public const NAMESPACE_MEDIARSS_WRONG5 = 'http://www.rssboard.org/media-rss/'; /** * iTunes RSS Namespace */ public const NAMESPACE_ITUNES = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; /** * XHTML Namespace */ public const NAMESPACE_XHTML = 'http://www.w3.org/1999/xhtml'; /** * IANA Link Relations Registry */ public const IANA_LINK_RELATIONS_REGISTRY = 'http://www.iana.org/assignments/relation/'; /** * No file source */ public const FILE_SOURCE_NONE = 0; /** * Remote file source */ public const FILE_SOURCE_REMOTE = 1; /** * Local file source */ public const FILE_SOURCE_LOCAL = 2; /** * fsockopen() file source */ public const FILE_SOURCE_FSOCKOPEN = 4; /** * cURL file source */ public const FILE_SOURCE_CURL = 8; /** * file_get_contents() file source */ public const FILE_SOURCE_FILE_GET_CONTENTS = 16; /** * @var array Raw data * @access private */ public $data = []; /** * @var mixed Error string * @access private */ public $error; /** * @var int HTTP status code * @see SimplePie::status_code() * @access private */ public $status_code = 0; /** * @var object Instance of \SimplePie\Sanitize (or other class) * @see SimplePie::set_sanitize_class() * @access private */ public $sanitize; /** * @var string SimplePie Useragent * @see SimplePie::set_useragent() * @access private */ public $useragent = ''; /** * @var string Feed URL * @see SimplePie::set_feed_url() * @access private */ public $feed_url; /** * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently * @see SimplePie::subscribe_url() * @access private */ public $permanent_url = null; /** * @var object Instance of \SimplePie\File to use as a feed * @see SimplePie::set_file() * @access private */ public $file; /** * @var string Raw feed data * @see SimplePie::set_raw_data() * @access private */ public $raw_data; /** * @var int Timeout for fetching remote files * @see SimplePie::set_timeout() * @access private */ public $timeout = 10; /** * @var array Custom curl options * @see SimplePie::set_curl_options() * @access private */ public $curl_options = []; /** * @var bool Forces fsockopen() to be used for remote files instead * of cURL, even if a new enough version is installed * @see SimplePie::force_fsockopen() * @access private */ public $force_fsockopen = false; /** * @var bool Force the given data/URL to be treated as a feed no matter what * it appears like * @see SimplePie::force_feed() * @access private */ public $force_feed = false; /** * @var bool Enable/Disable Caching * @see SimplePie::enable_cache() * @access private */ private $enable_cache = true; /** * @var DataCache|null * @see SimplePie::set_cache() */ private $cache = null; /** * @var NameFilter * @see SimplePie::set_cache_namefilter() */ private $cache_namefilter; /** * @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 */ public $cache_duration = 3600; /** * @var int Auto-discovery cache duration (in seconds) * @see SimplePie::set_autodiscovery_cache_duration() * @access private */ public $autodiscovery_cache_duration = 604800; // 7 Days. /** * @var string Cache location (relative to executing script) * @see SimplePie::set_cache_location() * @access private */ public $cache_location = './cache'; /** * @var string Function that creates the cache filename * @see SimplePie::set_cache_name_function() * @access private */ public $cache_name_function = 'md5'; /** * @var bool Reorder feed by date descending * @see SimplePie::enable_order_by_date() * @access private */ public $order_by_date = true; /** * @var mixed Force input encoding to be set to the follow value * (false, or anything type-cast to false, disables this feature) * @see SimplePie::set_input_encoding() * @access private */ public $input_encoding = false; /** * @var int Feed Autodiscovery Level * @see SimplePie::set_autodiscovery_level() * @access private */ public $autodiscovery = self::LOCATOR_ALL; /** * Class registry object * * @var \SimplePie\Registry */ public $registry; /** * @var int Maximum number of feeds to check with autodiscovery * @see SimplePie::set_max_checked_feeds() * @access private */ public $max_checked_feeds = 10; /** * @var array All the feeds found during the autodiscovery process * @see SimplePie::get_all_discovered_feeds() * @access private */ public $all_discovered_feeds = []; /** * @var string Web-accessible path to the handler_image.php file. * @see SimplePie::set_image_handler() * @access private */ public $image_handler = ''; /** * @var array Stores the URLs when multiple feeds are being initialized. * @see SimplePie::set_feed_url() * @access private */ public $multifeed_url = []; /** * @var array Stores SimplePie objects when multiple feeds initialized. * @access private */ public $multifeed_objects = []; /** * @var array Stores the get_object_vars() array for use with multifeeds. * @see SimplePie::set_feed_url() * @access private */ public $config_settings = null; /** * @var integer Stores the number of items to return per-feed with multifeeds. * @see SimplePie::set_item_limit() * @access private */ public $item_limit = 0; /** * @var bool Stores if last-modified and/or etag headers were sent with the * request when checking a feed. */ public $check_modified = false; /** * @var array Stores the default attributes to be stripped by strip_attributes(). * @see SimplePie::strip_attributes() * @access private */ public $strip_attributes = ['bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc']; /** * @var array Stores the default attributes to add to different tags by add_attributes(). * @see SimplePie::add_attributes() * @access private */ public $add_attributes = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']]; /** * @var array Stores the default tags to be stripped by strip_htmltags(). * @see SimplePie::strip_htmltags() * @access private */ public $strip_htmltags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style']; /** * @var array Stores the default attributes to be renamed by rename_attributes(). * @see SimplePie::rename_attributes() * @access private */ public $rename_attributes = []; /** * @var bool Should we throw exceptions, or use the old-style error property? * @access private */ public $enable_exceptions = false; /** * The SimplePie class contains feed level data and options * * To use SimplePie, create the SimplePie object with no parameters. You can * then set configuration options using the provided methods. After setting * them, you must initialise the feed using $feed->init(). At that point the * object's methods and properties will be available to you. * * Previously, it was possible to pass in the feed URL along with cache * options directly into the constructor. This has been removed as of 1.3 as * it caused a lot of confusion. * * @since 1.0 Preview Release */ public function __construct() { if (version_compare(PHP_VERSION, '7.2', '<')) { exit('Please upgrade to PHP 7.2 or newer.'); } $this->set_useragent(); $this->set_cache_namefilter(new CallableNameFilter($this->cache_name_function)); // Other objects, instances created here so we can set options on them $this->sanitize = new \SimplePie\Sanitize(); $this->registry = new \SimplePie\Registry(); if (func_num_args() > 0) { trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', \E_USER_DEPRECATED); $args = func_get_args(); switch (count($args)) { case 3: $this->set_cache_duration($args[2]); // no break case 2: $this->set_cache_location($args[1]); // no break case 1: $this->set_feed_url($args[0]); $this->init(); } } } /** * Used for converting object to a string */ public function __toString() { return md5(serialize($this->data)); } /** * Remove items that link back to this before destroying this object */ public function __destruct() { if (!gc_enabled()) { if (!empty($this->data['items'])) { foreach ($this->data['items'] as $item) { $item->__destruct(); } unset($item, $this->data['items']); } if (!empty($this->data['ordered_items'])) { foreach ($this->data['ordered_items'] as $item) { $item->__destruct(); } unset($item, $this->data['ordered_items']); } } } /** * Force the given data/URL to be treated as a feed * * This tells SimplePie to ignore the content-type provided by the server. * Be careful when using this option, as it will also disable autodiscovery. * * @since 1.1 * @param bool $enable Force the given data/URL to be treated as a feed */ public function force_feed($enable = false) { $this->force_feed = (bool) $enable; } /** * Set the URL of the feed you want to parse * * This allows you to enter the URL of the feed you want to parse, or the * website you want to try to use auto-discovery on. This takes priority * over any set raw data. * * You can set multiple feeds to mash together by passing an array instead * of a string for the $url. Remember that with each additional feed comes * additional processing and resources. * * @since 1.0 Preview Release * @see set_raw_data() * @param string|array $url This is the URL (or array of URLs) that you want to parse. */ public function set_feed_url($url) { $this->multifeed_url = []; if (is_array($url)) { foreach ($url as $value) { $this->multifeed_url[] = $this->registry->call(Misc::class, 'fix_protocol', [$value, 1]); } } else { $this->feed_url = $this->registry->call(Misc::class, 'fix_protocol', [$url, 1]); $this->permanent_url = $this->feed_url; } } /** * Set an instance of {@see \SimplePie\File} to use as a feed * * @param \SimplePie\File &$file * @return bool True on success, false on failure */ public function set_file(&$file) { if ($file instanceof \SimplePie\File) { $this->feed_url = $file->url; $this->permanent_url = $this->feed_url; $this->file = &$file; return true; } return false; } /** * Set the raw XML data to parse * * Allows you to use a string of RSS/Atom data instead of a remote feed. * * If you have a feed available as a string in PHP, you can tell SimplePie * to parse that data string instead of a remote feed. Any set feed URL * takes precedence. * * @since 1.0 Beta 3 * @param string $data RSS or Atom data as a string. * @see set_feed_url() */ public function set_raw_data($data) { $this->raw_data = $data; } /** * Set the default timeout for fetching remote feeds * * This allows you to change the maximum time the feed's server to respond * and send the feed back. * * @since 1.0 Beta 3 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. */ public function set_timeout($timeout = 10) { $this->timeout = (int) $timeout; } /** * Set custom curl options * * This allows you to change default curl options * * @since 1.0 Beta 3 * @param array $curl_options Curl options to add to default settings */ public function set_curl_options(array $curl_options = []) { $this->curl_options = $curl_options; } /** * Force SimplePie to use fsockopen() instead of cURL * * @since 1.0 Beta 3 * @param bool $enable Force fsockopen() to be used */ public function force_fsockopen($enable = false) { $this->force_fsockopen = (bool) $enable; } /** * Enable/disable caching in SimplePie. * * This option allows you to disable caching all-together in SimplePie. * However, disabling the cache can lead to longer load times. * * @since 1.0 Preview Release * @param bool $enable Enable caching */ public function enable_cache($enable = true) { $this->enable_cache = (bool) $enable; } /** * Set a PSR-16 implementation as cache * * @param CacheInterface $psr16cache The PSR-16 cache implementation * * @return void */ public function set_cache(CacheInterface $cache) { $this->cache = new Psr16($cache); } /** * 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. * * @deprecated since SimplePie 1.8.0, expired cache will not be used anymore. * * @param bool $enable Force use of cache on fail. */ public function force_cache_fallback($enable = false) { // @trigger_error(sprintf('SimplePie\SimplePie::force_cache_fallback() is deprecated since SimplePie 1.8.0, expired cache will not be used anymore.'), \E_USER_DEPRECATED); $this->force_cache_fallback = (bool) $enable; } /** * Set the length of time (in seconds) that the contents of a feed will be * cached * * @param int $seconds The feed content cache duration */ public function set_cache_duration($seconds = 3600) { $this->cache_duration = (int) $seconds; } /** * Set the length of time (in seconds) that the autodiscovered feed URL will * be cached * * @param int $seconds The autodiscovered feed URL cache duration. */ public function set_autodiscovery_cache_duration($seconds = 604800) { $this->autodiscovery_cache_duration = (int) $seconds; } /** * Set the file system location where the cached files should be stored * * @deprecated since SimplePie 1.8.0, use \SimplePie\SimplePie::set_cache() instead. * * @param string $location The file system location. */ public function set_cache_location($location = './cache') { // @trigger_error(sprintf('SimplePie\SimplePie::set_cache_location() is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()" instead.'), \E_USER_DEPRECATED); $this->cache_location = (string) $location; } /** * Return the filename (i.e. hash, without path and without extension) of the file to cache a given URL. * * @param string $url The URL of the feed to be cached. * @return string A filename (i.e. hash, without path and without extension). */ public function get_cache_filename($url) { // Append custom parameters to the URL to avoid cache pollution in case of multiple calls with different parameters. $url .= $this->force_feed ? '#force_feed' : ''; $options = []; if ($this->timeout != 10) { $options[CURLOPT_TIMEOUT] = $this->timeout; } if ($this->useragent !== \SimplePie\Misc::get_default_useragent()) { $options[CURLOPT_USERAGENT] = $this->useragent; } if (!empty($this->curl_options)) { foreach ($this->curl_options as $k => $v) { $options[$k] = $v; } } if (!empty($options)) { ksort($options); $url .= '#' . urlencode(var_export($options, true)); } return $this->cache_namefilter->filter($url); } /** * Set whether feed items should be sorted into reverse chronological order * * @param bool $enable Sort as reverse chronological order. */ public function enable_order_by_date($enable = true) { $this->order_by_date = (bool) $enable; } /** * Set the character encoding used to parse the feed * * This overrides the encoding reported by the feed, however it will fall * back to the normal encoding detection if the override fails * * @param string $encoding Character encoding */ public function set_input_encoding($encoding = false) { if ($encoding) { $this->input_encoding = (string) $encoding; } else { $this->input_encoding = false; } } /** * Set how much feed autodiscovery to do * * @see \SimplePie\SimplePie::LOCATOR_NONE * @see \SimplePie\SimplePie::LOCATOR_AUTODISCOVERY * @see \SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION * @see \SimplePie\SimplePie::LOCATOR_LOCAL_BODY * @see \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION * @see \SimplePie\SimplePie::LOCATOR_REMOTE_BODY * @see \SimplePie\SimplePie::LOCATOR_ALL * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator) */ public function set_autodiscovery_level($level = self::LOCATOR_ALL) { $this->autodiscovery = (int) $level; } /** * Get the class registry * * Use this to override SimplePie's default classes * @see \SimplePie\Registry * * @return Registry */ public function &get_registry() { return $this->registry; } /** * Set which class SimplePie uses for caching * * @deprecated since SimplePie 1.3, use {@see set_cache()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_cache_class($class = Cache::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::set_cache()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Cache::class, $class, true); } /** * Set which class SimplePie uses for auto-discovery * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_locator_class($class = Locator::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Locator::class, $class, true); } /** * Set which class SimplePie uses for XML parsing * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_parser_class($class = Parser::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Parser::class, $class, true); } /** * Set which class SimplePie uses for remote file fetching * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_file_class($class = File::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(File::class, $class, true); } /** * Set which class SimplePie uses for data sanitization * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_sanitize_class($class = Sanitize::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Sanitize::class, $class, true); } /** * Set which class SimplePie uses for handling feed items * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_item_class($class = Item::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Item::class, $class, true); } /** * Set which class SimplePie uses for handling author data * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_author_class($class = Author::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Author::class, $class, true); } /** * Set which class SimplePie uses for handling category data * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_category_class($class = Category::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Category::class, $class, true); } /** * Set which class SimplePie uses for feed enclosures * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_enclosure_class($class = Enclosure::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Enclosure::class, $class, true); } /** * Set which class SimplePie uses for `<media:text>` captions * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_caption_class($class = Caption::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Caption::class, $class, true); } /** * Set which class SimplePie uses for `<media:copyright>` * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_copyright_class($class = Copyright::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Copyright::class, $class, true); } /** * Set which class SimplePie uses for `<media:credit>` * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_credit_class($class = Credit::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Credit::class, $class, true); } /** * Set which class SimplePie uses for `<media:rating>` * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_rating_class($class = Rating::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Rating::class, $class, true); } /** * Set which class SimplePie uses for `<media:restriction>` * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_restriction_class($class = Restriction::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Restriction::class, $class, true); } /** * Set which class SimplePie uses for content-type sniffing * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_content_type_sniffer_class($class = Sniffer::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Sniffer::class, $class, true); } /** * Set which class SimplePie uses item sources * * @deprecated since SimplePie 1.3, use {@see get_registry()} instead * * @param string $class Name of custom class * * @return boolean True on success, false otherwise */ public function set_source_class($class = Source::class) { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); return $this->registry->register(Source::class, $class, true); } /** * Set the user agent string * * @param string $ua New user agent string. */ public function set_useragent($ua = null) { if ($ua === null) { $ua = \SimplePie\Misc::get_default_useragent(); } $this->useragent = (string) $ua; } /** * Set a namefilter to modify the cache filename with * * @param NameFilter $filter * * @return void */ public function set_cache_namefilter(NameFilter $filter): void { $this->cache_namefilter = $filter; } /** * Set callback function to create cache filename with * * @deprecated since SimplePie 1.8.0, use {@see set_cache_namefilter()} instead * * @param mixed $function Callback function */ public function set_cache_name_function($function = 'md5') { // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache_namefilter()" instead.', __METHOD__), \E_USER_DEPRECATED); if (is_callable($function)) { $this->cache_name_function = $function; $this->set_cache_namefilter(new CallableNameFilter($this->cache_name_function)); } } /** * Set options to make SP as fast as possible * * Forgoes a substantial amount of data sanitization in favor of speed. This * turns SimplePie into a dumb parser of feeds. * * @param bool $set Whether to set them or not */ public function set_stupidly_fast($set = false) { if ($set) { $this->enable_order_by_date(false); $this->remove_div(false); $this->strip_comments(false); $this->strip_htmltags(false); $this->strip_attributes(false); $this->add_attributes(false); $this->set_image_handler(false); $this->set_https_domains([]); } } /** * Set maximum number of feeds to check with autodiscovery * * @param int $max Maximum number of feeds to check */ public function set_max_checked_feeds($max = 10) { $this->max_checked_feeds = (int) $max; } public function remove_div($enable = true) { $this->sanitize->remove_div($enable); } public function strip_htmltags($tags = '', $encode = null) { if ($tags === '') { $tags = $this->strip_htmltags; } $this->sanitize->strip_htmltags($tags); if ($encode !== null) { $this->sanitize->encode_instead_of_strip($tags); } } public function encode_instead_of_strip($enable = true) { $this->sanitize->encode_instead_of_strip($enable); } public function rename_attributes($attribs = '') { if ($attribs === '') { $attribs = $this->rename_attributes; } $this->sanitize->rename_attributes($attribs); } public function strip_attributes($attribs = '') { if ($attribs === '') { $attribs = $this->strip_attributes; } $this->sanitize->strip_attributes($attribs); } public function add_attributes($attribs = '') { if ($attribs === '') { $attribs = $this->add_attributes; } $this->sanitize->add_attributes($attribs); } /** * Set the output encoding * * Allows you to override SimplePie's output to match that of your webpage. * This is useful for times when your webpages are not being served as * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and * is similar to {@see set_input_encoding()}. * * It should be noted, however, that not all character encodings can support * all characters. If your page is being served as ISO-8859-1 and you try * to display a Japanese feed, you'll likely see garbled characters. * Because of this, it is highly recommended to ensure that your webpages * are served as UTF-8. * * The number of supported character encodings depends on whether your web * host supports {@link http://php.net/mbstring mbstring}, * {@link http://php.net/iconv iconv}, or both. See * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for * more information. * * @param string $encoding */ public function set_output_encoding($encoding = 'UTF-8') { $this->sanitize->set_output_encoding($encoding); } public function strip_comments($strip = false) { $this->sanitize->strip_comments($strip); } /** * Set element/attribute key/value pairs of HTML attributes * containing URLs that need to be resolved relative to the feed * * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, * |q|@cite * * @since 1.0 * @param array|null $element_attribute Element/attribute key/value pairs, null for default */ public function set_url_replacements($element_attribute = null) { $this->sanitize->set_url_replacements($element_attribute); } /** * Set the list of domains for which to force HTTPS. * @see \SimplePie\Sanitize::set_https_domains() * @param array List of HTTPS domains. Example array('biz', 'example.com', 'example.org', 'www.example.net'). */ public function set_https_domains($domains = []) { if (is_array($domains)) { $this->sanitize->set_https_domains($domains); } } /** * Set the handler to enable the display of cached images. * * @param string $page Web-accessible path to the handler_image.php file. * @param string $qs The query string that the value should be passed to. */ public function set_image_handler($page = false, $qs = 'i') { if ($page !== false) { $this->sanitize->set_image_handler($page . '?' . $qs . '='); } else { $this->image_handler = ''; } } /** * Set the limit for items returned per-feed with multifeeds * * @param integer $limit The maximum number of items to return. */ public function set_item_limit($limit = 0) { $this->item_limit = (int) $limit; } /** * Enable throwing exceptions * * @param boolean $enable Should we throw exceptions, or use the old-style error property? */ public function enable_exceptions($enable = true) { $this->enable_exceptions = $enable; } /** * Initialize the feed object * * This is what makes everything happen. Period. This is where all of the * configuration options get processed, feeds are fetched, cached, and * parsed, and all of that other good stuff. * * @return boolean True if successful, false otherwise */ public function init() { // Check absolute bare minimum requirements. if (!extension_loaded('xml') || !extension_loaded('pcre')) { $this->error = 'XML or PCRE extensions not loaded!'; return false; } // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader. elseif (!extension_loaded('xmlreader')) { static $xml_is_sane = null; if ($xml_is_sane === null) { $parser_check = xml_parser_create(); xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); xml_parser_free($parser_check); $xml_is_sane = isset($values[0]['value']); } if (!$xml_is_sane) { return false; } } // The default sanitize class gets set in the constructor, check if it has // changed. if ($this->registry->get_class(Sanitize::class) !== 'SimplePie\Sanitize') { $this->sanitize = $this->registry->create(Sanitize::class); } if (method_exists($this->sanitize, 'set_registry')) { $this->sanitize->set_registry($this->registry); } // Pass whatever was set with config options over to the sanitizer. // Pass the classes in for legacy support; new classes should use the registry instead $this->sanitize->pass_cache_data( $this->enable_cache, $this->cache_location, $this->cache_namefilter, $this->registry->get_class(Cache::class), $this->cache ); $this->sanitize->pass_file_data($this->registry->get_class(File::class), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options); if (!empty($this->multifeed_url)) { $i = 0; $success = 0; $this->multifeed_objects = []; $this->error = []; foreach ($this->multifeed_url as $url) { $this->multifeed_objects[$i] = clone $this; $this->multifeed_objects[$i]->set_feed_url($url); $single_success = $this->multifeed_objects[$i]->init(); $success |= $single_success; if (!$single_success) { $this->error[$i] = $this->multifeed_objects[$i]->error(); } $i++; } return (bool) $success; } elseif ($this->feed_url === null && $this->raw_data === null) { return false; } $this->error = null; $this->data = []; $this->check_modified = false; $this->multifeed_objects = []; $cache = false; if ($this->feed_url !== null) { $parsed_feed_url = $this->registry->call(Misc::class, 'parse_url', [$this->feed_url]); // Decide whether to enable caching if ($this->enable_cache && $parsed_feed_url['scheme'] !== '') { $cache = $this->get_cache($this->feed_url); } // Fetch the data via \SimplePie\File into $this->raw_data if (($fetched = $this->fetch_data($cache)) === true) { return true; } elseif ($fetched === false) { return false; } [$headers, $sniffed] = $fetched; } // 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::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); return false; } // Set up array of possible encodings $encodings = []; // First check to see if input has been overridden. if ($this->input_encoding !== false) { $encodings[] = strtoupper($this->input_encoding); } $application_types = ['application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity']; $text_types = ['text/xml', 'text/xml-external-parsed-entity']; // RFC 3023 (only applies to sniffed content) if (isset($sniffed)) { if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { $encodings[] = strtoupper($charset[1]); } $encodings = array_merge($encodings, $this->registry->call(Misc::class, 'xml_encoding', [$this->raw_data, &$this->registry])); $encodings[] = 'UTF-8'; } elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { $encodings[] = strtoupper($charset[1]); } $encodings[] = 'US-ASCII'; } // Text MIME-type default elseif (substr($sniffed, 0, 5) === 'text/') { $encodings[] = 'UTF-8'; } } // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 $encodings = array_merge($encodings, $this->registry->call(Misc::class, 'xml_encoding', [$this->raw_data, &$this->registry])); $encodings[] = 'UTF-8'; $encodings[] = 'ISO-8859-1'; // There's no point in trying an encoding twice $encodings = array_unique($encodings); // Loop through each possible encoding, till we return something, or run out of possibilities foreach ($encodings as $encoding) { // Change the encoding to UTF-8 (as we always use UTF-8 internally) if ($utf8_data = $this->registry->call(Misc::class, 'change_encoding', [$this->raw_data, $encoding, 'UTF-8'])) { // Create new parser $parser = $this->registry->create(Parser::class); // If it's parsed fine if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url)) { $this->data = $parser->get_data(); if (!($this->get_type() & ~self::TYPE_NONE)) { $this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed."; $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); return false; } if (isset($headers)) { $this->data['headers'] = $headers; } $this->data['build'] = \SimplePie\Misc::get_build(); // Cache the file if caching is enabled $this->data['cache_expiration_time'] = $this->cache_duration + time(); if ($cache && !$cache->set_data($this->get_cache_filename($this->feed_url), $this->data, $this->cache_duration)) { trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); } return true; } } } if (isset($parser)) { // We have an error, just set \SimplePie\Misc::error to it and quit $this->error = $this->feed_url; $this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); } else { $this->error = 'The data could not be converted to UTF-8.'; 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 = []; 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.'; } } $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); return false; } /** * Fetch the data via \SimplePie\File * * If the data is already cached, attempt to fetch it from there instead * @param Base|DataCache|false $cache Cache handler, or false to not load from the cache * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type */ protected function fetch_data(&$cache) { if (is_object($cache) && $cache instanceof Base) { // @trigger_error(sprintf('Providing $cache as "\SimplePie\Cache\Base" in %s() is deprecated since SimplePie 1.8.0, please provide "\SimplePie\Cache\DataCache" implementation instead.', __METHOD__), \E_USER_DEPRECATED); $cache = new BaseDataCache($cache); } if ($cache !== false && !$cache instanceof DataCache) { throw new InvalidArgumentException(sprintf( '%s(): Argument #1 ($cache) must be of type %s|false', __METHOD__, DataCache::class ), 1); } $cacheKey = $this->get_cache_filename($this->feed_url); // If it's enabled, use the cache if ($cache) { // Load the Cache $this->data = $cache->get_data($cacheKey, []); if (!empty($this->data)) { // If the cache is for an outdated build of SimplePie if (!isset($this->data['build']) || $this->data['build'] !== \SimplePie\Misc::get_build()) { $cache->delete_data($cacheKey); $this->data = []; } // If we've hit a collision just rerun it with caching disabled elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) { $cache = false; $this->data = []; } // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. elseif (isset($this->data['feed_url'])) { // Do not need to do feed autodiscovery yet. if ($this->data['feed_url'] !== $this->data['url']) { $this->set_feed_url($this->data['feed_url']); $this->data['url'] = $this->data['feed_url']; $cache->set_data($this->get_cache_filename($this->feed_url), $this->data, $this->autodiscovery_cache_duration); return $this->init(); } $cache->delete_data($this->get_cache_filename($this->feed_url)); $this->data = []; } // Check if the cache has been updated elseif (isset($this->data['cache_expiration_time']) && $this->data['cache_expiration_time'] > time()) { // Want to know if we tried to send last-modified and/or etag headers // when requesting this file. (Note that it's up to the file to // support this, but we don't always send the headers either.) $this->check_modified = true; if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) { $headers = [ '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', ]; if (isset($this->data['headers']['last-modified'])) { $headers['if-modified-since'] = $this->data['headers']['last-modified']; } if (isset($this->data['headers']['etag'])) { $headers['if-none-match'] = $this->data['headers']['etag']; } $file = $this->registry->create(File::class, [$this->feed_url, $this->timeout / 10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]); $this->status_code = $file->status_code; if ($file->success) { if ($file->status_code === 304) { // Set raw_data to false here too, to signify that the cache // is still valid. $this->raw_data = false; $cache->set_data($cacheKey, $this->data, $this->cache_duration); return true; } } else { $this->check_modified = false; if ($this->force_cache_fallback) { $cache->set_data($cacheKey, $this->data, $this->cache_duration); return true; } unset($file); } } } // If the cache is still valid, just return true else { $this->raw_data = false; return true; } } // If the cache is empty else { $this->data = []; } } // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. if (!isset($file)) { if ($this->file instanceof \SimplePie\File && $this->file->url === $this->feed_url) { $file = &$this->file; } else { $headers = [ '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', ]; $file = $this->registry->create(File::class, [$this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]); } } $this->status_code = $file->status_code; // If the file connection has an error, set SimplePie::error to that and quit if (!$file->success && !($file->method & self::FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) { $this->error = $file->error; return !empty($this->data); } if (!$this->force_feed) { // Check if the supplied URL is a feed, if it isn't, look for it. $locate = $this->registry->create(Locator::class, [&$file, $this->timeout, $this->useragent, $this->max_checked_feeds, $this->force_fsockopen, $this->curl_options]); if (!$locate->is_feed($file)) { $copyStatusCode = $file->status_code; $copyContentType = $file->headers['content-type']; try { $microformats = false; if (class_exists('DOMXpath') && function_exists('Mf2\parse')) { $doc = new \DOMDocument(); @$doc->loadHTML($file->body); $xpath = new \DOMXpath($doc); // Check for both h-feed and h-entry, as both a feed with no entries // and a list of entries without an h-feed wrapper are both valid. $query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '. 'contains(concat(" ", @class, " "), " h-entry ")]'; $result = $xpath->query($query); $microformats = $result->length !== 0; } // Now also do feed discovery, but if microformats were found don't // overwrite the current value of file. $discovered = $locate->find( $this->autodiscovery, $this->all_discovered_feeds ); if ($microformats) { if ($hub = $locate->get_rel_link('hub')) { $self = $locate->get_rel_link('self'); $this->store_links($file, $hub, $self); } // Push the current file onto all_discovered feeds so the user can // be shown this as one of the options. if (isset($this->all_discovered_feeds)) { $this->all_discovered_feeds[] = $file; } } else { if ($discovered) { $file = $discovered; } else { // We need to unset this so that if SimplePie::set_file() has // been called that object is untouched unset($file); $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); return false; } } } catch (\SimplePie\Exception $e) { // We need to unset this so that if SimplePie::set_file() has been called that object is untouched unset($file); // This is usually because DOMDocument doesn't exist $this->error = $e->getMessage(); $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()]); return false; } if ($cache) { $this->data = [ 'url' => $this->feed_url, 'feed_url' => $file->url, 'build' => \SimplePie\Misc::get_build(), 'cache_expiration_time' => $this->cache_duration + time(), ]; if (!$cache->set_data($cacheKey, $this->data, $this->cache_duration)) { trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); } } } $this->feed_url = $file->url; $locate = null; } $this->raw_data = $file->body; $this->permanent_url = $file->permanent_url; $headers = $file->headers; $sniffer = $this->registry->create(Sniffer::class, [&$file]); $sniffed = $sniffer->get_type(); return [$headers, $sniffed]; } /** * Get the error message for the occurred error * * @return string|array Error message, or array of messages for multifeeds */ public function error() { return $this->error; } /** * Get the last HTTP status code * * @return int Status code */ public function status_code() { return $this->status_code; } /** * Get the raw XML * * This is the same as the old `$feed->enable_xml_dump(true)`, but returns * the data instead of printing it. * * @return string|boolean Raw XML data, false if the cache is used */ public function get_raw_data() { return $this->raw_data; } /** * Get the character encoding used for output * * @since Preview Release * @return string */ public function get_encoding() { return $this->sanitize->output_encoding; } /** * Send the content-type header with correct encoding * * This method ensures that the SimplePie-enabled page is being served with * the correct {@link http://www.iana.org/assignments/media-types/ mime-type} * and character encoding HTTP headers (character encoding determined by the * {@see set_output_encoding} config option). * * This won't work properly if any content or whitespace has already been * sent to the browser, because it relies on PHP's * {@link http://php.net/header header()} function, and these are the * circumstances under which the function works. * * Because it's setting these settings for the entire page (as is the nature * of HTTP headers), this should only be used once per page (again, at the * top). * * @param string $mime MIME type to serve the page as */ public function handle_content_type($mime = 'text/html') { if (!headers_sent()) { $header = "Content-type: $mime;"; if ($this->get_encoding()) { $header .= ' charset=' . $this->get_encoding(); } else { $header .= ' charset=UTF-8'; } header($header); } } /** * Get the type of the feed * * This returns a \SimplePie\SimplePie::TYPE_* constant, which can be tested against * using {@link http://php.net/language.operators.bitwise bitwise operators} * * @since 0.8 (usage changed to using constants in 1.0) * @see \SimplePie\SimplePie::TYPE_NONE Unknown. * @see \SimplePie\SimplePie::TYPE_RSS_090 RSS 0.90. * @see \SimplePie\SimplePie::TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape). * @see \SimplePie\SimplePie::TYPE_RSS_091_USERLAND RSS 0.91 (Userland). * @see \SimplePie\SimplePie::TYPE_RSS_091 RSS 0.91. * @see \SimplePie\SimplePie::TYPE_RSS_092 RSS 0.92. * @see \SimplePie\SimplePie::TYPE_RSS_093 RSS 0.93. * @see \SimplePie\SimplePie::TYPE_RSS_094 RSS 0.94. * @see \SimplePie\SimplePie::TYPE_RSS_10 RSS 1.0. * @see \SimplePie\SimplePie::TYPE_RSS_20 RSS 2.0.x. * @see \SimplePie\SimplePie::TYPE_RSS_RDF RDF-based RSS. * @see \SimplePie\SimplePie::TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format). * @see \SimplePie\SimplePie::TYPE_RSS_ALL Any version of RSS. * @see \SimplePie\SimplePie::TYPE_ATOM_03 Atom 0.3. * @see \SimplePie\SimplePie::TYPE_ATOM_10 Atom 1.0. * @see \SimplePie\SimplePie::TYPE_ATOM_ALL Any version of Atom. * @see \SimplePie\SimplePie::TYPE_ALL Any known/supported feed type. * @return int \SimplePie\SimplePie::TYPE_* constant */ public function get_type() { if (!isset($this->data['type'])) { $this->data['type'] = self::TYPE_ALL; if (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'])) { $this->data['type'] &= self::TYPE_ATOM_10; } elseif (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'])) { $this->data['type'] &= self::TYPE_ATOM_03; } elseif (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'])) { if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['channel']) || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['image']) || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['item']) || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['textinput'])) { $this->data['type'] &= self::TYPE_RSS_10; } if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['channel']) || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['image']) || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['item']) || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['textinput'])) { $this->data['type'] &= self::TYPE_RSS_090; } } elseif (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'])) { $this->data['type'] &= self::TYPE_RSS_ALL; if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) { switch (trim($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) { case '0.91': $this->data['type'] &= self::TYPE_RSS_091; if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][self::NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) { switch (trim($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][self::NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) { case '0': $this->data['type'] &= self::TYPE_RSS_091_NETSCAPE; break; case '24': $this->data['type'] &= self::TYPE_RSS_091_USERLAND; break; } } break; case '0.92': $this->data['type'] &= self::TYPE_RSS_092; break; case '0.93': $this->data['type'] &= self::TYPE_RSS_093; break; case '0.94': $this->data['type'] &= self::TYPE_RSS_094; break; case '2.0': $this->data['type'] &= self::TYPE_RSS_20; break; } } } else { $this->data['type'] = self::TYPE_NONE; } } return $this->data['type']; } /** * Get the URL for the feed * * When the 'permanent' mode is enabled, returns the original feed URL, * except in the case of an `HTTP 301 Moved Permanently` status response, * in which case the location of the first redirection is returned. * * When the 'permanent' mode is disabled (default), * may or may not be different from the URL passed to {@see set_feed_url()}, * depending on whether auto-discovery was used, and whether there were * any redirects along the way. * * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) * @todo Support <itunes:new-feed-url> * @todo Also, |atom:link|@rel=self * @param bool $permanent Permanent mode to return only the original URL or the first redirection * iff it is a 301 redirection * @return string|null */ public function subscribe_url($permanent = false) { if ($permanent) { if ($this->permanent_url !== null) { // sanitize encodes ampersands which are required when used in a url. return str_replace( '&', '&', $this->sanitize( $this->permanent_url, self::CONSTRUCT_IRI ) ); } } else { if ($this->feed_url !== null) { return str_replace( '&', '&', $this->sanitize( $this->feed_url, self::CONSTRUCT_IRI ) ); } } return null; } /** * Get data for an feed-level element * * This method allows you to get access to ANY element/attribute that is a * sub-element of the opening feed tag. * * The return value is an indexed array of elements matching the given * namespace and tag name. Each element has `attribs`, `data` and `child` * subkeys. For `attribs` and `child`, these contain namespace subkeys. * `attribs` then has one level of associative name => value data (where * `value` is a string) after the namespace. `child` has tag-indexed keys * after the namespace, each member of which is an indexed array matching * this same format. * * For example: * <pre> * // This is probably a bad example because we already support * // <media:content> natively, but it shows you how to parse through * // the nodes. * $group = $item->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'group'); * $content = $group[0]['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content']; * $file = $content[0]['attribs']['']['url']; * echo $file; * </pre> * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_feed_tags($namespace, $tag) { $type = $this->get_type(); if ($type & self::TYPE_ATOM_10) { if (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) { return $this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; } } if ($type & self::TYPE_ATOM_03) { if (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) { return $this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; } } if ($type & self::TYPE_RSS_RDF) { if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) { return $this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; } } if ($type & self::TYPE_RSS_SYNDICATION) { if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) { return $this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]; } } return null; } /** * Get data for an channel-level element * * This method allows you to get access to ANY element/attribute in the * channel/header section of the feed. * * See {@see SimplePie::get_feed_tags()} for a description of the return value * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_channel_tags($namespace, $tag) { $type = $this->get_type(); if ($type & self::TYPE_ATOM_ALL) { if ($return = $this->get_feed_tags($namespace, $tag)) { return $return; } } if ($type & self::TYPE_RSS_10) { if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'channel')) { if (isset($channel[0]['child'][$namespace][$tag])) { return $channel[0]['child'][$namespace][$tag]; } } } if ($type & self::TYPE_RSS_090) { if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'channel')) { if (isset($channel[0]['child'][$namespace][$tag])) { return $channel[0]['child'][$namespace][$tag]; } } } if ($type & self::TYPE_RSS_SYNDICATION) { if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_20, 'channel')) { if (isset($channel[0]['child'][$namespace][$tag])) { return $channel[0]['child'][$namespace][$tag]; } } } return null; } /** * Get data for an channel-level element * * This method allows you to get access to ANY element/attribute in the * image/logo section of the feed. * * See {@see SimplePie::get_feed_tags()} for a description of the return value * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_image_tags($namespace, $tag) { $type = $this->get_type(); if ($type & self::TYPE_RSS_10) { if ($image = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'image')) { if (isset($image[0]['child'][$namespace][$tag])) { return $image[0]['child'][$namespace][$tag]; } } } if ($type & self::TYPE_RSS_090) { if ($image = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'image')) { if (isset($image[0]['child'][$namespace][$tag])) { return $image[0]['child'][$namespace][$tag]; } } } if ($type & self::TYPE_RSS_SYNDICATION) { if ($image = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'image')) { if (isset($image[0]['child'][$namespace][$tag])) { return $image[0]['child'][$namespace][$tag]; } } } return null; } /** * Get the base URL value from the feed * * Uses `<xml:base>` if available, otherwise uses the first link in the * feed, or failing that, the URL of the feed itself. * * @see get_link * @see subscribe_url * * @param array $element * @return string */ public function get_base($element = []) { if (!empty($element['xml_base_explicit']) && isset($element['xml_base'])) { return $element['xml_base']; } elseif ($this->get_link() !== null) { return $this->get_link(); } return $this->subscribe_url(); } /** * Sanitize feed data * * @access private * @see \SimplePie\Sanitize::sanitize() * @param string $data Data to sanitize * @param int $type One of the \SimplePie\SimplePie::CONSTRUCT_* constants * @param string $base Base URL to resolve URLs against * @return string Sanitized data */ public function sanitize($data, $type, $base = '') { try { return $this->sanitize->sanitize($data, $type, $base); } catch (\SimplePie\Exception $e) { if (!$this->enable_exceptions) { $this->error = $e->getMessage(); $this->registry->call(Misc::class, 'error', [$this->error, E_USER_WARNING, $e->getFile(), $e->getLine()]); return ''; } throw $e; } } /** * Get the title of the feed * * Uses `<atom:title>`, `<title>` or `<dc:title>` * * @since 1.0 (previously called `get_feed_title` since 0.8) * @return string|null */ public function get_title() { if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'title')) { return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'title')) { return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } return null; } /** * Get a category for the feed * * @since Unknown * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 * @return \SimplePie\Category|null */ public function get_category($key = 0) { $categories = $this->get_categories(); if (isset($categories[$key])) { return $categories[$key]; } return null; } /** * Get all categories for the feed * * Uses `<atom:category>`, `<category>` or `<dc:subject>` * * @since Unknown * @return array|null List of {@see \SimplePie\Category} objects */ public function get_categories() { $categories = []; foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'category') as $category) { $term = null; $scheme = null; $label = null; if (isset($category['attribs']['']['term'])) { $term = $this->sanitize($category['attribs']['']['term'], self::CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], self::CONSTRUCT_TEXT); } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], self::CONSTRUCT_TEXT); } $categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]); } foreach ((array) $this->get_channel_tags(self::NAMESPACE_RSS_20, 'category') as $category) { // This is really the label, but keep this as the term also for BC. // Label will also work on retrieving because that falls back to term. $term = $this->sanitize($category['data'], self::CONSTRUCT_TEXT); if (isset($category['attribs']['']['domain'])) { $scheme = $this->sanitize($category['attribs']['']['domain'], self::CONSTRUCT_TEXT); } else { $scheme = null; } $categories[] = $this->registry->create(Category::class, [$term, $scheme, null]); } foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_11, 'subject') as $category) { $categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], self::CONSTRUCT_TEXT), null, null]); } foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_10, 'subject') as $category) { $categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], self::CONSTRUCT_TEXT), null, null]); } if (!empty($categories)) { return array_unique($categories); } return null; } /** * Get an author for the feed * * @since 1.1 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 * @return \SimplePie\Author|null */ public function get_author($key = 0) { $authors = $this->get_authors(); if (isset($authors[$key])) { return $authors[$key]; } return null; } /** * Get all authors for the feed * * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` * * @since 1.1 * @return array|null List of {@see \SimplePie\Author} objects */ public function get_authors() { $authors = []; foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'author') as $author) { $name = null; $uri = null; $email = null; if (isset($author['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'], self::CONSTRUCT_TEXT); } if (isset($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($author['child'][self::NAMESPACE_ATOM_10]['uri'][0])); } if (isset($author['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'], self::CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $authors[] = $this->registry->create(Author::class, [$name, $uri, $email]); } } if ($author = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'author')) { $name = null; $url = null; $email = null; if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'], self::CONSTRUCT_TEXT); } if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0])); } if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'], self::CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $authors[] = $this->registry->create(Author::class, [$name, $url, $email]); } } foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_11, 'creator') as $author) { $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]); } foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_10, 'creator') as $author) { $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]); } foreach ((array) $this->get_channel_tags(self::NAMESPACE_ITUNES, 'author') as $author) { $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]); } if (!empty($authors)) { return array_unique($authors); } return null; } /** * Get a contributor for the feed * * @since 1.1 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 * @return \SimplePie\Author|null */ public function get_contributor($key = 0) { $contributors = $this->get_contributors(); if (isset($contributors[$key])) { return $contributors[$key]; } return null; } /** * Get all contributors for the feed * * Uses `<atom:contributor>` * * @since 1.1 * @return array|null List of {@see \SimplePie\Author} objects */ public function get_contributors() { $contributors = []; foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'contributor') as $contributor) { $name = null; $uri = null; $email = null; if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'], self::CONSTRUCT_TEXT); } if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0])); } if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'], self::CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $contributors[] = $this->registry->create(Author::class, [$name, $uri, $email]); } } foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'contributor') as $contributor) { $name = null; $url = null; $email = null; if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'], self::CONSTRUCT_TEXT); } if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0])); } if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'], self::CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $contributors[] = $this->registry->create(Author::class, [$name, $url, $email]); } } if (!empty($contributors)) { return array_unique($contributors); } return null; } /** * Get a single link for the feed * * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 * @param string $rel The relationship of the link to return * @return string|null Link URL */ public function get_link($key = 0, $rel = 'alternate') { $links = $this->get_links($rel); if (isset($links[$key])) { return $links[$key]; } return null; } /** * Get the permalink for the item * * Returns the first link available with a relationship of "alternate". * Identical to {@see get_link()} with key 0 * * @see get_link * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) * @internal Added for parity between the parent-level and the item/entry-level. * @return string|null Link URL */ public function get_permalink() { return $this->get_link(0); } /** * Get all links for the feed * * Uses `<atom:link>` or `<link>` * * @since Beta 2 * @param string $rel The relationship of links to return * @return array|null Links found for the feed (strings) */ public function get_links($rel = 'alternate') { if (!isset($this->data['links'])) { $this->data['links'] = []; if ($links = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'link')) { foreach ($links as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], self::CONSTRUCT_IRI, $this->get_base($link)); } } } if ($links = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'link')) { foreach ($links as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], self::CONSTRUCT_IRI, $this->get_base($link)); } } } if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0])); } $keys = array_keys($this->data['links']); foreach ($keys as $key) { if ($this->registry->call(Misc::class, 'is_isegment_nz_nc', [$key])) { if (isset($this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key])) { $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key]); $this->data['links'][$key] = &$this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key]; } else { $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key] = &$this->data['links'][$key]; } } elseif (substr($key, 0, 41) === self::IANA_LINK_RELATIONS_REGISTRY) { $this->data['links'][substr($key, 41)] = &$this->data['links'][$key]; } $this->data['links'][$key] = array_unique($this->data['links'][$key]); } } if (isset($this->data['headers']['link'])) { $link_headers = $this->data['headers']['link']; if (is_array($link_headers)) { $link_headers = implode(',', $link_headers); } // https://datatracker.ietf.org/doc/html/rfc8288 if (is_string($link_headers) && preg_match_all('/<(?P<uri>[^>]+)>\s*;\s*rel\s*=\s*(?P<quote>"?)' . preg_quote($rel) . '(?P=quote)\s*(?=,|$)/i', $link_headers, $matches)) { return $matches['uri']; } } if (isset($this->data['links'][$rel])) { return $this->data['links'][$rel]; } return null; } public function get_all_discovered_feeds() { return $this->all_discovered_feeds; } /** * Get the content for the item * * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`, * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>` * * @since 1.0 (previously called `get_feed_description()` since 0.8) * @return string|null */ public function get_description() { if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'subtitle')) { return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'tagline')) { return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'description')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'description')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'description')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'description')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'description')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'summary')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'subtitle')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0])); } return null; } /** * Get the copyright info for the feed * * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>` * * @since 1.0 (previously called `get_feed_copyright()` since 0.8) * @return string|null */ public function get_copyright() { if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'rights')) { return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'copyright')) { return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'copyright')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'rights')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'rights')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } return null; } /** * Get the language for the feed * * Uses `<language>`, `<dc:language>`, or @xml_lang * * @since 1.0 (previously called `get_feed_language()` since 0.8) * @return string|null */ public function get_language() { if ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'language')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'language')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'language')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) { return $this->sanitize($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], self::CONSTRUCT_TEXT); } elseif (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) { return $this->sanitize($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], self::CONSTRUCT_TEXT); } elseif (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['xml_lang'])) { return $this->sanitize($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['xml_lang'], self::CONSTRUCT_TEXT); } elseif (isset($this->data['headers']['content-language'])) { return $this->sanitize($this->data['headers']['content-language'], self::CONSTRUCT_TEXT); } return null; } /** * Get the latitude coordinates for the item * * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications * * Uses `<geo:lat>` or `<georss:point>` * * @since 1.0 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo * @link http://www.georss.org/ GeoRSS * @return string|null */ public function get_latitude() { if ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'lat')) { return (float) $return[0]['data']; } elseif (($return = $this->get_channel_tags(self::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[1]; } return null; } /** * Get the longitude coordinates for the feed * * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications * * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` * * @since 1.0 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo * @link http://www.georss.org/ GeoRSS * @return string|null */ public function get_longitude() { if ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'long')) { return (float) $return[0]['data']; } elseif ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'lon')) { return (float) $return[0]['data']; } elseif (($return = $this->get_channel_tags(self::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[2]; } return null; } /** * Get the feed logo's title * * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title. * * Uses `<image><title>` or `<image><dc:title>` * * @return string|null */ public function get_image_title() { if ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(self::NAMESPACE_DC_11, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(self::NAMESPACE_DC_10, 'title')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); } return null; } /** * Get the feed logo's URL * * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to * have a "feed logo" URL. This points directly to the image itself. * * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, * `<image><title>` or `<image><dc:title>` * * @return string|null */ public function get_image_url() { if ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'image')) { return $this->sanitize($return[0]['attribs']['']['href'], self::CONSTRUCT_IRI); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'logo')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'icon')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'url')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'url')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } return null; } /** * Get the feed logo's link * * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This * points to a human-readable page that the image should link to. * * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, * `<image><title>` or `<image><dc:title>` * * @return string|null */ public function get_image_link() { if ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'link')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'link')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'link')) { return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); } return null; } /** * Get the feed logo's link * * RSS 2.0 feeds are allowed to have a "feed logo" width. * * Uses `<image><width>` or defaults to 88 if no width is specified and * the feed is an RSS 2.0 feed. * * @return int|null */ public function get_image_width() { if ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'width')) { return intval($return[0]['data']); } elseif ($this->get_type() & self::TYPE_RSS_SYNDICATION && $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) { return 88; } return null; } /** * Get the feed logo's height * * RSS 2.0 feeds are allowed to have a "feed logo" height. * * Uses `<image><height>` or defaults to 31 if no height is specified and * the feed is an RSS 2.0 feed. * * @return int|null */ public function get_image_height() { if ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'height')) { return intval($return[0]['data']); } elseif ($this->get_type() & self::TYPE_RSS_SYNDICATION && $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) { return 31; } return null; } /** * Get the number of items in the feed * * This is well-suited for {@link http://php.net/for for()} loops with * {@see get_item()} * * @param int $max Maximum value to return. 0 for no limit * @return int Number of items in the feed */ public function get_item_quantity($max = 0) { $max = (int) $max; $qty = count($this->get_items()); if ($max === 0) { return $qty; } return ($qty > $max) ? $max : $qty; } /** * Get a single item from the feed * * This is better suited for {@link http://php.net/for for()} loops, whereas * {@see get_items()} is better suited for * {@link http://php.net/foreach foreach()} loops. * * @see get_item_quantity() * @since Beta 2 * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 * @return \SimplePie\Item|null */ public function get_item($key = 0) { $items = $this->get_items(); if (isset($items[$key])) { return $items[$key]; } return null; } /** * Get all items from the feed * * This is better suited for {@link http://php.net/for for()} loops, whereas * {@see get_items()} is better suited for * {@link http://php.net/foreach foreach()} loops. * * @see get_item_quantity * @since Beta 2 * @param int $start Index to start at * @param int $end Number of items to return. 0 for all items after `$start` * @return \SimplePie\Item[]|null List of {@see \SimplePie\Item} objects */ public function get_items($start = 0, $end = 0) { if (!isset($this->data['items'])) { 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 []; } return $this->data['items']; } $this->data['items'] = []; if ($items = $this->get_feed_tags(self::NAMESPACE_ATOM_10, 'entry')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); } } if ($items = $this->get_feed_tags(self::NAMESPACE_ATOM_03, 'entry')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); } } if ($items = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'item')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); } } if ($items = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'item')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); } } if ($items = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'item')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); } } } if (empty($this->data['items'])) { return []; } if ($this->order_by_date) { if (!isset($this->data['ordered_items'])) { $this->data['ordered_items'] = $this->data['items']; usort($this->data['ordered_items'], [get_class($this), 'sort_items']); } $items = $this->data['ordered_items']; } else { $items = $this->data['items']; } // Slice the data as desired if ($end === 0) { return array_slice($items, $start); } return array_slice($items, $start, $end); } /** * Set the favicon handler * * @deprecated Use your own favicon handling instead */ public function set_favicon_handler($page = false, $qs = 'i') { trigger_error('Favicon handling has been removed, please use your own handling', \E_USER_DEPRECATED); return false; } /** * Get the favicon for the current feed * * @deprecated Use your own favicon handling instead */ public function get_favicon() { trigger_error('Favicon handling has been removed, please use your own handling', \E_USER_DEPRECATED); if (($url = $this->get_link()) !== null) { return 'https://www.google.com/s2/favicons?domain=' . urlencode($url); } return false; } /** * Magic method handler * * @param string $method Method name * @param array $args Arguments to the method * @return mixed */ public function __call($method, $args) { if (strpos($method, 'subscribe_') === 0) { trigger_error('subscribe_*() has been deprecated, implement the callback yourself', \E_USER_DEPRECATED); return ''; } if ($method === 'enable_xml_dump') { trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', \E_USER_DEPRECATED); return false; } $class = get_class($this); $trace = debug_backtrace(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection $file = $trace[0]['file']; $line = $trace[0]['line']; throw new SimplePieException("Call to undefined method $class::$method() in $file on line $line"); } /** * Sorting callback for items * * @access private * @param SimplePie $a * @param SimplePie $b * @return boolean */ public static function sort_items($a, $b) { $a_date = $a->get_date('U'); $b_date = $b->get_date('U'); if ($a_date && $b_date) { return $a_date > $b_date ? -1 : 1; } // Sort items without dates to the top. if ($a_date) { return 1; } if ($b_date) { return -1; } return 0; } /** * Merge items from several feeds into one * * If you're merging multiple feeds together, they need to all have dates * for the items or else SimplePie will refuse to sort them. * * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings * @param array $urls List of SimplePie feed objects to merge * @param int $start Starting item * @param int $end Number of items to return * @param int $limit Maximum number of items per feed * @return array */ public static function merge_items($urls, $start = 0, $end = 0, $limit = 0) { if (is_array($urls) && sizeof($urls) > 0) { $items = []; foreach ($urls as $arg) { if ($arg instanceof SimplePie) { $items = array_merge($items, $arg->get_items(0, $limit)); } else { trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); } } usort($items, [get_class($urls[0]), 'sort_items']); if ($end === 0) { return array_slice($items, $start); } return array_slice($items, $start, $end); } trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); return []; } /** * 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'; } } } /** * Get a DataCache * * @param string $feed_url Only needed for BC, can be removed in SimplePie 2.0.0 * * @return DataCache */ private function get_cache($feed_url = '') { if ($this->cache === null) { // @trigger_error(sprintf('Not providing as PSR-16 cache implementation is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()".'), \E_USER_DEPRECATED); $cache = $this->registry->call(Cache::class, 'get_handler', [ $this->cache_location, $this->get_cache_filename($feed_url), Base::TYPE_FEED ]); return new BaseDataCache($cache); } return $this->cache; } } class_alias('SimplePie\SimplePie', 'SimplePie'); PK �Z�ۊYC YC Decode/HTML/Entities.phpnu �[��� <?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue * @author Ryan Parman * @author Sam Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Decode HTML Entities * * This implements HTML5 as of revision 967 (2007-06-28) * * @deprecated Use DOMDocument instead! * @package SimplePie */ class SimplePie_Decode_HTML_Entities { /** * Data to be parsed * * @access private * @var string */ var $data = ''; /** * Currently consumed bytes * * @access private * @var string */ var $consumed = ''; /** * Position of the current byte being parsed * * @access private * @var int */ var $position = 0; /** * Create an instance of the class with the input data * * @access public * @param string $data Input data */ public function __construct($data) { $this->data = $data; } /** * Parse the input data * * @access public * @return string Output data */ public function parse() { while (($this->position = strpos($this->data, '&', $this->position)) !== false) { $this->consume(); $this->entity(); $this->consumed = ''; } return $this->data; } /** * Consume the next byte * * @access private * @return mixed The next byte, or false, if there is no more data */ public function consume() { if (isset($this->data[$this->position])) { $this->consumed .= $this->data[$this->position]; return $this->data[$this->position++]; } return false; } /** * Consume a range of characters * * @access private * @param string $chars Characters to consume * @return mixed A series of characters that match the range, or false */ public function consume_range($chars) { if ($len = strspn($this->data, $chars, $this->position)) { $data = substr($this->data, $this->position, $len); $this->consumed .= $data; $this->position += $len; return $data; } return false; } /** * Unconsume one byte * * @access private */ public function unconsume() { $this->consumed = substr($this->consumed, 0, -1); $this->position--; } /** * Decode an entity * * @access private */ public function entity() { switch ($this->consume()) { case "\x09": case "\x0A": case "\x0B": case "\x0C": case "\x20": case "\x3C": case "\x26": case false: break; case "\x23": switch ($this->consume()) { case "\x78": case "\x58": $range = '0123456789ABCDEFabcdef'; $hex = true; break; default: $range = '0123456789'; $hex = false; $this->unconsume(); break; } if ($codepoint = $this->consume_range($range)) { static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); if ($hex) { $codepoint = hexdec($codepoint); } else { $codepoint = intval($codepoint); } if (isset($windows_1252_specials[$codepoint])) { $replacement = $windows_1252_specials[$codepoint]; } else { $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); } if (!in_array($this->consume(), array(';', false), true)) { $this->unconsume(); } $consumed_length = strlen($this->consumed); $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); $this->position += strlen($replacement) - $consumed_length; } break; default: static $entities = array( 'Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C" ); for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) { $consumed = substr($this->consumed, 1); if (isset($entities[$consumed])) { $match = $consumed; } } if ($match !== null) { $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); $this->position += strlen($entities[$match]) - strlen($consumed) - 1; } break; } } } PK �Z��ٷ;� ;� File.phpnu �[��� <?php if (class_exists('ParagonIE_Sodium_File', false)) { return; } /** * Class ParagonIE_Sodium_File */ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util { /* PHP's default buffer size is 8192 for fread()/fwrite(). */ const BUFFER_SIZE = 8192; /** * Box a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $nonce Number to be used only once * @param string $keyPair ECDH secret key and ECDH public key concatenated * * @return bool * @throws SodiumException * @throws TypeError */ public static function box( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $keyPair ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } /* Input validation: */ if (!is_string($keyPair)) { throw new TypeError('Argument 4 must be a string, ' . gettype($keyPair) . ' given.'); } if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_NONCEBYTES bytes'); } if (self::strlen($keyPair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $keyPair); fclose($ifp); fclose($ofp); return $res; } /** * Open a boxed file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_box_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $nonce * @param string $keypair * @return bool * @throws SodiumException * @throws TypeError */ public static function box_open( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $keypair ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } if (!is_string($keypair)) { throw new TypeError('Argument 4 must be a string, ' . gettype($keypair) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_NONCEBYTES bytes'); } if (self::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { if (isset($ephKeypair)) { unset($ephKeypair); } } return $res; } /** * Seal a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_seal(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $publicKey ECDH public key * * @return bool * @throws SodiumException * @throws TypeError */ public static function box_seal( $inputFile, $outputFile, #[\SensitiveParameter] $publicKey ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($publicKey)) { throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.'); } /* Input validation: */ if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } /** @var string $ephKeypair */ $ephKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair(); /** @var string $msgKeypair */ $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( ParagonIE_Sodium_Compat::crypto_box_secretkey($ephKeypair), $publicKey ); /** @var string $ephemeralPK */ $ephemeralPK = ParagonIE_Sodium_Compat::crypto_box_publickey($ephKeypair); /** @var string $nonce */ $nonce = ParagonIE_Sodium_Compat::crypto_generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var int $firstWrite */ $firstWrite = fwrite( $ofp, $ephemeralPK, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES ); if (!is_int($firstWrite)) { fclose($ifp); fclose($ofp); ParagonIE_Sodium_Compat::memzero($ephKeypair); throw new SodiumException('Could not write to output file'); } if ($firstWrite !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { ParagonIE_Sodium_Compat::memzero($ephKeypair); fclose($ifp); fclose($ofp); throw new SodiumException('Error writing public key to output file'); } $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $msgKeypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { /** @psalm-suppress PossiblyUndefinedVariable */ unset($ephKeypair); } return $res; } /** * Open a sealed file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_seal_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_box_seal_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $ecdhKeypair * @return bool * @throws SodiumException * @throws TypeError */ public static function box_seal_open( $inputFile, $outputFile, #[\SensitiveParameter] $ecdhKeypair ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($ecdhKeypair)) { throw new TypeError('Argument 3 must be a string, ' . gettype($ecdhKeypair) . ' given.'); } /* Input validation: */ if (self::strlen($ecdhKeypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } $publicKey = ParagonIE_Sodium_Compat::crypto_box_publickey($ecdhKeypair); /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $ephemeralPK = fread($ifp, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES); if (!is_string($ephemeralPK)) { throw new SodiumException('Could not read input file'); } if (self::strlen($ephemeralPK) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { fclose($ifp); fclose($ofp); throw new SodiumException('Could not read public key from sealed file'); } $nonce = ParagonIE_Sodium_Compat::crypto_generichash( $ephemeralPK . $publicKey, '', 24 ); $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( ParagonIE_Sodium_Compat::crypto_box_secretkey($ecdhKeypair), $ephemeralPK ); $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { if (isset($ephKeypair)) { unset($ephKeypair); } } return $res; } /** * Calculate the BLAKE2b hash of a file. * * @param string $filePath Absolute path to a file on the filesystem * @param string|null $key BLAKE2b key * @param int $outputLength Length of hash output * * @return string BLAKE2b hash * @throws SodiumException * @throws TypeError * @psalm-suppress FailedTypeResolution */ public static function generichash( $filePath, #[\SensitiveParameter] $key = '', $outputLength = 32 ) { /* Type checks: */ if (!is_string($filePath)) { throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($key)) { if (is_null($key)) { $key = ''; } else { throw new TypeError('Argument 2 must be a string, ' . gettype($key) . ' given.'); } } if (!is_int($outputLength)) { if (!is_numeric($outputLength)) { throw new TypeError('Argument 3 must be an integer, ' . gettype($outputLength) . ' given.'); } $outputLength = (int) $outputLength; } /* Input validation: */ if (!empty($key)) { if (self::strlen($key) < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new TypeError('Argument 2 must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes'); } if (self::strlen($key) > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new TypeError('Argument 2 must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes'); } } if ($outputLength < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MIN) { throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MIN'); } if ($outputLength > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MAX) { throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MAX'); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } $ctx = ParagonIE_Sodium_Compat::crypto_generichash_init($key, $outputLength); while ($size > 0) { $blockSize = $size > 64 ? 64 : $size; $read = fread($fp, $blockSize); if (!is_string($read)) { throw new SodiumException('Could not read input file'); } ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, $read); $size -= $blockSize; } fclose($fp); return ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, $outputLength); } /** * Encrypt a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_secretbox(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $nonce Number to be used only once * @param string $key Encryption key * * @return bool * @throws SodiumException * @throws TypeError */ public static function secretbox( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given..'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) { throw new TypeError('Argument 3 must be CRYPTO_SECRETBOX_NONCEBYTES bytes'); } if (!is_string($key)) { throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.'); } if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_KEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::secretbox_encrypt($ifp, $ofp, $size, $nonce, $key); fclose($ifp); fclose($ofp); return $res; } /** * Seal a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_secretbox_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_secretbox_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function secretbox_open( $inputFile, $outputFile, $nonce, #[\SensitiveParameter] $key ) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } if (!is_string($key)) { throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_NONCEBYTES bytes'); } if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOXBOX_KEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($key); } catch (SodiumException $ex) { /** @psalm-suppress PossiblyUndefinedVariable */ unset($key); } return $res; } /** * Sign a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces * the same result. * * @param string $filePath Absolute path to a file on the filesystem * @param string $secretKey Secret signing key * * @return string Ed25519 signature * @throws SodiumException * @throws TypeError */ public static function sign( $filePath, #[\SensitiveParameter] $secretKey ) { /* Type checks: */ if (!is_string($filePath)) { throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($secretKey)) { throw new TypeError('Argument 2 must be a string, ' . gettype($secretKey) . ' given.'); } /* Input validation: */ if (self::strlen($secretKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_SECRETKEYBYTES) { throw new TypeError('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES bytes'); } if (PHP_INT_SIZE === 4) { return self::sign_core32($filePath, $secretKey); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var string $az */ $az = hash('sha512', self::substr($secretKey, 0, 32), true); $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $nonceHash */ $nonceHash = hash_final($hs, true); /** @var string $pk */ $pk = self::substr($secretKey, 32, 32); /** @var string $nonce */ $nonce = ParagonIE_Sodium_Core_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32); /** @var string $sig */ $sig = ParagonIE_Sodium_Core_Ed25519::ge_p3_tobytes( ParagonIE_Sodium_Core_Ed25519::ge_scalarmult_base($nonce) ); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hramHash */ $hramHash = hash_final($hs, true); /** @var string $hram */ $hram = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hramHash); /** @var string $sigAfter */ $sigAfter = ParagonIE_Sodium_Core_Ed25519::sc_muladd($hram, $az, $nonce); /** @var string $sig */ $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } fclose($fp); return $sig; } /** * Verify a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but * produces the same result. * * @param string $sig Ed25519 signature * @param string $filePath Absolute path to a file on the filesystem * @param string $publicKey Signing public key * * @return bool * @throws SodiumException * @throws TypeError * @throws Exception */ public static function verify( $sig, $filePath, $publicKey ) { /* Type checks: */ if (!is_string($sig)) { throw new TypeError('Argument 1 must be a string, ' . gettype($sig) . ' given.'); } if (!is_string($filePath)) { throw new TypeError('Argument 2 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($publicKey)) { throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.'); } /* Input validation: */ if (self::strlen($sig) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_BYTES) { throw new TypeError('Argument 1 must be CRYPTO_SIGN_BYTES bytes'); } if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new TypeError('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES bytes'); } if (self::strlen($sig) < 64) { throw new SodiumException('Signature is too short'); } if (PHP_INT_SIZE === 4) { return self::verify_core32($sig, $filePath, $publicKey); } /* Security checks */ if ( (ParagonIE_Sodium_Core_Ed25519::chrToInt($sig[63]) & 240) && ParagonIE_Sodium_Core_Ed25519::check_S_lt_L(self::substr($sig, 32, 32)) ) { throw new SodiumException('S < L - Invalid signature'); } if (ParagonIE_Sodium_Core_Ed25519::small_order($sig)) { throw new SodiumException('Signature is on too small of an order'); } if ((self::chrToInt($sig[63]) & 224) !== 0) { throw new SodiumException('Invalid signature'); } $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($publicKey[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ $A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($publicKey, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hDigest */ $hDigest = hash_final($hs, true); /** @var string $h */ $h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ $R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R); // Close the file handle fclose($fp); // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; return self::verify_32($rcheck, self::substr($sig, 0, 32)); } /** * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $boxKeypair * @return bool * @throws SodiumException * @throws TypeError */ protected static function box_encrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair) { if (PHP_INT_SIZE === 4) { return self::secretbox_encrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto32::box_beforenm( ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair) ) ); } return self::secretbox_encrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto::box_beforenm( ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto::box_publickey($boxKeypair) ) ); } /** * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $boxKeypair * @return bool * @throws SodiumException * @throws TypeError */ protected static function box_decrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair) { if (PHP_INT_SIZE === 4) { return self::secretbox_decrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto32::box_beforenm( ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair) ) ); } return self::secretbox_decrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto::box_beforenm( ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto::box_publickey($boxKeypair) ) ); } /** * Encrypt a file * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_encrypt($ifp, $ofp, $mlen, $nonce, $key) { if (PHP_INT_SIZE === 4) { return self::secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key); } $plaintext = fread($ifp, 32); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $first32 = self::ftell($ifp); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen0 = $mlen; if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( $block0, $realNonce, $subkey ); $state = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_Util::substr( $block0, 0, ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES ) ); // Pre-write 16 blank bytes for the Poly1305 tag $start = self::ftell($ofp); fwrite($ofp, str_repeat("\x00", 16)); /** @var string $c */ $cBlock = ParagonIE_Sodium_Core_Util::substr( $block0, ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES ); $state->update($cBlock); fwrite($ofp, $cBlock); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ fseek($ifp, $first32, SEEK_SET); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $plaintext = fread($ifp, $blockSize); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $cBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( $plaintext, $realNonce, $iter, $subkey ); fwrite($ofp, $cBlock, $blockSize); $state->update($cBlock); $mlen -= $blockSize; $iter += $incr; } try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $end = self::ftell($ofp); /* * Write the Poly1305 authentication tag that provides integrity * over the ciphertext (encrypt-then-MAC) */ fseek($ofp, $start, SEEK_SET); fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES); fseek($ofp, $end, SEEK_SET); unset($state); return true; } /** * Decrypt a file * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_decrypt($ifp, $ofp, $mlen, $nonce, $key) { if (PHP_INT_SIZE === 4) { return self::secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key); } $tag = fread($ifp, 16); if (!is_string($tag)) { throw new SodiumException('Could not read input file'); } /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( 64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); /* Verify the Poly1305 MAC -before- attempting to decrypt! */ $state = new ParagonIE_Sodium_Core_Poly1305_State(self::substr($block0, 0, 32)); if (!self::onetimeauth_verify($state, $ifp, $tag, $mlen)) { throw new SodiumException('Invalid MAC'); } /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ $first32 = fread($ifp, 32); if (!is_string($first32)) { throw new SodiumException('Could not read input file'); } $first32len = self::strlen($first32); fwrite( $ofp, self::xorStrings( self::substr($block0, 32, $first32len), self::substr($first32, 0, $first32len) ) ); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* Decrypts ciphertext, writes to output file. */ while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $pBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( $ciphertext, $realNonce, $iter, $subkey ); fwrite($ofp, $pBlock, $blockSize); $mlen -= $blockSize; $iter += $incr; } return true; } /** * @param ParagonIE_Sodium_Core_Poly1305_State $state * @param resource $ifp * @param string $tag * @param int $mlen * @return bool * @throws SodiumException * @throws TypeError */ protected static function onetimeauth_verify( ParagonIE_Sodium_Core_Poly1305_State $state, $ifp, $tag = '', $mlen = 0 ) { /** @var int $pos */ $pos = self::ftell($ifp); /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $state->update($ciphertext); $mlen -= $blockSize; $iter += $incr; } $res = ParagonIE_Sodium_Core_Util::verify_16($tag, $state->finish()); fseek($ifp, $pos, SEEK_SET); return $res; } /** * Update a hash context with the contents of a file, without * loading the entire file into memory. * * @param resource|HashContext $hash * @param resource $fp * @param int $size * @return resource|object Resource on PHP < 7.2, HashContext object on PHP >= 7.2 * @throws SodiumException * @throws TypeError * @psalm-suppress PossiblyInvalidArgument * PHP 7.2 changes from a resource to an object, * which causes Psalm to complain about an error. * @psalm-suppress TypeCoercion * Ditto. */ public static function updateHashWithFile($hash, $fp, $size = 0) { /* Type checks: */ if (PHP_VERSION_ID < 70200) { if (!is_resource($hash)) { throw new TypeError('Argument 1 must be a resource, ' . gettype($hash) . ' given.'); } } else { if (!is_object($hash)) { throw new TypeError('Argument 1 must be an object (PHP 7.2+), ' . gettype($hash) . ' given.'); } } if (!is_resource($fp)) { throw new TypeError('Argument 2 must be a resource, ' . gettype($fp) . ' given.'); } if (!is_int($size)) { throw new TypeError('Argument 3 must be an integer, ' . gettype($size) . ' given.'); } /** @var int $originalPosition */ $originalPosition = self::ftell($fp); // Move file pointer to beginning of file fseek($fp, 0, SEEK_SET); for ($i = 0; $i < $size; $i += self::BUFFER_SIZE) { /** @var string|bool $message */ $message = fread( $fp, ($size - $i) > self::BUFFER_SIZE ? $size - $i : self::BUFFER_SIZE ); if (!is_string($message)) { throw new SodiumException('Unexpected error reading from file.'); } /** @var string $message */ /** @psalm-suppress InvalidArgument */ self::hash_update($hash, $message); } // Reset file pointer's position fseek($fp, $originalPosition, SEEK_SET); return $hash; } /** * Sign a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces * the same result. (32-bit) * * @param string $filePath Absolute path to a file on the filesystem * @param string $secretKey Secret signing key * * @return string Ed25519 signature * @throws SodiumException * @throws TypeError */ private static function sign_core32($filePath, $secretKey) { $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var string $az */ $az = hash('sha512', self::substr($secretKey, 0, 32), true); $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); $nonceHash = hash_final($hs, true); $pk = self::substr($secretKey, 32, 32); $nonce = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32); $sig = ParagonIE_Sodium_Core32_Ed25519::ge_p3_tobytes( ParagonIE_Sodium_Core32_Ed25519::ge_scalarmult_base($nonce) ); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); $hramHash = hash_final($hs, true); $hram = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hramHash); $sigAfter = ParagonIE_Sodium_Core32_Ed25519::sc_muladd($hram, $az, $nonce); /** @var string $sig */ $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } fclose($fp); return $sig; } /** * * Verify a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but * produces the same result. (32-bit) * * @param string $sig Ed25519 signature * @param string $filePath Absolute path to a file on the filesystem * @param string $publicKey Signing public key * * @return bool * @throws SodiumException * @throws Exception */ public static function verify_core32($sig, $filePath, $publicKey) { /* Security checks */ if (ParagonIE_Sodium_Core32_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))) { throw new SodiumException('S < L - Invalid signature'); } if (ParagonIE_Sodium_Core32_Ed25519::small_order($sig)) { throw new SodiumException('Signature is on too small of an order'); } if ((self::chrToInt($sig[63]) & 224) !== 0) { throw new SodiumException('Invalid signature'); } $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($publicKey[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var int|bool $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var int $size */ /** @var resource|bool $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $fp */ /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ $A = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($publicKey, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hDigest */ $hDigest = hash_final($hs, true); /** @var string $h */ $h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ $R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R); // Close the file handle fclose($fp); // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; return self::verify_32($rcheck, self::substr($sig, 0, 32)); } /** * Encrypt a file (32-bit) * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key) { $plaintext = fread($ifp, 32); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $first32 = self::ftell($ifp); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen0 = $mlen; if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( $block0, $realNonce, $subkey ); $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES ) ); // Pre-write 16 blank bytes for the Poly1305 tag $start = self::ftell($ofp); fwrite($ofp, str_repeat("\x00", 16)); /** @var string $c */ $cBlock = ParagonIE_Sodium_Core32_Util::substr( $block0, ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES ); $state->update($cBlock); fwrite($ofp, $cBlock); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ fseek($ifp, $first32, SEEK_SET); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $plaintext = fread($ifp, $blockSize); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $cBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( $plaintext, $realNonce, $iter, $subkey ); fwrite($ofp, $cBlock, $blockSize); $state->update($cBlock); $mlen -= $blockSize; $iter += $incr; } try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $end = self::ftell($ofp); /* * Write the Poly1305 authentication tag that provides integrity * over the ciphertext (encrypt-then-MAC) */ fseek($ofp, $start, SEEK_SET); fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES); fseek($ofp, $end, SEEK_SET); unset($state); return true; } /** * Decrypt a file (32-bit) * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key) { $tag = fread($ifp, 16); if (!is_string($tag)) { throw new SodiumException('Could not read input file'); } /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); /* Verify the Poly1305 MAC -before- attempting to decrypt! */ $state = new ParagonIE_Sodium_Core32_Poly1305_State(self::substr($block0, 0, 32)); if (!self::onetimeauth_verify_core32($state, $ifp, $tag, $mlen)) { throw new SodiumException('Invalid MAC'); } /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ $first32 = fread($ifp, 32); if (!is_string($first32)) { throw new SodiumException('Could not read input file'); } $first32len = self::strlen($first32); fwrite( $ofp, self::xorStrings( self::substr($block0, 32, $first32len), self::substr($first32, 0, $first32len) ) ); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* Decrypts ciphertext, writes to output file. */ while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $pBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( $ciphertext, $realNonce, $iter, $subkey ); fwrite($ofp, $pBlock, $blockSize); $mlen -= $blockSize; $iter += $incr; } return true; } /** * One-time message authentication for 32-bit systems * * @param ParagonIE_Sodium_Core32_Poly1305_State $state * @param resource $ifp * @param string $tag * @param int $mlen * @return bool * @throws SodiumException * @throws TypeError */ protected static function onetimeauth_verify_core32( ParagonIE_Sodium_Core32_Poly1305_State $state, $ifp, $tag = '', $mlen = 0 ) { /** @var int $pos */ $pos = self::ftell($ifp); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $state->update($ciphertext); $mlen -= $blockSize; } $res = ParagonIE_Sodium_Core32_Util::verify_16($tag, $state->finish()); fseek($ifp, $pos, SEEK_SET); return $res; } /** * @param resource $resource * @return int * @throws SodiumException */ private static function ftell($resource) { $return = ftell($resource); if (!is_int($return)) { throw new SodiumException('ftell() returned false'); } return (int) $return; } } PK �Z�p�5J$ J$ Content/Type/Sniffer.phpnu �[��� <?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue * @author Ryan Parman * @author Sam Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ namespace SimplePie\Content\Type; /** * Content-type sniffing * * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06 * * This is used since we can't always trust Content-Type headers, and is based * upon the HTML5 parsing rules. * * * This class can be overloaded with {@see \SimplePie\SimplePie::set_content_type_sniffer_class()} * * @package SimplePie * @subpackage HTTP */ class Sniffer { /** * File object * * @var \SimplePie\File */ public $file; /** * Create an instance of the class with the input file * * @param Sniffer $file Input file */ public function __construct($file) { $this->file = $file; } /** * Get the Content-Type of the specified file * * @return string Actual Content-Type */ public function get_type() { if (isset($this->file->headers['content-type'])) { if (!isset($this->file->headers['content-encoding']) && ($this->file->headers['content-type'] === 'text/plain' || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1' || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) { return $this->text_or_binary(); } if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) { $official = substr($this->file->headers['content-type'], 0, $pos); } else { $official = $this->file->headers['content-type']; } $official = trim(strtolower($official)); if ($official === 'unknown/unknown' || $official === 'application/unknown') { return $this->unknown(); } elseif (substr($official, -4) === '+xml' || $official === 'text/xml' || $official === 'application/xml') { return $official; } elseif (substr($official, 0, 6) === 'image/') { if ($return = $this->image()) { return $return; } return $official; } elseif ($official === 'text/html') { return $this->feed_or_html(); } return $official; } return $this->unknown(); } /** * Sniff text or binary * * @return string Actual Content-Type */ public function text_or_binary() { if (substr($this->file->body, 0, 2) === "\xFE\xFF" || substr($this->file->body, 0, 2) === "\xFF\xFE" || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") { return 'text/plain'; } elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) { return 'application/octet-stream'; } return 'text/plain'; } /** * Sniff unknown * * @return string Actual Content-Type */ public function unknown() { $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html' || strtolower(substr($this->file->body, $ws, 5)) === '<html' || strtolower(substr($this->file->body, $ws, 7)) === '<script') { return 'text/html'; } elseif (substr($this->file->body, 0, 5) === '%PDF-') { return 'application/pdf'; } elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') { return 'application/postscript'; } elseif (substr($this->file->body, 0, 6) === 'GIF87a' || substr($this->file->body, 0, 6) === 'GIF89a') { return 'image/gif'; } elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { return 'image/png'; } elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") { return 'image/jpeg'; } elseif (substr($this->file->body, 0, 2) === "\x42\x4D") { return 'image/bmp'; } elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") { return 'image/vnd.microsoft.icon'; } return $this->text_or_binary(); } /** * Sniff images * * @return string Actual Content-Type */ public function image() { if (substr($this->file->body, 0, 6) === 'GIF87a' || substr($this->file->body, 0, 6) === 'GIF89a') { return 'image/gif'; } elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { return 'image/png'; } elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") { return 'image/jpeg'; } elseif (substr($this->file->body, 0, 2) === "\x42\x4D") { return 'image/bmp'; } elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") { return 'image/vnd.microsoft.icon'; } return false; } /** * Sniff HTML * * @return string Actual Content-Type */ public function feed_or_html() { $len = strlen($this->file->body); $pos = strspn($this->file->body, "\x09\x0A\x0D\x20\xEF\xBB\xBF"); while ($pos < $len) { switch ($this->file->body[$pos]) { case "\x09": case "\x0A": case "\x0D": case "\x20": $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); continue 2; case '<': $pos++; break; default: return 'text/html'; } if (substr($this->file->body, $pos, 3) === '!--') { $pos += 3; if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) { $pos += 3; } else { return 'text/html'; } } elseif (substr($this->file->body, $pos, 1) === '!') { if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) { $pos++; } else { return 'text/html'; } } elseif (substr($this->file->body, $pos, 1) === '?') { if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) { $pos += 2; } else { return 'text/html'; } } elseif (substr($this->file->body, $pos, 3) === 'rss' || substr($this->file->body, $pos, 7) === 'rdf:RDF') { return 'application/rss+xml'; } elseif (substr($this->file->body, $pos, 4) === 'feed') { return 'application/atom+xml'; } else { return 'text/html'; } } return 'text/html'; } } class_alias('SimplePie\Content\Type\Sniffer', 'SimplePie_Content_Type_Sniffer'); PK �Zw�li i Rating.phpnu �[��� <?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue * @author Ryan Parman * @author Sam Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ namespace SimplePie; /** * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively * * Used by {@see \SimplePie\Enclosure::get_rating()} and {@see \SimplePie\Enclosure::get_ratings()} * * This class can be overloaded with {@see \SimplePie\SimplePie::set_rating_class()} * * @package SimplePie * @subpackage API */ class Rating { /** * Rating scheme * * @var string * @see get_scheme() */ public $scheme; /** * Rating value * * @var string * @see get_value() */ public $value; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors */ public function __construct($scheme = null, $value = null) { $this->scheme = $scheme; $this->value = $value; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the organizational scheme for the rating * * @return string|null */ public function get_scheme() { if ($this->scheme !== null) { return $this->scheme; } return null; } /** * Get the value of the rating * * @return string|null */ public function get_value() { if ($this->value !== null) { return $this->value; } return null; } } class_alias('SimplePie\Rating', 'SimplePie_Rating'); PK �Z��B!"