]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-16668 import htmlpurifier 3.1.1 to HEAD
authorskodak <skodak>
Wed, 24 Sep 2008 21:32:10 +0000 (21:32 +0000)
committerskodak <skodak>
Wed, 24 Sep 2008 21:32:10 +0000 (21:32 +0000)
70 files changed:
lib/htmlpurifier/HTMLPurifier.php
lib/htmlpurifier/HTMLPurifier.safe-includes.php
lib/htmlpurifier/HTMLPurifier/AttrDef.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Length.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Number.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Pixels.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Switch.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/AttrDef/URI.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeEmbed.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeObject.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeParam.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/AttrValidator.php
lib/htmlpurifier/HTMLPurifier/CSSDefinition.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Required.php
lib/htmlpurifier/HTMLPurifier/Config.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Validator.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema.ser
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/Encoder.php
lib/htmlpurifier/HTMLPurifier/Generator.php
lib/htmlpurifier/HTMLPurifier/HTMLDefinition.php
lib/htmlpurifier/HTMLPurifier/HTMLModule.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Bdo.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Edit.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Hypertext.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Image.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Legacy.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/List.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Object.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Presentation.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Proprietary.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Ruby.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeEmbed.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeObject.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/HTMLModule/Scripting.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/StyleAttribute.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tables.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Target.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Text.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy.php
lib/htmlpurifier/HTMLPurifier/HTMLModuleManager.php
lib/htmlpurifier/HTMLPurifier/Injector.php
lib/htmlpurifier/HTMLPurifier/Injector/SafeObject.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/Length.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/Printer.php
lib/htmlpurifier/HTMLPurifier/Printer/ConfigForm.php
lib/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php
lib/htmlpurifier/HTMLPurifier/Strategy/RemoveForeignElements.php
lib/htmlpurifier/HTMLPurifier/Token.php
lib/htmlpurifier/HTMLPurifier/URI.php
lib/htmlpurifier/HTMLPurifier/URIDefinition.php
lib/htmlpurifier/HTMLPurifier/URIFilter.php
lib/htmlpurifier/HTMLPurifier/URIFilter/HostBlacklist.php
lib/htmlpurifier/HTMLPurifier/URIFilter/MakeAbsolute.php
lib/htmlpurifier/HTMLPurifier/URIFilter/Munge.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/UnitConverter.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/VarParser.php
lib/htmlpurifier/HTMLPurifier/VarParser/Flexible.php
lib/htmlpurifier/readme_moodle.txt

index 03709f1268b845fb025e5211970261cda91cb02b..da2269dab48cfab6e6fa1a3698d9a9cbd43ac61c 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 /*
-    HTML Purifier 3.1.0 - Standards Compliant HTML Filtering
+    HTML Purifier 3.1.1 - 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 = '3.1.0';
+    public $version = '3.1.1';
     
     /** Constant with version of HTML Purifier */
-    const VERSION = '3.1.0';
+    const VERSION = '3.1.1';
     
     /** Global configuration object */
     public $config;
index 4273da369ed72097efd5553db8fa44699b0b3659..42acd76bbb31d409fd33574fa6e7cfa442cec0cc 100644 (file)
@@ -23,7 +23,6 @@ require_once $__dir . '/HTMLPurifier/Definition.php';
 require_once $__dir . '/HTMLPurifier/CSSDefinition.php';\r
 require_once $__dir . '/HTMLPurifier/ChildDef.php';\r
 require_once $__dir . '/HTMLPurifier/Config.php';\r
-require_once $__dir . '/HTMLPurifier/ConfigDef.php';\r
 require_once $__dir . '/HTMLPurifier/ConfigSchema.php';\r
 require_once $__dir . '/HTMLPurifier/ContentSets.php';\r
 require_once $__dir . '/HTMLPurifier/Context.php';\r
@@ -46,6 +45,7 @@ require_once $__dir . '/HTMLPurifier/IDAccumulator.php';
 require_once $__dir . '/HTMLPurifier/Injector.php';\r
 require_once $__dir . '/HTMLPurifier/Language.php';\r
 require_once $__dir . '/HTMLPurifier/LanguageFactory.php';\r
+require_once $__dir . '/HTMLPurifier/Length.php';\r
 require_once $__dir . '/HTMLPurifier/Lexer.php';\r
 require_once $__dir . '/HTMLPurifier/PercentEncoder.php';\r
 require_once $__dir . '/HTMLPurifier/Strategy.php';\r
@@ -60,12 +60,14 @@ require_once $__dir . '/HTMLPurifier/URIFilter.php';
 require_once $__dir . '/HTMLPurifier/URIParser.php';\r
 require_once $__dir . '/HTMLPurifier/URIScheme.php';\r
 require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php';\r
+require_once $__dir . '/HTMLPurifier/UnitConverter.php';\r
 require_once $__dir . '/HTMLPurifier/VarParser.php';\r
 require_once $__dir . '/HTMLPurifier/VarParserException.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php';\r
+require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/Text.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/URI.php';\r
 require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php';\r
@@ -110,6 +112,9 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
 require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';\r
 require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';\r
 require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';\r
+require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';\r
+require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';\r
+require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';\r
 require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';\r
 require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';\r
 require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';\r
@@ -118,9 +123,6 @@ require_once $__dir . '/HTMLPurifier/ChildDef/Required.php';
 require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php';\r
 require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php';\r
 require_once $__dir . '/HTMLPurifier/ChildDef/Table.php';\r
-require_once $__dir . '/HTMLPurifier/ConfigDef/Directive.php';\r
-require_once $__dir . '/HTMLPurifier/ConfigDef/DirectiveAlias.php';\r
-require_once $__dir . '/HTMLPurifier/ConfigDef/Namespace.php';\r
 require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php';\r
 require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php';\r
 require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php';\r
@@ -138,6 +140,8 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php';
 require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php';\r
 require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php';\r
 require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php';\r
+require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php';\r
+require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php';\r
 require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php';\r
 require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';\r
 require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';\r
@@ -153,6 +157,7 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php';
 require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php';\r
 require_once $__dir . '/HTMLPurifier/Injector/Linkify.php';\r
 require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php';\r
+require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php';\r
 require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php';\r
 require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php';\r
 require_once $__dir . '/HTMLPurifier/Strategy/Composite.php';\r
@@ -173,6 +178,7 @@ require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
 require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';\r
 require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';\r
 require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';\r
+require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';\r
 require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';\r
 require_once $__dir . '/HTMLPurifier/URIScheme/http.php';\r
 require_once $__dir . '/HTMLPurifier/URIScheme/https.php';\r
index 2c59a8d73ada710e0f43685566b3a48651f63b41..eefe1c5d16034a467227c9140bdb06cb547b9a96 100644 (file)
@@ -51,16 +51,13 @@ abstract class HTMLPurifier_AttrDef
      * 
      * @warning This processing is inconsistent with XML's whitespace handling
      *          as specified by section 3.3.3 and referenced XHTML 1.0 section
-     *          4.7.  Compliant processing requires all line breaks normalized
-     *          to "\n", so the fix is not as simple as fixing it in this
-     *          function.  Trim and whitespace collapsing are supposed to only
-     *          occur in NMTOKENs.  However, note that we are NOT necessarily
-     *          parsing XML, thus, this behavior may still be correct.
+     *          4.7.  However, note that we are NOT necessarily
+     *          parsing XML, thus, this behavior may still be correct. We
+     *          assume that newlines have been normalized.
      */
     public function parseCDATA($string) {
         $string = trim($string);
-        $string = str_replace("\n", '', $string);
-        $string = str_replace(array("\r", "\t"), ' ', $string);
+        $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
         return $string;
     }
     
index 128158e19c68161dc7dbef99c419b61987f301a6..53afaf089f6360cd0a7f68df60526ba344757518 100644 (file)
@@ -29,6 +29,12 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
         $declarations = explode(';', $css);
         $propvalues = array();
         
+        /**
+         * Name of the current CSS property being validated.
+         */
+        $property = false;
+        $context->register('CurrentCSSProperty', $property);
+        
         foreach ($declarations as $declaration) {
             if (!$declaration) continue;
             if (!strpos($declaration, ':')) continue;
@@ -61,6 +67,8 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
             $propvalues[$property] = $result;
         }
         
+        $context->destroy('CurrentCSSProperty');
+        
         // procedure does not write the new CSS simultaneously, so it's
         // slightly inefficient, but it's the only way of getting rid of
         // duplicates. Perhaps config to optimize it, but not now.
index 51f83ac5bf7a3b97cfbc494284fe760fa44af219..d7f04a8bf34821c76e76e1c14ec3620f4376df15 100644 (file)
@@ -16,7 +16,6 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
             'cursive' => true
         );
         
-        $string = $this->parseCDATA($string);
         // assume that no font names contain commas in them
         $fonts = explode(',', $string);
         $final = '';
@@ -35,13 +34,40 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
                 $quote = $font[0];
                 if ($font[$length - 1] !== $quote) continue;
                 $font = substr($font, 1, $length - 2);
-                // double-backslash processing is buggy
-                $font = str_replace("\\$quote", $quote, $font); // de-escape quote
-                $font = str_replace("\\\n", "\n", $font);       // de-escape newlines
+                
+                $new_font = '';
+                for ($i = 0, $c = strlen($font); $i < $c; $i++) {
+                    if ($font[$i] === '\\') {
+                        $i++;
+                        if ($i >= $c) {
+                            $new_font .= '\\';
+                            break;
+                        }
+                        if (ctype_xdigit($font[$i])) {
+                            $code = $font[$i];
+                            for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
+                                if (!ctype_xdigit($font[$i])) break;
+                                $code .= $font[$i];
+                            }
+                            // We have to be extremely careful when adding
+                            // new characters, to make sure we're not breaking
+                            // the encoding.
+                            $char = HTMLPurifier_Encoder::unichr(hexdec($code));
+                            if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
+                            $new_font .= $char;
+                            if ($i < $c && trim($font[$i]) !== '') $i--;
+                            continue;
+                        }
+                        if ($font[$i] === "\n") continue;
+                    }
+                    $new_font .= $font[$i];
+                }
+                
+                $font = $new_font;
             }
             // $font is a pure representation of the font name
             
