diff --git a/phpgwapi/inc/htmlpurifier/INSTALL b/phpgwapi/inc/htmlpurifier/INSTALL index 2b4069e243..e64ccb0b7d 100755 --- a/phpgwapi/inc/htmlpurifier/INSTALL +++ b/phpgwapi/inc/htmlpurifier/INSTALL @@ -18,6 +18,7 @@ with these contents. HTML Purifier is PHP 5 only, and is actively tested from PHP 5.0.5 and up. It has no core dependencies with other libraries. PHP 4 support was deprecated on December 31, 2007 with HTML Purifier 3.0.0. +HTML Purifier is not compatible with zend.ze1_compatibility_mode. These optional extensions can enhance the capabilities of HTML Purifier: diff --git a/phpgwapi/inc/htmlpurifier/NEWS b/phpgwapi/inc/htmlpurifier/NEWS index 72f59b402f..8e7aef02be 100755 --- a/phpgwapi/inc/htmlpurifier/NEWS +++ b/phpgwapi/inc/htmlpurifier/NEWS @@ -9,6 +9,77 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier . Internal change ========================== +4.3.0, released 2011-03-27 +# Fixed broken caching of customized raw definitions, but requires an + API change. The old API still works but will emit a warning, + see http://htmlpurifier.org/docs/enduser-customize.html#optimized + for how to upgrade your code. +# Protect against Internet Explorer innerHTML behavior by specially + treating attributes with backticks but no angled brackets, quotes or + spaces. This constitutes a slight semantic change, which can be + reverted using %Output.FixInnerHTML. Reported by Neike Taika-Tessaro + and Mario Heiderich. +# Protect against cssText/innerHTML by restricting allowed characters + used in fonts further than mandated by the specification and encoding + some extra special characters in URLs. Reported by Neike + Taika-Tessaro and Mario Heiderich. +! Added %HTML.Nofollow to add rel="nofollow" to external links. +! More types of SPL autoloaders allowed on later versions of PHP. +! Implementations for position, top, left, right, bottom, z-index + when %CSS.Trusted is on. +! Add %Cache.SerializerPermissions option for custom serializer + directory/file permissions +! Fix longstanding bug in Flash support for non-IE browsers, and + allow more wmode attributes. +! Add %CSS.AllowedFonts to restrict permissible font names. +- Switch to an iterative traversal of the DOM, which prevents us + from running out of stack space for deeply nested documents. + Thanks Maxim Krizhanovsky for contributing a patch. +- Make removal of conditional IE comments ungreedy; thanks Bernd + for reporting. +- Escape CDATA before removing Internet Explorer comments. +- Fix removal of id attributes under certain conditions by ensuring + armor attributes are preserved when recreating tags. +- Check if schema.ser was corrupted. +- Check if zend.ze1_compatibility_mode is on, and error out if it is. + This safety check is only done for HTMLPurifier.auto.php; if you + are using standalone or the specialized includes files, you're + expected to know what you're doing. +- Stop repeatedly writing the cache file after I'm done customizing a + raw definition. Reported by ajh. +- Switch to using require_once in the Bootstrap to work around bad + interaction with Zend Debugger and APC. Reported by Antonio Parraga. +- Fix URI handling when hostname is missing but scheme is present. + Reported by Neike Taika-Tessaro. +- Fix missing numeric entities on DirectLex; thanks Neike Taika-Tessaro + for reporting. +- Fix harmless notice from indexing into empty string. Thanks Matthijs + Kooijman for reporting. +- Don't autoclose no parent elements are able to support the element + that triggered the autoclose. In particular fixes strange behavior + of stray
  • tags. Thanks pkuliga@gmail.com for reporting and + Neike Taika-Tessaro for debugging assistance. + +4.2.0, released 2010-09-15 +! Added %Core.RemoveProcessingInstructions, which lets you remove + statements. +! Added %URI.DisableResources functionality; the directive originally + did nothing. Thanks David Rothstein for reporting. +! Add documentation about configuration directive types. +! Add %CSS.ForbiddenProperties configuration directive. +! Add %HTML.FlashAllowFullScreen to permit embedded Flash objects + to utilize full-screen mode. +! Add optional support for the file URI scheme, enable + by explicitly setting %URI.AllowedSchemes. +! Add %Core.NormalizeNewlines options to allow turning off newline + normalization. +- Fix improper handling of Internet Explorer conditional comments + by parser. Thanks zmonteca for reporting. +- Fix missing attributes bug when running on Mac Snow Leopard and APC. + Thanks sidepodcast for the fix. +- Warn if an element is allowed, but an attribute it requires is + not allowed. + 4.1.1, released 2010-05-31 - Fix undefined index warnings in maintenance scripts. - Fix bug in DirectLex for parsing elements with a single attribute diff --git a/phpgwapi/inc/htmlpurifier/VERSION b/phpgwapi/inc/htmlpurifier/VERSION new file mode 100644 index 0000000000..8191138914 --- /dev/null +++ b/phpgwapi/inc/htmlpurifier/VERSION @@ -0,0 +1 @@ +4.3.0 \ No newline at end of file diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.autoload.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.autoload.php index 8d40176406..62da5b60d4 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.autoload.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.autoload.php @@ -3,6 +3,7 @@ /** * @file * Convenience file that registers autoload handler for HTML Purifier. + * It also does some sanity checks. */ if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) { @@ -18,4 +19,8 @@ if (function_exists('spl_autoload_register') && function_exists('spl_autoload_un } } +if (ini_get('zend.ze1_compatibility_mode')) { + trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR); +} + // vim: et sw=4 sts=4 diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.includes.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.includes.php index 2ed0f0c17f..b9baf8f0a2 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.includes.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.includes.php @@ -7,7 +7,7 @@ * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * FILE, changes will be overwritten the next time the script is run. * - * @version 4.1.1 + * @version 4.3.0 * * @warning * You must *not* include any other HTML Purifier files before this file, @@ -125,6 +125,7 @@ require 'HTMLPurifier/AttrTransform/Lang.php'; require 'HTMLPurifier/AttrTransform/Length.php'; require 'HTMLPurifier/AttrTransform/Name.php'; require 'HTMLPurifier/AttrTransform/NameSync.php'; +require 'HTMLPurifier/AttrTransform/Nofollow.php'; require 'HTMLPurifier/AttrTransform/SafeEmbed.php'; require 'HTMLPurifier/AttrTransform/SafeObject.php'; require 'HTMLPurifier/AttrTransform/SafeParam.php'; @@ -151,6 +152,7 @@ require 'HTMLPurifier/HTMLModule/Image.php'; require 'HTMLPurifier/HTMLModule/Legacy.php'; require 'HTMLPurifier/HTMLModule/List.php'; require 'HTMLPurifier/HTMLModule/Name.php'; +require 'HTMLPurifier/HTMLModule/Nofollow.php'; require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; require 'HTMLPurifier/HTMLModule/Object.php'; require 'HTMLPurifier/HTMLModule/Presentation.php'; @@ -196,10 +198,12 @@ require 'HTMLPurifier/Token/Start.php'; require 'HTMLPurifier/Token/Text.php'; require 'HTMLPurifier/URIFilter/DisableExternal.php'; require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; +require 'HTMLPurifier/URIFilter/DisableResources.php'; require 'HTMLPurifier/URIFilter/HostBlacklist.php'; require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; require 'HTMLPurifier/URIFilter/Munge.php'; require 'HTMLPurifier/URIScheme/data.php'; +require 'HTMLPurifier/URIScheme/file.php'; require 'HTMLPurifier/URIScheme/ftp.php'; require 'HTMLPurifier/URIScheme/http.php'; require 'HTMLPurifier/URIScheme/https.php'; diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.php index ba2c7b3067..914ba25ae6 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.php @@ -19,7 +19,7 @@ */ /* - HTML Purifier 4.1.1 - Standards Compliant HTML Filtering + HTML Purifier 4.3.0 - Standards Compliant HTML Filtering Copyright (C) 2006-2008 Edward Z. Yang This library is free software; you can redistribute it and/or @@ -55,10 +55,10 @@ class HTMLPurifier { /** Version of HTML Purifier */ - public $version = '4.1.1'; + public $version = '4.3.0'; /** Constant with version of HTML Purifier */ - const VERSION = '4.1.1'; + const VERSION = '4.3.0'; /** Global configuration object */ public $config; diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.safe-includes.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.safe-includes.php index 6402de0458..a5c0d5bb80 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.safe-includes.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier.safe-includes.php @@ -119,6 +119,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Nofollow.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; @@ -145,6 +146,7 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/List.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Nofollow.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php'; @@ -190,10 +192,12 @@ require_once $__dir . '/HTMLPurifier/Token/Start.php'; require_once $__dir . '/HTMLPurifier/Token/Text.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php'; require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; require_once $__dir . '/HTMLPurifier/URIScheme/data.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/file.php'; require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php index 42c2054c2a..0d9a4e12c2 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php @@ -2,11 +2,43 @@ /** * Validates a font family list according to CSS spec - * @todo whitelisting allowed fonts would be nice */ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef { + protected $mask = null; + + public function __construct() { + $this->mask = '- '; + for ($c = 'a'; $c <= 'z'; $c++) $this->mask .= $c; + for ($c = 'A'; $c <= 'Z'; $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. + } + public function validate($string, $config, $context) { static $generic_names = array( 'serif' => true, @@ -15,6 +47,7 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef 'fantasy' => true, 'cursive' => true ); + $allowed_fonts = $config->get('CSS.AllowedFonts'); // assume that no font names contain commas in them $fonts = explode(',', $string); @@ -24,7 +57,9 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef if ($font === '') continue; // match a generic name if (isset($generic_names[$font])) { - $final .= $font . ', '; + if ($allowed_fonts === null || isset($allowed_fonts[$font])) { + $final .= $font . ', '; + } continue; } // match a quoted name @@ -40,6 +75,10 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef // $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 . ', '; @@ -50,17 +89,103 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef // shouldn't show up regardless $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); - // These ugly transforms don't pose a security - // risk (as \\ and \" might). 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. - // (warning: this code relies on the selection of quotation - // mark below) - $font = str_replace('\\', '\\5C ', $font); - $font = str_replace('"', '\\22 ', $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. - // complicated font, requires quoting - $final .= "\"$font\", "; // note that this will later get turned into " + // Edgy: alphanumeric, spaces, dashes 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; diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php index 1df17dc25b..c2f767e573 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php @@ -43,6 +43,15 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI // extra sanity check; should have been done by URI $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result); + // suspicious characters are ()'; we're going to percent encode + // them for safety. + $result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result); + + // there's an extra bug where ampersands lose their escaping on + // an innerHTML cycle, so a very unlucky query parameter could + // then change the meaning of the URL. Unfortunately, there's + // not much we can do about that... + return "url(\"$result\")"; } diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php index 2156c10c66..feca469d70 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php @@ -23,6 +23,12 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef 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 diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php index 3f992ec31b..bd86a74554 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php @@ -19,6 +19,7 @@ class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform public function __construct() { $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded + $this->wmode = new HTMLPurifier_AttrDef_Enum(array('window', 'opaque', 'transparent')); } public function transform($attr, $config, $context) { @@ -33,8 +34,15 @@ class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform case 'allowNetworking': $attr['value'] = 'internal'; break; + case 'allowFullScreen': + if ($config->get('HTML.FlashAllowFullScreen')) { + $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false'; + } else { + $attr['value'] = 'false'; + } + break; case 'wmode': - $attr['value'] = 'window'; + $attr['value'] = $this->wmode->validate($attr['value'], $config, $context); break; case 'movie': case 'src': diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Bootstrap.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Bootstrap.php index 559f61a232..607c5b1880 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Bootstrap.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Bootstrap.php @@ -37,7 +37,12 @@ class HTMLPurifier_Bootstrap public static function autoload($class) { $file = HTMLPurifier_Bootstrap::getPath($class); if (!$file) return false; - require HTMLPURIFIER_PREFIX . '/' . $file; + // Technically speaking, it should be ok and more efficient to + // just do 'require', but Antonio Parraga reports that with + // Zend extensions such as Zend debugger and APC, this invariant + // may be broken. Since we have efficient alternatives, pay + // the cost here and avoid the bug. + require_once HTMLPURIFIER_PREFIX . '/' . $file; return true; } @@ -65,10 +70,11 @@ class HTMLPurifier_Bootstrap if ( ($funcs = spl_autoload_functions()) === false ) { spl_autoload_register($autoload); } elseif (function_exists('spl_autoload_unregister')) { + $buggy = version_compare(PHP_VERSION, '5.2.11', '<'); $compat = version_compare(PHP_VERSION, '5.1.2', '<=') && version_compare(PHP_VERSION, '5.1.0', '>='); foreach ($funcs as $func) { - if (is_array($func)) { + if ($buggy && is_array($func)) { // :TRICKY: There are some compatibility issues and some // places where we need to error out $reflector = new ReflectionMethod($func[0], $func[1]); diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/CSSDefinition.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/CSSDefinition.php index 6a2e6f56d9..91619f5d38 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/CSSDefinition.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/CSSDefinition.php @@ -219,6 +219,10 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition $this->doSetupTricky($config); } + if ($config->get('CSS.Trusted')) { + $this->doSetupTrusted($config); + } + $allow_important = $config->get('CSS.AllowImportant'); // wrap all attr-defs with decorator that handles !important foreach ($this->info as $k => $v) { @@ -260,6 +264,23 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); } + protected function doSetupTrusted($config) { + $this->info['position'] = new HTMLPurifier_AttrDef_Enum(array( + 'static', 'relative', 'absolute', 'fixed' + )); + $this->info['top'] = + $this->info['left'] = + $this->info['right'] = + $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_Enum(array('auto')), + )); + $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Integer(), + new HTMLPurifier_AttrDef_Enum(array('auto')), + )); + } /** * Performs extra config-based processing. Based off of @@ -272,20 +293,29 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition // setup allowed elements $support = "(for information on implementing this, see the ". "support forums) "; - $allowed_attributes = $config->get('CSS.AllowedProperties'); - if ($allowed_attributes !== null) { + $allowed_properties = $config->get('CSS.AllowedProperties'); + if ($allowed_properties !== null) { foreach ($this->info as $name => $d) { - if(!isset($allowed_attributes[$name])) unset($this->info[$name]); - unset($allowed_attributes[$name]); + if(!isset($allowed_properties[$name])) unset($this->info[$name]); + unset($allowed_properties[$name]); } // emit errors - foreach ($allowed_attributes as $name => $d) { + foreach ($allowed_properties as $name => $d) { // :TODO: Is this htmlspecialchars() call really necessary? $name = htmlspecialchars($name); trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); } } + $forbidden_properties = $config->get('CSS.ForbiddenProperties'); + if ($forbidden_properties !== null) { + foreach ($this->info as $name => $d) { + if (isset($forbidden_properties[$name])) { + unset($this->info[$name]); + } + } + } + } } diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Config.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Config.php index 2a334b0d83..b6551398f8 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Config.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Config.php @@ -20,7 +20,7 @@ class HTMLPurifier_Config /** * HTML Purifier's version */ - public $version = '4.1.1'; + public $version = '4.3.0'; /** * Bool indicator whether or not to automatically finalize @@ -76,7 +76,8 @@ class HTMLPurifier_Config /** * Set to false if you do not want line and file numbers in errors - * (useful when unit testing) + * (useful when unit testing). This will also compress some errors + * and exceptions. */ public $chatty = true; @@ -318,26 +319,64 @@ class HTMLPurifier_Config * Retrieves object reference to the HTML definition. * @param $raw Return a copy that has not been setup yet. Must be * called before it's been setup, otherwise won't work. + * @param $optimized If true, this method may return null, to + * indicate that a cached version of the modified + * definition object is available and no further edits + * are necessary. Consider using + * maybeGetRawHTMLDefinition, which is more explicitly + * named, instead. */ - public function getHTMLDefinition($raw = false) { - return $this->getDefinition('HTML', $raw); + public function getHTMLDefinition($raw = false, $optimized = false) { + return $this->getDefinition('HTML', $raw, $optimized); } /** * Retrieves object reference to the CSS definition * @param $raw Return a copy that has not been setup yet. Must be * called before it's been setup, otherwise won't work. + * @param $optimized If true, this method may return null, to + * indicate that a cached version of the modified + * definition object is available and no further edits + * are necessary. Consider using + * maybeGetRawCSSDefinition, which is more explicitly + * named, instead. */ - public function getCSSDefinition($raw = false) { - return $this->getDefinition('CSS', $raw); + public function getCSSDefinition($raw = false, $optimized = false) { + return $this->getDefinition('CSS', $raw, $optimized); + } + + /** + * Retrieves object reference to the URI definition + * @param $raw Return a copy that has not been setup yet. Must be + * called before it's been setup, otherwise won't work. + * @param $optimized If true, this method may return null, to + * indicate that a cached version of the modified + * definition object is available and no further edits + * are necessary. Consider using + * maybeGetRawURIDefinition, which is more explicitly + * named, instead. + */ + public function getURIDefinition($raw = false, $optimized = false) { + return $this->getDefinition('URI', $raw, $optimized); } /** * Retrieves a definition * @param $type Type of definition: HTML, CSS, etc * @param $raw Whether or not definition should be returned raw + * @param $optimized Only has an effect when $raw is true. Whether + * or not to return null if the result is already present in + * the cache. This is off by default for backwards + * compatibility reasons, but you need to do things this + * way in order to ensure that caching is done properly. + * Check out enduser-customize.html for more details. + * We probably won't ever change this default, as much as the + * maybe semantics is the "right thing to do." */ - public function getDefinition($type, $raw = false) { + public function getDefinition($type, $raw = false, $optimized = false) { + if ($optimized && !$raw) { + throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false"); + } if (!$this->finalized) $this->autoFinalize(); // temporarily suspend locks, so we can handle recursive definition calls $lock = $this->lock; @@ -346,52 +385,137 @@ class HTMLPurifier_Config $cache = $factory->create($type, $this); $this->lock = $lock; if (!$raw) { - // see if we can quickly supply a definition + // full definition + // --------------- + // check if definition is in memory if (!empty($this->definitions[$type])) { - if (!$this->definitions[$type]->setup) { - $this->definitions[$type]->setup($this); - $cache->set($this->definitions[$type], $this); + $def = $this->definitions[$type]; + // check if the definition is setup + if ($def->setup) { + return $def; + } else { + $def->setup($this); + if ($def->optimized) $cache->add($def, $this); + return $def; } - return $this->definitions[$type]; } - // memory check missed, try cache - $this->definitions[$type] = $cache->get($this); - if ($this->definitions[$type]) { - // definition in cache, return it - return $this->definitions[$type]; + // check if definition is in cache + $def = $cache->get($this); + if ($def) { + // definition in cache, save to memory and return it + $this->definitions[$type] = $def; + return $def; } - } elseif ( - !empty($this->definitions[$type]) && - !$this->definitions[$type]->setup - ) { - // raw requested, raw in memory, quick return - return $this->definitions[$type]; + // initialize it + $def = $this->initDefinition($type); + // set it up + $this->lock = $type; + $def->setup($this); + $this->lock = null; + // save in cache + $cache->add($def, $this); + // return it + return $def; + } else { + // raw definition + // -------------- + // check preconditions + $def = null; + if ($optimized) { + if (is_null($this->get($type . '.DefinitionID'))) { + // fatally error out if definition ID not set + throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID"); + } + } + if (!empty($this->definitions[$type])) { + $def = $this->definitions[$type]; + if ($def->setup && !$optimized) { + $extra = $this->chatty ? " (try moving this code block earlier in your initialization)" : ""; + throw new HTMLPurifier_Exception("Cannot retrieve raw definition after it has already been setup" . $extra); + } + if ($def->optimized === null) { + $extra = $this->chatty ? " (try flushing your cache)" : ""; + throw new HTMLPurifier_Exception("Optimization status of definition is unknown" . $extra); + } + if ($def->optimized !== $optimized) { + $msg = $optimized ? "optimized" : "unoptimized"; + $extra = $this->chatty ? " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)" : ""; + throw new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra); + } + } + // check if definition was in memory + if ($def) { + if ($def->setup) { + // invariant: $optimized === true (checked above) + return null; + } else { + return $def; + } + } + // if optimized, check if definition was in cache + // (because we do the memory check first, this formulation + // is prone to cache slamming, but I think + // guaranteeing that either /all/ of the raw + // setup code or /none/ of it is run is more important.) + if ($optimized) { + // This code path only gets run once; once we put + // something in $definitions (which is guaranteed by the + // trailing code), we always short-circuit above. + $def = $cache->get($this); + if ($def) { + // save the full definition for later, but don't + // return it yet + $this->definitions[$type] = $def; + return null; + } + } + // check invariants for creation + if (!$optimized) { + if (!is_null($this->get($type . '.DefinitionID'))) { + if ($this->chatty) { + $this->triggerError("Due to a documentation error in previous version of HTML Purifier, your definitions are not being cached. If this is OK, you can remove the %$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, modify your code to use maybeGetRawDefinition, and test if the returned value is null before making any edits (if it is null, that means that a cached version is available, and no raw operations are necessary). See Customize for more details", E_USER_WARNING); + } else { + $this->triggerError("Useless DefinitionID declaration", E_USER_WARNING); + } + } + } + // initialize it + $def = $this->initDefinition($type); + $def->optimized = $optimized; + return $def; } + throw new HTMLPurifier_Exception("The impossible happened!"); + } + + private function initDefinition($type) { // quick checks failed, let's create the object if ($type == 'HTML') { - $this->definitions[$type] = new HTMLPurifier_HTMLDefinition(); + $def = new HTMLPurifier_HTMLDefinition(); } elseif ($type == 'CSS') { - $this->definitions[$type] = new HTMLPurifier_CSSDefinition(); + $def = new HTMLPurifier_CSSDefinition(); } elseif ($type == 'URI') { - $this->definitions[$type] = new HTMLPurifier_URIDefinition(); + $def = new HTMLPurifier_URIDefinition(); } else { throw new HTMLPurifier_Exception("Definition of $type type not supported"); } - // quick abort if raw - if ($raw) { - if (is_null($this->get($type . '.DefinitionID'))) { - // fatally error out if definition ID not set - throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID"); - } - return $this->definitions[$type]; - } - // set it up - $this->lock = $type; - $this->definitions[$type]->setup($this); - $this->lock = null; - // save in cache - $cache->set($this->definitions[$type], $this); - return $this->definitions[$type]; + $this->definitions[$type] = $def; + return $def; + } + + public function maybeGetRawDefinition($name) { + return $this->getDefinition($name, true, true); + } + + public function maybeGetRawHTMLDefinition() { + return $this->getDefinition('HTML', true, true); + } + + public function maybeGetRawCSSDefinition() { + return $this->getDefinition('CSS', true, true); + } + + public function maybeGetRawURIDefinition() { + return $this->getDefinition('URI', true, true); } /** @@ -549,17 +673,22 @@ class HTMLPurifier_Config /** * Produces a nicely formatted error message by supplying the - * stack frame information from two levels up and OUTSIDE of - * HTMLPurifier_Config. + * stack frame information OUTSIDE of HTMLPurifier_Config. */ protected function triggerError($msg, $no) { // determine previous stack frame - $backtrace = debug_backtrace(); - if ($this->chatty && isset($backtrace[1])) { - $frame = $backtrace[1]; - $extra = " on line {$frame['line']} in file {$frame['file']}"; - } else { - $extra = ''; + $extra = ''; + if ($this->chatty) { + $trace = debug_backtrace(); + // zip(tail(trace), trace) -- but PHP is not Haskell har har + for ($i = 0, $c = count($trace); $i < $c - 1; $i++) { + if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') { + continue; + } + $frame = $trace[$i]; + $extra = " invoked on line {$frame['line']} in file {$frame['file']}"; + break; + } } trigger_error($msg . $extra, $no); } diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema.php index 67be5c71fd..fadf7a5890 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema.php @@ -60,7 +60,13 @@ class HTMLPurifier_ConfigSchema { * Unserializes the default ConfigSchema. */ public static function makeFromSerial() { - return unserialize(file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser')); + $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser'); + $r = unserialize($contents); + if (!$r) { + $hash = sha1($contents); + trigger_error("Unserialization of configuration schema failed, sha1 of file was $hash", E_USER_ERROR); + } + return $r; } /** diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser index 22b8d54a59..245ba5d2d0 100755 Binary files a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser and b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser differ diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt index 7fa6536b2c..321eaa2d80 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -3,6 +3,11 @@ TYPE: bool VERSION: 3.1.0 DEFAULT: false --DESCRIPTION-- +

    + Warning: Deprecated in favor of %HTML.SafeObject and + %Output.FlashCompat (turn both on to allow YouTube videos and other + Flash content). +

    This directive enables YouTube video embedding in HTML Purifier. Check this document diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt index 3e231d2d16..0b2c106da5 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -5,11 +5,14 @@ DEFAULT: NULL --DESCRIPTION--

    - This is a convenience directive that rolls the functionality of - %HTML.AllowedElements and %HTML.AllowedAttributes into one directive. + This is a preferred convenience directive that combines + %HTML.AllowedElements and %HTML.AllowedAttributes. Specify elements and attributes that are allowed using: - element1[attr1|attr2],element2.... You can also use - newlines instead of commas to separate elements. + element1[attr1|attr2],element2.... For example, + if you would like to only allow paragraphs and links, specify + a[href],p. You can specify attributes that apply + to all elements using an asterisk, e.g. *[lang]. + You can also use newlines instead of commas to separate elements.

    Warning: diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt index 888d558196..1d3fa7907d 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -4,12 +4,17 @@ VERSION: 1.3.0 DEFAULT: NULL --DESCRIPTION--

    - If HTML Purifier's tag set is unsatisfactory for your needs, you - can overload it with your own list of tags to allow. Note that this - method is subtractive: it does its job by taking away from HTML Purifier - usual feature set, so you cannot add a tag that HTML Purifier never - supported in the first place (like embed, form or head). If you - change this, you probably also want to change %HTML.AllowedAttributes. + If HTML Purifier's tag set is unsatisfactory for your needs, you can + overload it with your own list of tags to allow. If you change + this, you probably also want to change %HTML.AllowedAttributes; see + also %HTML.Allowed which lets you set allowed elements and + attributes at the same time. +

    +

    + If you attempt to allow an element that HTML Purifier does not know + about, HTML Purifier will raise an error. You will need to manually + tell HTML Purifier about this element by using the + advanced customization features.

    Warning: If another directive conflicts with the diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt index 89133b1a38..1db9237e9e 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt @@ -5,4 +5,5 @@ DEFAULT: false --DESCRIPTION-- Indicates whether or not the user input is trusted or not. If the input is trusted, a more expansive set of allowed tags and attributes will be used. +See also %CSS.Trusted. --# vim: et sw=4 sts=4 diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt index ae3a913f24..666635a5ff 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -12,6 +12,6 @@ array ( --DESCRIPTION-- Whitelist that defines the schemes that a URI is allowed to have. This prevents XSS attacks from using pseudo-schemes like javascript or mocha. -There is also support for the data URI scheme, but it is not -enabled by default. +There is also support for the data and file +URI schemes, but they are not enabled by default. --# vim: et sw=4 sts=4 diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt index 51e6ea91f7..f891de4996 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -1,12 +1,15 @@ URI.DisableResources TYPE: bool -VERSION: 1.3.0 +VERSION: 4.2.0 DEFAULT: false --DESCRIPTION-- -

    Disables embedding resources, essentially meaning no pictures. You can still link to them though. See %URI.DisableExternalResources for why this might be a good idea.

    +

    + Note: While this directive has been available since 1.3.0, + it didn't actually start doing anything until 4.2.0. +

    --# vim: et sw=4 sts=4 diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Definition.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Definition.php index a7408c9749..c7f82eba43 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Definition.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Definition.php @@ -12,6 +12,17 @@ abstract class HTMLPurifier_Definition */ public $setup = false; + /** + * If true, write out the final definition object to the cache after + * setup. This will be true only if all invocations to get a raw + * definition object are also optimized. This does not cause file + * system thrashing because on subsequent calls the cached object + * is used and any writes to the raw definition object are short + * circuited. See enduser-customize.html for the high-level + * picture. + */ + public $optimized = null; + /** * What type of definition is it? */ diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php index 7a6aa93f02..73d5e90a60 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php @@ -9,14 +9,14 @@ class HTMLPurifier_DefinitionCache_Serializer extends $file = $this->generateFilePath($config); if (file_exists($file)) return false; if (!$this->_prepareDir($config)) return false; - return $this->_write($file, serialize($def)); + return $this->_write($file, serialize($def), $config); } public function set($def, $config) { if (!$this->checkDefType($def)) return; $file = $this->generateFilePath($config); if (!$this->_prepareDir($config)) return false; - return $this->_write($file, serialize($def)); + return $this->_write($file, serialize($def), $config); } public function replace($def, $config) { @@ -24,7 +24,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends $file = $this->generateFilePath($config); if (!file_exists($file)) return false; if (!$this->_prepareDir($config)) return false; - return $this->_write($file, serialize($def)); + return $this->_write($file, serialize($def), $config); } public function get($config) { @@ -97,18 +97,34 @@ class HTMLPurifier_DefinitionCache_Serializer extends * Convenience wrapper function for file_put_contents * @param $file File name to write to * @param $data Data to write into file + * @param $config Config object * @return Number of bytes written if success, or false if failure. */ - private function _write($file, $data) { - return file_put_contents($file, $data); + private function _write($file, $data, $config) { + $result = file_put_contents($file, $data); + if ($result !== false) { + // set permissions of the new file (no execute) + $chmod = $config->get('Cache.SerializerPermissions'); + if (!$chmod) { + $chmod = 0644; // invalid config or simpletest + } + $chmod = $chmod & 0666; + chmod($file, $chmod); + } + return $result; } /** * Prepares the directory that this type stores the serials in + * @param $config Config object * @return True if successful */ private function _prepareDir($config) { $directory = $this->generateDirectoryPath($config); + $chmod = $config->get('Cache.SerializerPermissions'); + if (!$chmod) { + $chmod = 0755; // invalid config or simpletest + } if (!is_dir($directory)) { $base = $this->generateBaseDirectoryPath($config); if (!is_dir($base)) { @@ -116,13 +132,13 @@ class HTMLPurifier_DefinitionCache_Serializer extends please create or change using %Cache.SerializerPath', E_USER_WARNING); return false; - } elseif (!$this->_testPermissions($base)) { + } elseif (!$this->_testPermissions($base, $chmod)) { return false; } - $old = umask(0022); // disable group and world writes - mkdir($directory); + $old = umask(0000); + mkdir($directory, $chmod); umask($old); - } elseif (!$this->_testPermissions($directory)) { + } elseif (!$this->_testPermissions($directory, $chmod)) { return false; } return true; @@ -131,8 +147,11 @@ class HTMLPurifier_DefinitionCache_Serializer extends /** * Tests permissions on a directory and throws out friendly * error messages and attempts to chmod it itself if possible + * @param $dir Directory path + * @param $chmod Permissions + * @return True if directory writable */ - private function _testPermissions($dir) { + private function _testPermissions($dir, $chmod) { // early abort, if it is writable, everything is hunky-dory if (is_writable($dir)) return true; if (!is_dir($dir)) { @@ -146,17 +165,17 @@ class HTMLPurifier_DefinitionCache_Serializer extends // POSIX system, we can give more specific advice if (fileowner($dir) === posix_getuid()) { // we can chmod it ourselves - chmod($dir, 0755); - return true; + $chmod = $chmod | 0700; + if (chmod($dir, $chmod)) return true; } elseif (filegroup($dir) === posix_getgid()) { - $chmod = '775'; + $chmod = $chmod | 0070; } else { // PHP's probably running as nobody, so we'll // need to give global permissions - $chmod = '777'; + $chmod = $chmod | 0777; } trigger_error('Directory '.$dir.' not writable, '. - 'please chmod to ' . $chmod, + 'please chmod to ' . decoct($chmod), E_USER_WARNING); } else { // generic error message diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser index f2b8b8f2db..e8b08128be 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser @@ -1 +1 @@ -a:246:{s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";} \ No newline at end of file +a:253:{s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:6:"there4";s:3:"∴";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:4:"sup2";s:2:"²";s:4:"sup3";s:2:"³";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"sup1";s:2:"¹";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"frac14";s:2:"¼";s:6:"frac12";s:2:"½";s:6:"frac34";s:2:"¾";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";} \ No newline at end of file diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Generator.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Generator.php index 4a62417271..fee1a5f84e 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Generator.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Generator.php @@ -36,6 +36,11 @@ class HTMLPurifier_Generator */ private $_flashCompat; + /** + * Cache of %Output.FixInnerHTML + */ + private $_innerHTMLFix; + /** * Stack for keeping track of object information when outputting IE * compatibility code. @@ -54,6 +59,7 @@ class HTMLPurifier_Generator public function __construct($config, $context) { $this->config = $config; $this->_scriptFix = $config->get('Output.CommentScriptContents'); + $this->_innerHTMLFix = $config->get('Output.FixInnerHTML'); $this->_sortAttr = $config->get('Output.SortAttr'); $this->_flashCompat = $config->get('Output.FlashCompat'); $this->_def = $config->getHTMLDefinition(); @@ -98,9 +104,11 @@ class HTMLPurifier_Generator } // Normalize newlines to system defined value - $nl = $this->config->get('Output.Newline'); - if ($nl === null) $nl = PHP_EOL; - if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + if ($this->config->get('Core.NormalizeNewlines')) { + $nl = $this->config->get('Output.Newline'); + if ($nl === null) $nl = PHP_EOL; + if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + } return $html; } @@ -130,19 +138,7 @@ class HTMLPurifier_Generator $_extra = ''; if ($this->_flashCompat) { if ($token->name == "object" && !empty($this->_flashStack)) { - $flash = array_pop($this->_flashStack); - $compat_token = new HTMLPurifier_Token_Empty("embed"); - foreach ($flash->attr as $name => $val) { - if ($name == "classid") continue; - if ($name == "type") continue; - if ($name == "data") $name = "src"; - $compat_token->attr[$name] = $val; - } - foreach ($flash->param as $name => $val) { - if ($name == "movie") $name = "src"; - $compat_token->attr[$name] = $val; - } - $_extra = ""; + // doesn't do anything for now } } return $_extra . 'name . '>'; @@ -200,6 +196,37 @@ class HTMLPurifier_Generator continue; } } + // Workaround for Internet Explorer innerHTML bug. + // Essentially, Internet Explorer, when calculating + // innerHTML, omits quotes if there are no instances of + // angled brackets, quotes or spaces. However, when parsing + // HTML (for example, when you assign to innerHTML), it + // treats backticks as quotes. Thus, + // `` + // becomes + // `` + // becomes + // + // Fortunately, all we need to do is trigger an appropriate + // quoting style, which we do by adding an extra space. + // This also is consistent with the W3C spec, which states + // that user agents may ignore leading or trailing + // whitespace (in fact, most don't, at least for attributes + // like alt, but an extra space at the end is barely + // noticeable). Still, we have a configuration knob for + // this, since this transformation is not necesary if you + // don't process user input with innerHTML or you don't plan + // on supporting Internet Explorer. + if ($this->_innerHTMLFix) { + if (strpos($value, '`') !== false) { + // check if correct quoting style would not already be + // triggered + if (strcspn($value, '"\' <>') === strlen($value)) { + // protect! + $value .= ' '; + } + } + } $html .= $key.'="'.$this->escape($value).'" '; } return rtrim($html); @@ -215,7 +242,10 @@ class HTMLPurifier_Generator * permissible for non-attribute output. * @return String escaped data. */ - public function escape($string, $quote = ENT_COMPAT) { + public function escape($string, $quote = null) { + // Workaround for APC bug on Mac Leopard reported by sidepodcast + // http://htmlpurifier.org/phorum/read.php?3,4823,4846 + if ($quote === null) $quote = ENT_COMPAT; return htmlspecialchars($string, $quote, 'UTF-8'); } diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php index c99ac11eb2..33bb38ac5f 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php @@ -300,7 +300,12 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition unset($allowed_attributes_mutable[$key]); } } - if ($delete) unset($this->info[$tag]->attr[$attr]); + if ($delete) { + if ($this->info[$tag]->attr[$attr]->required) { + trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING); + } + unset($this->info[$tag]->attr[$attr]); + } } } // emit errors diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php index ea256716bb..9f3758a322 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -21,7 +21,7 @@ class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule 'allowscriptaccess' => 'Enum#never', 'allownetworking' => 'Enum#internal', 'flashvars' => 'Text', - 'wmode' => 'Enum#window', + 'wmode' => 'Enum#window,transparent,opaque', 'name' => 'ID', ) ); diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php index 64ab8c0703..00da342ef8 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php @@ -29,7 +29,6 @@ class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule 'width' => 'Pixels#' . $max, 'height' => 'Pixels#' . $max, 'data' => 'URI#embedded', - 'classid' => 'Enum#clsid:d27cdb6e-ae6d-11cf-96b8-444553540000', 'codebase' => new HTMLPurifier_AttrDef_Enum(array( 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0')), ) diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php index f5c4a1d2cb..362e3b78db 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php @@ -216,19 +216,19 @@ class HTMLPurifier_HTMLModuleManager } } - // add proprietary module (this gets special treatment because - // it is completely removed from doctypes, etc.) + // custom modules if ($config->get('HTML.Proprietary')) { $modules[] = 'Proprietary'; } - - // add SafeObject/Safeembed modules if ($config->get('HTML.SafeObject')) { $modules[] = 'SafeObject'; } if ($config->get('HTML.SafeEmbed')) { $modules[] = 'SafeEmbed'; } + if ($config->get('HTML.Nofollow')) { + $modules[] = 'Nofollow'; + } // merge in custom modules $modules = array_merge($modules, $this->userModules); diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php index 9e178ce01a..c1d8b04128 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php @@ -22,6 +22,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector 'movie' => true, 'flashvars' => true, 'src' => true, + 'allowFullScreen' => true, // if omitted, assume to be 'false' ); public function prepare($config, $context) { diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer.php index b05e115468..9bdbbbb25b 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer.php @@ -230,6 +230,17 @@ class HTMLPurifier_Lexer ); } + /** + * Special Internet Explorer conditional comments should be removed. + */ + protected static function removeIEConditional($string) { + return preg_replace( + '##si', // probably should generalize for all strings + '', + $string + ); + } + /** * Callback function for escapeCDATA() that does the work. * @@ -252,8 +263,10 @@ class HTMLPurifier_Lexer public function normalize($html, $config, $context) { // normalize newlines to \n - $html = str_replace("\r\n", "\n", $html); - $html = str_replace("\r", "\n", $html); + if ($config->get('Core.NormalizeNewlines')) { + $html = str_replace("\r\n", "\n", $html); + $html = str_replace("\r", "\n", $html); + } if ($config->get('HTML.Trusted')) { // escape convoluted CDATA @@ -263,6 +276,8 @@ class HTMLPurifier_Lexer // escape CDATA $html = $this->escapeCDATA($html); + $html = $this->removeIEConditional($html); + // extract body from document if applicable if ($config->get('Core.ConvertDocumentToFragment')) { $e = false; @@ -284,6 +299,11 @@ class HTMLPurifier_Lexer // represent non-SGML characters (horror, horror!) $html = HTMLPurifier_Encoder::cleanUTF8($html); + // if processing instructions are to removed, remove them now + if ($config->get('Core.RemoveProcessingInstructions')) { + $html = preg_replace('#<\?.+?\?>#s', '', $html); + } + return $html; } diff --git a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php index 20dc2ed48c..82f377450b 100755 --- a/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php +++ b/phpgwapi/inc/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php @@ -72,23 +72,57 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer } /** - * Recursive function that tokenizes a node, putting it into an accumulator. - * + * Iterative function that tokenizes a node, putting it into an accumulator. + * To iterate is human, to recurse divine - L. Peter Deutsch * @param $node DOMNode to be tokenized. * @param $tokens Array-list of already tokenized tokens. - * @param $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. * @returns Tokens of node appended to previously passed tokens. */ - protected function tokenizeDOM($node, &$tokens, $collect = false) { + protected function tokenizeDOM($node, &$tokens) { + $level = 0; + $nodes = array($level => array($node)); + $closingNodes = array(); + do { + while (!empty($nodes[$level])) { + $node = array_shift($nodes[$level]); // FIFO + $collect = $level > 0 ? true : false; + $needEndingTag = $this->createStartNode($node, $tokens, $collect); + if ($needEndingTag) { + $closingNodes[$level][] = $node; + } + if ($node->childNodes && $node->childNodes->length) { + $level++; + $nodes[$level] = array(); + foreach ($node->childNodes as $childNode) { + array_push($nodes[$level], $childNode); + } + } + } + $level--; + if ($level && isset($closingNodes[$level])) { + while($node = array_pop($closingNodes[$level])) { + $this->createEndNode($node, $tokens); + } + } + } while ($level > 0); + } + + /** + * @param $node DOMNode to be tokenized. + * @param $tokens Array-list of already tokenized tokens. + * @param $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. + * @returns bool if the token needs an endtoken + */ + protected function createStartNode($node, &$tokens, $collect) { // 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) { $tokens[] = $this->factory->createText($node->data); - return; + return false; } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) { // undo libxml's special treatment of