diff --git a/composer.json b/composer.json index 0308dc27..39ad1807 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,10 @@ { "type": "vcs", "url": "https://github.com/freescout-help-desk/laravel-selfupdater" + }, + { + "type": "vcs", + "url": "https://github.com/freescout-help-desk/Purifier" } ], "require": { @@ -23,7 +27,7 @@ "fideloper/proxy": "3.3.4", "laravel/framework": "v5.5.40", "laravel/tinker": "v1.0.7", - "mews/purifier": "3.2.2", + "mews/purifier": "dev-customized", "spatie/laravel-activitylog": "2.7.0", "lord/laroute": "2.4.7", "axn/laravel-laroute": "1.3.0", @@ -91,8 +95,7 @@ "overrides/erusev/parsedown/" ], "psr-0": { - "Swift_": "overrides/swiftmailer/swiftmailer/lib/classes", - "HTMLPurifier_": "overrides/ezyang/htmlpurifier/library" + "Swift_": "overrides/swiftmailer/swiftmailer/lib/classes" }, "files": [ "app/Misc/Functions.php" @@ -210,7 +213,6 @@ "Whoops\\": "overrides/filp/whoops/src/Whoops/", "Whoops\\Handler\\": "overrides/filp/whoops/src/Whoops/Handler/", "Whoops\\Util\\": "overrides/filp/whoops/src/Whoops/Util/", - "Mews\\Purifier\\": "overrides/mews/purifier/src/", "Cron\\": "overrides/mtdowling/cron-expression/src/Cron/", "Psy\\": "overrides/psy/psysh/src/", "Psy\\Input\\": "overrides/psy/psysh/src/Input/", @@ -277,8 +279,6 @@ "vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php", "vendor/chumper/zipper/src/Chumper/Zipper/Repositories/ZipRepository.php", "vendor/fzaninotto/faker/src/Faker/Provider/Base.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php", "vendor/laravel/framework/src/Illuminate/Database/DetectsLostConnections.php", "vendor/laravel/framework/src/Illuminate/Support/Collection.php", @@ -404,13 +404,6 @@ "vendor/symfony/translation/Loader/ArrayLoader.php", "vendor/symfony/translation/Formatter/MessageFormatter.php", "vendor/symfony/translation/Translator.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/Length.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php", - "vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php", "vendor/javoscript/laravel-macroable-models/src/MacroableModels.php", "vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php", "vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php", @@ -473,7 +466,6 @@ "vendor/league/flysystem/src/Filesystem.php", "vendor/league/flysystem/src/FilesystemInterface.php", "vendor/league/flysystem/src/FileNotFoundException.php", - "vendor/mews/purifier/src/Purifier.php", "vendor/mtdowling/cron-expression/src/Cron/CronExpression.php", "vendor/laravel/framework/src/Illuminate/Mail/Mailer.php", "vendor/filp/whoops/src/Whoops/Run.php", diff --git a/composer.lock b/composer.lock index 98727a5e..947923a1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "da1068393c4ab3db8b261fd14ab64f03", + "content-hash": "e597d601a6307036f019cd690319c679", "packages": [ { "name": "anahkiasen/underscore-php", @@ -998,23 +998,30 @@ }, { "name": "ezyang/htmlpurifier", - "version": "v4.12.0", + "version": "v4.19.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "a617e55bc62a87eec73bd456d146d134ad716f03" + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/a617e55bc62a87eec73bd456d146d134ad716f03", - "reference": "a617e55bc62a87eec73bd456d146d134ad716f03", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", "shasum": "" }, "require": { - "php": ">=5.2" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd" + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" }, "type": "library", "autoload": { @@ -1023,7 +1030,10 @@ ], "psr-0": { "HTMLPurifier": "library/" - } + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1043,9 +1053,9 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/master" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" }, - "time": "2019-10-28T03:44:26+00:00" + "time": "2025-10-17T16:34:55+00:00" }, { "name": "fideloper/proxy", @@ -2038,30 +2048,24 @@ }, { "name": "mews/purifier", - "version": "3.2.2", + "version": "dev-customized", "source": { "type": "git", - "url": "https://github.com/mewebstudio/Purifier.git", - "reference": "75e4d9a0553b31c1fd31aef65f9561c30dbe5e5e" + "url": "https://github.com/freescout-help-desk/Purifier.git", + "reference": "6ef282797274c62687aa31b5fc74665135bb8e7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mewebstudio/Purifier/zipball/75e4d9a0553b31c1fd31aef65f9561c30dbe5e5e", - "reference": "75e4d9a0553b31c1fd31aef65f9561c30dbe5e5e", + "url": "https://api.github.com/repos/freescout-help-desk/Purifier/zipball/6ef282797274c62687aa31b5fc74665135bb8e7d", + "reference": "6ef282797274c62687aa31b5fc74665135bb8e7d", "shasum": "" }, "require": { - "ezyang/htmlpurifier": "4.12.*", - "illuminate/config": "^5.1|^6.0|^7.0", - "illuminate/filesystem": "^5.1|^6.0|^7.0", - "illuminate/support": "^5.1|^6.0|^7.0", - "php": "^7.2" - }, - "require-dev": { - "graham-campbell/testbench": "^3.2", - "mockery/mockery": "0.9.*", - "phpunit/phpunit": "^4.8|^5.0", - "scrutinizer/ocular": "^1.3" + "ezyang/htmlpurifier": "^4.19.0", + "illuminate/config": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/filesystem": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^7.1|^8.0" }, "suggest": { "laravel/framework": "To test the Laravel bindings", @@ -2070,23 +2074,27 @@ "type": "package", "extra": { "laravel": { - "aliases": { - "Purifier": "Mews\\Purifier\\Facades\\Purifier" - }, "providers": [ "Mews\\Purifier\\PurifierServiceProvider" - ] + ], + "aliases": { + "Purifier": "Mews\\Purifier\\Facades\\Purifier" + } } }, "autoload": { - "files": [ - "src/helpers.php" - ], "psr-4": { "Mews\\Purifier\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Mews\\Tests\\Purifier\\": "tests/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2098,25 +2106,21 @@ "role": "Developer" } ], - "description": "Laravel 5/6/7 HtmlPurifier Package", + "description": "Laravel 5/6/7/8/9/10 HtmlPurifier Package", "homepage": "https://github.com/mewebstudio/purifier", "keywords": [ + "HtmlPurifier", + "Laravel HtmlPurifier", + "Laravel Purifier", + "Laravel Security", "Purifier", - "htmlpurifier", - "laravel5 HtmlPurifier", - "laravel5 Purifier", - "laravel5 Security", - "laravel6 HtmlPurifier", - "laravel6 Purifier", - "laravel6 Security", "security", "xss" ], "support": { - "issues": "https://github.com/mewebstudio/Purifier/issues", - "source": "https://github.com/mewebstudio/Purifier/tree/3.2.2" + "source": "https://github.com/freescout-help-desk/Purifier/tree/customized" }, - "time": "2020-04-10T19:42:16+00:00" + "time": "2026-07-27T13:06:24+00:00" }, { "name": "monolog/monolog", @@ -7419,7 +7423,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "codedge/laravel-selfupdater": 20 + "codedge/laravel-selfupdater": 20, + "mews/purifier": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php deleted file mode 100644 index eed1558c..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ /dev/null @@ -1,219 +0,0 @@ -mask = '_- '; - for ($c = 'a'; $c <= 'z'; (function_exists('str_increment') ? $c = str_increment($c) : $c++)) { - $this->mask .= $c; - } - for ($c = 'A'; $c <= 'Z'; (function_exists('str_increment') ? $c = str_increment($c) : $c++)) { - $this->mask .= $c; - } - for ($c = '0'; $c <= '9'; $c++) { - $this->mask .= $c; - } // cast-y, but should be fine - // special bytes used by UTF-8 - for ($i = 0x80; $i <= 0xFF; $i++) { - // We don't bother excluding invalid bytes in this range, - // because the our restriction of well-formed UTF-8 will - // prevent these from ever occurring. - $this->mask .= chr($i); - } - - /* - PHP's internal strcspn implementation is - O(length of string * length of mask), making it inefficient - for large masks. However, it's still faster than - preg_match 8) - for (p = s1;;) { - spanp = s2; - do { - if (*spanp == c || p == s1_end) { - return p - s1; - } - } while (spanp++ < (s2_end - 1)); - c = *++p; - } - */ - // possible optimization: invert the mask. - } - - /** - * @param string $string - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool|string - */ - public function validate($string, $config, $context) - { - static $generic_names = array( - 'serif' => true, - 'sans-serif' => true, - 'monospace' => true, - 'fantasy' => true, - 'cursive' => true - ); - $allowed_fonts = $config->get('CSS.AllowedFonts'); - - // assume that no font names contain commas in them - $fonts = explode(',', $string); - $final = ''; - foreach ($fonts as $font) { - $font = trim($font); - if ($font === '') { - continue; - } - // match a generic name - if (isset($generic_names[$font])) { - if ($allowed_fonts === null || isset($allowed_fonts[$font])) { - $final .= $font . ', '; - } - continue; - } - // match a quoted name - if ($font[0] === '"' || $font[0] === "'") { - $length = strlen($font); - if ($length <= 2) { - continue; - } - $quote = $font[0]; - if ($font[$length - 1] !== $quote) { - continue; - } - $font = substr($font, 1, $length - 2); - } - - $font = $this->expandCSSEscape($font); - - // $font is a pure representation of the font name - - if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) { - continue; - } - - if (ctype_alnum($font) && $font !== '') { - // very simple font, allow it in unharmed - $final .= $font . ', '; - continue; - } - - // bugger out on whitespace. form feed (0C) really - // shouldn't show up regardless - $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); - - // Here, there are various classes of characters which need - // to be treated differently: - // - Alphanumeric characters are essentially safe. We - // handled these above. - // - Spaces require quoting, though most parsers will do - // the right thing if there aren't any characters that - // can be misinterpreted - // - Dashes rarely occur, but they fairly unproblematic - // for parsing/rendering purposes. - // The above characters cover the majority of Western font - // names. - // - Arbitrary Unicode characters not in ASCII. Because - // most parsers give little thought to Unicode, treatment - // of these codepoints is basically uniform, even for - // punctuation-like codepoints. These characters can - // show up in non-Western pages and are supported by most - // major browsers, for example: "MS 明朝" is a - // legitimate font-name - // . See - // the CSS3 spec for more examples: - // - // You can see live samples of these on the Internet: - // - // However, most of these fonts have ASCII equivalents: - // for example, 'MS Mincho', and it's considered - // professional to use ASCII font names instead of - // Unicode font names. Thanks Takeshi Terada for - // providing this information. - // The following characters, to my knowledge, have not been - // used to name font names. - // - Single quote. While theoretically you might find a - // font name that has a single quote in its name (serving - // as an apostrophe, e.g. Dave's Scribble), I haven't - // been able to find any actual examples of this. - // Internet Explorer's cssText translation (which I - // believe is invoked by innerHTML) normalizes any - // quoting to single quotes, and fails to escape single - // quotes. (Note that this is not IE's behavior for all - // CSS properties, just some sort of special casing for - // font-family). So a single quote *cannot* be used - // safely in the font-family context if there will be an - // innerHTML/cssText translation. Note that Firefox 3.x - // does this too. - // - Double quote. In IE, these get normalized to - // single-quotes, no matter what the encoding. (Fun - // fact, in IE8, the 'content' CSS property gained - // support, where they special cased to preserve encoded - // double quotes, but still translate unadorned double - // quotes into single quotes.) So, because their - // fixpoint behavior is identical to single quotes, they - // cannot be allowed either. Firefox 3.x displays - // single-quote style behavior. - // - Backslashes are reduced by one (so \\ -> \) every - // iteration, so they cannot be used safely. This shows - // up in IE7, IE8 and FF3 - // - Semicolons, commas and backticks are handled properly. - // - The rest of the ASCII punctuation is handled properly. - // We haven't checked what browsers do to unadorned - // versions, but this is not important as long as the - // browser doesn't /remove/ surrounding quotes (as IE does - // for HTML). - // - // With these results in hand, we conclude that there are - // various levels of safety: - // - Paranoid: alphanumeric, spaces and dashes(?) - // - International: Paranoid + non-ASCII Unicode - // - Edgy: Everything except quotes, backslashes - // - NoJS: Standards compliance, e.g. sod IE. Note that - // with some judicious character escaping (since certain - // types of escaping doesn't work) this is theoretically - // OK as long as innerHTML/cssText is not called. - // We believe that international is a reasonable default - // (that we will implement now), and once we do more - // extensive research, we may feel comfortable with dropping - // it down to edgy. - - // Edgy: alphanumeric, spaces, dashes, underscores and Unicode. Use of - // str(c)spn assumes that the string was already well formed - // Unicode (which of course it is). - if (strspn($font, $this->mask) !== strlen($font)) { - continue; - } - - // Historical: - // In the absence of innerHTML/cssText, these ugly - // transforms don't pose a security risk (as \\ and \" - // might--these escapes are not supported by most browsers). - // We could try to be clever and use single-quote wrapping - // when there is a double quote present, but I have choosen - // not to implement that. (NOTE: you can reduce the amount - // of escapes by one depending on what quoting style you use) - // $font = str_replace('\\', '\\5C ', $font); - // $font = str_replace('"', '\\22 ', $font); - // $font = str_replace("'", '\\27 ', $font); - - // font possibly with spaces, requires quoting - $final .= "'$font', "; - } - $final = rtrim($final, ', '); - if ($final === '') { - return false; - } - return $final; - } - -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php deleted file mode 100644 index 922dbf87..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php +++ /dev/null @@ -1,146 +0,0 @@ -ipv4 = new HTMLPurifier_AttrDef_URI_IPv4(); - $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6(); - } - - /** - * @param string $string - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool|string - */ - public function validate($string, $config, $context) - { - $length = strlen($string); - // empty hostname is OK; it's usually semantically equivalent: - // the default host as defined by a URI scheme is used: - // - // If the URI scheme defines a default for host, then that - // default applies when the host subcomponent is undefined - // or when the registered name is empty (zero length). - if ($string === '') { - return ''; - } - if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') { - //IPv6 - $ip = substr($string, 1, $length - 2); - $valid = $this->ipv6->validate($ip, $config, $context); - if ($valid === false) { - return false; - } - return '[' . $valid . ']'; - } - - // need to do checks on unusual encodings too - $ipv4 = $this->ipv4->validate($string, $config, $context); - if ($ipv4 !== false) { - return $ipv4; - } - - // A regular domain name. - - // This doesn't match I18N domain names, but we don't have proper IRI support, - // so force users to insert Punycode. - - // There is not a good sense in which underscores should be - // allowed, since it's technically not! (And if you go as - // far to allow everything as specified by the DNS spec... - // well, that's literally everything, modulo some space limits - // for the components and the overall name (which, by the way, - // we are NOT checking!). So we (arbitrarily) decide this: - // let's allow underscores wherever we would have allowed - // hyphens, if they are enabled. This is a pretty good match - // for browser behavior, for example, a large number of browsers - // cannot handle foo_.example.com, but foo_bar.example.com is - // fairly well supported. - $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : ''; - - // Based off of RFC 1738, but amended so that - // as per RFC 3696, the top label need only not be all numeric. - // The productions describing this are: - $a = '[a-z]'; // alpha - $an = '[a-z0-9]'; // alphanum - $and = "[a-z0-9-$underscore]"; // alphanum | "-" - // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum - $domainlabel = "$an(?:$and*$an)?"; - // AMENDED as per RFC 3696 - // toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum - // side condition: not all numeric - $toplabel = "$an(?:$and*$an)?"; - // hostname = *( domainlabel "." ) toplabel [ "." ] - if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) { - if (!ctype_digit($matches[1])) { - return $string; - } - } - - // PHP 5.3 and later support this functionality natively - if (function_exists('idn_to_ascii')) { - try { - if (defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46')) { - $string = idn_to_ascii($string, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); - } else { - $string = idn_to_ascii($string); - } - } catch (\Exception $e) { - - } - - // If we have Net_IDNA2 support, we can support IRIs by - // punycoding them. (This is the most portable thing to do, - // since otherwise we have to assume browsers support - } elseif ($config->get('Core.EnableIDNA')) { - $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true)); - // we need to encode each period separately - $parts = explode('.', $string); - try { - $new_parts = array(); - foreach ($parts as $part) { - $encodable = false; - for ($i = 0, $c = strlen($part); $i < $c; $i++) { - if (ord($part[$i]) > 0x7a) { - $encodable = true; - break; - } - } - if (!$encodable) { - $new_parts[] = $part; - } else { - $new_parts[] = $idna->encode($part); - } - } - $string = implode('.', $new_parts); - } catch (Exception $e) { - // XXX error reporting - } - } - // Try again - if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) { - return $string; - } - return false; - } -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php deleted file mode 100644 index 6786e5ed..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php +++ /dev/null @@ -1,42 +0,0 @@ -idDef = new HTMLPurifier_AttrDef_HTML_ID(); - } - - /** - * @param array $attr - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return array - */ - public function transform($attr, $config, $context) - { - if (!isset($attr['name'])) { - return $attr; - } - $name = $attr['name']; - if (isset($attr['id']) && $attr['id'] === $name) { - return $attr; - } - $result = $this->idDef->validate($name, $config, $context); - if ($result === false) { - unset($attr['name']); - } else { - $attr['name'] = $result; - } - return $attr; - } -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php deleted file mode 100644 index 25265daa..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php +++ /dev/null @@ -1,205 +0,0 @@ -getHTMLDefinition(); - $e =& $context->get('ErrorCollector', true); - - // initialize IDAccumulator if necessary - $ok =& $context->get('IDAccumulator', true); - if (!$ok) { - $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); - $context->register('IDAccumulator', $id_accumulator); - } - - // initialize CurrentToken if necessary - $current_token =& $context->get('CurrentToken', true); - if (!$current_token) { - $context->register('CurrentToken', $token); - } - - if (!$token instanceof HTMLPurifier_Token_Start && - !$token instanceof HTMLPurifier_Token_Empty - ) { - return; - } - - // create alias to global definition array, see also $defs - // DEFINITION CALL - $d_defs = $definition->info_global_attr; - - // don't update token until the very end, to ensure an atomic update - $attr = $token->attr; - - // do global transformations (pre) - // nothing currently utilizes this - foreach ($definition->info_attr_transform_pre as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) { - $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - } - - // do local transformations only applicable to this element (pre) - // ex.

to

- foreach ($definition->info[$token->name]->attr_transform_pre as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) { - $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - } - - // create alias to this element's attribute definition array, see - // also $d_defs (global attribute definition array) - // DEFINITION CALL - $defs = $definition->info[$token->name]->attr; - - $attr_key = false; - $context->register('CurrentAttr', $attr_key); - - // Used to make sure width/height are changed proportionally - // when HTML.MaxImgLength is used. - $proportions_change = []; - - // iterate through all the attribute keypairs - // Watch out for name collisions: $key has previously been used - foreach ($attr as $attr_key => $value) { - - // call the definition - if (isset($defs[$attr_key])) { - // there is a local definition defined - if ($defs[$attr_key] === false) { - // We've explicitly been told not to allow this element. - // This is usually when there's a global definition - // that must be overridden. - // Theoretically speaking, we could have a - // AttrDef_DenyAll, but this is faster! - $result = false; - } else { - // validate according to the element's definition - $result = $defs[$attr_key]->validate( - $value, - $config, - $context - ); - if ($token->name == 'img' - && $result - && (int)$value - && (int)$result - && ($attr_key == 'width' || $attr_key == 'height') - ) { - $proportions_change[$attr_key]['original'] = (int)$value; - $proportions_change[$attr_key]['new'] = (int)$result; - } - } - } elseif (isset($d_defs[$attr_key])) { - // there is a global definition defined, validate according - // to the global definition - $result = $d_defs[$attr_key]->validate( - $value, - $config, - $context - ); - } else { - // system never heard of the attribute? DELETE! - $result = false; - } - - // put the results into effect - if ($result === false || $result === null) { - // this is a generic error message that should replaced - // with more specific ones when possible - if ($e) { - $e->send(E_ERROR, 'AttrValidator: Attribute removed'); - } - - // remove the attribute - unset($attr[$attr_key]); - } elseif (is_string($result)) { - // generally, if a substitution is happening, there - // was some sort of implicit correction going on. We'll - // delegate it to the attribute classes to say exactly what. - - // simple substitution - $attr[$attr_key] = $result; - } else { - // nothing happens - } - - // we'd also want slightly more complicated substitution - // involving an array as the return value, - // although we're not sure how colliding attributes would - // resolve (certain ones would be completely overriden, - // others would prepend themselves). - } - - if (!empty($proportions_change['width']) && !empty($proportions_change['height'])) { - if ($proportions_change['width']['new'] < $proportions_change['width']['original'] - && (int)$proportions_change['width']['original'] - ) { - // Adjust height. - $attr['height'] = floor($proportions_change['height']['original'] * $proportions_change['width']['new'] / $proportions_change['width']['original']); - } elseif ($proportions_change['height']['new'] < $proportions_change['height']['original'] - && (int)$proportions_change['height']['original'] - ) { - // Adjust width. - $attr['width'] = floor($proportions_change['height']['original'] * $proportions_change['height']['new'] / $proportions_change['height']['original']); - } - } - - $context->destroy('CurrentAttr'); - - // post transforms - - // global (error reporting untested) - foreach ($definition->info_attr_transform_post as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) { - $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - } - - // local (error reporting untested) - foreach ($definition->info[$token->name]->attr_transform_post as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) { - $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - } - - $token->attr = $attr; - - // destroy CurrentToken if we made it ourselves - if (!$current_token) { - $context->destroy('CurrentToken'); - } - - } - - -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php deleted file mode 100644 index 3d584e72..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php +++ /dev/null @@ -1,94 +0,0 @@ - true, 'ul' => true, 'ol' => true); - - public $whitespace; - - /** - * @param array $children - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return array - */ - public function validateChildren($children, $config, $context) - { - // Flag for subclasses - $this->whitespace = false; - - // if there are no tokens, delete parent node - if (empty($children)) { - return false; - } - - // if li is not allowed, delete parent node - if (!isset($config->getHTMLDefinition()->info['li'])) { - trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING); - return false; - } - - // the new set of children - $result = array(); - - // a little sanity check to make sure it's not ALL whitespace - $all_whitespace = true; - - $current_li = null; - - foreach ($children as $node) { - if (!empty($node->is_whitespace)) { - $result[] = $node; - continue; - } - $all_whitespace = false; // phew, we're not talking about whitespace - - if ($node->name === 'li') { - // good - $current_li = $node; - $result[] = $node; - } else { - // we want to tuck this into the previous li - // Invariant: we expect the node to be ol/ul - // ToDo: Make this more robust in the case of not ol/ul - // by distinguishing between existing li and li created - // to handle non-list elements; non-list elements should - // not be appended to an existing li; only li created - // for non-list. This distinction is not currently made. - if ($current_li === null) { - $current_li = new HTMLPurifier_Node_Element('li'); - $result[] = $current_li; - } - $current_li->children[] = $node; - $current_li->empty = false; // XXX fascinating! Check for this error elsewhere ToDo - } - } - if (empty($result)) { - return false; - } - if ($all_whitespace) { - return false; - } - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php deleted file mode 100644 index 1a3367f5..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php +++ /dev/null @@ -1,617 +0,0 @@ -= $c) { - $r .= self::unsafeIconv($in, $out, substr($text, $i)); - break; - } - // wibble the boundary - if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) { - $chunk_size = $max_chunk_size; - } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) { - $chunk_size = $max_chunk_size - 1; - } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) { - $chunk_size = $max_chunk_size - 2; - } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) { - $chunk_size = $max_chunk_size - 3; - } else { - return false; // rather confusing UTF-8... - } - $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths - $r .= self::unsafeIconv($in, $out, $chunk); - $i += $chunk_size; - } - return $r; - } else { - return false; - } - } else { - return false; - } - } - - /** - * Cleans a UTF-8 string for well-formedness and SGML validity - * - * It will parse according to UTF-8 and return a valid UTF8 string, with - * non-SGML codepoints excluded. - * - * Specifically, it will permit: - * \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF} - * Source: https://www.w3.org/TR/REC-xml/#NT-Char - * Arguably this function should be modernized to the HTML5 set - * of allowed characters: - * https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream - * which simultaneously expand and restrict the set of allowed characters. - * - * @param string $str The string to clean - * @param bool $force_php - * @return string - * - * @note Just for reference, the non-SGML code points are 0 to 31 and - * 127 to 159, inclusive. However, we allow code points 9, 10 - * and 13, which are the tab, line feed and carriage return - * respectively. 128 and above the code points map to multibyte - * UTF-8 representations. - * - * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and - * hsivonen@iki.fi at under the - * LGPL license. Notes on what changed are inside, but in general, - * the original code transformed UTF-8 text into an array of integer - * Unicode codepoints. Understandably, transforming that back to - * a string would be somewhat expensive, so the function was modded to - * directly operate on the string. However, this discourages code - * reuse, and the logic enumerated here would be useful for any - * function that needs to be able to understand UTF-8 characters. - * As of right now, only smart lossless character encoding converters - * would need that, and I'm probably not going to implement them. - */ - public static function cleanUTF8($str, $force_php = false) - { - // UTF-8 validity is checked since PHP 4.3.5 - // This is an optimization: if the string is already valid UTF-8, no - // need to do PHP stuff. 99% of the time, this will be the case. - if (preg_match( - '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', - $str ?? '' - )) { - return $str; - } - - $mState = 0; // cached expected number of octets after the current octet - // until the beginning of the next UTF8 character sequence - $mUcs4 = 0; // cached Unicode character - $mBytes = 1; // cached expected number of octets in the current sequence - - // original code involved an $out that was an array of Unicode - // codepoints. Instead of having to convert back into UTF-8, we've - // decided to directly append valid UTF-8 characters onto a string - // $out once they're done. $char accumulates raw bytes, while $mUcs4 - // turns into the Unicode code point, so there's some redundancy. - - $out = ''; - $char = ''; - - $len = strlen($str); - for ($i = 0; $i < $len; $i++) { - $in = ord($str[$i]); - $char .= $str[$i]; // append byte to char - if (0 == $mState) { - // When mState is zero we expect either a US-ASCII character - // or a multi-octet sequence. - if (0 == (0x80 & ($in))) { - // US-ASCII, pass straight through. - if (($in <= 31 || $in == 127) && - !($in == 9 || $in == 13 || $in == 10) // save \r\t\n - ) { - // control characters, remove - } else { - $out .= $char; - } - // reset - $char = ''; - $mBytes = 1; - } elseif (0xC0 == (0xE0 & ($in))) { - // First octet of 2 octet sequence - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x1F) << 6; - $mState = 1; - $mBytes = 2; - } elseif (0xE0 == (0xF0 & ($in))) { - // First octet of 3 octet sequence - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x0F) << 12; - $mState = 2; - $mBytes = 3; - } elseif (0xF0 == (0xF8 & ($in))) { - // First octet of 4 octet sequence - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x07) << 18; - $mState = 3; - $mBytes = 4; - } elseif (0xF8 == (0xFC & ($in))) { - // First octet of 5 octet sequence. - // - // This is illegal because the encoded codepoint must be - // either: - // (a) not the shortest form or - // (b) outside the Unicode range of 0-0x10FFFF. - // Rather than trying to resynchronize, we will carry on - // until the end of the sequence and let the later error - // handling code catch it. - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x03) << 24; - $mState = 4; - $mBytes = 5; - } elseif (0xFC == (0xFE & ($in))) { - // First octet of 6 octet sequence, see comments for 5 - // octet sequence. - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 1) << 30; - $mState = 5; - $mBytes = 6; - } else { - // Current octet is neither in the US-ASCII range nor a - // legal first octet of a multi-octet sequence. - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - $char = ''; - } - } else { - // When mState is non-zero, we expect a continuation of the - // multi-octet sequence - if (0x80 == (0xC0 & ($in))) { - // Legal continuation. - $shift = ($mState - 1) * 6; - $tmp = $in; - $tmp = ($tmp & 0x0000003F) << $shift; - $mUcs4 |= $tmp; - - if (0 == --$mState) { - // End of the multi-octet sequence. mUcs4 now contains - // the final Unicode codepoint to be output - - // Check for illegal sequences and codepoints. - - // From Unicode 3.1, non-shortest form is illegal - if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || - ((3 == $mBytes) && ($mUcs4 < 0x0800)) || - ((4 == $mBytes) && ($mUcs4 < 0x10000)) || - (4 < $mBytes) || - // From Unicode 3.2, surrogate characters = illegal - (($mUcs4 & 0xFFFFF800) == 0xD800) || - // Codepoints outside the Unicode range are illegal - ($mUcs4 > 0x10FFFF) - ) { - - } elseif (0xFEFF != $mUcs4 && // omit BOM - // check for valid Char unicode codepoints - ( - 0x9 == $mUcs4 || - 0xA == $mUcs4 || - 0xD == $mUcs4 || - (0x20 <= $mUcs4 && 0x7E >= $mUcs4) || - // 7F-9F is not strictly prohibited by XML, - // but it is non-SGML, and thus we don't allow it - (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) || - (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) || - (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4) - ) - ) { - $out .= $char; - } - // initialize UTF8 cache (reset) - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - $char = ''; - } - } else { - // ((0xC0 & (*in) != 0x80) && (mState != 0)) - // Incomplete multi-octet sequence. - // used to result in complete fail, but we'll reset - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - $char =''; - } - } - } - return $out; - } - - /** - * Translates a Unicode codepoint into its corresponding UTF-8 character. - * @note Based on Feyd's function at - * , - * which is in public domain. - * @note While we're going to do code point parsing anyway, a good - * optimization would be to refuse to translate code points that - * are non-SGML characters. However, this could lead to duplication. - * @note This is very similar to the unichr function in - * maintenance/generate-entity-file.php (although this is superior, - * due to its sanity checks). - */ - - // +----------+----------+----------+----------+ - // | 33222222 | 22221111 | 111111 | | - // | 10987654 | 32109876 | 54321098 | 76543210 | bit - // +----------+----------+----------+----------+ - // | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F - // | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF - // | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF - // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF - // +----------+----------+----------+----------+ - // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF) - // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes - // +----------+----------+----------+----------+ - - public static function unichr($code) - { - if ($code > 1114111 or $code < 0 or - ($code >= 55296 and $code <= 57343) ) { - // bits are set outside the "valid" range as defined - // by UNICODE 4.1.0 - return ''; - } - - $x = $y = $z = $w = 0; - if ($code < 128) { - // regular ASCII character - $x = $code; - } else { - // set up bits for UTF-8 - $x = ($code & 63) | 128; - if ($code < 2048) { - $y = (($code & 2047) >> 6) | 192; - } else { - $y = (($code & 4032) >> 6) | 128; - if ($code < 65536) { - $z = (($code >> 12) & 15) | 224; - } else { - $z = (($code >> 12) & 63) | 128; - $w = (($code >> 18) & 7) | 240; - } - } - } - // set up the actual character - $ret = ''; - if ($w) { - $ret .= chr($w); - } - if ($z) { - $ret .= chr($z); - } - if ($y) { - $ret .= chr($y); - } - $ret .= chr($x); - - return $ret; - } - - /** - * @return bool - */ - public static function iconvAvailable() - { - static $iconv = null; - if ($iconv === null) { - $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE; - } - return $iconv; - } - - /** - * Convert a string to UTF-8 based on configuration. - * @param string $str The string to convert - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return string - */ - public static function convertToUTF8($str, $config, $context) - { - $encoding = $config->get('Core.Encoding'); - if ($encoding === 'utf-8') { - return $str; - } - static $iconv = null; - if ($iconv === null) { - $iconv = self::iconvAvailable(); - } - if ($iconv && !$config->get('Test.ForceNoIconv')) { - // unaffected by bugs, since UTF-8 support all characters - $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str); - if ($str === false) { - // $encoding is not a valid encoding - trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR); - return ''; - } - // If the string is bjorked by Shift_JIS or a similar encoding - // that doesn't support all of ASCII, convert the naughty - // characters to their true byte-wise ASCII/UTF-8 equivalents. - $str = strtr($str, self::testEncodingSupportsASCII($encoding)); - return $str; - } elseif ($encoding === 'iso-8859-1') { - $str = utf8_encode($str); - return $str; - } - $bug = HTMLPurifier_Encoder::testIconvTruncateBug(); - if ($bug == self::ICONV_OK) { - trigger_error('Encoding not supported, please install iconv', E_USER_ERROR); - } else { - trigger_error( - 'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' . - 'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541', - E_USER_ERROR - ); - } - } - - /** - * Converts a string from UTF-8 based on configuration. - * @param string $str The string to convert - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return string - * @note Currently, this is a lossy conversion, with unexpressable - * characters being omitted. - */ - public static function convertFromUTF8($str, $config, $context) - { - $encoding = $config->get('Core.Encoding'); - if ($escape = $config->get('Core.EscapeNonASCIICharacters')) { - $str = self::convertToASCIIDumbLossless($str); - } - if ($encoding === 'utf-8') { - return $str; - } - static $iconv = null; - if ($iconv === null) { - $iconv = self::iconvAvailable(); - } - if ($iconv && !$config->get('Test.ForceNoIconv')) { - // Undo our previous fix in convertToUTF8, otherwise iconv will barf - $ascii_fix = self::testEncodingSupportsASCII($encoding); - if (!$escape && !empty($ascii_fix)) { - $clear_fix = array(); - foreach ($ascii_fix as $utf8 => $native) { - $clear_fix[$utf8] = ''; - } - $str = strtr($str, $clear_fix); - } - $str = strtr($str, array_flip($ascii_fix)); - // Normal stuff - $str = self::iconv('utf-8', $encoding . '//IGNORE', $str); - return $str; - } elseif ($encoding === 'iso-8859-1') { - $str = utf8_decode($str); - return $str; - } - trigger_error('Encoding not supported', E_USER_ERROR); - // You might be tempted to assume that the ASCII representation - // might be OK, however, this is *not* universally true over all - // encodings. So we take the conservative route here, rather - // than forcibly turn on %Core.EscapeNonASCIICharacters - } - - /** - * Lossless (character-wise) conversion of HTML to ASCII - * @param string $str UTF-8 string to be converted to ASCII - * @return string ASCII encoded string with non-ASCII character entity-ized - * @warning Adapted from MediaWiki, claiming fair use: this is a common - * algorithm. If you disagree with this license fudgery, - * implement it yourself. - * @note Uses decimal numeric entities since they are best supported. - * @note This is a DUMB function: it has no concept of keeping - * character entities that the projected character encoding - * can allow. We could possibly implement a smart version - * but that would require it to also know which Unicode - * codepoints the charset supported (not an easy task). - * @note Sort of with cleanUTF8() but it assumes that $str is - * well-formed UTF-8 - */ - public static function convertToASCIIDumbLossless($str) - { - $bytesleft = 0; - $result = ''; - $working = 0; - $len = strlen($str); - for ($i = 0; $i < $len; $i++) { - $bytevalue = ord($str[$i]); - if ($bytevalue <= 0x7F) { //0xxx xxxx - $result .= chr($bytevalue); - $bytesleft = 0; - } elseif ($bytevalue <= 0xBF) { //10xx xxxx - $working = $working << 6; - $working += ($bytevalue & 0x3F); - $bytesleft--; - if ($bytesleft <= 0) { - $result .= "&#" . $working . ";"; - } - } elseif ($bytevalue <= 0xDF) { //110x xxxx - $working = $bytevalue & 0x1F; - $bytesleft = 1; - } elseif ($bytevalue <= 0xEF) { //1110 xxxx - $working = $bytevalue & 0x0F; - $bytesleft = 2; - } else { //1111 0xxx - $working = $bytevalue & 0x07; - $bytesleft = 3; - } - } - return $result; - } - - /** No bugs detected in iconv. */ - const ICONV_OK = 0; - - /** Iconv truncates output if converting from UTF-8 to another - * character set with //IGNORE, and a non-encodable character is found */ - const ICONV_TRUNCATES = 1; - - /** Iconv does not support //IGNORE, making it unusable for - * transcoding purposes */ - const ICONV_UNUSABLE = 2; - - /** - * glibc iconv has a known bug where it doesn't handle the magic - * //IGNORE stanza correctly. In particular, rather than ignore - * characters, it will return an EILSEQ after consuming some number - * of characters, and expect you to restart iconv as if it were - * an E2BIG. Old versions of PHP did not respect the errno, and - * returned the fragment, so as a result you would see iconv - * mysteriously truncating output. We can work around this by - * manually chopping our input into segments of about 8000 - * characters, as long as PHP ignores the error code. If PHP starts - * paying attention to the error code, iconv becomes unusable. - * - * @return int Error code indicating severity of bug. - */ - public static function testIconvTruncateBug() - { - static $code = null; - if ($code === null) { - // better not use iconv, otherwise infinite loop! - $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000)); - if ($r === false) { - $code = self::ICONV_UNUSABLE; - } elseif (($c = strlen($r)) < 9000) { - $code = self::ICONV_TRUNCATES; - } elseif ($c > 9000) { - trigger_error( - 'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' . - 'include your iconv version as per phpversion()', - E_USER_ERROR - ); - } else { - $code = self::ICONV_OK; - } - } - return $code; - } - - /** - * This expensive function tests whether or not a given character - * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will - * fail this test, and require special processing. Variable width - * encodings shouldn't ever fail. - * - * @param string $encoding Encoding name to test, as per iconv format - * @param bool $bypass Whether or not to bypass the precompiled arrays. - * @return Array of UTF-8 characters to their corresponding ASCII, - * which can be used to "undo" any overzealous iconv action. - */ - public static function testEncodingSupportsASCII($encoding, $bypass = false) - { - // All calls to iconv here are unsafe, proof by case analysis: - // If ICONV_OK, no difference. - // If ICONV_TRUNCATE, all calls involve one character inputs, - // so bug is not triggered. - // If ICONV_UNUSABLE, this call is irrelevant - static $encodings = array(); - if (!$bypass) { - if (isset($encodings[$encoding])) { - return $encodings[$encoding]; - } - $lenc = strtolower($encoding); - switch ($lenc) { - case 'shift_jis': - return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~'); - case 'johab': - return array("\xE2\x82\xA9" => '\\'); - } - if (strpos($lenc, 'iso-8859-') === 0) { - return array(); - } - } - $ret = array(); - if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) { - return false; - } - for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars - $c = chr($i); // UTF-8 char - $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion - if ($r === '' || - // This line is needed for iconv implementations that do not - // omit characters that do not exist in the target character set - ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c) - ) { - // Reverse engineer: what's the UTF-8 equiv of this byte - // sequence? This assumes that there's no variable width - // encoding that doesn't support ASCII. - $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c; - } - } - $encodings[$encoding] = $ret; - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Length.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Length.php deleted file mode 100644 index 18a6b8ed..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Length.php +++ /dev/null @@ -1,162 +0,0 @@ - true, 'ex' => true, 'px' => true, 'in' => true, - 'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true, - 'ch' => true, 'rem' => true, 'vw' => true, 'vh' => true, - 'vmin' => true, 'vmax' => true - ); - - /** - * @param string $n Magnitude - * @param bool|string $u Unit - */ - public function __construct($n = '0', $u = false) - { - $this->n = (string) $n; - $this->unit = $u !== false ? (string) $u : false; - } - - /** - * @param string $s Unit string, like '2em' or '3.4in' - * @return HTMLPurifier_Length - * @warning Does not perform validation. - */ - public static function make($s) - { - if ($s instanceof HTMLPurifier_Length) { - return $s; - } - $n_length = strspn($s, '1234567890.+-'); - $n = substr($s, 0, $n_length); - $unit = substr($s, $n_length); - if ($unit === '') { - $unit = false; - } - return new HTMLPurifier_Length($n, $unit); - } - - /** - * Validates the number and unit. - * @return bool - */ - protected function validate() - { - // Special case: - if ($this->n === '+0' || $this->n === '-0') { - $this->n = '0'; - } - if ($this->n === '0' && $this->unit === false) { - return true; - } - if ($this->unit === false || !ctype_lower((string)$this->unit)) { - $this->unit = strtolower($this->unit); - } - if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) { - return false; - } - // Hack: - $def = new HTMLPurifier_AttrDef_CSS_Number(); - $result = $def->validate($this->n, false, false); - if ($result === false) { - return false; - } - $this->n = $result; - return true; - } - - /** - * Returns string representation of number. - * @return string - */ - public function toString() - { - if (!$this->isValid()) { - return false; - } - return $this->n . $this->unit; - } - - /** - * Retrieves string numeric magnitude. - * @return string - */ - public function getN() - { - return $this->n; - } - - /** - * Retrieves string unit. - * @return string - */ - public function getUnit() - { - return $this->unit; - } - - /** - * Returns true if this length unit is valid. - * @return bool - */ - public function isValid() - { - if ($this->isValid === null) { - $this->isValid = $this->validate(); - } - return $this->isValid; - } - - /** - * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal. - * @param HTMLPurifier_Length $l - * @return int - * @warning If both values are too large or small, this calculation will - * not work properly - */ - public function compareTo($l) - { - if ($l === false) { - return false; - } - if ($l->unit !== $this->unit) { - $converter = new HTMLPurifier_UnitConverter(); - $l = $converter->convert($l, $this->unit); - if ($l === false) { - return false; - } - } - return $this->n - $l->n; - } -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php deleted file mode 100644 index d8aa5030..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php +++ /dev/null @@ -1,387 +0,0 @@ -get('Core.LexerImpl'); - } - - $needs_tracking = - $config->get('Core.MaintainLineNumbers') || - $config->get('Core.CollectErrors'); - - $inst = null; - if (is_object($lexer)) { - $inst = $lexer; - } else { - if (is_null($lexer)) { - do { - // auto-detection algorithm - if ($needs_tracking) { - $lexer = 'DirectLex'; - break; - } - - if (class_exists('DOMDocument', false) && - method_exists('DOMDocument', 'loadHTML') && - !extension_loaded('domxml') - ) { - // check for DOM support, because while it's part of the - // core, it can be disabled compile time. Also, the PECL - // domxml extension overrides the default DOM, and is evil - // and nasty and we shan't bother to support it - $lexer = 'DOMLex'; - } else { - $lexer = 'DirectLex'; - } - } while (0); - } // do..while so we can break - - // instantiate recognized string names - switch ($lexer) { - case 'DOMLex': - $inst = new HTMLPurifier_Lexer_DOMLex(); - break; - case 'DirectLex': - $inst = new HTMLPurifier_Lexer_DirectLex(); - break; - case 'PH5P': - $inst = new HTMLPurifier_Lexer_PH5P(); - break; - default: - throw new HTMLPurifier_Exception( - "Cannot instantiate unrecognized Lexer type " . - htmlspecialchars($lexer) - ); - } - } - - if (!$inst) { - throw new HTMLPurifier_Exception('No lexer was instantiated'); - } - - // once PHP DOM implements native line numbers, or we - // hack out something using XSLT, remove this stipulation - if ($needs_tracking && !$inst->tracksLineNumbers) { - throw new HTMLPurifier_Exception( - 'Cannot use lexer that does not support line numbers with ' . - 'Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)' - ); - } - - return $inst; - - } - - // -- CONVENIENCE MEMBERS --------------------------------------------- - - public function __construct() - { - $this->_entity_parser = new HTMLPurifier_EntityParser(); - } - - /** - * Most common entity to raw value conversion table for special entities. - * @type array - */ - protected $_special_entity2str = - array( - '"' => '"', - '&' => '&', - '<' => '<', - '>' => '>', - ''' => "'", - ''' => "'", - ''' => "'" - ); - - public function parseText($string, $config) { - return $this->parseData($string, false, $config); - } - - public function parseAttr($string, $config) { - return $this->parseData($string, true, $config); - } - - /** - * Parses special entities into the proper characters. - * - * This string will translate escaped versions of the special characters - * into the correct ones. - * - * @param string $string String character data to be parsed. - * @return string Parsed character data. - */ - public function parseData($string, $is_attr, $config) - { - // following functions require at least one character - if ($string === '') { - return ''; - } - - // subtracts amps that cannot possibly be escaped - $num_amp = substr_count($string, '&') - substr_count($string, '& ') - - ($string[strlen($string) - 1] === '&' ? 1 : 0); - - if (!$num_amp) { - return $string; - } // abort if no entities - $num_esc_amp = substr_count($string, '&'); - $string = strtr($string, $this->_special_entity2str); - - // code duplication for sake of optimization, see above - $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') - - ($string[strlen($string) - 1] === '&' ? 1 : 0); - - if ($num_amp_2 <= $num_esc_amp) { - return $string; - } - - // hmm... now we have some uncommon entities. Use the callback. - if ($config->get('Core.LegacyEntityDecoder')) { - $string = $this->_entity_parser->substituteSpecialEntities($string); - } else { - if ($is_attr) { - $string = $this->_entity_parser->substituteAttrEntities($string); - } else { - $string = $this->_entity_parser->substituteTextEntities($string); - } - } - return $string; - } - - /** - * Lexes an HTML string into tokens. - * @param $string String HTML. - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return HTMLPurifier_Token[] array representation of HTML. - */ - public function tokenizeHTML($string, $config, $context) - { - trigger_error('Call to abstract class', E_USER_ERROR); - } - - /** - * Translates CDATA sections into regular sections (through escaping). - * @param string $string HTML string to process. - * @return string HTML with CDATA sections escaped. - */ - protected static function escapeCDATA($string) - { - return preg_replace_callback( - '//s', - array('HTMLPurifier_Lexer', 'CDATACallback'), - $string - ); - } - - /** - * Special CDATA case that is especially convoluted for #i', '', $html); - } - - return $html; - } - - /** - * Takes a string of HTML (fragment or document) and returns the content - * @todo Consider making protected - */ - public function extractBody($html) - { - $matches = array(); - $result = preg_match('|(.*?)]*>(.*)|is', $html ?? '', $matches); - if ($result) { - // Make sure it's not in a comment - $comment_start = strrpos($matches[1], ''); - if ($comment_start === false || - ($comment_end !== false && $comment_end > $comment_start)) { - return $matches[2]; - } - } - return $html; - } -} - -// vim: et sw=4 sts=4 diff --git a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php b/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php deleted file mode 100644 index 3e718e20..00000000 --- a/overrides/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php +++ /dev/null @@ -1,338 +0,0 @@ -factory = new HTMLPurifier_TokenFactory(); - } - - /** - * @param string $html - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return HTMLPurifier_Token[] - */ - public function tokenizeHTML($html, $config, $context) - { - $html = $this->normalize($html, $config, $context); - - // attempt to armor stray angled brackets that cannot possibly - // form tags and thus are probably being used as emoticons - if ($config->get('Core.AggressivelyFixLt')) { - $char = '[^a-z!\/]'; - $comment = "/|\z)/is"; - $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html ?? ''); - do { - $old = $html; - $html = preg_replace("/<($char)/i", '<\\1', $html); - } while ($html !== $old); - $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments - } - - // preprocess html, essential for UTF-8 - $html = $this->wrapHTML($html, $config, $context); - - $doc = new DOMDocument(); - $doc->encoding = 'UTF-8'; // theoretically, the above has this covered - - $options = 0; - if ($config->get('Core.AllowParseManyTags') && defined('LIBXML_PARSEHUGE')) { - $options |= LIBXML_PARSEHUGE; - } - - set_error_handler(array($this, 'muteErrorHandler')); - // loadHTML() fails on PHP 5.3 when second parameter is given - if ($options) { - $doc->loadHTML($html, $options); - } else { - $doc->loadHTML($html); - } - restore_error_handler(); - - $body = $doc->getElementsByTagName('html')->item(0)-> // - getElementsByTagName('body')->item(0); // - - $div = $body->getElementsByTagName('div')->item(0); //

- $tokens = array(); - $this->tokenizeDOM($div, $tokens, $config); - // If the div has a sibling, that means we tripped across - // a premature
tag. So remove the div we parsed, - // and then tokenize the rest of body. We can't tokenize - // the sibling directly as we'll lose the tags in that case. - if ($div->nextSibling) { - $body->removeChild($div); - $this->tokenizeDOM($body, $tokens, $config); - } - return $tokens; - } - - /** - * Iterative function that tokenizes a node, putting it into an accumulator. - * To iterate is human, to recurse divine - L. Peter Deutsch - * @param DOMNode $node DOMNode to be tokenized. - * @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens. - * @return HTMLPurifier_Token of node appended to previously passed tokens. - */ - protected function tokenizeDOM($node, &$tokens, $config) - { - $level = 0; - $nodes = array($level => new HTMLPurifier_Queue(array($node))); - $closingNodes = array(); - do { - while (!$nodes[$level]->isEmpty()) { - $node = $nodes[$level]->shift(); // FIFO - $collect = $level > 0 ? true : false; - $needEndingTag = $this->createStartNode($node, $tokens, $collect, $config); - if ($needEndingTag) { - $closingNodes[$level][] = $node; - } - if ($node->childNodes && $node->childNodes->length) { - $level++; - $nodes[$level] = new HTMLPurifier_Queue(); - foreach ($node->childNodes as $childNode) { - $nodes[$level]->push($childNode); - } - } - } - $level--; - if ($level && isset($closingNodes[$level])) { - while ($node = array_pop($closingNodes[$level])) { - $this->createEndNode($node, $tokens); - } - } - } while ($level > 0); - } - - /** - * Portably retrieve the tag name of a node; deals with older versions - * of libxml like 2.7.6 - * @param DOMNode $node - */ - protected function getTagName($node) - { - if (isset($node->tagName)) { - return $node->tagName; - } else if (isset($node->nodeName)) { - return $node->nodeName; - } else if (isset($node->localName)) { - return $node->localName; - } - return null; - } - - /** - * Portably retrieve the data of a node; deals with older versions - * of libxml like 2.7.6 - * @param DOMNode $node - */ - protected function getData($node) - { - if (isset($node->data)) { - return $node->data; - } else if (isset($node->nodeValue)) { - return $node->nodeValue; - } else if (isset($node->textContent)) { - return $node->textContent; - } - return null; - } - - - /** - * @param DOMNode $node DOMNode to be tokenized. - * @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens. - * @param bool $collect Says whether or start and close are collected, set to - * false at first recursion because it's the implicit DIV - * tag you're dealing with. - * @return bool if the token needs an endtoken - * @todo data and tagName properties don't seem to exist in DOMNode? - */ - protected function createStartNode($node, &$tokens, $collect, $config) - { - // intercept non element nodes. WE MUST catch all of them, - // but we're not getting the character reference nodes because - // those should have been preprocessed - if ($node->nodeType === XML_TEXT_NODE) { - $data = $this->getData($node); // Handle variable data property - if ($data !== null) { - $tokens[] = $this->factory->createText($data); - } - return false; - } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) { - // undo libxml's special treatment of