-            if (ctype_alnum($font)) {
+            if (ctype_alnum($font) && $font !== '') {
                 // very simple font, allow it in unharmed
                 $final .= $font . ', ';
                 continue;
@@ -50,8 +76,8 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
             // complicated font, requires quoting
             
             // armor single quotes and new lines
+            $font = str_replace("\\", "\\\\", $font);
             $font = str_replace("'", "\\'", $font);
-            $font = str_replace("\n", "\\\n", $font);
             $final .= "'$font', ";
         }
         $final = rtrim($final, ', ');
index 2684f8ccf4bd397bf070166a820ad070e1faa233..2b8db17cc078e7361646db4b196034dd7ea13e12 100644 (file)
@@ -6,46 +6,40 @@
 class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
 {
     
-    /**
-     * Valid unit lookup table.
-     * @warning The code assumes all units are two characters long.  Be careful
-     *          if we have to change this behavior!
-     */
-    protected $units = array('em' => true, 'ex' => true, 'px' => true, 'in' => true,
-         'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true);
-    /**
-     * Instance of HTMLPurifier_AttrDef_Number to defer number validation to
-     */
-    protected $number_def;
+    protected $min, $max;
     
     /**
-     * @param $non_negative Bool indication whether or not negative values are
-     *                      allowed.
+     * @param HTMLPurifier_Length $max Minimum length, or null for no bound. String is also acceptable.
+     * @param HTMLPurifier_Length $max Maximum length, or null for no bound. String is also acceptable.
      */
-    public function __construct($non_negative = false) {
-        $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
+    public function __construct($min = null, $max = null) {
+        $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
+        $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
     }
     
-    public function validate($length, $config, $context) {
-        
-        $length = $this->parseCDATA($length);
-        if ($length === '') return false;
-        if ($length === '0') return '0';
-        $strlen = strlen($length);
-        if ($strlen === 1) return false; // impossible!
-        
-        // we assume all units are two characters
-        $unit = substr($length, $strlen - 2);
-        if (!ctype_lower($unit)) $unit = strtolower($unit);
-        $number = substr($length, 0, $strlen - 2);
+    public function validate($string, $config, $context) {
+        $string = $this->parseCDATA($string);
         
-        if (!isset($this->units[$unit])) return false;
+        // Optimizations
+        if ($string === '') return false;
+        if ($string === '0') return '0';
+        if (strlen($string) === 1) return false;
         
-        $number = $this->number_def->validate($number, $config, $context);
-        if ($number === false) return false;
+        $length = HTMLPurifier_Length::make($string);
+        if (!$length->isValid()) return false;
         
-        return $number . $unit;
+        if ($this->min) {
+            $c = $length->compareTo($this->min);
+            if ($c === false) return false;
+            if ($c < 0) return false;
+        }
+        if ($this->max) {
+            $c = $length->compareTo($this->max);
+            if ($c === false) return false;
+            if ($c > 0) return false;
+        }
         
+        return $length->toString();
     }
     
 }
index 3d4028ee8c1a4160c8e0b0ae43fc4e8652dad669..15153e4b9cd3da95877691ff81ec42678eb44d77 100644 (file)
@@ -18,6 +18,10 @@ class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
         $this->non_negative = $non_negative;
     }
     
+    /**
+     * @warning Some contexts do not pass $config, $context. These
+     *          variables should not be used without checking HTMLPurifier_Length
+     */
     public function validate($number, $config, $context) {
         
         $number = $this->parseCDATA($number);
index 2108f8049227321f8e2d0dd16341591feb79fc69..af16d5915572080277f36bf2c4c0a03362ef62df 100644 (file)
@@ -13,10 +13,13 @@ class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
         static $allowed_values = array(
             'line-through' => true,
             'overline' => true,
-            'underline' => true
+            'underline' => true,
         );
         
         $string = strtolower($this->parseCDATA($string));
+        
+        if ($string === 'none') return $string;
+        
         $parts = explode(' ', $string);
         $final = '';
         foreach ($parts as $part) {
index 156b7242a8976ba9d0c49254701eb1427b0c8333..6b615fe94de511d4986df996b06207a47f91d119 100644 (file)
@@ -6,6 +6,12 @@
 class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
 {
     
+    protected $max;
+    
+    public function __construct($max = null) {
+        $this->max = $max;
+    }
+    
     public function validate($string, $config, $context) {
         
         $string = trim($string);
@@ -24,11 +30,18 @@ class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
         // crash operating systems, see <http://ha.ckers.org/imagecrash.html>
         // WARNING, above link WILL crash you if you're using Windows
         
-        if ($int > 1200) return '1200';
+        if ($this->max !== null && $int > $this->max) return (string) $this->max;
         
         return (string) $int;
         
     }
     
+    public function make($string) {
+        if ($string === '') $max = null;
+        else $max = (int) $string;
+        $class = get_class($this);
+        return new $class($max);
+    }
+    
 }
 
diff --git a/lib/htmlpurifier/HTMLPurifier/AttrDef/Switch.php b/lib/htmlpurifier/HTMLPurifier/AttrDef/Switch.php
new file mode 100644 (file)
index 0000000..31398e2
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Decorator that, depending on a token, switches between two definitions.
+ */
+class HTMLPurifier_AttrDef_Switch
+{
+    
+    protected $tag;
+    protected $withTag, $withoutTag;
+    
+    /**
+     * @param string $tag Tag name to switch upon
+     * @param HTMLPurifier_AttrDef $with_tag Call if token matches tag
+     * @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token
+     */
+    public function __construct($tag, $with_tag, $without_tag) {
+        $this->tag = $tag;
+        $this->withTag = $with_tag;
+        $this->withoutTag = $without_tag;
+    }
+    
+    public function validate($string, $config, $context) {
+        $token = $context->get('CurrentToken', true);
+        if (!$token || $token->name !== $this->tag) {
+            return $this->withoutTag->validate($string, $config, $context);
+        } else {
+            return $this->withTag->validate($string, $config, $context);
+        }
+    }
+    
+}
index 9c632f1ae7aeb570c7315bfb55292f6b4a3a050a..b814e9eeefbb103d3e2a7fd9c7851ee6582a15f6 100644 (file)
@@ -18,6 +18,11 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
         $this->embedsResource = (bool) $embeds_resource;
     }
     
+    public function make($string) {
+        $embeds = (bool) $string;
+        return new HTMLPurifier_AttrDef_URI($embeds);
+    }
+    
     public function validate($uri, $config, $context) {
         
         if ($config->get('URI', 'Disable')) return false;
@@ -50,6 +55,10 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
             $result = $scheme_obj->validate($uri, $config, $context);
             if (!$result) break;
             
+            // Post chained filtering
+            $result = $uri_def->postFilter($uri, $config, $context);
+            if (!$result) break;
+            
             // survived gauntlet
             $ok = true;
             
@@ -59,18 +68,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
         if (!$ok) return false;
         
         // back to string
-        $result = $uri->toString();
-        
-        // munge entire URI if necessary
-        if (
-            !is_null($uri->host) && // indicator for authority
-            !empty($scheme_obj->browsable) &&
-            !is_null($munge = $config->get('URI', 'Munge'))
-        ) {
-            $result = str_replace('%s', rawurlencode($result), $munge);
-        }
-        
-        return $result;
+        return $uri->toString();
         
     }
     
diff --git a/lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeEmbed.php b/lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeEmbed.php
new file mode 100644 (file)
index 0000000..d3c1883
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform 
+{
+    public $name = "SafeEmbed";
+
+    public function transform($attr, $config, $context) {
+        $attr['allowscriptaccess'] = 'never';
+        $attr['allownetworking'] = 'internal';
+        $attr['type'] = 'application/x-shockwave-flash';
+        return $attr;
+    }
+}
diff --git a/lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeObject.php b/lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeObject.php
new file mode 100644 (file)
index 0000000..28d1ad6
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Writes default type for all objects. Currently only supports flash.
+ */
+class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform
+{
+    public $name = "SafeObject";
+
+    function transform($attr, $config, $context) {
+        if (!isset($attr['type'])) $attr['type'] = 'application/x-shockwave-flash';
+        return $attr;
+    }
+}
diff --git a/lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeParam.php b/lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeParam.php
new file mode 100644 (file)
index 0000000..ae72654
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Validates name/value pairs in param tags to be used in safe objects. This
+ * will only allow name values it recognizes, and pre-fill certain attributes
+ * with required values.
+ * 
+ * @note
+ *      This class only supports Flash. In the future, Quicktime support
+ *      may be added.
+ * 
+ * @warning
+ *      This class expects an injector to add the necessary parameters tags.
+ */
+class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform 
+{
+    public $name = "SafeParam";
+    private $uri;
+    
+    public function __construct() {
+        $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded
+    }
+    
+    public function transform($attr, $config, $context) {
+        // If we add support for other objects, we'll need to alter the
+        // transforms.
+        switch ($attr['name']) {
+            // application/x-shockwave-flash
+            // Keep this synchronized with Injector/SafeObject.php
+            case 'allowScriptAccess':
+                $attr['value'] = 'never';
+                break;
+            case 'allowNetworking':
+                $attr['value'] = 'internal';
+                break;
+            case 'wmode':
+                $attr['value'] = 'window';
+                break;
+            case 'movie':
+                $attr['value'] = $this->uri->validate($attr['value'], $config, $context);
+                break;
+            // add other cases to support other param name/value pairs
+            default:
+                $attr['name'] = $attr['value'] = null;
+        }
+        return $attr;
+    }
+}
index 3b2bd4b37597e25355d2429758e71a39d4c015cd..ba4510e3a4a7be21a8f3a3dd0c919a3678315b2a 100644 (file)
@@ -43,8 +43,8 @@ class HTMLPurifier_AttrValidator
         // DEFINITION CALL
         $d_defs = $definition->info_global_attr;
         
-        // reference attributes for easy manipulation
-        $attr =& $token->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
@@ -139,6 +139,8 @@ class HTMLPurifier_AttrValidator
             if ($e && ($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');
         
index 0d42ed42e626e7ba8dced73a0a109a748d1ba243..36dfa618b7fba5431028c9b0960e7cf154d9e816 100644 (file)
@@ -90,7 +90,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
         $this->info['border-left-width'] = 
         $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
             new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
-            new HTMLPurifier_AttrDef_CSS_Length(true) //disallow negative
+            new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
         ));
         
         $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
@@ -116,7 +116,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
         $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
             new HTMLPurifier_AttrDef_Enum(array('normal')),
             new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
-            new HTMLPurifier_AttrDef_CSS_Length(true),
+            new HTMLPurifier_AttrDef_CSS_Length('0'),
             new HTMLPurifier_AttrDef_CSS_Percentage(true)
         ));
         
@@ -138,7 +138,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
         $this->info['padding-bottom'] = 
         $this->info['padding-left'] = 
         $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_CSS_Length(true),
+            new HTMLPurifier_AttrDef_CSS_Length('0'),
             new HTMLPurifier_AttrDef_CSS_Percentage(true)
         ));
         
@@ -149,14 +149,26 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
             new HTMLPurifier_AttrDef_CSS_Percentage()
         ));
         
-        $this->info['width'] =
-        $this->info['height'] =
-        new HTMLPurifier_AttrDef_CSS_DenyElementDecorator(
-        new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_CSS_Length(true),
+        $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
+            new HTMLPurifier_AttrDef_CSS_Length('0'),
             new HTMLPurifier_AttrDef_CSS_Percentage(true),
             new HTMLPurifier_AttrDef_Enum(array('auto'))
-        )), 'img');
+        ));
+        $max = $config->get('CSS', 'MaxImgLength');
+        
+        $this->info['width'] =
+        $this->info['height'] =
+            $max === null ?
+            $trusted_wh : 
+            new HTMLPurifier_AttrDef_Switch('img',
+                // For img tags:
+                new HTMLPurifier_AttrDef_CSS_Composite(array(
+                    new HTMLPurifier_AttrDef_CSS_Length('0', $max),
+                    new HTMLPurifier_AttrDef_Enum(array('auto'))
+                )),
+                // For everyone else:
+                $trusted_wh
+            );
         
         $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
         
index a4be02eac739bd850bd8d4243e53aeb57973401c..2009fc883528d4fe81a328c0866362ea1863428b 100644 (file)
@@ -55,10 +55,7 @@ class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
         $escape_invalid_children = $config->get('Core', 'EscapeInvalidChildren');
         
         // generator
-        static $gen = null;
-        if ($gen === null) {
-            $gen = new HTMLPurifier_Generator($config, $context);
-        }
+        $gen = new HTMLPurifier_Generator($config, $context);
         
         foreach ($tokens_of_children as $token) {
             if (!empty($token->is_whitespace)) {
@@ -83,7 +80,7 @@ class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
                         $result[] = $token;
                     } elseif ($pcdata_allowed && $escape_invalid_children) {
                         $result[] = new HTMLPurifier_Token_Text(
-                            $gen->generateFromToken($token, $config)
+                            $gen->generateFromToken($token)
                         );
                     }
                     continue;
@@ -94,7 +91,7 @@ class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
             } elseif ($pcdata_allowed && $escape_invalid_children) {
                 $result[] =
                     new HTMLPurifier_Token_Text(
-                        $gen->generateFromToken( $token, $config )
+                        $gen->generateFromToken($token)
                     );
             } else {
                 // drop silently
index be264a6250bd0b362360541c0bad077d08f9b349..e2fe21b6b573446f12cf84adf160f3b08a86e668 100644 (file)
@@ -20,7 +20,7 @@ class HTMLPurifier_Config
     /**
      * HTML Purifier's version
      */
-    public $version = '3.1.0';
+    public $version = '3.1.1';
     
     /**
      * Bool indicator whether or not to automatically finalize 
@@ -125,7 +125,7 @@ class HTMLPurifier_Config
                 E_USER_WARNING);
             return;
         }
-        if ($this->def->info[$namespace][$key]->class == 'alias') {
+        if (isset($this->def->info[$namespace][$key]->isAlias)) {
             $d = $this->def->info[$namespace][$key];
             trigger_error('Cannot get value from aliased directive, use real name ' . $d->namespace . '.' . $d->name,
                 E_USER_ERROR);
@@ -196,40 +196,48 @@ class HTMLPurifier_Config
                 E_USER_WARNING);
             return;
         }
-        if ($this->def->info[$namespace][$key]->class == 'alias') {
+        $def = $this->def->info[$namespace][$key];
+        
+        if (isset($def->isAlias)) {
             if ($from_alias) {
                 trigger_error('Double-aliases not allowed, please fix '.
                     'ConfigSchema bug with' . "$namespace.$key", E_USER_ERROR);
                 return;
             }
-            $this->set($new_ns = $this->def->info[$namespace][$key]->namespace,
-                       $new_dir = $this->def->info[$namespace][$key]->name,
+            $this->set($new_ns  = $def->namespace,
+                       $new_dir = $def->name,
                        $value, true);
             trigger_error("$namespace.$key is an alias, preferred directive name is $new_ns.$new_dir", E_USER_NOTICE);
             return;
         }
+        
+        // Raw type might be negative when using the fully optimized form
+        // of stdclass, which indicates allow_null == true
+        $rtype = is_int($def) ? $def : $def->type;
+        if ($rtype < 0) {
+            $type = -$rtype;
+            $allow_null = true;
+        } else {
+            $type = $rtype;
+            $allow_null = isset($def->allow_null);
+        }
+        
         try {
-            $value = $this->parser->parse(
-                        $value,
-                        $type = $this->def->info[$namespace][$key]->type,
-                        $this->def->info[$namespace][$key]->allow_null
-                     );
+            $value = $this->parser->parse($value, $type, $allow_null);
         } catch (HTMLPurifier_VarParserException $e) {
-            trigger_error('Value for ' . "$namespace.$key" . ' is of invalid type, should be ' . $type, E_USER_WARNING);
+            trigger_error('Value for ' . "$namespace.$key" . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
             return;
         }
-        if (is_string($value)) {
+        if (is_string($value) && is_object($def)) {
             // resolve value alias if defined
-            if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
-                $value = $this->def->info[$namespace][$key]->aliases[$value];
+            if (isset($def->aliases[$value])) {
+                $value = $def->aliases[$value];
             }
-            if ($this->def->info[$namespace][$key]->allowed !== true) {
-                // check to see if the value is allowed
-                if (!isset($this->def->info[$namespace][$key]->allowed[$value])) {
-                    trigger_error('Value not supported, valid values are: ' .
-                        $this->_listify($this->def->info[$namespace][$key]->allowed), E_USER_WARNING);
-                    return;
-                }
+            // check to see if the value is allowed
+            if (isset($def->allowed) && !isset($def->allowed[$value])) {
+                trigger_error('Value not supported, valid values are: ' .
+                    $this->_listify($def->allowed), E_USER_WARNING);
+                return;
             }
         }
         $this->conf[$namespace][$key] = $value;
@@ -386,7 +394,7 @@ class HTMLPurifier_Config
                     if (isset($blacklisted_directives["$ns.$directive"])) continue;
                     if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
                 }
-                if ($def->class == 'alias') continue;
+                if (isset($def->isAlias)) continue;
                 if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
                 $ret[] = array($ns, $directive);
             }
index bfa84a1b7eae8f245d2da659c4a43fed351bfd25..b324181ce49ecbd7d07cd0d30bff88f24eacaf43 100644 (file)
@@ -12,7 +12,33 @@ class HTMLPurifier_ConfigSchema {
     public $defaults = array();
     
     /**
-     * Definition of the directives.
+     * Definition of the directives. The structure of this is:
+     * 
+     *  array(
+     *      'Namespace' => array(
+     *          'Directive' => new stdclass(),
+     *      )
+     *  )
+     * 
+     * The stdclass may have the following properties:
+     * 
+     *  - If isAlias isn't set:
+     *      - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
+     *      - allow_null: If set, this directive allows null values
+     *      - aliases: If set, an associative array of value aliases to real values
+     *      - allowed: If set, a lookup array of allowed (string) values
+     *  - If isAlias is set:
+     *      - namespace: Namespace this directive aliases to
+     *      - name: Directive name this directive aliases to
+     * 
+     * In certain degenerate cases, stdclass will actually be an integer. In
+     * that case, the value is equivalent to an stdclass with the type
+     * property set to the integer. If the integer is negative, type is
+     * equal to the absolute value of integer, and allow_null is true.
+     * 
+     * This class is friendly with HTMLPurifier_Config. If you need introspection
+     * about the schema, you're better of using the ConfigSchema_Interchange,
+     * which uses more memory but has much richer information.
      */
     public $info = array();
     
@@ -21,15 +47,6 @@ class HTMLPurifier_ConfigSchema {
      */
     static protected $singleton;
     
-    /**
-     * Variable parser.
-     */
-    protected $parser;
-    
-    public function __construct() {
-        $this->parser = new HTMLPurifier_VarParser_Flexible();
-    }
-    
     /**
      * Unserializes the default ConfigSchema.
      */
@@ -62,11 +79,11 @@ class HTMLPurifier_ConfigSchema {
      * @param $allow_null Whether or not to allow null values
      */
     public function add($namespace, $name, $default, $type, $allow_null) {
-        $default = $this->parser->parse($default, $type, $allow_null);
-        $this->info[$namespace][$name] = new HTMLPurifier_ConfigDef_Directive();
-        $this->info[$namespace][$name]->type = $type;
-        $this->info[$namespace][$name]->allow_null = $allow_null;
-        $this->defaults[$namespace][$name]   = $default;
+        $obj = new stdclass();
+        $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
+        if ($allow_null) $obj->allow_null = true;
+        $this->info[$namespace][$name] = $obj;
+        $this->defaults[$namespace][$name] = $default;
     }
     
     /**
@@ -90,6 +107,9 @@ class HTMLPurifier_ConfigSchema {
      * @param $aliases Hash of aliased values to the real alias
      */
     public function addValueAliases($namespace, $name, $aliases) {
+        if (!isset($this->info[$namespace][$name]->aliases)) {
+            $this->info[$namespace][$name]->aliases = array();
+        }
         foreach ($aliases as $alias => $real) {
             $this->info[$namespace][$name]->aliases[$alias] = $real;
         }
@@ -104,7 +124,6 @@ class HTMLPurifier_ConfigSchema {
      * @param $allowed Lookup array of allowed values
      */
     public function addAllowedValues($namespace, $name, $allowed) {
-        $type = $this->info[$namespace][$name]->type;
         $this->info[$namespace][$name]->allowed = $allowed;
     }
     
@@ -116,7 +135,26 @@ class HTMLPurifier_ConfigSchema {
      * @param $new_name Directive that the alias will be to
      */
     public function addAlias($namespace, $name, $new_namespace, $new_name) {
-        $this->info[$namespace][$name] = new HTMLPurifier_ConfigDef_DirectiveAlias($new_namespace, $new_name);
+        $obj = new stdclass;
+        $obj->namespace = $new_namespace;
+        $obj->name = $new_name;
+        $obj->isAlias = true;
+        $this->info[$namespace][$name] = $obj;
+    }
+    
+    /**
+     * Replaces any stdclass that only has the type property with type integer.
+     */
+    public function postProcess() {
+        foreach ($this->info as $namespace => $info) {
+            foreach ($info as $directive => $v) {
+                if (count((array) $v) == 1) {
+                    $this->info[$namespace][$directive] = $v->type;
+                } elseif (count((array) $v) == 2 && isset($v->allow_null)) {
+                    $this->info[$namespace][$directive] = -$v->type;
+                }
+            }
+        }
     }
     
     // DEPRECATED METHODS
@@ -124,7 +162,6 @@ class HTMLPurifier_ConfigSchema {
     /** @see HTMLPurifier_ConfigSchema->set() */
     public static function define($namespace, $name, $default, $type, $description) {
         HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
-        // process modifiers (OPTIMIZE!)
         $type_values = explode('/', $type, 2);
         $type = $type_values[0];
         $modifier = isset($type_values[1]) ? $type_values[1] : false;
@@ -168,7 +205,8 @@ class HTMLPurifier_ConfigSchema {
     /** @deprecated, use HTMLPurifier_VarParser->parse() */
     public function validate($a, $b, $c = false) {
         trigger_error("HTMLPurifier_ConfigSchema->validate deprecated, use HTMLPurifier_VarParser->parse instead", E_USER_NOTICE);
-        return $this->parser->parse($a, $b, $c);
+        $parser = new HTMLPurifier_VarParser();
+        return $parser->parse($a, $b, $c);
     }
     
     /**
index 69b71387d62e221cc3e830aeea367a2e053cb177..727f58eefabb9cdb0040e5f5317049f3a04e469b 100644 (file)
@@ -43,6 +43,7 @@ class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
                 );
             }
         }
+        $schema->postProcess();
         return $schema;
     }
     
index e36ef8f130ca4645bf54d5932584af7e792e98f1..64a35430da6b8a29b161d419c3132ce4c13a4f43 100644 (file)
@@ -111,7 +111,8 @@ class HTMLPurifier_ConfigSchema_Validator
         if (!is_null($d->allowed) || !empty($d->valueAliases)) {
             // allowed and valueAliases require that we be dealing with
             // strings, so check for that early.
-            if (!isset(HTMLPurifier_VarParser::$stringTypes[$d->type])) {
+            $d_int = HTMLPurifier_VarParser::$types[$d->type];
+            if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
                 $this->error('type', 'must be a string type when used with allowed or value aliases');
             }
         }
index 619cf532bf025a75103ce58695745c006174ec36..1af702b7df5a22925c42a39c0618b5b9f7176770 100644 (file)
@@ -1 +1 @@
-O:25:"HTMLPurifier_ConfigSchema":3:{s:8:"defaults";a:12:{s:4:"Attr";a:11:{s:19:"AllowedFrameTargets";a:0:{}s:10:"AllowedRel";a:0:{}s:10:"AllowedRev";a:0:{}s:19:"DefaultInvalidImage";s:0:"";s:22:"DefaultInvalidImageAlt";s:13:"Invalid image";s:14:"DefaultTextDir";s:3:"ltr";s:8:"EnableID";b:0;s:11:"IDBlacklist";a:0:{}s:17:"IDBlacklistRegexp";N;s:8:"IDPrefix";s:0:"";s:13:"IDPrefixLocal";s:0:"";}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";b:0;s:6:"Custom";a:0:{}s:7:"Linkify";b:0;s:15:"PurifierLinkify";b:0;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";s:3:"#%s";}s:3:"CSS";a:5:{s:14:"AllowImportant";b:0;s:11:"AllowTricky";b:0;s:17:"AllowedProperties";N;s:13:"DefinitionRev";i:1;s:11:"Proprietary";b:0;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";s:10:"Serializer";s:14:"SerializerPath";N;}s:4:"Core";a:15:{s:17:"AggressivelyFixLt";b:0;s:13:"CollectErrors";b:0;s:13:"ColorKeywords";a:17:{s:6:"maroon";s:7:"#800000";s:3:"red";s:7:"#FF0000";s:6:"orange";s:7:"#FFA500";s:6:"yellow";s:7:"#FFFF00";s:5:"olive";s:7:"#808000";s:6:"purple";s:7:"#800080";s:7:"fuchsia";s:7:"#FF00FF";s:5:"white";s:7:"#FFFFFF";s:4:"lime";s:7:"#00FF00";s:5:"green";s:7:"#008000";s:4:"navy";s:7:"#000080";s:4:"blue";s:7:"#0000FF";s:4:"aqua";s:7:"#00FFFF";s:4:"teal";s:7:"#008080";s:5:"black";s:7:"#000000";s:6:"silver";s:7:"#C0C0C0";s:4:"gray";s:7:"#808080";}s:25:"ConvertDocumentToFragment";b:1;s:31:"DirectLexLineNumberSyncInterval";i:0;s:8:"Encoding";s:5:"utf-8";s:21:"EscapeInvalidChildren";b:0;s:17:"EscapeInvalidTags";b:0;s:24:"EscapeNonASCIICharacters";b:0;s:14:"HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:8:"Language";s:2:"en";s:9:"LexerImpl";N;s:19:"MaintainLineNumbers";N;s:16:"RemoveInvalidImg";b:1;s:20:"RemoveScriptContents";N;}s:6:"Filter";a:3:{s:6:"Custom";a:0:{}s:18:"ExtractStyleBlocks";b:0;s:7:"YouTube";b:0;}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";b:1;s:23:"ExtractStyleBlocksScope";N;s:26:"ExtractStyleBlocksTidyImpl";N;}s:4:"HTML";a:20:{s:7:"Allowed";N;s:17:"AllowedAttributes";N;s:15:"AllowedElements";N;s:14:"AllowedModules";N;s:12:"BlockWrapper";s:1:"p";s:11:"CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:13:"CustomDoctype";N;s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Doctype";N;s:19:"ForbiddenAttributes";a:0:{}s:17:"ForbiddenElements";a:0:{}s:6:"Parent";s:3:"div";s:11:"Proprietary";b:0;s:6:"Strict";b:0;s:7:"TidyAdd";a:0:{}s:9:"TidyLevel";s:6:"medium";s:10:"TidyRemove";a:0:{}s:7:"Trusted";b:0;s:5:"XHTML";b:1;}s:6:"Output";a:3:{s:21:"CommentScriptContents";b:1;s:7:"Newline";N;s:10:"TidyFormat";b:0;}s:4:"Test";a:1:{s:12:"ForceNoIconv";b:0;}s:3:"URI";a:14:{s:14:"AllowedSchemes";a:6:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;}s:4:"Base";N;s:13:"DefaultScheme";s:4:"http";s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Disable";b:0;s:15:"DisableExternal";b:0;s:24:"DisableExternalResources";b:0;s:16:"DisableResources";b:0;s:4:"Host";N;s:13:"HostBlacklist";a:0:{}s:12:"MakeAbsolute";b:0;s:5:"Munge";N;s:22:"OverrideAllowedSchemes";b:1;}}s:4:"info";a:12:{s:4:"Attr";a:12:{s:19:"AllowedFrameTargets";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"AllowedRel";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"AllowedRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:19:"DefaultInvalidImage";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:22:"DefaultInvalidImageAlt";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"DefaultTextDir";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}s:7:"aliases";a:0:{}}s:8:"EnableID";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"IDBlacklist";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"IDBlacklistRegexp";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:8:"IDPrefix";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"IDPrefixLocal";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"DisableURI";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:3:"URI";s:4:"name";s:7:"Disable";}}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:6:"Custom";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Linkify";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:15:"PurifierLinkify";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:3:"CSS";a:5:{s:14:"AllowImportant";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"AllowTricky";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"AllowedProperties";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefinitionRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"Proprietary";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:5:"Cache";a:2:{s:14:"DefinitionImpl";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"SerializerPath";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:4:"Core";a:20:{s:15:"DefinitionCache";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:5:"Cache";s:4:"name";s:14:"DefinitionImpl";}s:17:"AggressivelyFixLt";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"CollectErrors";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"ColorKeywords";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"hash";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:25:"ConvertDocumentToFragment";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:19:"AcceptFullDocuments";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:4:"Core";s:4:"name";s:25:"ConvertDocumentToFragment";}s:31:"DirectLexLineNumberSyncInterval";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:8:"Encoding";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:7:"istring";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:21:"EscapeInvalidChildren";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"EscapeInvalidTags";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:24:"EscapeNonASCIICharacters";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"HiddenElements";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:8:"Language";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:9:"LexerImpl";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:5:"mixed";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:19:"MaintainLineNumbers";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:16:"RemoveInvalidImg";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:20:"RemoveScriptContents";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:5:"XHTML";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:4:"HTML";s:4:"name";s:5:"XHTML";}s:21:"CommentScriptContents";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:6:"Output";s:4:"name";s:21:"CommentScriptContents";}s:10:"TidyFormat";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:6:"Output";s:4:"name";s:10:"TidyFormat";}}s:6:"Filter";a:5:{s:6:"Custom";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:18:"ExtractStyleBlocks";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"YouTube";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:26:"ExtractStyleBlocksEscaping";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:11:"FilterParam";s:4:"name";s:26:"ExtractStyleBlocksEscaping";}s:23:"ExtractStyleBlocksScope";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:11:"FilterParam";s:4:"name";s:23:"ExtractStyleBlocksScope";}}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:23:"ExtractStyleBlocksScope";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:26:"ExtractStyleBlocksTidyImpl";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:5:"mixed";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:4:"HTML";a:21:{s:12:"EnableAttrID";O:37:"HTMLPurifier_ConfigDef_DirectiveAlias":3:{s:5:"class";s:5:"alias";s:9:"namespace";s:4:"Attr";s:4:"name";s:8:"EnableID";}s:7:"Allowed";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:5:"itext";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"AllowedAttributes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:15:"AllowedElements";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:14:"AllowedModules";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"BlockWrapper";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"CoreModules";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"CustomDoctype";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"DefinitionID";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefinitionRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Doctype";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}s:7:"aliases";a:0:{}}s:19:"ForbiddenAttributes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:17:"ForbiddenElements";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:6:"Parent";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:11:"Proprietary";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:6:"Strict";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"TidyAdd";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:9:"TidyLevel";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}s:7:"aliases";a:0:{}}s:10:"TidyRemove";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Trusted";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:5:"XHTML";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:6:"Output";a:3:{s:21:"CommentScriptContents";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Newline";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:10:"TidyFormat";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:4:"Test";a:1:{s:12:"ForceNoIconv";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}s:3:"URI";a:14:{s:14:"AllowedSchemes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"lookup";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:4:"Base";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefaultScheme";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"DefinitionID";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"DefinitionRev";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:3:"int";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:7:"Disable";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:15:"DisableExternal";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:24:"DisableExternalResources";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:16:"DisableResources";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:4:"Host";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:13:"HostBlacklist";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"list";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:12:"MakeAbsolute";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:5:"Munge";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:6:"string";s:10:"allow_null";b:1;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}s:22:"OverrideAllowedSchemes";O:32:"HTMLPurifier_ConfigDef_Directive":5:{s:5:"class";s:9:"directive";s:4:"type";s:4:"bool";s:10:"allow_null";b:0;s:7:"allowed";b:1;s:7:"aliases";a:0:{}}}}s:9:"\0*\0parser";O:31:"HTMLPurifier_VarParser_Flexible":0:{}}
\ No newline at end of file
+O:25:"HTMLPurifier_ConfigSchema":2:{s:8:"defaults";a:12:{s:4:"Attr";a:11:{s:19:"AllowedFrameTargets";a:0:{}s:10:"AllowedRel";a:0:{}s:10:"AllowedRev";a:0:{}s:19:"DefaultInvalidImage";s:0:"";s:22:"DefaultInvalidImageAlt";s:13:"Invalid image";s:14:"DefaultTextDir";s:3:"ltr";s:8:"EnableID";b:0;s:11:"IDBlacklist";a:0:{}s:17:"IDBlacklistRegexp";N;s:8:"IDPrefix";s:0:"";s:13:"IDPrefixLocal";s:0:"";}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";b:0;s:6:"Custom";a:0:{}s:7:"Linkify";b:0;s:15:"PurifierLinkify";b:0;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";s:3:"#%s";}s:3:"CSS";a:6:{s:14:"AllowImportant";b:0;s:11:"AllowTricky";b:0;s:17:"AllowedProperties";N;s:13:"DefinitionRev";i:1;s:12:"MaxImgLength";s:6:"1200px";s:11:"Proprietary";b:0;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";s:10:"Serializer";s:14:"SerializerPath";N;}s:4:"Core";a:15:{s:17:"AggressivelyFixLt";b:0;s:13:"CollectErrors";b:0;s:13:"ColorKeywords";a:17:{s:6:"maroon";s:7:"#800000";s:3:"red";s:7:"#FF0000";s:6:"orange";s:7:"#FFA500";s:6:"yellow";s:7:"#FFFF00";s:5:"olive";s:7:"#808000";s:6:"purple";s:7:"#800080";s:7:"fuchsia";s:7:"#FF00FF";s:5:"white";s:7:"#FFFFFF";s:4:"lime";s:7:"#00FF00";s:5:"green";s:7:"#008000";s:4:"navy";s:7:"#000080";s:4:"blue";s:7:"#0000FF";s:4:"aqua";s:7:"#00FFFF";s:4:"teal";s:7:"#008080";s:5:"black";s:7:"#000000";s:6:"silver";s:7:"#C0C0C0";s:4:"gray";s:7:"#808080";}s:25:"ConvertDocumentToFragment";b:1;s:31:"DirectLexLineNumberSyncInterval";i:0;s:8:"Encoding";s:5:"utf-8";s:21:"EscapeInvalidChildren";b:0;s:17:"EscapeInvalidTags";b:0;s:24:"EscapeNonASCIICharacters";b:0;s:14:"HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:8:"Language";s:2:"en";s:9:"LexerImpl";N;s:19:"MaintainLineNumbers";N;s:16:"RemoveInvalidImg";b:1;s:20:"RemoveScriptContents";N;}s:6:"Filter";a:3:{s:6:"Custom";a:0:{}s:18:"ExtractStyleBlocks";b:0;s:7:"YouTube";b:0;}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";b:1;s:23:"ExtractStyleBlocksScope";N;s:26:"ExtractStyleBlocksTidyImpl";N;}s:4:"HTML";a:23:{s:7:"Allowed";N;s:17:"AllowedAttributes";N;s:15:"AllowedElements";N;s:14:"AllowedModules";N;s:12:"BlockWrapper";s:1:"p";s:11:"CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:13:"CustomDoctype";N;s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Doctype";N;s:19:"ForbiddenAttributes";a:0:{}s:17:"ForbiddenElements";a:0:{}s:12:"MaxImgLength";i:1200;s:6:"Parent";s:3:"div";s:11:"Proprietary";b:0;s:9:"SafeEmbed";b:0;s:10:"SafeObject";b:0;s:6:"Strict";b:0;s:7:"TidyAdd";a:0:{}s:9:"TidyLevel";s:6:"medium";s:10:"TidyRemove";a:0:{}s:7:"Trusted";b:0;s:5:"XHTML";b:1;}s:6:"Output";a:3:{s:21:"CommentScriptContents";b:1;s:7:"Newline";N;s:10:"TidyFormat";b:0;}s:4:"Test";a:1:{s:12:"ForceNoIconv";b:0;}s:3:"URI";a:16:{s:14:"AllowedSchemes";a:6:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;}s:4:"Base";N;s:13:"DefaultScheme";s:4:"http";s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Disable";b:0;s:15:"DisableExternal";b:0;s:24:"DisableExternalResources";b:0;s:16:"DisableResources";b:0;s:4:"Host";N;s:13:"HostBlacklist";a:0:{}s:12:"MakeAbsolute";b:0;s:5:"Munge";N;s:14:"MungeResources";b:0;s:14:"MungeSecretKey";N;s:22:"OverrideAllowedSchemes";b:1;}}s:4:"info";a:12:{s:4:"Attr";a:12:{s:19:"AllowedFrameTargets";i:8;s:10:"AllowedRel";i:8;s:10:"AllowedRev";i:8;s:19:"DefaultInvalidImage";i:1;s:22:"DefaultInvalidImageAlt";i:1;s:14:"DefaultTextDir";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}}s:8:"EnableID";i:7;s:11:"IDBlacklist";i:9;s:17:"IDBlacklistRegexp";i:-1;s:8:"IDPrefix";i:1;s:13:"IDPrefixLocal";i:1;s:10:"DisableURI";O:8:"stdClass":3:{s:9:"namespace";s:3:"URI";s:4:"name";s:7:"Disable";s:7:"isAlias";b:1;}}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";i:7;s:6:"Custom";i:9;s:7:"Linkify";i:7;s:15:"PurifierLinkify";i:7;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";i:1;}s:3:"CSS";a:6:{s:14:"AllowImportant";i:7;s:11:"AllowTricky";i:7;s:17:"AllowedProperties";i:-8;s:13:"DefinitionRev";i:5;s:12:"MaxImgLength";i:-1;s:11:"Proprietary";i:7;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";i:-1;s:14:"SerializerPath";i:-1;}s:4:"Core";a:20:{s:15:"DefinitionCache";O:8:"stdClass":3:{s:9:"namespace";s:5:"Cache";s:4:"name";s:14:"DefinitionImpl";s:7:"isAlias";b:1;}s:17:"AggressivelyFixLt";i:7;s:13:"CollectErrors";i:7;s:13:"ColorKeywords";i:10;s:25:"ConvertDocumentToFragment";i:7;s:19:"AcceptFullDocuments";O:8:"stdClass":3:{s:9:"namespace";s:4:"Core";s:4:"name";s:25:"ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:31:"DirectLexLineNumberSyncInterval";i:5;s:8:"Encoding";i:2;s:21:"EscapeInvalidChildren";i:7;s:17:"EscapeInvalidTags";i:7;s:24:"EscapeNonASCIICharacters";i:7;s:14:"HiddenElements";i:8;s:8:"Language";i:1;s:9:"LexerImpl";i:-11;s:19:"MaintainLineNumbers";i:-7;s:16:"RemoveInvalidImg";i:7;s:20:"RemoveScriptContents";i:-7;s:5:"XHTML";O:8:"stdClass":3:{s:9:"namespace";s:4:"HTML";s:4:"name";s:5:"XHTML";s:7:"isAlias";b:1;}s:21:"CommentScriptContents";O:8:"stdClass":3:{s:9:"namespace";s:6:"Output";s:4:"name";s:21:"CommentScriptContents";s:7:"isAlias";b:1;}s:10:"TidyFormat";O:8:"stdClass":3:{s:9:"namespace";s:6:"Output";s:4:"name";s:10:"TidyFormat";s:7:"isAlias";b:1;}}s:6:"Filter";a:5:{s:6:"Custom";i:9;s:18:"ExtractStyleBlocks";i:7;s:7:"YouTube";i:7;s:26:"ExtractStyleBlocksEscaping";O:8:"stdClass":3:{s:9:"namespace";s:11:"FilterParam";s:4:"name";s:26:"ExtractStyleBlocksEscaping";s:7:"isAlias";b:1;}s:23:"ExtractStyleBlocksScope";O:8:"stdClass":3:{s:9:"namespace";s:11:"FilterParam";s:4:"name";s:23:"ExtractStyleBlocksScope";s:7:"isAlias";b:1;}}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";i:7;s:23:"ExtractStyleBlocksScope";i:-1;s:26:"ExtractStyleBlocksTidyImpl";i:-11;}s:4:"HTML";a:24:{s:12:"EnableAttrID";O:8:"stdClass":3:{s:9:"namespace";s:4:"Attr";s:4:"name";s:8:"EnableID";s:7:"isAlias";b:1;}s:7:"Allowed";i:-4;s:17:"AllowedAttributes";i:-8;s:15:"AllowedElements";i:-8;s:14:"AllowedModules";i:-8;s:12:"BlockWrapper";i:1;s:11:"CoreModules";i:8;s:13:"CustomDoctype";i:-1;s:12:"DefinitionID";i:-1;s:13:"DefinitionRev";i:5;s:7:"Doctype";O:8:"stdClass":3:{s:4:"type";i:1;s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}}s:19:"ForbiddenAttributes";i:8;s:17:"ForbiddenElements";i:8;s:12:"MaxImgLength";i:-5;s:6:"Parent";i:1;s:11:"Proprietary";i:7;s:9:"SafeEmbed";i:7;s:10:"SafeObject";i:7;s:6:"Strict";i:7;s:7:"TidyAdd";i:8;s:9:"TidyLevel";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}}s:10:"TidyRemove";i:8;s:7:"Trusted";i:7;s:5:"XHTML";i:7;}s:6:"Output";a:3:{s:21:"CommentScriptContents";i:7;s:7:"Newline";i:-1;s:10:"TidyFormat";i:7;}s:4:"Test";a:1:{s:12:"ForceNoIconv";i:7;}s:3:"URI";a:16:{s:14:"AllowedSchemes";i:8;s:4:"Base";i:-1;s:13:"DefaultScheme";i:1;s:12:"DefinitionID";i:-1;s:13:"DefinitionRev";i:5;s:7:"Disable";i:7;s:15:"DisableExternal";i:7;s:24:"DisableExternalResources";i:7;s:16:"DisableResources";i:7;s:4:"Host";i:-1;s:13:"HostBlacklist";i:9;s:12:"MakeAbsolute";i:7;s:5:"Munge";i:-1;s:14:"MungeResources";i:7;s:14:"MungeSecretKey";i:-1;s:22:"OverrideAllowedSchemes";i:7;}}}
\ No newline at end of file
diff --git a/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt
new file mode 100644 (file)
index 0000000..824111e
--- /dev/null
@@ -0,0 +1,15 @@
+CSS.MaxImgLength
+TYPE: string/null
+DEFAULT: '1200px'
+VERSION: 3.1.1
+--DESCRIPTION--
+<p>
+ This parameter sets the maximum allowed length on <code>img</code> tags,
+ effectively the <code>width</code> and <code>height</code> properties.
+ Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is
+ in place to prevent imagecrash attacks, disable with null at your own risk.
+ This directive is similar to %HTML.MaxImgLength, and both should be
+ concurrently edited, although there are
+ subtle differences in the input format (the CSS max is a number with
+ a unit).
+</p>
diff --git a/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt
new file mode 100644 (file)
index 0000000..15d8dce
--- /dev/null
@@ -0,0 +1,13 @@
+HTML.MaxImgLength
+TYPE: int/null
+DEFAULT: 1200
+VERSION: 3.1.1
+--DESCRIPTION--
+<p>
+ This directive controls the maximum number of pixels in the width and
+ height attributes in <code>img</code> tags. This is
+ in place to prevent imagecrash attacks, disable with null at your own risk.
+ This directive is similar to %CSS.MaxImgLength, and both should be
+ concurrently edited, although there are
+ subtle differences in the input format (the HTML max is an integer).
+</p>
\ No newline at end of file
diff --git a/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt
new file mode 100644 (file)
index 0000000..98ed8f9
--- /dev/null
@@ -0,0 +1,13 @@
+HTML.SafeEmbed
+TYPE: bool
+VERSION: 3.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to permit embed tags in documents, with a number of extra
+    security features added to prevent script execution. This is similar to
+    what websites like MySpace do to embed tags. Embed is a proprietary
+    element and will cause your website to stop validating. You probably want
+    to enable this with %HTML.SafeObject.
+    <strong>Highly experimental.</strong>
+</p>
\ No newline at end of file
diff --git a/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt
new file mode 100644 (file)
index 0000000..6270abe
--- /dev/null
@@ -0,0 +1,13 @@
+HTML.SafeObject
+TYPE: bool
+VERSION: 3.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to permit object tags in documents, with a number of extra
+    security features added to prevent script execution. This is similar to
+    what websites like MySpace do to object tags. You may also want to 
+    enable %HTML.SafeEmbed for maximum interoperability with Internet Explorer,
+    although embed tags will cause your website to stop validating.
+    <strong>Highly experimental.</strong>
+</p>
index b1207aab79973ff987361287e2aa4de96eaf938d..7743ac2923917097783555f17f55c35da9a1e048 100644 (file)
@@ -6,7 +6,7 @@ DEFAULT: NULL
 \r
 <p>\r
     Munges all browsable (usually http, https and ftp)\r
-    absolute URI's into another URI, usually a URI redirection service.\r
+    absolute URIs into another URI, usually a URI redirection service.\r
     This directive accepts a URI, formatted with a <code>%s</code> where \r
     the url-encoded original URI should be inserted (sample: \r
     <code>http://www.google.com/url?q=%s</code>).\r
@@ -19,13 +19,64 @@ DEFAULT: NULL
         Prevent PageRank leaks, while being fairly transparent \r
         to users (you may also want to add some client side JavaScript to \r
         override the text in the statusbar). <strong>Notice</strong>:\r
-        Many security experts believe that this form of protection does\r
-not deter spam-bots. \r
+        Many security experts believe that this form of protection does not deter spam-bots. \r
     </li>\r
     <li>\r
         Redirect users to a splash page telling them they are leaving your\r
-        website. While this is poor usability practice, it is often\r
-mandated\r
+        website. While this is poor usability practice, it is often mandated\r
         in corporate environments.\r
     </li>\r
 </ul>\r
+<p>\r
+    Prior to HTML Purifier 3.1.1, this directive also enabled the munging\r
+    of browsable external resources, which could break things if your redirection\r
+    script was a splash page or used <code>meta</code> tags. To revert to\r
+    previous behavior, please use %URI.MungeResources.\r
+</p>\r
+<p>\r
+    You may want to also use %URI.MungeSecretKey along with this directive\r
+    in order to enforce what URIs your redirector script allows. Open\r
+    redirector scripts can be a security risk and negatively affect the\r
+    reputation of your domain name.\r
+</p>\r
+<p>\r
+    Starting with HTML Purifier 3.1.1, there is also these substitutions:\r
+</p>\r
+<table>\r
+    <thead>\r
+        <tr>\r
+            <th>Key</th>\r
+            <th>Description</th>\r
+            <th>Example <code>&lt;a href=""&gt;</code></th>\r
+        </tr>\r
+    </thead>\r
+    <tbody>\r
+        <tr>\r
+            <td>%r</td>\r
+            <td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td>\r
+            <td></td>\r
+        </tr>\r
+        <tr>\r
+            <td>%n</td>\r
+            <td>The name of the tag this URI came from</td>\r
+            <td>a</td>\r
+        </tr>\r
+        <tr>\r
+            <td>%m</td>\r
+            <td>The name of the attribute this URI came from</td>\r
+            <td>href</td>\r
+        </tr>\r
+        <tr>\r
+            <td>%p</td>\r
+            <td>The name of the CSS property this URI came from, or blank if irrelevant</td>\r
+            <td></td>\r
+        </tr>\r
+    </tbody>\r
+</table>\r
+<p>\r
+    Admittedly, these letters are somewhat arbitrary; the only stipulation\r
+    was that they couldn't be a through f. r is for resource (I would have preferred\r
+    e, but you take what you can get), n is for name, m\r
+    was picked because it came after n (and I couldn't use a), p is for\r
+    property.\r
+</p>\r
diff --git a/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt
new file mode 100644 (file)
index 0000000..69b1ea8
--- /dev/null
@@ -0,0 +1,16 @@
+URI.MungeResources
+TYPE: bool
+VERSION: 3.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    If true, any URI munging directives like %URI.Munge
+    will also apply to embedded resources, such as <code>&lt;img src=""&gt;</code>.
+    Be careful enabling this directive if you have a redirector script
+    that does not use the <code>Location</code> HTTP header; all of your images
+    and other embedded resources will break.
+</p>
+<p>
+    <strong>Warning:</strong> It is strongly advised you use this in conjunction
+    %URI.MungeSecretKey to mitigate the security risk of an open redirector.
+</p>
diff --git a/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
new file mode 100644 (file)
index 0000000..a2f5a02
--- /dev/null
@@ -0,0 +1,29 @@
+URI.MungeSecretKey
+TYPE: string/null
+VERSION: 3.1.1
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+    This directive enables secure checksum generation along with %URI.Munge.
+    It should be set to a secure key that is not shared with anyone else.
+    The checksum can be placed in the URI using %t. Use of this checksum
+    affords an additional level of protection by allowing a redirector
+    to check if a URI has passed through HTML Purifier with this line:
+</p>
+
+<pre>$checksum === sha1($secret_key . ':' . $url)</pre>
+
+<p>
+    If the output is TRUE, the redirector script should accept the URI.
+</p>
+
+<p>
+    Please note that it would still be possible for an attacker to procure
+    secure hashes en-mass by abusing your website's Preview feature or the
+    like, but this service affords an additional level of protection
+    that should be combined with website blacklisting.
+</p>
+
+<p>
+    Remember this has no effect if %URI.Munge is not on.
+</p>
index 763684f789b2cd24f7282b474b2f25b7ae8156eb..0518814ec3632aab6128c9d8886ffd3be24f9bc2 100644 (file)
@@ -46,35 +46,13 @@ class HTMLPurifier_Encoder
      */
     public static function cleanUTF8($str, $force_php = false) {
         
-        static $non_sgml_chars = array();
-        if (empty($non_sgml_chars)) {
-            for ($i = 0; $i <= 31; $i++) {
-                // non-SGML ASCII chars
-                // save \r, \t and \n
-                if ($i == 9 || $i == 13 || $i == 10) continue;
-                $non_sgml_chars[chr($i)] = '';
-            }
-            for ($i = 127; $i <= 159; $i++) {
-                $non_sgml_chars[HTMLPurifier_Encoder::unichr($i)] = '';
-            }
-        }
-        
-        static $iconv = null;
-        if ($iconv === null) $iconv = function_exists('iconv');
-        
         // 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 iconv/php stuff. 99% of the time, this will be the case.
-        if (preg_match('/^.{1}/us', $str)) {
-            return strtr($str, $non_sgml_chars);
-        }
-        
-        if ($iconv && !$force_php) {
-            // do the shortcut way
-            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
-            $str = iconv('UTF-8', 'UTF-8//IGNORE', $str);
-            restore_error_handler();
-            return strtr($str, $non_sgml_chars);
+        // need to do PHP stuff. 99% of the time, this will be the case.
+        // The regexp matches the XML char production, as well as well as excluding
+        // non-SGML codepoints U+007F to U+009F
+        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
@@ -185,7 +163,17 @@ class HTMLPurifier_Encoder
                         ) {
                             
                         } elseif (0xFEFF != $mUcs4 && // omit BOM
-                            !($mUcs4 >= 128 && $mUcs4 <= 159) // omit non-SGML
+                            // 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) ||
+                                (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
+                            )
                         ) {
                             $out .= $char;
                         }
@@ -276,17 +264,20 @@ class HTMLPurifier_Encoder
      * Converts a string to UTF-8 based on configuration.
      */
     public static function convertToUTF8($str, $config, $context) {
-        static $iconv = null;
-        if ($iconv === null) $iconv = function_exists('iconv');
         $encoding = $config->get('Core', 'Encoding');
         if ($encoding === 'utf-8') return $str;
+        static $iconv = null;
+        if ($iconv === null) $iconv = function_exists('iconv');
+        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
         if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
-            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
             $str = iconv($encoding, 'utf-8//IGNORE', $str);
+            // 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, HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding));
             restore_error_handler();
             return $str;
         } elseif ($encoding === 'iso-8859-1') {
-            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
             $str = utf8_encode($str);
             restore_error_handler();
             return $str;
@@ -300,20 +291,28 @@ class HTMLPurifier_Encoder
      *       characters being omitted.
      */
     public static function convertFromUTF8($str, $config, $context) {
-        static $iconv = null;
-        if ($iconv === null) $iconv = function_exists('iconv');
         $encoding = $config->get('Core', 'Encoding');
         if ($encoding === 'utf-8') return $str;
-        if ($config->get('Core', 'EscapeNonASCIICharacters')) {
+        static $iconv = null;
+        if ($iconv === null) $iconv = function_exists('iconv');
+        if ($escape = $config->get('Core', 'EscapeNonASCIICharacters')) {
             $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
         }
+        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
         if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
-            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+            // Undo our previous fix in convertToUTF8, otherwise iconv will barf
+            $ascii_fix = HTMLPurifier_Encoder::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 = iconv('utf-8', $encoding . '//IGNORE', $str);
             restore_error_handler();
             return $str;
         } elseif ($encoding === 'iso-8859-1') {
-            set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
             $str = utf8_decode($str);
             restore_error_handler();
             return $str;
@@ -368,6 +367,47 @@ class HTMLPurifier_Encoder
         return $result;
     }
     
+    /**
+     * 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) {
+        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();
+        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+        if (iconv('UTF-8', $encoding, 'a') === false) return false;
+        for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
+            $c = chr($i);
+            if (iconv('UTF-8', "$encoding//IGNORE", $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[iconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
+            }
+        }
+        restore_error_handler();
+        $encodings[$encoding] = $ret;
+        return $ret;
+    }
+    
     
 }
 
index e35ce8cf355c9b852b11effdb977c38435461594..42ea17fe6bc994ec9f6753bda2ffb7ee0c599ff0 100644 (file)
@@ -35,8 +35,7 @@ class HTMLPurifier_Generator
      * @param $config Instance of HTMLPurifier_Config
      * @param $context Instance of HTMLPurifier_Context
      */
-    public function __construct($config = null, $context = null) {
-        if (!$config) $config = HTMLPurifier_Config::createDefault();
+    public function __construct($config, $context) {
         $this->config = $config;
         $this->_scriptFix = $config->get('Output', 'CommentScriptContents');
         $this->_def = $config->getHTMLDefinition();
index ce299f79cee8f0e1f5279205eb942461fbef9f84..e647228c54815661b8b62c63e0b8503bb86a2dc8 100644 (file)
@@ -76,6 +76,11 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
      */
     public $info_content_sets = array();
     
+    /**
+     * Indexed list of HTMLPurifier_Injector to be used.
+     */
+    public $info_injector = array();
+    
     /**
      * Doctype object
      */
@@ -186,18 +191,22 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
         $this->doctype = $this->manager->doctype;
         
         foreach ($this->manager->modules as $module) {
-            foreach($module->info_tag_transform         as $k => $v) {
+            foreach($module->info_tag_transform as $k => $v) {
                 if ($v === false) unset($this->info_tag_transform[$k]);
                 else $this->info_tag_transform[$k] = $v;
             }
-            foreach($module->info_attr_transform_pre    as $k => $v) {
+            foreach($module->info_attr_transform_pre as $k => $v) {
                 if ($v === false) unset($this->info_attr_transform_pre[$k]);
                 else $this->info_attr_transform_pre[$k] = $v;
             }
-            foreach($module->info_attr_transform_post   as $k => $v) {
+            foreach($module->info_attr_transform_post as $k => $v) {
                 if ($v === false) unset($this->info_attr_transform_post[$k]);
                 else $this->info_attr_transform_post[$k] = $v;
             }
+            foreach ($module->info_injector as $k => $v) {
+                if ($v === false) unset($this->info_injector[$k]);
+                else $this->info_injector[$k] = $v;
+            }
         }
         
         $this->info = $this->manager->getElements();
@@ -356,6 +365,14 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
             }
         }
         
+        // setup injectors -----------------------------------------------------
+        foreach ($this->info_injector as $i => $injector) {
+            if ($injector->checkNeeded($config) !== false) {
+                // remove injector that does not have it's required
+                // elements/attributes present, and is thus not needed.
+                unset($this->info_injector[$i]);
+            }
+        }
     }
     
     /**
index db78983b2b1bdda4ad76a526b865eae6505f78f8..22af15469018edf22dea6172384d632dbaa9ef0e 100644 (file)
@@ -71,6 +71,14 @@ class HTMLPurifier_HTMLModule
      */
     public $info_attr_transform_post = array();
     
+    /**
+     * List of HTMLPurifier_Injector to be performed during well-formedness fixing.
+     * An injector will only be invoked if all of it's pre-requisites are met;
+     * if an injector fails setup, there will be no error; it will simply be
+     * silently disabled.
+     */
+    public $info_injector = array();
+    
     /**
      * Boolean flag that indicates whether or not getChildDef is implemented.
      * For optimization reasons: may save a call to a function. Be sure
@@ -222,5 +230,14 @@ class HTMLPurifier_HTMLModule
         }
         return $ret;
     }
+    
+    /**
+     * Lazy load construction of the module after determining whether
+     * or not it's needed, and also when a finalized configuration object
+     * is available.
+     * @param $config Instance of HTMLPurifier_Config
+     */
+    public function setup($config) {}
+    
 }
 
index b65057cabc4ed4edef5df91f01fcde94039b653e..0d643e8172f878bd83effc3e1746172fd30d8e3a 100644 (file)
@@ -12,7 +12,7 @@ class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
         'I18N' => array('dir' => false)
     );
     
-    public function __construct() {
+    public function setup($config) {
         $bdo = $this->addElement(
             'bdo', 'Inline', 'Inline', array('Core', 'Lang'),
             array(
index 07d972e3bbe958537823efb922acf20ddac2f962..157fca16d7ca7454640ac6f20bf3a4c9012059a4 100644 (file)
@@ -9,7 +9,7 @@ class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
     
     public $name = 'Edit';
     
-    public function __construct() {
+    public function setup($config) {
         $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
         $attr = array(
             'cite' => 'URI',
index 4a347e6f4cf72a11ecb0310ec9e39d246a89e028..4451f2bbc7f7c910b1c408198d8042f49aecf8b3 100644 (file)
@@ -8,7 +8,7 @@ class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
     
     public $name = 'Hypertext';
     
-    public function __construct() {
+    public function setup($config) {
         $a = $this->addElement(
             'a', 'Inline', 'Inline', 'Common',
             array(
index 7d2a98c3f82cf1591adfac80016bc4588337851d..7a80e3271627ad18300a154a6d1ad3b05804cc66 100644 (file)
@@ -10,17 +10,25 @@ class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
     
     public $name = 'Image';
     
-    public function __construct() {
+    public function setup($config) {
+        $max = $config->get('HTML', 'MaxImgLength');
         $img = $this->addElement(
             'img', 'Inline', 'Empty', 'Common',
             array(
                 'alt*' => 'Text',
-                'height' => 'Length',
+                // According to the spec, it's Length, but percents can
+                // be abused, so we allow only Pixels.
+                'height' => 'Pixels#' . $max,
+                'width'  => 'Pixels#' . $max,
                 'longdesc' => 'URI', 
                 'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
-                'width' => 'Length'
             )
         );
+        if ($max === null || $config->get('HTML', 'Trusted')) {
+            $img->attr['height'] =
+            $img->attr['width'] = 'Length';
+        }
+        
         // kind of strange, but splitting things up would be inefficient
         $img->attr_transform_pre[] =
         $img->attr_transform_post[] =
index 903273ea4ce7afa80fb47a0116b9971c0ca021a6..dd1f04e000e58609ed35433b59c7f72edd2afa38 100644 (file)
@@ -21,7 +21,7 @@ class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
     
     public $name = 'Legacy';
     
-    public function __construct() {
+    public function setup($config) {
         
         $this->addElement('basefont', 'Inline', 'Empty', false, array(
             'color' => 'Color',
index 7d4f9cf2ef786483895d135f634eaded5f2daf7e..a2cf2f56002ad831e3b58afaa37b9df98d6ab5d2 100644 (file)
@@ -19,7 +19,7 @@ class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule
     
     public $content_sets = array('Flow' => 'List');
     
-    public function __construct() {
+    public function setup($config) {
         $this->addElement('ol', 'List', 'Required: li', 'Common');
         $this->addElement('ul', 'List', 'Required: li', 'Common');
         $this->addElement('dl', 'List', 'Required: dt | dd', 'Common');
index 609d3351038ea090ca9cb669915c6e6afebebe20..7dfd6b3134193dad68e37c9937a08e01956e902d 100644 (file)
@@ -11,7 +11,7 @@ class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule
     public $name = 'Object';\r
     public $safe = false;\r
     \r
-    public function __construct() {\r
+    public function setup($config) {\r
         \r
         $this->addElement('object', 'Inline', 'Optional: #PCDATA | Flow | param', 'Common', \r
             array(\r
index bd8368a8f80e8ed0d72481ee046fbfbbd4f15bcf..857b3abf01b59c07a63ebc3d446adf9db34c0115 100644 (file)
@@ -15,7 +15,7 @@ class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule
     
     public $name = 'Presentation';
     
-    public function __construct() {
+    public function setup($config) {
         $this->addElement('b',      'Inline', 'Inline', 'Common');
         $this->addElement('big',    'Inline', 'Inline', 'Common');
         $this->addElement('hr',     'Block',  'Empty',  'Common');
index d5c7241873528587739ec8260df961979ae41d01..bcbcd124ff018e547f2446b5e7fbf12c9076c906 100644 (file)
@@ -9,7 +9,7 @@ class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule
     
     public $name = 'Proprietary';
     
-    public function __construct() {
+    public function setup($config) {
         
         $this->addElement('marquee', 'Inline', 'Flow', 'Common', 
             array(
index 21ec79a261209dcd29347229d32a5cc53bf8b5d4..caee035fcf912c901da45ea087305bb4014b344b 100644 (file)
@@ -9,7 +9,7 @@ class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule
     
     public $name = 'Ruby';
     
-    public function __construct() {
+    public function setup($config) {
         $this->addElement('ruby', 'Inline',
             'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))',
             'Common');
diff --git a/lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeEmbed.php b/lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeEmbed.php
new file mode 100644 (file)
index 0000000..8e57752
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * A "safe" embed module. See SafeObject. This is a proprietary element.
+ */
+class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'SafeEmbed';
+    
+    public function setup($config) {
+        
+        $max = $config->get('HTML', 'MaxImgLength');
+        $embed = $this->addElement(
+            'embed', 'Inline', 'Empty', 'Common',
+            array(
+                'src*' => 'URI#embedded',
+                'type' => 'Enum#application/x-shockwave-flash',
+                'width' => 'Pixels#' . $max,
+                'height' => 'Pixels#' . $max,
+                'allowscriptaccess' => 'Enum#never',
+                'allownetworking' => 'Enum#internal',
+                'wmode' => 'Enum#window',
+                'name' => 'ID',
+            )
+        );
+        $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed();
+        
+    }
+    
+}
diff --git a/lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeObject.php b/lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeObject.php
new file mode 100644 (file)
index 0000000..a33be7d
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * A "safe" object module. In theory, objects permitted by this module will
+ * be safe, and untrusted users can be allowed to embed arbitrary flash objects
+ * (maybe other types too, but only Flash is supported as of right now).
+ * Highly experimental.
+ */
+class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule
+{
+    
+    public $name = 'SafeObject';
+    
+    public function setup($config) {
+        
+        // These definitions are not intrinsically safe: the attribute transforms
+        // are a vital part of ensuring safety.
+        
+        $max = $config->get('HTML', 'MaxImgLength');
+        $object = $this->addElement(
+            'object',
+            'Inline',
+            'Optional: param | Flow | #PCDATA',
+            'Common',
+            array(
+                // While technically not required by the spec, we're forcing
+                // it to this value.
+                'type'   => 'Enum#application/x-shockwave-flash',
+                'width'  => 'Pixels#' . $max,
+                'height' => 'Pixels#' . $max,
+                'data'   => 'URI#embedded'
+            )
+        );
+        $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject();
+
+        $param = $this->addElement('param', false, 'Empty', false,
+            array(
+                'id' => 'ID',
+                'name*' => 'Text', 
+                'value' => 'Text'
+            )
+        );
+        $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam();
+        $this->info_injector[] = 'SafeObject';
+    
+    }
+    
+}
index b3371b3a2bdd9bf58e9b70b7910e0b9bfddf30da..2b7e2ced49b825fc3fcc763caa801a2865c51334 100644 (file)
@@ -20,7 +20,7 @@ class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule
     public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');
     public $safe = false;
     
-    public function __construct() {
+    public function setup($config) {
         // TODO: create custom child-definition for noscript that
         // auto-wraps stray #PCDATA in a similar manner to 
         // blockquote's custom definition (we would use it but
index 3963e22c02390c37a2409b87f2bb8410b5d28c3b..9775536dccb2b497a99853c7b81c6232ed2de131 100644 (file)
@@ -15,7 +15,7 @@ class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule
         'Core' => array(0 => array('Style'))
     );
     
-    public function __construct() {
+    public function setup($config) {
         $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
     }
     
index d6a13fff33f10c193e479f7d9549c755b0737464..dd70bddfc9969e981719faf0c2bf889ebe51d97b 100644 (file)
@@ -8,7 +8,7 @@ class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
     
     public $name = 'Tables';
     
-    public function __construct() {
+    public function setup($config) {
         
         $this->addElement('caption', false, 'Inline', 'Common');
         
index 18203d046e534b054f7196283dcb82a9a3e31603..042d458c5886cfac133c224e06c2e05c8d3bcad3 100644 (file)
@@ -8,7 +8,7 @@ class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule
     
     public $name = 'Target';
     
-    public function __construct() {
+    public function setup($config) {
         $elements = array('a');
         foreach ($elements as $name) {
             $e = $this->addBlankElement($name);
index aff71e9ad289f9882d7292382d2792a6fb14d2f6..3fce4ee53faf88d9001d739a5291cabc89962672 100644 (file)
@@ -20,7 +20,7 @@ class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
         'Flow' => 'Heading | Block | Inline'
     );
     
-    public function __construct() {
+    public function setup($config) {
         
         // Inline Phrasal -------------------------------------------------
         $this->addElement('abbr',    'Inline', 'Inline', 'Common');
index 1606ffc676cafc9b11d60e1f8eb1e0a710a4dd10..a95d453b407a394dd1534c0ffd9da81af49a39b5 100644 (file)
@@ -35,7 +35,7 @@ class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
      * @todo Wildcard matching and error reporting when an added or
      *       subtracted fix has no effect.
      */
-    public function construct($config) {
+    public function setup($config) {
         
         // create fixes, initialize fixesForLevel
         $fixes = $this->makeFixes();
index 612c3c013934f0eca245c52933514dfd2b682bf8..3664e88fcadd2888bd9538d8112fbf7ce2bf8fe4 100644 (file)
@@ -221,15 +221,35 @@ class HTMLPurifier_HTMLModuleManager
             $modules[] = 'Proprietary';
         }
         
+        // add SafeObject/Safeembed modules
+        if ($config->get('HTML', 'SafeObject')) {
+            $modules[] = 'SafeObject';
+        }
+        if ($config->get('HTML', 'SafeEmbed')) {
+            $modules[] = 'SafeEmbed';
+        }
+        
         foreach ($modules as $module) {
             $this->processModule($module);
+            $this->modules[$module]->setup($config);
         }
         
         foreach ($this->doctype->tidyModules as $module) {
             $this->processModule($module);
-            if (method_exists($this->modules[$module], 'construct')) {
-                $this->modules[$module]->construct($config);
+            $this->modules[$module]->setup($config);
+        }
+        
+        // prepare any injectors
+        foreach ($this->modules as $module) {
+            $n = array();
+            foreach ($module->info_injector as $i => $injector) {
+                if (!is_object($injector)) {
+                    $class = "HTMLPurifier_Injector_$injector";
+                    $injector = new $class;
+                }
+                $n[$injector->name] = $injector;
             }
+            $module->info_injector = $n;
         }
         
         // setup lookup table based on all valid modules
index c9f9f2eb55af2aa1edf7b2093bf1fc5e7d525e84..1947c340fe80a89bfabc8d2a0077add28e190694 100644 (file)
@@ -58,26 +58,42 @@ abstract class HTMLPurifier_Injector
      * Prepares the injector by giving it the config and context objects:
      * this allows references to important variables to be made within
      * the injector. This function also checks if the HTML environment
-     * will work with the Injector: if p tags are not allowed, the
-     * Auto-Paragraphing injector should not be enabled.
+     * will work with the Injector (see checkNeeded()).
      * @param $config Instance of HTMLPurifier_Config
      * @param $context Instance of HTMLPurifier_Context
      * @return Boolean false if success, string of missing needed element/attribute if failure
      */
     public function prepare($config, $context) {
         $this->htmlDefinition = $config->getHTMLDefinition();
-        // perform $needed checks
+        // Even though this might fail, some unit tests ignore this and
+        // still test checkNeeded, so be careful. Maybe get rid of that
+        // dependency.
+        $result = $this->checkNeeded($config);
+        if ($result !== false) return $result;
+        $this->currentNesting =& $context->get('CurrentNesting');
+        $this->inputTokens    =& $context->get('InputTokens');
+        $this->inputIndex     =& $context->get('InputIndex');
+        return false;
+    }
+    
+    /**
+     * This function checks if the HTML environment
+     * will work with the Injector: if p tags are not allowed, the
+     * Auto-Paragraphing injector should not be enabled.
+     * @param $config Instance of HTMLPurifier_Config
+     * @param $context Instance of HTMLPurifier_Context
+     * @return Boolean false if success, string of missing needed element/attribute if failure
+     */
+    public function checkNeeded($config) {
+        $def = $config->getHTMLDefinition();
         foreach ($this->needed as $element => $attributes) {
             if (is_int($element)) $element = $attributes;
-            if (!isset($this->htmlDefinition->info[$element])) return $element;
+            if (!isset($def->info[$element])) return $element;
             if (!is_array($attributes)) continue;
             foreach ($attributes as $name) {
-                if (!isset($this->htmlDefinition->info[$element]->attr[$name])) return "$element.$name";
+                if (!isset($def->info[$element]->attr[$name])) return "$element.$name";
             }
         }
-        $this->currentNesting =& $context->get('CurrentNesting');
-        $this->inputTokens    =& $context->get('InputTokens');
-        $this->inputIndex     =& $context->get('InputIndex');
         return false;
     }
     
diff --git a/lib/htmlpurifier/HTMLPurifier/Injector/SafeObject.php b/lib/htmlpurifier/HTMLPurifier/Injector/SafeObject.php
new file mode 100644 (file)
index 0000000..f379406
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * Adds important param elements to inside of object in order to make
+ * things safe.
+ */
+class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
+{
+    public $name = 'SafeObject';
+    public $needed = array('object', 'param');
+    
+    protected $objectStack = array();
+    protected $paramStack  = array();
+    
+    // Keep this synchronized with AttrTransform/SafeParam.php
+    protected $addParam = array(
+        'allowScriptAccess' => 'never',
+        'allowNetworking' => 'internal',
+    );
+    protected $allowedParam = array(
+        'wmode' => true,
+        'movie' => true,
+    );
+    
+    public function prepare($config, $context) {
+        parent::prepare($config, $context);
+    }
+    
+    public function handleElement(&$token) {
+        if ($token->name == 'object') {
+            $this->objectStack[] = $token;
+            $this->paramStack[] = array();
+            $new = array($token);
+            foreach ($this->addParam as $name => $value) {
+                $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
+            }
+            $token = $new;
+        } elseif ($token->name == 'param') {
+            $nest = count($this->currentNesting) - 1;
+            if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
+                $i = count($this->objectStack) - 1;
+                if (!isset($token->attr['name'])) {
+                    $token = false;
+                    return;
+                }
+                $n = $token->attr['name'];
+                // We need this fix because YouTube doesn't supply a data
+                // attribute, which we need if a type is specified. This is
+                // *very* Flash specific.
+                if (!isset($this->objectStack[$i]->attr['data']) && $token->attr['name'] == 'movie') {
+                    $this->objectStack[$i]->attr['data'] = $token->attr['value'];
+                }
+                // Check if the parameter is the correct value but has not
+                // already been added
+                if (
+                    !isset($this->paramStack[$i][$n]) &&
+                    isset($this->addParam[$n]) &&
+                    $token->attr['name'] === $this->addParam[$n]
+                ) {
+                    // keep token, and add to param stack
+                    $this->paramStack[$i][$n] = true;
+                } elseif (isset($this->allowedParam[$n])) {
+                    // keep token, don't do anything to it
+                    // (could possibly check for duplicates here)
+                } else {
+                    $token = false;
+                }
+            } else {
+                // not directly inside an object, DENY!
+                $token = false;
+            }
+        }
+    }
+    
+    public function notifyEnd($token) {
+        if ($token->name == 'object') {
+            array_pop($this->objectStack);
+            array_pop($this->paramStack);
+        }
+    }
+    
+}
+
diff --git a/lib/htmlpurifier/HTMLPurifier/Length.php b/lib/htmlpurifier/HTMLPurifier/Length.php
new file mode 100644 (file)
index 0000000..758f9bb
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Represents a measurable length, with a string numeric magnitude
+ * and a unit. This object is immutable.
+ */
+class HTMLPurifier_Length
+{
+    
+    /**
+     * String numeric magnitude.
+     */
+    protected $n;
+    
+    /**
+     * String unit. False is permitted if $n = 0.
+     */
+    protected $unit;
+    
+    /**
+     * Whether or not this length is valid. Null if not calculated yet.
+     */
+    protected $isValid;
+    
+    /**
+     * Lookup array of units recognized by CSS 2.1
+     */
+    protected static $allowedUnits = array(
+        'em' => true, 'ex' => true, 'px' => true, 'in' => true,
+        'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true
+    );
+    
+    /**
+     * @param number $n Magnitude
+     * @param 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'
+     * @warning Does not perform validation.
+     */
+    static public 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.
+     */
+    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 (!ctype_lower($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.
+     */
+    public function toString() {
+        if (!$this->isValid()) return false;
+        return $this->n . $this->unit;
+    }
+    
+    /**
+     * Retrieves string numeric magnitude.
+     */
+    public function getN() {return $this->n;}
+    
+    /**
+     * Retrieves string unit.
+     */
+    public function getUnit() {return $this->unit;}
+    
+    /**
+     * Returns true if this length unit is valid.
+     */
+    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.
+     * @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;
+    }
+    
+}
index 680ffa1d1a54ca2bf714921c51dcf16194e5186a..238cf951d8ab43eb74e60a1f5cb6ce23e8ae60b2 100644 (file)
@@ -20,18 +20,15 @@ class HTMLPurifier_Printer
      * Initialize $generator.
      */
     public function __construct() {
-        $this->generator = new HTMLPurifier_Generator();
     }
     
     /**
      * Give generator necessary configuration if possible
      */
     public function prepareGenerator($config) {
-        // hack for smoketests/configForm.php
         $all = $config->getAll();
-        if (empty($all['HTML'])) return;
         $context = new HTMLPurifier_Context();
-        $this->generator->generateFromTokens(array(), $config, $context);
+        $this->generator = new HTMLPurifier_Generator($config, $context);
     }
     
     /**
index 172d7bb48ed9c3cb67c9c1a0e8db83246ffb9f9c..9caf7e39a6ba76cb87e750304e0073ea7d137c3d 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+/**
+ * @todo Rewrite to use Interchange objects
+ */
 class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
 {
     
@@ -38,8 +41,8 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
         $this->name   = $name;
         $this->compress = $compress;
         // initialize sub-printers
-        $this->fields['default']    = new HTMLPurifier_Printer_ConfigForm_default();
-        $this->fields['bool']       = new HTMLPurifier_Printer_ConfigForm_bool();
+        $this->fields[0]    = new HTMLPurifier_Printer_ConfigForm_default();
+        $this->fields[HTMLPurifier_VarParser::BOOL]       = new HTMLPurifier_Printer_ConfigForm_bool();
     }
     
     /**
@@ -68,14 +71,23 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
     
     /**
      * Returns HTML output for a configuration form
-     * @param $config Configuration object of current form state
+     * @param $config Configuration object of current form state, or an array
+     *        where [0] has an HTML namespace and [1] is being rendered.
      * @param $allowed Optional namespace(s) and directives to restrict form to.
      */
     public function render($config, $allowed = true, $render_controls = true) {
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        
         $this->config = $config;
-        $this->prepareGenerator($config);
+        $this->genConfig = $gen_config;
+        $this->prepareGenerator($gen_config);
         
-        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed);
+        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
         $all = array();
         foreach ($allowed as $key) {
             list($ns, $directive) = $key;
@@ -148,13 +160,19 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
             
             $ret .= $this->start('td');
                 $def = $this->config->def->info[$ns][$directive];
-                $type = $def->type;
-                if (!isset($this->fields[$type])) $type = 'default';
+                if (is_int($def)) {
+                    $allow_null = $def < 0;
+                    $type = abs($def);
+                } else {
+                    $type = $def->type;
+                    $allow_null = isset($def->allow_null);
+                }
+                if (!isset($this->fields[$type])) $type = 0; // default
                 $type_obj = $this->fields[$type];
-                if ($def->allow_null) {
+                if ($allow_null) {
                     $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
                 }
-                $ret .= $type_obj->render($ns, $directive, $value, $this->name, $this->config);
+                $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
             $ret .= $this->end('td');
             $ret .= $this->end('tr');
         }
@@ -180,7 +198,14 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
         $this->obj = $obj;
     }
     public function render($ns, $directive, $value, $name, $config) {
-        $this->prepareGenerator($config);
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        $this->prepareGenerator($gen_config);
+        
         $ret = '';
         $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
         $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
@@ -202,7 +227,7 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
         $ret .= $this->elementEmpty('input', $attr);
         $ret .= $this->text(' or ');
         $ret .= $this->elementEmpty('br');
-        $ret .= $this->obj->render($ns, $directive, $value, $name, $config);
+        $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
         return $ret;
     }
 }
@@ -214,22 +239,33 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
     public $cols = 18;
     public $rows = 5;
     public function render($ns, $directive, $value, $name, $config) {
-        $this->prepareGenerator($config);
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        $this->prepareGenerator($gen_config);
         // this should probably be split up a little
         $ret = '';
         $def = $config->def->info[$ns][$directive];
+        if (is_int($def)) {
+            $type = abs($def);
+        } else {
+            $type = $def->type;
+        }
         if (is_array($value)) {
-            switch ($def->type) {
-                case 'lookup':
+            switch ($type) {
+                case HTMLPurifier_VarParser::LOOKUP:
                     $array = $value;
                     $value = array();
                     foreach ($array as $val => $b) {
                         $value[] = $val;
                     }
-                case 'list':
+                case HTMLPurifier_VarParser::ALIST:
                     $value = implode(PHP_EOL, $value);
                     break;
-                case 'hash':
+                case HTMLPurifier_VarParser::HASH:
                     $nvalue = '';
                     foreach ($value as $i => $v) {
                         $nvalue .= "$i:$v" . PHP_EOL;
@@ -240,7 +276,7 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
                     $value = '';
             }
         }
-        if ($def->type === 'mixed') {
+        if ($type === HTMLPurifier_VarParser::MIXED) {
             return 'Not supported';
             $value = serialize($value);
         }
@@ -249,7 +285,7 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
             'id' => "$name:$ns.$directive"
         );
         if ($value === null) $attr['disabled'] = 'disabled';
-        if (is_array($def->allowed)) {
+        if (isset($def->allowed)) {
             $ret .= $this->start('select', $attr);
             foreach ($def->allowed as $val => $b) {
                 $attr = array();
@@ -258,8 +294,11 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
             }
             $ret .= $this->end('select');
         } elseif (
-            $def->type == 'text' || $def->type == 'itext' ||
-            $def->type == 'list' || $def->type == 'hash' || $def->type == 'lookup'
+            $type === HTMLPurifier_VarParser::TEXT ||
+            $type === HTMLPurifier_VarParser::ITEXT ||
+            $type === HTMLPurifier_VarParser::ALIST ||
+            $type === HTMLPurifier_VarParser::HASH ||
+            $type === HTMLPurifier_VarParser::LOOKUP
         ) {
             $attr['cols'] = $this->cols;
             $attr['rows'] = $this->rows;
@@ -280,7 +319,13 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
  */
 class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
     public function render($ns, $directive, $value, $name, $config) {
-        $this->prepareGenerator($config);
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        $this->prepareGenerator($gen_config);
         $ret = '';
         $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
         
index 8843627f13d877e77306f9dea6b827e1ca5ecb42..1ca6271158477cf64b132105804e6e4aa4bd4a6e 100644 (file)
@@ -38,6 +38,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
         $this->injectors = array();
         
         $injectors = $config->getBatch('AutoFormat');
+        $def_injectors = $definition->info_injector;
         $custom_injectors = $injectors['Custom'];
         unset($injectors['Custom']); // special case
         foreach ($injectors as $injector => $b) {
@@ -45,6 +46,10 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
             if (!$b) continue;
             $this->injectors[] = new $injector;
         }
+        foreach ($def_injectors as $injector) {
+            // assumed to be objects
+            $this->injectors[] = $injector;
+        }
         foreach ($custom_injectors as $injector) {
             if (is_string($injector)) {
                 $injector = "HTMLPurifier_Injector_$injector";
@@ -169,7 +174,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
                 if ($escape_invalid_tags) {
                     if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
                     $result[] = new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token, $config, $context)
+                        $generator->generateFromToken($token)
                     );
                 } elseif ($e) {
                     $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
@@ -209,7 +214,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
             if ($skipped_tags === false) {
                 if ($escape_invalid_tags) {
                     $result[] = new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token, $config, $context)
+                        $generator->generateFromToken($token)
                     );
                     if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
                 } elseif ($e) {
index 9de69a1185426f3cdac3e4c4affdcf85036a2a43..165308dd0cfaee537db8c2edb40db9f97a1fef17 100644 (file)
@@ -101,7 +101,7 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
                     // invalid tag, generate HTML representation and insert in
                     if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
                     $token = new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token, $config, $context)
+                        $generator->generateFromToken($token)
                     );
                 } else {
                     // check if we need to destroy all of the tag's children
index fd2ba53f259434ab2e51d75915b1b0e21868b674..8803307bda9eef68435cdcd8fc6ab36476d05706 100644 (file)
@@ -4,7 +4,6 @@
  * Abstract base token class that all others inherit from.
  */
 class HTMLPurifier_Token {
-    public $type; /**< Type of node to bypass <tt>is_a()</tt>. */
     public $line; /**< Line number node was on in source document. Null if unknown. */
     
     /**
index 43f1b1928752832f211ef72ba15591914e938505..cab09bfbafb5b414f0cdb2fd3739f95e3226a58b 100644 (file)
@@ -128,6 +128,17 @@ class HTMLPurifier_URI
             $this->path = ''; // just to be safe
         }
         
+        // qf = query and fragment
+        $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
+        
+        if (!is_null($this->query)) {
+            $this->query = $qf_encoder->encode($this->query);
+        }
+        
+        if (!is_null($this->fragment)) {
+            $this->fragment = $qf_encoder->encode($this->fragment);
+        }
+        
         return true;
         
     }
index 390d97ec157f108b62fbf48429df9665476d63b1..6977338ae10c5ee4d22f64c33bfcbaa07b965368 100644 (file)
@@ -5,6 +5,7 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
     
     public $type = 'URI';
     protected $filters = array();
+    protected $postFilters = array();
     protected $registeredFilters = array();
     
     /**
@@ -27,6 +28,7 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
         $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
         $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
         $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
+        $this->registerFilter(new HTMLPurifier_URIFilter_Munge());
     }
     
     public function registerFilter($filter) {
@@ -34,8 +36,13 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
     }
     
     public function addFilter($filter, $config) {
-        $filter->prepare($config);
-        $this->filters[$filter->name] = $filter;
+        $r = $filter->prepare($config);
+        if ($r === false) return; // null is ok, for backwards compat
+        if ($filter->post) {
+            $this->postFilters[$filter->name] = $filter;
+        } else {
+            $this->filters[$filter->name] = $filter;
+        }
     }
     
     protected function doSetup($config) {
@@ -66,8 +73,16 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
     }
     
     public function filter(&$uri, $config, $context) {
-        foreach ($this->filters as $name => $x) {
-            $result = $this->filters[$name]->filter($uri, $config, $context);
+        foreach ($this->filters as $name => $f) {
+            $result = $f->filter($uri, $config, $context);
+            if (!$result) return false;
+        }
+        return true;
+    }
+    
+    public function postFilter(&$uri, $config, $context) {
+        foreach ($this->postFilters as $name => $f) {
+            $result = $f->filter($uri, $config, $context);
             if (!$result) return false;
         }
         return true;
index 0108258f7e75924432cc61e9367e34e7b12995a7..6953ed43da5b386e69bb24003d1377ce3cadc5b0 100644 (file)
@@ -19,10 +19,15 @@ abstract class HTMLPurifier_URIFilter
      */
     public $name;
     
+    /**
+     * True if this filter should be run after scheme validation.
+     */
+    public $post = false;
+    
     /**
      * Performs initialization for the filter
      */
-    public function prepare($config) {}
+    public function prepare($config) {return true;}
     
     /**
      * Filter a URI object
index bd7197010347f07a69254682860981963c7dc89a..013204796229c0b6bc7dd7c230f530c95eb6e3e4 100644 (file)
@@ -6,6 +6,7 @@ class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
     protected $blacklist = array();
     public function prepare($config) {
         $this->blacklist = $config->get('URI', 'HostBlacklist');
+        return true;
     }
     public function filter(&$uri, $config, $context) {
         foreach($this->blacklist as $blacklisted_host_fragment) {
index 647f779e9ae8c61aa3155bcbc7874f6983019966..289db51ad3a1614e087ac25a5c5e823c08ee6b99 100644 (file)
@@ -11,14 +11,15 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
         $def = $config->getDefinition('URI');
         $this->base = $def->base;
         if (is_null($this->base)) {
-            trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_ERROR);
-            return;
+            trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING);
+            return false;
         }
         $this->base->fragment = null; // fragment is invalid for base URI
         $stack = explode('/', $this->base->path);
         array_pop($stack); // discard last segment
         $stack = $this->_collapseStack($stack); // do pre-parsing
         $this->basePathStack = $stack;
+        return true;
     }
     public function filter(&$uri, $config, $context) {
         if (is_null($this->base)) return true; // abort early
diff --git a/lib/htmlpurifier/HTMLPurifier/URIFilter/Munge.php b/lib/htmlpurifier/HTMLPurifier/URIFilter/Munge.php
new file mode 100644 (file)
index 0000000..ad6578e
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
+{
+    public $name = 'Munge';
+    public $post = true;
+    private $target, $parser, $doEmbed, $secretKey;
+    
+    protected $replace = array();
+    
+    public function prepare($config) {
+        $this->target    = $config->get('URI', $this->name);
+        $this->parser    = new HTMLPurifier_URIParser();
+        $this->doEmbed   = $config->get('URI', 'MungeResources');
+        $this->secretKey = $config->get('URI', 'MungeSecretKey');
+        return true;
+    }
+    public function filter(&$uri, $config, $context) {
+        if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true;
+        
+        $scheme_obj = $uri->getSchemeObj($config, $context);
+        if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
+        if (is_null($uri->host) || empty($scheme_obj->browsable)) {
+            return true;
+        }
+        
+        $this->makeReplace($uri, $config, $context);
+        $this->replace = array_map('rawurlencode', $this->replace);
+        
+        $new_uri = strtr($this->target, $this->replace);
+        $uri = $this->parser->parse($new_uri); // overwrite
+        return true;
+    }
+    
+    protected function makeReplace($uri, $config, $context) {
+        $string = $uri->toString();
+        // always available
+        $this->replace['%s'] = $string;
+        $this->replace['%r'] = $context->get('EmbeddedURI', true);
+        $token = $context->get('CurrentToken', true);
+        $this->replace['%n'] = $token ? $token->name : null;
+        $this->replace['%m'] = $context->get('CurrentAttr', true);
+        $this->replace['%p'] = $context->get('CurrentCSSProperty', true);
+        // not always available
+        if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string);
+    }
+    
+}
diff --git a/lib/htmlpurifier/HTMLPurifier/UnitConverter.php b/lib/htmlpurifier/HTMLPurifier/UnitConverter.php
new file mode 100644 (file)
index 0000000..6e30461
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * Class for converting between different unit-lengths as specified by
+ * CSS.
+ */
+class HTMLPurifier_UnitConverter
+{
+    
+    const ENGLISH = 1;
+    const METRIC = 2;
+    const DIGITAL = 3;
+    
+    /**
+     * Units information array. Units are grouped into measuring systems
+     * (English, Metric), and are assigned an integer representing
+     * the conversion factor between that unit and the smallest unit in
+     * the system. Numeric indexes are actually magical constants that
+     * encode conversion data from one system to the next, with a O(n^2)
+     * constraint on memory (this is generally not a problem, since
+     * the number of measuring systems is small.)
+     */
+    protected static $units = array(
+        self::ENGLISH => array(
+            'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary
+            'pt' => 4,
+            'pc' => 48,
+            'in' => 288,
+            self::METRIC => array('pt', '0.352777778', 'mm'),
+        ),
+        self::METRIC => array(
+            'mm' => 1,
+            'cm' => 10,
+            self::ENGLISH => array('mm', '2.83464567', 'pt'),
+        ),
+    );
+    
+    /**
+     * Minimum bcmath precision for output.
+     */
+    protected $outputPrecision;
+    
+    /**
+     * Bcmath precision for internal calculations.
+     */
+    protected $internalPrecision;
+    
+    /**
+     * Whether or not BCMath is available
+     */
+    private $bcmath;
+    
+    public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) {
+        $this->outputPrecision = $output_precision;
+        $this->internalPrecision = $internal_precision;
+        $this->bcmath = !$force_no_bcmath && function_exists('bcmul');
+    }
+    
+    /**
+     * Converts a length object of one unit into another unit.
+     * @param HTMLPurifier_Length $length
+     *      Instance of HTMLPurifier_Length to convert. You must validate()
+     *      it before passing it here!
+     * @param string $to_unit
+     *      Unit to convert to.
+     * @note
+     *      About precision: This conversion function pays very special
+     *      attention to the incoming precision of values and attempts
+     *      to maintain a number of significant figure. Results are
+     *      fairly accurate up to nine digits. Some caveats:
+     *          - If a number is zero-padded as a result of this significant
+     *            figure tracking, the zeroes will be eliminated.
+     *          - If a number contains less than four sigfigs ($outputPrecision)
+     *            and this causes some decimals to be excluded, those
+     *            decimals will be added on.
+     */
+    public function convert($length, $to_unit) {
+        
+        if (!$length->isValid()) return false;
+        
+        $n    = $length->getN();
+        $unit = $length->getUnit();
+        
+        if ($n === '0' || $unit === false) {
+            return new HTMLPurifier_Length('0', false);
+        }
+        
+        $state = $dest_state = false;
+        foreach (self::$units as $k => $x) {
+            if (isset($x[$unit])) $state = $k;
+            if (isset($x[$to_unit])) $dest_state = $k;
+        }
+        if (!$state || !$dest_state) return false;
+        
+        // Some calculations about the initial precision of the number;
+        // this will be useful when we need to do final rounding.
+        $sigfigs = $this->getSigFigs($n);
+        if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision;
+        
+        // BCMath's internal precision deals only with decimals. Use
+        // our default if the initial number has no decimals, or increase
+        // it by how ever many decimals, thus, the number of guard digits
+        // will always be greater than or equal to internalPrecision.
+        $log = (int) floor(log(abs($n), 10));
+        $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision
+        
+        for ($i = 0; $i < 2; $i++) {
+            
+            // Determine what unit IN THIS SYSTEM we need to convert to
+            if ($dest_state === $state) {
+                // Simple conversion
+                $dest_unit = $to_unit;
+            } else {
+                // Convert to the smallest unit, pending a system shift
+                $dest_unit = self::$units[$state][$dest_state][0];
+            }
+            
+            // Do the conversion if necessary
+            if ($dest_unit !== $unit) {
+                $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);
+                $n = $this->mul($n, $factor, $cp);
+                $unit = $dest_unit;
+            }
+            
+            // Output was zero, so bail out early. Shouldn't ever happen.
+            if ($n === '') {
+                $n = '0';
+                $unit = $to_unit;
+                break;
+            }
+            
+            // It was a simple conversion, so bail out
+            if ($dest_state === $state) {
+                break;
+            }
+            
+            if ($i !== 0) {
+                // Conversion failed! Apparently, the system we forwarded
+                // to didn't have this unit. This should never happen!
+                return false;
+            }
+            
+            // Pre-condition: $i == 0
+            
+            // Perform conversion to next system of units
+            $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp);
+            $unit = self::$units[$state][$dest_state][2];
+            $state = $dest_state;
+            
+            // One more loop around to convert the unit in the new system.
+            
+        }
+        
+        // Post-condition: $unit == $to_unit
+        if ($unit !== $to_unit) return false;
+        
+        // Useful for debugging:
+        //echo "<pre>n";
+        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n";
+        
+        $n = $this->round($n, $sigfigs);
+        if (strpos($n, '.') !== false) $n = rtrim($n, '0');
+        $n = rtrim($n, '.');
+        
+        return new HTMLPurifier_Length($n, $unit);
+    }
+    
+    /**
+     * Returns the number of significant figures in a string number.
+     * @param string $n Decimal number
+     * @return int number of sigfigs
+     */
+    public function getSigFigs($n) {
+        $n = ltrim($n, '0+-');
+        $dp = strpos($n, '.'); // decimal position
+        if ($dp === false) {
+            $sigfigs = strlen(rtrim($n, '0'));
+        } else {
+            $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character
+            if ($dp !== 0) $sigfigs--;
+        }
+        return $sigfigs;
+    }
+    
+    /**
+     * Adds two numbers, using arbitrary precision when available.
+     */
+    private function add($s1, $s2, $scale) {
+        if ($this->bcmath) return bcadd($s1, $s2, $scale);
+        else return $this->scale($s1 + $s2, $scale);
+    }
+    
+    /**
+     * Multiples two numbers, using arbitrary precision when available.
+     */
+    private function mul($s1, $s2, $scale) {
+        if ($this->bcmath) return bcmul($s1, $s2, $scale);
+        else return $this->scale($s1 * $s2, $scale);
+    }
+    
+    /**
+     * Divides two numbers, using arbitrary precision when available.
+     */
+    private function div($s1, $s2, $scale) {
+        if ($this->bcmath) return bcdiv($s1, $s2, $scale);
+        else return $this->scale($s1 / $s2, $scale);
+    }
+    
+    /**
+     * Rounds a number according to the number of sigfigs it should have,
+     * using arbitrary precision when available.
+     */
+    private function round($n, $sigfigs) {
+        $new_log = (int) floor(log(abs($n), 10)); // Number of digits left of decimal - 1
+        $rp = $sigfigs - $new_log - 1; // Number of decimal places needed
+        $neg = $n < 0 ? '-' : ''; // Negative sign
+        if ($this->bcmath) {
+            if ($rp >= 0) {
+                $n = bcadd($n, $neg . '0.' .  str_repeat('0', $rp) . '5', $rp + 1);
+                $n = bcdiv($n, '1', $rp);
+            } else {
+                // This algorithm partially depends on the standardized
+                // form of numbers that comes out of bcmath.
+                $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0);
+                $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1);
+            }
+            return $n;
+        } else {
+            return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1);
+        }
+    }
+    
+    /**
+     * Scales a float to $scale digits right of decimal point, like BCMath.
+     */
+    private function scale($r, $scale) {
+        return sprintf('%.' . $scale . 'f', (float) $r);
+    }
+    
+}
index 418622be556f60515fac1eef568fe366e4b0debf..f9dfd84a76d06fcdde6b97f421d432b7964766d4 100644 (file)
@@ -7,21 +7,34 @@
 class HTMLPurifier_VarParser
 {
     
+    const STRING    = 1;
+    const ISTRING   = 2;
+    const TEXT      = 3;
+    const ITEXT     = 4;
+    const INT       = 5;
+    const FLOAT     = 6;
+    const BOOL      = 7;
+    const LOOKUP    = 8;
+    const ALIST     = 9;
+    const HASH      = 10;
+    const MIXED     = 11;
+    
     /**
-     * Lookup table of allowed types.
+     * Lookup table of allowed types. Mainly for backwards compatibility, but
+     * also convenient for transforming string type names to the integer constants.
      */
     static public $types = array(
-        'string'    => true,
-        'istring'   => true,
-        'text'      => true,
-        'itext'     => true,
-        'int'       => true,
-        'float'     => true,
-        'bool'      => true,
-        'lookup'    => true,
-        'list'      => true,
-        'hash'      => true,
-        'mixed'     => true
+        'string'    => self::STRING,
+        'istring'   => self::ISTRING,
+        'text'      => self::TEXT,
+        'itext'     => self::ITEXT,
+        'int'       => self::INT,
+        'float'     => self::FLOAT,
+        'bool'      => self::BOOL,
+        'lookup'    => self::LOOKUP,
+        'list'      => self::ALIST,
+        'hash'      => self::HASH,
+        'mixed'     => self::MIXED
     );
     
     /**
@@ -29,10 +42,10 @@ class HTMLPurifier_VarParser
      * allowed value lists.
      */
     static public $stringTypes = array(
-        'string'    => true,
-        'istring'   => true,
-        'text'      => true,
-        'itext'     => true,
+        self::STRING    => true,
+        self::ISTRING   => true,
+        self::TEXT      => true,
+        self::ITEXT     => true,
     );
     
     /**
@@ -46,42 +59,46 @@ class HTMLPurifier_VarParser
      * @return Validated and type-coerced variable
      */
     final public function parse($var, $type, $allow_null = false) {
-        if (!isset(HTMLPurifier_VarParser::$types[$type])) {
-            throw new HTMLPurifier_VarParserException("Invalid type '$type'");
+        if (is_string($type)) {
+            if (!isset(HTMLPurifier_VarParser::$types[$type])) {
+                throw new HTMLPurifier_VarParserException("Invalid type '$type'");
+            } else {
+                $type = HTMLPurifier_VarParser::$types[$type];
+            }
         }
         $var = $this->parseImplementation($var, $type, $allow_null);
         if ($allow_null && $var === null) return null;
         // These are basic checks, to make sure nothing horribly wrong
         // happened in our implementations.
         switch ($type) {
-            case 'string':
-            case 'istring':
-            case 'text':
-            case 'itext':
+            case (self::STRING):
+            case (self::ISTRING):
+            case (self::TEXT):
+            case (self::ITEXT):
                 if (!is_string($var)) break;
-                if ($type[0] == 'i') $var = strtolower($var);
+                if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var);
                 return $var;
-            case 'int':
+            case (self::INT):
                 if (!is_int($var)) break;
                 return $var;
-            case 'float':
+            case (self::FLOAT):
                 if (!is_float($var)) break;
                 return $var;
-            case 'bool':
+            case (self::BOOL):
                 if (!is_bool($var)) break;
                 return $var;
-            case 'lookup':
-            case 'list':
-            case 'hash':
+            case (self::LOOKUP):
+            case (self::ALIST):
+            case (self::HASH):
                 if (!is_array($var)) break;
-                if ($type === 'lookup') {
+                if ($type === self::LOOKUP) {
                     foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true');
-                } elseif ($type === 'list') {
+                } elseif ($type === self::ALIST) {
                     $keys = array_keys($var);
                     if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform');
                 }
                 return $var;
-            case 'mixed':
+            case (self::MIXED):
                 return $var;
             default:
                 $this->errorInconsistent(get_class($this), $type);
@@ -111,7 +128,7 @@ class HTMLPurifier_VarParser
      *       updating subclasses.
      */
     protected function errorInconsistent($class, $type) {
-        throw new HTMLPurifier_Exception("Inconsistency in $class: $type not implemented");
+        throw new HTMLPurifier_Exception("Inconsistency in $class: ".HTMLPurifier_VarParser::getTypeName($type)." not implemented");
     }
     
     /**
@@ -119,7 +136,17 @@ class HTMLPurifier_VarParser
      */
     protected function errorGeneric($var, $type) {
         $vtype = gettype($var);
-        $this->error("Expected type $type, got $vtype");
+        $this->error("Expected type ".HTMLPurifier_VarParser::getTypeName($type).", got $vtype");
+    }
+    
+    static public function getTypeName($type) {
+        static $lookup;
+        if (!$lookup) {
+            // Lazy load the alternative lookup table
+            $lookup = array_flip(HTMLPurifier_VarParser::$types);
+        }
+        if (!isset($lookup[$type])) return 'unknown';
+        return $lookup[$type];
     }
     
 }
index a2041fdd34db60e2445c63d01d1633fa589a2180..ec2c90f4504a3b07d41840347ce2ce73e127865d 100644 (file)
@@ -14,19 +14,19 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
             // Note: if code "breaks" from the switch, it triggers a generic
             // exception to be thrown. Specific errors can be specifically
             // done here.
-            case 'mixed':
-            case 'istring':
-            case 'string':
-            case 'text':
-            case 'itext':
+            case self::MIXED :
+            case self::ISTRING :
+            case self::STRING :
+            case self::TEXT :
+            case self::ITEXT :
                 return $var;
-            case 'int':
+            case self::INT :
                 if (is_string($var) && ctype_digit($var)) $var = (int) $var;
                 return $var;
-            case 'float':
+            case self::FLOAT :
                 if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var;
                 return $var;
-            case 'bool':
+            case self::BOOL :
                 if (is_int($var) && ($var === 0 || $var === 1)) {
                     $var = (bool) $var;
                 } elseif (is_string($var)) {
@@ -39,9 +39,9 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
                     }
                 }
                 return $var;
-            case 'list':
-            case 'hash':
-            case 'lookup':
+            case self::ALIST :
+            case self::HASH :
+            case self::LOOKUP :
                 if (is_string($var)) {
                     // special case: technically, this is an array with
                     // a single empty string item, but having an empty
@@ -56,7 +56,7 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
                     }
                     // remove spaces
                     foreach ($var as $i => $j) $var[$i] = trim($j);
-                    if ($type === 'hash') {
+                    if ($type === self::HASH) {
                         // key:value,key2:value2
                         $nvar = array();
                         foreach ($var as $keypair) {
@@ -70,8 +70,8 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
                 if (!is_array($var)) break;
                 $keys = array_keys($var);
                 if ($keys === array_keys($keys)) {
-                    if ($type == 'list') return $var;
-                    elseif ($type == 'lookup') {
+                    if ($type == self::ALIST) return $var;
+                    elseif ($type == self::LOOKUP) {
                         $new = array();
                         foreach ($var as $key) {
                             $new[$key] = true;
@@ -79,7 +79,7 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
                         return $new;
                     } else break;
                 }
-                if ($type === 'lookup') {
+                if ($type === self::LOOKUP) {
                     foreach ($var as $key => $value) {
                         $var[$key] = true;
                     }
index b6bf56c38065787534a0b545b47d9b151849035f..99e67dbc4f0d29a34c076630008d35b35750085c 100644 (file)
@@ -1,12 +1,10 @@
-Description of HTML Purifier v3.1.0 library import into Moodle
+Description of HTML Purifier v3.1.1 library import into Moodle
 
 Changes:
  * HMLTModule/Text.php - added  <nolink>, <tex>, <lang> and <algebra> tags
  * HMLTModule/XMLCommonAttributes.php - remove xml:lang - needed for multilang
  * AttrDef/Lang.php - relax lang check - needed for multilang
 
- * temporary work dir fix from http://htmlpurifier.org/phorum/read.php?2,1809,1809#msg-1809
-
 skodak
 
 $Id$